Clean and organize project structure
Major reorganization: - Created scripts/ directory for all utility scripts - Created config/ directory for configuration files - Moved all test files to tests/ directory - Updated all script paths to work with new structure - Updated README.md with new project structure diagram New structure: ├── src/ # Source code (API + MCP) ├── scripts/ # Utility scripts (start-*.sh, docs_server.py, etc.) ├── tests/ # All test files and debug utilities ├── config/ # Configuration files (JSON, Caddy config) ├── docs/ # Documentation website └── logs/ # Log files All scripts updated to use relative paths from project root. Documentation updated with new folder structure. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,204 +1,169 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test suite for LangMem API
|
||||
Test the LangMem API endpoints
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import pytest
|
||||
import httpx
|
||||
import json
|
||||
from uuid import uuid4
|
||||
|
||||
# Configuration
|
||||
API_BASE_URL = "http://localhost:8765"
|
||||
API_KEY = "langmem_api_key_2025"
|
||||
TEST_USER_ID = f"test_user_{uuid4()}"
|
||||
|
||||
class TestLangMemAPI:
|
||||
"""Test suite for LangMem API endpoints"""
|
||||
async def test_api_endpoints():
|
||||
"""Test all API endpoints"""
|
||||
print("🧪 Testing LangMem API Endpoints")
|
||||
print("=" * 50)
|
||||
|
||||
def setup_method(self):
|
||||
"""Setup test client"""
|
||||
self.client = httpx.AsyncClient(base_url=API_BASE_URL)
|
||||
self.headers = {"Authorization": f"Bearer {API_KEY}"}
|
||||
self.test_user_id = f"test_user_{uuid4()}"
|
||||
headers = {"Authorization": f"Bearer {API_KEY}"}
|
||||
|
||||
async def teardown_method(self):
|
||||
"""Cleanup test client"""
|
||||
await self.client.aclose()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_root_endpoint(self):
|
||||
"""Test root endpoint"""
|
||||
response = await self.client.get("/")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["message"] == "LangMem API - Long-term Memory System"
|
||||
assert data["version"] == "1.0.0"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_health_check(self):
|
||||
"""Test health check endpoint"""
|
||||
response = await self.client.get("/health")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "status" in data
|
||||
assert "services" in data
|
||||
assert "timestamp" in data
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_store_memory(self):
|
||||
"""Test storing a memory"""
|
||||
memory_data = {
|
||||
"content": "This is a test memory about Python programming",
|
||||
"user_id": self.test_user_id,
|
||||
"session_id": "test_session_1",
|
||||
"metadata": {
|
||||
"category": "programming",
|
||||
"language": "python",
|
||||
"importance": "high"
|
||||
async with httpx.AsyncClient() as client:
|
||||
# Test 1: Root endpoint
|
||||
print("\n1. Testing root endpoint...")
|
||||
try:
|
||||
response = await client.get(f"{API_BASE_URL}/")
|
||||
print(f"✅ Root endpoint: {response.status_code}")
|
||||
print(f" Response: {response.json()}")
|
||||
except Exception as e:
|
||||
print(f"❌ Root endpoint failed: {e}")
|
||||
|
||||
# Test 2: Health check
|
||||
print("\n2. Testing health check...")
|
||||
try:
|
||||
response = await client.get(f"{API_BASE_URL}/health")
|
||||
print(f"✅ Health check: {response.status_code}")
|
||||
data = response.json()
|
||||
print(f" Overall status: {data.get('status')}")
|
||||
for service, status in data.get('services', {}).items():
|
||||
print(f" {service}: {status}")
|
||||
except Exception as e:
|
||||
print(f"❌ Health check failed: {e}")
|
||||
|
||||
# Test 3: Store memory
|
||||
print("\n3. Testing memory storage...")
|
||||
try:
|
||||
memory_data = {
|
||||
"content": "FastAPI is a modern web framework for building APIs with Python",
|
||||
"user_id": TEST_USER_ID,
|
||||
"session_id": "test_session_1",
|
||||
"metadata": {
|
||||
"category": "programming",
|
||||
"language": "python",
|
||||
"framework": "fastapi"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response = await client.post(
|
||||
f"{API_BASE_URL}/v1/memories/store",
|
||||
json=memory_data,
|
||||
headers=headers,
|
||||
timeout=30.0
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
memory_id = data["id"]
|
||||
print(f"✅ Memory stored successfully: {memory_id}")
|
||||
print(f" Status: {data['status']}")
|
||||
else:
|
||||
print(f"❌ Memory storage failed: {response.status_code}")
|
||||
print(f" Response: {response.text}")
|
||||
return
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Memory storage failed: {e}")
|
||||
return
|
||||
|
||||
response = await self.client.post(
|
||||
"/v1/memories/store",
|
||||
json=memory_data,
|
||||
headers=self.headers
|
||||
)
|
||||
# Test 4: Search memories
|
||||
print("\n4. Testing memory search...")
|
||||
try:
|
||||
search_data = {
|
||||
"query": "Python web framework",
|
||||
"user_id": TEST_USER_ID,
|
||||
"limit": 5,
|
||||
"threshold": 0.5
|
||||
}
|
||||
|
||||
response = await client.post(
|
||||
f"{API_BASE_URL}/v1/memories/search",
|
||||
json=search_data,
|
||||
headers=headers,
|
||||
timeout=30.0
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
print(f"✅ Memory search successful")
|
||||
print(f" Found {data['total_count']} memories")
|
||||
for memory in data['memories']:
|
||||
print(f" - {memory['content'][:50]}... (similarity: {memory['similarity']:.3f})")
|
||||
else:
|
||||
print(f"❌ Memory search failed: {response.status_code}")
|
||||
print(f" Response: {response.text}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Memory search failed: {e}")
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "stored"
|
||||
assert data["user_id"] == self.test_user_id
|
||||
assert "id" in data
|
||||
assert "created_at" in data
|
||||
# Test 5: Retrieve memories for conversation
|
||||
print("\n5. Testing memory retrieval...")
|
||||
try:
|
||||
retrieve_data = {
|
||||
"messages": [
|
||||
{"role": "user", "content": "I want to learn about web development"},
|
||||
{"role": "assistant", "content": "Great! What technology are you interested in?"},
|
||||
{"role": "user", "content": "I heard Python is good for web APIs"}
|
||||
],
|
||||
"user_id": TEST_USER_ID,
|
||||
"session_id": "test_session_1"
|
||||
}
|
||||
|
||||
response = await client.post(
|
||||
f"{API_BASE_URL}/v1/memories/retrieve",
|
||||
json=retrieve_data,
|
||||
headers=headers,
|
||||
timeout=30.0
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
print(f"✅ Memory retrieval successful")
|
||||
print(f" Retrieved {data['total_count']} relevant memories")
|
||||
for memory in data['memories']:
|
||||
print(f" - {memory['content'][:50]}... (similarity: {memory['similarity']:.3f})")
|
||||
else:
|
||||
print(f"❌ Memory retrieval failed: {response.status_code}")
|
||||
print(f" Response: {response.text}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Memory retrieval failed: {e}")
|
||||
|
||||
return data["id"]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_search_memories(self):
|
||||
"""Test searching memories"""
|
||||
# First store a memory
|
||||
memory_id = await self.test_store_memory()
|
||||
# Test 6: Get user memories
|
||||
print("\n6. Testing user memory listing...")
|
||||
try:
|
||||
response = await client.get(
|
||||
f"{API_BASE_URL}/v1/memories/users/{TEST_USER_ID}",
|
||||
headers=headers,
|
||||
timeout=30.0
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
print(f"✅ User memory listing successful")
|
||||
print(f" User has {data['total_count']} memories")
|
||||
for memory in data['memories']:
|
||||
print(f" - {memory['content'][:50]}... (created: {memory['created_at'][:19]})")
|
||||
else:
|
||||
print(f"❌ User memory listing failed: {response.status_code}")
|
||||
print(f" Response: {response.text}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ User memory listing failed: {e}")
|
||||
|
||||
# Wait a moment for indexing
|
||||
await asyncio.sleep(1)
|
||||
|
||||
# Search for the memory
|
||||
search_data = {
|
||||
"query": "Python programming",
|
||||
"user_id": self.test_user_id,
|
||||
"limit": 10,
|
||||
"threshold": 0.5,
|
||||
"include_graph": True
|
||||
}
|
||||
|
||||
response = await self.client.post(
|
||||
"/v1/memories/search",
|
||||
json=search_data,
|
||||
headers=self.headers
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "memories" in data
|
||||
assert "context" in data
|
||||
assert "total_count" in data
|
||||
assert data["total_count"] > 0
|
||||
|
||||
# Check first memory result
|
||||
if data["memories"]:
|
||||
memory = data["memories"][0]
|
||||
assert "id" in memory
|
||||
assert "content" in memory
|
||||
assert "similarity" in memory
|
||||
assert memory["user_id"] == self.test_user_id
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_retrieve_memories(self):
|
||||
"""Test retrieving memories for conversation context"""
|
||||
# Store a memory first
|
||||
await self.test_store_memory()
|
||||
|
||||
# Wait a moment for indexing
|
||||
await asyncio.sleep(1)
|
||||
|
||||
# Retrieve memories based on conversation
|
||||
retrieve_data = {
|
||||
"messages": [
|
||||
{"role": "user", "content": "I want to learn about Python"},
|
||||
{"role": "assistant", "content": "Python is a great programming language"},
|
||||
{"role": "user", "content": "Tell me more about Python programming"}
|
||||
],
|
||||
"user_id": self.test_user_id,
|
||||
"session_id": "test_session_1"
|
||||
}
|
||||
|
||||
response = await self.client.post(
|
||||
"/v1/memories/retrieve",
|
||||
json=retrieve_data,
|
||||
headers=self.headers
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "memories" in data
|
||||
assert "context" in data
|
||||
assert "total_count" in data
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_user_memories(self):
|
||||
"""Test getting all memories for a user"""
|
||||
# Store a memory first
|
||||
await self.test_store_memory()
|
||||
|
||||
response = await self.client.get(
|
||||
f"/v1/memories/users/{self.test_user_id}",
|
||||
headers=self.headers
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "memories" in data
|
||||
assert "total_count" in data
|
||||
assert data["total_count"] > 0
|
||||
|
||||
# Check memory structure
|
||||
if data["memories"]:
|
||||
memory = data["memories"][0]
|
||||
assert "id" in memory
|
||||
assert "content" in memory
|
||||
assert "user_id" in memory
|
||||
assert "created_at" in memory
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_memory(self):
|
||||
"""Test deleting a memory"""
|
||||
# Store a memory first
|
||||
memory_id = await self.test_store_memory()
|
||||
|
||||
# Delete the memory
|
||||
response = await self.client.delete(
|
||||
f"/v1/memories/{memory_id}",
|
||||
headers=self.headers
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "deleted"
|
||||
assert data["id"] == memory_id
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_authentication_required(self):
|
||||
"""Test that authentication is required"""
|
||||
response = await self.client.get("/v1/memories/users/test_user")
|
||||
assert response.status_code == 401
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_invalid_api_key(self):
|
||||
"""Test invalid API key"""
|
||||
headers = {"Authorization": "Bearer invalid_key"}
|
||||
response = await self.client.get("/v1/memories/users/test_user", headers=headers)
|
||||
assert response.status_code == 401
|
||||
print("\n" + "=" * 50)
|
||||
print("🎉 API Testing Complete!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__, "-v"])
|
||||
asyncio.run(test_api_endpoints())
|
||||
Reference in New Issue
Block a user