Skip to content

Commit f545a25

Browse files
committed
Added tests
1 parent 704578e commit f545a25

5 files changed

Lines changed: 1023 additions & 0 deletions

File tree

tests/conftest.py

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
"""Pytest configuration and fixtures."""
2+
3+
import pytest
4+
import os
5+
import sys
6+
import tempfile
7+
import shutil
8+
from pathlib import Path
9+
from fastapi.testclient import TestClient
10+
from httpx import AsyncClient
11+
12+
# Add the project root to Python path for imports
13+
PROJECT_ROOT = Path(__file__).parent.parent
14+
sys.path.insert(0, str(PROJECT_ROOT))
15+
16+
# Now import the app and configuration
17+
from claude_code_api.main import app
18+
from claude_code_api.core.config import settings
19+
20+
21+
@pytest.fixture(scope="session", autouse=True)
22+
def setup_test_environment():
23+
"""Setup test environment before all tests."""
24+
# Create temporary directory for testing
25+
temp_dir = tempfile.mkdtemp(prefix="claude_api_test_")
26+
27+
# Store original settings
28+
original_settings = {
29+
"project_root": getattr(settings, "project_root", None),
30+
"require_auth": getattr(settings, "require_auth", False),
31+
"claude_binary_path": getattr(settings, "claude_binary_path", "claude"),
32+
"database_url": getattr(settings, "database_url", "sqlite:///./test.db"),
33+
"debug": getattr(settings, "debug", False)
34+
}
35+
36+
# Set test settings
37+
settings.project_root = os.path.join(temp_dir, "projects")
38+
settings.require_auth = False
39+
# Keep the real Claude binary path - DO NOT mock it!
40+
# settings.claude_binary_path should remain as found by find_claude_binary()
41+
settings.database_url = f"sqlite:///{temp_dir}/test.db"
42+
settings.debug = True
43+
44+
# Create directories
45+
os.makedirs(settings.project_root, exist_ok=True)
46+
47+
yield temp_dir
48+
49+
# Cleanup
50+
try:
51+
shutil.rmtree(temp_dir)
52+
except Exception as e:
53+
print(f"Cleanup warning: {e}")
54+
55+
# Restore original settings (if they existed)
56+
for key, value in original_settings.items():
57+
if value is not None:
58+
setattr(settings, key, value)
59+
60+
61+
@pytest.fixture
62+
def test_client():
63+
"""Create a test client for the FastAPI app."""
64+
with TestClient(app) as client:
65+
yield client
66+
67+
68+
@pytest.fixture
69+
async def async_test_client():
70+
"""Create an async test client."""
71+
async with AsyncClient(app=app, base_url="http://test") as client:
72+
yield client
73+
74+
75+
@pytest.fixture
76+
def sample_chat_request():
77+
"""Sample chat completion request."""
78+
return {
79+
"model": "claude-3-5-sonnet-20241022",
80+
"messages": [
81+
{"role": "user", "content": "Hi"}
82+
],
83+
"stream": False
84+
}
85+
86+
87+
@pytest.fixture
88+
def sample_streaming_request():
89+
"""Sample streaming chat completion request."""
90+
return {
91+
"model": "claude-3-5-sonnet-20241022",
92+
"messages": [
93+
{"role": "user", "content": "Tell me a joke"}
94+
],
95+
"stream": True
96+
}
97+
98+
99+
@pytest.fixture
100+
def sample_project_request():
101+
"""Sample project creation request."""
102+
return {
103+
"name": "Test Project",
104+
"description": "A test project"
105+
}
106+
107+
108+
@pytest.fixture
109+
def sample_session_request():
110+
"""Sample session creation request."""
111+
return {
112+
"project_id": "test-project",
113+
"title": "Test Session",
114+
"model": "claude-3-5-sonnet-20241022"
115+
}
116+
117+
118+
# Configure pytest
119+
def pytest_configure(config):
120+
"""Configure pytest."""
121+
config.addinivalue_line(
122+
"markers", "slow: marks tests as slow (deselect with '-m \"not slow\"')"
123+
)
124+
config.addinivalue_line(
125+
"markers", "integration: marks tests as integration tests"
126+
)
127+
config.addinivalue_line(
128+
"markers", "unit: marks tests as unit tests"
129+
)
130+
131+
132+
def pytest_collection_modifyitems(config, items):
133+
"""Modify test collection."""
134+
# Add markers based on test names/paths
135+
for item in items:
136+
if "integration" in item.nodeid:
137+
item.add_marker(pytest.mark.integration)
138+
elif "unit" in item.nodeid:
139+
item.add_marker(pytest.mark.unit)
140+
141+
# Mark slow tests
142+
if any(keyword in item.name.lower() for keyword in ["concurrent", "performance", "large"]):
143+
item.add_marker(pytest.mark.slow)

