Skip to content

Commit 280747f

Browse files
committed
integration tests for js/ts
1 parent 0748874 commit 280747f

1 file changed

Lines changed: 363 additions & 0 deletions

File tree

Lines changed: 363 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,363 @@
1+
"""True E2E integration tests for JavaScript/TypeScript optimization flow.
2+
3+
These tests call the ACTUAL backend /testgen API endpoint and verify the full flow:
4+
1. CLI sends code to backend for test generation
5+
2. Backend generates tests using LLM
6+
3. Backend validates generated code with correct parser (JS vs TS)
7+
4. CLI receives tests, instruments them, runs them
8+
5. CLI parses test results and timing data
9+
10+
REQUIREMENTS:
11+
- Backend server running at CODEFLASH_API_URL (default: http://localhost:8000)
12+
- Valid CODEFLASH_API_KEY environment variable
13+
- Node.js and npm installed
14+
- npm dependencies in test fixture directories
15+
16+
Run these tests with:
17+
pytest tests/test_languages/test_javascript_integration.py -v --run-integration
18+
19+
Or set environment variables:
20+
CODEFLASH_API_URL=https://api.codeflash.ai
21+
CODEFLASH_API_KEY=your-api-key
22+
"""
23+
24+
import os
25+
import subprocess
26+
from pathlib import Path
27+
from unittest.mock import MagicMock
28+
29+
import pytest
30+
31+
from codeflash.discovery.functions_to_optimize import FunctionToOptimize
32+
from codeflash.languages.base import Language
33+
from codeflash.models.models import FunctionParent
34+
from codeflash.verification.verification_utils import TestConfig
35+
36+
37+
def is_backend_available() -> bool:
38+
"""Check if the backend API is accessible."""
39+
try:
40+
import requests
41+
api_url = os.environ.get("CODEFLASH_API_URL", "http://localhost:8000")
42+
response = requests.get(f"{api_url}/health", timeout=5)
43+
return response.status_code == 200
44+
except Exception:
45+
return False
46+
47+
48+
def has_api_key() -> bool:
49+
"""Check if API key is configured."""
50+
return bool(os.environ.get("CODEFLASH_API_KEY"))
51+
52+
53+
def is_node_available() -> bool:
54+
"""Check if Node.js is available."""
55+
try:
56+
result = subprocess.run(["node", "--version"], capture_output=True, text=True)
57+
return result.returncode == 0
58+
except FileNotFoundError:
59+
return False
60+
61+
62+
def skip_if_not_integration():
63+
"""Skip test if integration environment is not available."""
64+
if not is_backend_available():
65+
pytest.skip("Backend API not available. Set CODEFLASH_API_URL and start backend server.")
66+
if not has_api_key():
67+
pytest.skip("API key not configured. Set CODEFLASH_API_KEY environment variable.")
68+
if not is_node_available():
69+
pytest.skip("Node.js not available")
70+
71+
72+
def skip_if_js_not_supported():
73+
"""Skip test if JavaScript/TypeScript languages are not supported."""
74+
try:
75+
from codeflash.languages import get_language_support
76+
get_language_support(Language.JAVASCRIPT)
77+
except Exception as e:
78+
pytest.skip(f"JavaScript/TypeScript language support not available: {e}")
79+
80+
81+
# Mark all tests in this module as integration tests
82+
pytestmark = pytest.mark.integration
83+
84+
85+
class TestBackendTestGeneration:
86+
"""Tests that verify the backend /testgen endpoint works correctly for JS/TS."""
87+
88+
def test_typescript_testgen_uses_typescript_validator(self, tmp_path):
89+
"""Verify backend validates TypeScript code with TypeScript parser.
90+
91+
This is the critical test - TypeScript-specific syntax like 'as unknown as number'
92+
should pass validation when the backend is told it's TypeScript.
93+
"""
94+
skip_if_not_integration()
95+
skip_if_js_not_supported()
96+
from codeflash.api.aiservice import AiServiceClient
97+
from codeflash.discovery.functions_to_optimize import find_all_functions_in_file
98+
99+
# Create a TypeScript file with TypeScript-specific syntax
100+
ts_file = tmp_path / "utils.ts"
101+
ts_file.write_text("""
102+
export function castValue(input: unknown): number {
103+
// This uses TypeScript's double assertion pattern
104+
return input as unknown as number;
105+
}
106+
""")
107+
108+
functions = find_all_functions_in_file(ts_file)
109+
func = functions[ts_file][0]
110+
111+
# Verify the function is identified as TypeScript
112+
assert func.language == "typescript"
113+
114+
# Call the actual backend
115+
ai_client = AiServiceClient()
116+
response = ai_client.generate_regression_tests(
117+
source_code_being_tested=ts_file.read_text(),
118+
function_to_optimize=func,
119+
helper_function_names=[],
120+
module_path=ts_file,
121+
test_module_path=tmp_path / "tests" / "utils.test.ts",
122+
test_framework="vitest",
123+
test_timeout=30,
124+
trace_id="integration-test-ts-validator",
125+
test_index=0,
126+
language="typescript", # This MUST be passed to use TS validator
127+
)
128+
129+
# Backend should return valid tests
130+
assert response is not None
131+
assert "generated_tests" in response or hasattr(response, "generated_tests")
132+
133+
def test_javascript_testgen_rejects_typescript_syntax(self, tmp_path):
134+
"""Verify backend rejects TypeScript syntax when told it's JavaScript.
135+
136+
If backend incorrectly validates TypeScript as JavaScript, this would fail.
137+
"""
138+
skip_if_not_integration()
139+
skip_if_js_not_supported()
140+
from codeflash.api.aiservice import AiServiceClient
141+
from codeflash.discovery.functions_to_optimize import FunctionToOptimize
142+
143+
# TypeScript code should fail when sent as JavaScript
144+
ts_code = """
145+
function castValue(input) {
146+
// TypeScript syntax that JavaScript parser should reject
147+
const value = input as unknown as number;
148+
return value;
149+
}
150+
"""
151+
func = FunctionToOptimize(
152+
function_name="castValue",
153+
file_path=tmp_path / "utils.js", # .js extension
154+
parents=[],
155+
starting_line=2,
156+
ending_line=6,
157+
language="javascript", # Claiming it's JavaScript
158+
)
159+
160+
ai_client = AiServiceClient()
161+
162+
# This should fail because the source contains TypeScript syntax
163+
# but we're telling the backend it's JavaScript
164+
with pytest.raises(Exception):
165+
ai_client.generate_regression_tests(
166+
source_code_being_tested=ts_code,
167+
function_to_optimize=func,
168+
helper_function_names=[],
169+
module_path=tmp_path / "utils.js",
170+
test_module_path=tmp_path / "tests" / "utils.test.js",
171+
test_framework="jest",
172+
test_timeout=30,
173+
trace_id="integration-test-js-rejects-ts",
174+
test_index=0,
175+
language="javascript",
176+
)
177+
178+
179+
class TestFullOptimizationPipeline:
180+
"""Tests that verify the complete optimization flow with real backend."""
181+
182+
@pytest.fixture
183+
def vitest_project(self):
184+
"""Get the Vitest sample project."""
185+
project_root = Path(__file__).parent.parent.parent
186+
vitest_dir = project_root / "code_to_optimize" / "js" / "code_to_optimize_vitest"
187+
188+
if not vitest_dir.exists():
189+
pytest.skip("code_to_optimize_vitest directory not found")
190+
191+
return vitest_dir
192+
193+
def test_typescript_full_flow_with_backend(self, vitest_project):
194+
"""Test complete TypeScript optimization flow with actual backend.
195+
196+
This is the equivalent of Python's test_instrument_tests.py tests but
197+
for TypeScript, using the real backend API.
198+
"""
199+
skip_if_not_integration()
200+
skip_if_js_not_supported()
201+
from codeflash.api.aiservice import AiServiceClient
202+
from codeflash.discovery.functions_to_optimize import find_all_functions_in_file
203+
from codeflash.languages import current as lang_current
204+
from codeflash.optimization.function_optimizer import FunctionOptimizer
205+
206+
lang_current._current_language = Language.TYPESCRIPT
207+
208+
# Find the fibonacci function
209+
fib_file = vitest_project / "fibonacci.ts"
210+
if not fib_file.exists():
211+
pytest.skip("fibonacci.ts not found")
212+
213+
functions = find_all_functions_in_file(fib_file)
214+
fib_func_info = next(f for f in functions[fib_file] if f.function_name == "fibonacci")
215+
216+
# Verify language detection
217+
assert fib_func_info.language == "typescript"
218+
219+
# Create FunctionToOptimize
220+
func = FunctionToOptimize(
221+
function_name=fib_func_info.function_name,
222+
file_path=fib_func_info.file_path,
223+
parents=[FunctionParent(name=p.name, type=p.type) for p in fib_func_info.parents],
224+
starting_line=fib_func_info.starting_line,
225+
ending_line=fib_func_info.ending_line,
226+
language=fib_func_info.language,
227+
)
228+
229+
# Create test config
230+
test_config = TestConfig(
231+
tests_root=vitest_project / "tests",
232+
tests_project_rootdir=vitest_project,
233+
project_root_path=vitest_project,
234+
pytest_cmd="vitest",
235+
test_framework="vitest",
236+
)
237+
238+
# Use REAL AI service client
239+
ai_client = AiServiceClient()
240+
241+
# Create optimizer
242+
func_optimizer = FunctionOptimizer(
243+
function_to_optimize=func,
244+
test_cfg=test_config,
245+
aiservice_client=ai_client,
246+
)
247+
248+
# Get code context
249+
result = func_optimizer.get_code_optimization_context()
250+
context = result.unwrap()
251+
252+
assert context is not None
253+
assert context.read_writable_code.language == "typescript"
254+
255+
# Generate tests via backend
256+
tests_result = func_optimizer.generate_tests()
257+
258+
# Verify tests were generated
259+
assert tests_result is not None
260+
# Tests should contain TypeScript-compatible code
261+
262+
263+
class TestLanguageConsistencyWithBackend:
264+
"""Tests verifying language parameter flows correctly to backend."""
265+
266+
def test_language_in_testgen_request_payload(self, tmp_path):
267+
"""Verify the language parameter is sent correctly to the backend."""
268+
skip_if_not_integration()
269+
skip_if_js_not_supported()
270+
from codeflash.api.aiservice import AiServiceClient
271+
from codeflash.discovery.functions_to_optimize import find_all_functions_in_file
272+
from unittest.mock import patch
273+
274+
ts_file = tmp_path / "utils.ts"
275+
ts_file.write_text("""
276+
export function add(a: number, b: number): number {
277+
return a + b;
278+
}
279+
""")
280+
281+
functions = find_all_functions_in_file(ts_file)
282+
func = functions[ts_file][0]
283+
284+
ai_client = AiServiceClient()
285+
286+
# Spy on the actual request
287+
original_request = ai_client.make_ai_service_request
288+
captured_payload = None
289+
290+
def spy_request(*args, **kwargs):
291+
nonlocal captured_payload
292+
if 'payload' in kwargs:
293+
captured_payload = kwargs['payload']
294+
elif len(args) > 1:
295+
captured_payload = args[1]
296+
return original_request(*args, **kwargs)
297+
298+
with patch.object(ai_client, 'make_ai_service_request', side_effect=spy_request):
299+
try:
300+
ai_client.generate_regression_tests(
301+
source_code_being_tested=ts_file.read_text(),
302+
function_to_optimize=func,
303+
helper_function_names=[],
304+
module_path=ts_file,
305+
test_module_path=tmp_path / "tests" / "utils.test.ts",
306+
test_framework="vitest",
307+
test_timeout=30,
308+
trace_id="integration-test-language-payload",
309+
test_index=0,
310+
language="typescript",
311+
)
312+
except Exception:
313+
pass # We just want to capture the payload
314+
315+
# Verify language was in the payload
316+
assert captured_payload is not None
317+
assert captured_payload.get('language') == 'typescript', \
318+
f"Expected language='typescript' in payload, got: {captured_payload.get('language')}"
319+
320+
321+
class TestRefinementWithBackend:
322+
"""Tests for the refinement flow with actual backend."""
323+
324+
def test_typescript_refinement_uses_correct_validator(self, tmp_path):
325+
"""Verify refinement validates TypeScript with TypeScript parser.
326+
327+
The refiner_context.py had bugs where it always used JavaScript validator.
328+
This test ensures the fix works end-to-end.
329+
"""
330+
skip_if_not_integration()
331+
skip_if_js_not_supported()
332+
from codeflash.api.aiservice import AiServiceClient
333+
334+
# TypeScript code that should pass TypeScript validation
335+
original_code = """
336+
export function processValue(value: unknown): number {
337+
return value as number;
338+
}
339+
"""
340+
optimized_code = """
341+
export function processValue(value: unknown): number {
342+
// Optimized: using double assertion for type safety
343+
return value as unknown as number;
344+
}
345+
"""
346+
347+
ai_client = AiServiceClient()
348+
349+
# Call refinement endpoint with TypeScript
350+
try:
351+
response = ai_client.refine_code(
352+
original_code=original_code,
353+
optimized_code=optimized_code,
354+
language="typescript", # Must be TypeScript
355+
# ... other parameters
356+
)
357+
358+
# If refinement succeeds, the TypeScript validator was used
359+
assert response is not None
360+
except Exception as e:
361+
# If it fails with "Invalid JavaScript syntax", the bug still exists
362+
assert "Invalid JavaScript" not in str(e), \
363+
"Backend still using JavaScript validator for TypeScript refinement!"

0 commit comments

Comments
 (0)