mirror of
https://github.com/codeflash-ai/codeflash-internal.git
synced 2026-05-04 18:25:18 +00:00
366 lines
13 KiB
Python
366 lines
13 KiB
Python
"""
|
|
Integration tests for JavaScript optimization API endpoints.
|
|
|
|
These tests call the actual API endpoints to verify end-to-end functionality.
|
|
|
|
NOTE: These tests require authentication mocking which is complex due to Django
|
|
middleware being instantiated at startup. The tests are marked to skip by default.
|
|
Run with: pytest -m integration tests/optimizer/test_javascript_api.py
|
|
|
|
For actual API testing, use curl or httpie against a running dev server with a valid
|
|
API key.
|
|
"""
|
|
|
|
import json
|
|
import uuid
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
from django.test import AsyncClient
|
|
|
|
|
|
# Mark all tests in this module as integration tests that skip by default
|
|
pytestmark = [
|
|
pytest.mark.integration,
|
|
pytest.mark.skip(reason="Integration tests require running server - run with -m integration --runintegration"),
|
|
]
|
|
|
|
|
|
@pytest.fixture
|
|
def api_client():
|
|
"""Create an async test client."""
|
|
return AsyncClient()
|
|
|
|
|
|
@pytest.fixture
|
|
def valid_trace_id():
|
|
"""Generate a valid UUIDv4 trace ID."""
|
|
return str(uuid.uuid4())
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_auth(settings):
|
|
"""Mock authentication by patching the AuthBearer class method.
|
|
|
|
Note: This mock is applied but Django middleware is instantiated at startup,
|
|
so the mock may not take effect unless the server is restarted after patching.
|
|
"""
|
|
from authapp.auth import AuthBearer
|
|
|
|
# Store original authenticate method
|
|
original_authenticate = AuthBearer.authenticate
|
|
|
|
async def fake_authenticate(self, request, token):
|
|
request.user = "test-user-123"
|
|
request.tier = "pro"
|
|
request.api_key_id = 1
|
|
request.organization_id = "test-org"
|
|
request.should_log_features = False
|
|
return token
|
|
|
|
# Create a mock subscription
|
|
class FakeSubscription:
|
|
subscription_status = "active"
|
|
optimizations_used = 0
|
|
optimizations_limit = 1000
|
|
total_lifetime_optimizations = 0
|
|
plan_type = "pro"
|
|
|
|
async def asave(self, *args, **kwargs):
|
|
return None
|
|
|
|
class FakeFilter:
|
|
async def afirst(self):
|
|
return FakeSubscription()
|
|
|
|
async def aupdate(self, **kwargs):
|
|
return 1
|
|
|
|
# Patch the method on the class (affects all instances)
|
|
AuthBearer.authenticate = fake_authenticate
|
|
|
|
with patch(
|
|
"aiservice.middleware.track_usage_middleware.Subscriptions.objects.filter",
|
|
return_value=FakeFilter()
|
|
):
|
|
yield
|
|
|
|
# Restore original method
|
|
AuthBearer.authenticate = original_authenticate
|
|
|
|
|
|
@pytest.fixture
|
|
def auth_headers():
|
|
"""Headers with authentication token."""
|
|
return {"HTTP_AUTHORIZATION": "Bearer test-token-123"}
|
|
|
|
|
|
class TestOptimizeJavaScriptRouting:
|
|
"""Test that JavaScript optimization requests are properly routed."""
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.django_db
|
|
async def test_javascript_language_routes_correctly(self, api_client, valid_trace_id, mock_auth, auth_headers):
|
|
"""Test that language=javascript routes to JavaScript optimizer."""
|
|
response = await api_client.post(
|
|
"/ai/optimize/",
|
|
data=json.dumps({
|
|
"source_code": "function add(a, b) { return a + b; }",
|
|
"dependency_code": None,
|
|
"trace_id": valid_trace_id,
|
|
"language": "javascript",
|
|
"language_version": "ES2022",
|
|
"n_candidates": 1,
|
|
}),
|
|
content_type="application/json",
|
|
**auth_headers,
|
|
)
|
|
|
|
# The request should be processed (may fail due to LLM mocking,
|
|
# but should not fail due to routing)
|
|
assert response.status_code in [200, 400, 500]
|
|
|
|
# If it fails, check that it's not a "not implemented" error
|
|
if response.status_code != 200:
|
|
data = response.json()
|
|
assert "not yet implemented" not in data.get("error", "").lower()
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.django_db
|
|
async def test_typescript_language_routes_correctly(self, api_client, valid_trace_id, mock_auth, auth_headers):
|
|
"""Test that language=typescript routes to JavaScript optimizer."""
|
|
response = await api_client.post(
|
|
"/ai/optimize/",
|
|
data=json.dumps({
|
|
"source_code": "function add(a: number, b: number): number { return a + b; }",
|
|
"dependency_code": None,
|
|
"trace_id": valid_trace_id,
|
|
"language": "typescript",
|
|
"language_version": "ES2022",
|
|
"n_candidates": 1,
|
|
}),
|
|
content_type="application/json",
|
|
**auth_headers,
|
|
)
|
|
|
|
assert response.status_code in [200, 400, 500]
|
|
|
|
if response.status_code != 200:
|
|
data = response.json()
|
|
assert "not yet implemented" not in data.get("error", "").lower()
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.django_db
|
|
async def test_python_still_works(self, api_client, valid_trace_id, mock_auth, auth_headers):
|
|
"""Test that Python optimization still works (backward compatibility)."""
|
|
response = await api_client.post(
|
|
"/ai/optimize/",
|
|
data=json.dumps({
|
|
"source_code": "def add(a, b): return a + b",
|
|
"dependency_code": None,
|
|
"trace_id": valid_trace_id,
|
|
"language": "python",
|
|
"python_version": "3.11",
|
|
"n_candidates": 1,
|
|
}),
|
|
content_type="application/json",
|
|
**auth_headers,
|
|
)
|
|
|
|
assert response.status_code in [200, 400, 500]
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.django_db
|
|
async def test_default_language_is_python(self, api_client, valid_trace_id, mock_auth, auth_headers):
|
|
"""Test that omitting language defaults to Python."""
|
|
response = await api_client.post(
|
|
"/ai/optimize/",
|
|
data=json.dumps({
|
|
"source_code": "def add(a, b): return a + b",
|
|
"dependency_code": None,
|
|
"trace_id": valid_trace_id,
|
|
"python_version": "3.11",
|
|
"n_candidates": 1,
|
|
}),
|
|
content_type="application/json",
|
|
**auth_headers,
|
|
)
|
|
|
|
# Should work as Python (the default)
|
|
assert response.status_code in [200, 400, 500]
|
|
|
|
|
|
class TestOptimizeJavaScriptValidation:
|
|
"""Test JavaScript-specific validation in the API."""
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.django_db
|
|
async def test_invalid_javascript_syntax_rejected(self, api_client, valid_trace_id, mock_auth, auth_headers):
|
|
"""Test that invalid JavaScript syntax is rejected."""
|
|
response = await api_client.post(
|
|
"/ai/optimize/",
|
|
data=json.dumps({
|
|
"source_code": "function broken( { return; }", # Invalid syntax
|
|
"dependency_code": None,
|
|
"trace_id": valid_trace_id,
|
|
"language": "javascript",
|
|
"n_candidates": 1,
|
|
}),
|
|
content_type="application/json",
|
|
**auth_headers,
|
|
)
|
|
|
|
assert response.status_code == 400
|
|
data = response.json()
|
|
assert "error" in data
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.django_db
|
|
async def test_empty_source_code_rejected(self, api_client, valid_trace_id, mock_auth, auth_headers):
|
|
"""Test that empty source code is rejected."""
|
|
response = await api_client.post(
|
|
"/ai/optimize/",
|
|
data=json.dumps({
|
|
"source_code": "",
|
|
"dependency_code": None,
|
|
"trace_id": valid_trace_id,
|
|
"language": "javascript",
|
|
"n_candidates": 1,
|
|
}),
|
|
content_type="application/json",
|
|
**auth_headers,
|
|
)
|
|
|
|
assert response.status_code == 400
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.django_db
|
|
async def test_invalid_trace_id_rejected(self, api_client, mock_auth, auth_headers):
|
|
"""Test that invalid trace ID is rejected."""
|
|
response = await api_client.post(
|
|
"/ai/optimize/",
|
|
data=json.dumps({
|
|
"source_code": "function add(a, b) { return a + b; }",
|
|
"dependency_code": None,
|
|
"trace_id": "not-a-valid-uuid",
|
|
"language": "javascript",
|
|
"n_candidates": 1,
|
|
}),
|
|
content_type="application/json",
|
|
**auth_headers,
|
|
)
|
|
|
|
assert response.status_code == 400
|
|
data = response.json()
|
|
assert "trace" in data.get("error", "").lower() or "uuid" in data.get("error", "").lower()
|
|
|
|
|
|
class TestTestGenJavaScriptRouting:
|
|
"""Test that JavaScript test generation requests are properly routed."""
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.django_db
|
|
async def test_javascript_testgen_routes_correctly(self, api_client, valid_trace_id, mock_auth, auth_headers):
|
|
"""Test that language=javascript routes to JavaScript testgen."""
|
|
response = await api_client.post(
|
|
"/ai/testgen/",
|
|
data=json.dumps({
|
|
"source_code_being_tested": "function add(a, b) { return a + b; }",
|
|
"function_to_optimize": {
|
|
"function_name": "add",
|
|
"file_path": "/test.js",
|
|
"parents": [],
|
|
"starting_line": 1,
|
|
"ending_line": 1,
|
|
},
|
|
"module_path": "./math",
|
|
"test_module_path": "./math.test",
|
|
"test_framework": "jest",
|
|
"test_timeout": 30,
|
|
"trace_id": valid_trace_id,
|
|
"language": "javascript",
|
|
}),
|
|
content_type="application/json",
|
|
**auth_headers,
|
|
)
|
|
|
|
assert response.status_code in [200, 400, 500]
|
|
|
|
if response.status_code != 200:
|
|
data = response.json()
|
|
assert "not yet implemented" not in data.get("error", "").lower()
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.django_db
|
|
async def test_invalid_test_framework_rejected(self, api_client, valid_trace_id, mock_auth, auth_headers):
|
|
"""Test that invalid test framework is rejected for JavaScript."""
|
|
response = await api_client.post(
|
|
"/ai/testgen/",
|
|
data=json.dumps({
|
|
"source_code_being_tested": "function add(a, b) { return a + b; }",
|
|
"function_to_optimize": {
|
|
"function_name": "add",
|
|
"file_path": "/test.js",
|
|
"parents": [],
|
|
"starting_line": 1,
|
|
"ending_line": 1,
|
|
},
|
|
"module_path": "./math",
|
|
"test_module_path": "./math.test",
|
|
"test_framework": "pytest", # Wrong framework for JS
|
|
"test_timeout": 30,
|
|
"trace_id": valid_trace_id,
|
|
"language": "javascript",
|
|
}),
|
|
content_type="application/json",
|
|
**auth_headers,
|
|
)
|
|
|
|
assert response.status_code == 400
|
|
data = response.json()
|
|
assert "framework" in data.get("error", "").lower()
|
|
|
|
|
|
class TestSchemaValidation:
|
|
"""Test that API schema validation works correctly."""
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.django_db
|
|
async def test_language_field_accepted(self, api_client, valid_trace_id, mock_auth, auth_headers):
|
|
"""Test that the language field is accepted in the API."""
|
|
response = await api_client.post(
|
|
"/ai/optimize/",
|
|
data=json.dumps({
|
|
"source_code": "const x = 1;",
|
|
"dependency_code": None,
|
|
"trace_id": valid_trace_id,
|
|
"language": "javascript",
|
|
"language_version": "ES2022",
|
|
"n_candidates": 0, # 0 candidates to avoid LLM call
|
|
}),
|
|
content_type="application/json",
|
|
**auth_headers,
|
|
)
|
|
|
|
# Should not fail schema validation (may fail elsewhere)
|
|
assert response.status_code != 422 # 422 = schema validation error
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.django_db
|
|
async def test_language_version_field_accepted(self, api_client, valid_trace_id, mock_auth, auth_headers):
|
|
"""Test that the language_version field is accepted in the API."""
|
|
response = await api_client.post(
|
|
"/ai/optimize/",
|
|
data=json.dumps({
|
|
"source_code": "const x = 1;",
|
|
"dependency_code": None,
|
|
"trace_id": valid_trace_id,
|
|
"language": "javascript",
|
|
"language_version": "ES2020",
|
|
"n_candidates": 0,
|
|
}),
|
|
content_type="application/json",
|
|
**auth_headers,
|
|
)
|
|
|
|
assert response.status_code != 422
|