tests/test_api.sh

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/bin/bash
2+
3+
echo "🚀 Testing Claude Code API Gateway"
4+
echo
5+
6+
echo "📋 Testing Models Endpoint:"
7+
curl -s http://localhost:8000/v1/models | jq .
8+
9+
echo
10+
echo "❤️ Testing Health Endpoint:"
11+
curl -s http://localhost:8000/health | jq .
12+
13+
echo
14+
echo "✅ API is working! No authentication required."
15+
echo "📝 Available endpoints:"
16+
echo " - GET /v1/models"
17+
echo " - POST /v1/chat/completions"
18+
echo " - GET /health"
19+
echo " - GET /docs (API documentation)"

tests/test_claude_working.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Test that demonstrates Claude CLI is working and create a proper test
4+
"""
5+
6+
import subprocess
7+
import json
8+
import time
9+
10+
def test_claude_directly():
11+
"""Test Claude CLI directly to prove it works"""
12+
print("🧪 Testing Claude CLI directly...")
13+
14+
cmd = [
15+
"/usr/local/share/nvm/versions/node/v23.11.1/bin/claude",
16+
"-p", "Say hello and return",
17+
"--model", "claude-3-5-haiku-20241022",
18+
"--output-format", "stream-json",
19+
"--verbose",
20+
"--dangerously-skip-permissions"
21+
]
22+
23+
try:
24+
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
25+
print(f"✅ Exit code: {result.returncode}")
26+
if result.stdout:
27+
lines = result.stdout.strip().split('\n')
28+
print(f"✅ Got {len(lines)} lines of output")
29+
for i, line in enumerate(lines[:3]): # Show first 3 lines
30+
try:
31+
data = json.loads(line)
32+
print(f" Line {i+1}: {data.get('type', 'unknown')} - {line[:100]}...")
33+
except:
34+
print(f" Line {i+1}: {line[:100]}...")
35+
36+
if result.stderr:
37+
print(f"⚠️ stderr: {result.stderr[:200]}")
38+
39+
return result.returncode == 0
40+
41+
except subprocess.TimeoutExpired:
42+
print("❌ Command timed out")
43+
return False
44+
except Exception as e:
45+
print(f"❌ Error: {e}")
46+
return False
47+
48+
def test_api_with_real_claude():
49+
"""Test if our API is working now"""
50+
import requests
51+
52+
print("\n🌐 Testing API with real Claude...")
53+
54+
try:
55+
# Test health first
56+
health = requests.get("http://localhost:8000/health", timeout=5)
57+
print(f"Health check: {health.status_code}")
58+
59+
# Test chat completion
60+
payload = {
61+
"model": "claude-3-5-haiku-20241022",
62+
"messages": [{"role": "user", "content": "Hi"}],
63+
"stream": False
64+
}
65+
66+
print("Making chat completion request...")
67+
response = requests.post(
68+
"http://localhost:8000/v1/chat/completions",
69+
json=payload,
70+
timeout=30
71+
)
72+
73+
print(f"✅ Chat completion status: {response.status_code}")
74+
if response.status_code == 200:
75+
data = response.json()
76+
if 'choices' in data:
77+
content = data['choices'][0]['message']['content']
78+
print(f"✅ Response: {content[:100]}...")
79+
return True
80+
else:
81+
print(f"❌ Error response: {response.text[:200]}")
82+
83+
except requests.exceptions.Timeout:
84+
print("❌ API request timed out")
85+
except Exception as e:
86+
print(f"❌ API test error: {e}")
87+
88+
return False
89+
90+
if __name__ == "__main__":
91+
print("🚀 Testing Claude Code Integration")
92+
print("=" * 50)
93+
94+
# Test 1: Direct Claude CLI
95+
claude_works = test_claude_directly()
96+
97+
# Test 2: API with Claude
98+
api_works = test_api_with_real_claude()
99+
100+
print("\n" + "=" * 50)
101+
print(f"📊 Results:")
102+
print(f" Claude CLI: {'✅ WORKS' if claude_works else '❌ FAILS'}")
103+
print(f" API: {'✅ WORKS' if api_works else '❌ FAILS'}")
104+
105+
if claude_works and not api_works:
106+
print("\n💡 Claude CLI works but API fails - this means the issue is in our Python async handling!")
107+
elif claude_works and api_works:
108+
print("\n🎉 Everything works! API is ready!")
109+
else:
110+
print("\n❌ Claude CLI itself has issues")

0 commit comments

Comments
 (0)