mirror of
https://github.com/codeflash-ai/codeflash.git
synced 2026-05-04 18:25:17 +00:00
# Conflicts: # .claude/rules/architecture.md # .claude/rules/code-style.md # .github/workflows/claude.yml # .github/workflows/duplicate-code-detector.yml # codeflash/api/aiservice.py # codeflash/cli_cmds/console.py # codeflash/cli_cmds/logging_config.py # codeflash/code_utils/deduplicate_code.py # codeflash/discovery/discover_unit_tests.py # codeflash/languages/base.py # codeflash/languages/code_replacer.py # codeflash/languages/javascript/mocha_runner.py # codeflash/languages/javascript/support.py # codeflash/languages/python/support.py # codeflash/optimization/function_optimizer.py # codeflash/verification/parse_test_output.py # codeflash/verification/verification_utils.py # codeflash/verification/verifier.py # packages/codeflash/package-lock.json # packages/codeflash/package.json # tests/languages/javascript/test_support_dispatch.py # tests/test_codeflash_capture.py # tests/test_languages/test_javascript_test_runner.py # tests/test_multi_file_code_replacement.py
315 lines
12 KiB
Python
315 lines
12 KiB
Python
"""E2E tests for JavaScript/TypeScript optimization flow with backend.
|
|
|
|
These tests call the actual backend /testgen API endpoint and verify:
|
|
1. Language parameter is correctly passed to backend
|
|
2. Backend validates generated code with correct parser (JS vs TS)
|
|
3. CLI receives and processes tests correctly
|
|
|
|
Similar to test_validate_python_code.py but for JavaScript/TypeScript.
|
|
"""
|
|
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
from codeflash.api.aiservice import AiServiceClient
|
|
from codeflash.languages.base import Language
|
|
from codeflash.models.models import CodeString, OptimizedCandidateSource
|
|
|
|
|
|
def skip_if_js_not_supported():
|
|
"""Skip test if JavaScript/TypeScript languages are not supported."""
|
|
try:
|
|
from codeflash.languages import get_language_support
|
|
|
|
get_language_support(Language.JAVASCRIPT)
|
|
except Exception as e:
|
|
pytest.skip(f"JavaScript/TypeScript language support not available: {e}")
|
|
|
|
|
|
class TestJavaScriptCodeStringValidation:
|
|
"""Tests for JavaScript CodeString validation - mirrors test_validate_python_code.py."""
|
|
|
|
def test_javascript_string(self):
|
|
"""Test valid JavaScript code string."""
|
|
skip_if_js_not_supported()
|
|
code = CodeString(code="console.log('Hello, World!');", language="javascript")
|
|
assert code.code == "console.log('Hello, World!');"
|
|
|
|
def test_valid_javascript_code(self):
|
|
"""Test that valid JavaScript code passes validation."""
|
|
skip_if_js_not_supported()
|
|
valid_code = "const x = 1;\nconst y = x + 2;\nconsole.log(y);"
|
|
cs = CodeString(code=valid_code, language="javascript")
|
|
assert cs.code == valid_code
|
|
|
|
def test_invalid_javascript_code_syntax(self):
|
|
"""Test that invalid JavaScript code fails validation."""
|
|
skip_if_js_not_supported()
|
|
from pydantic import ValidationError
|
|
|
|
invalid_code = "const x = 1;\nconsole.log(x" # Missing parenthesis
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
CodeString(code=invalid_code, language="javascript")
|
|
assert "Invalid Javascript code" in str(exc_info.value)
|
|
|
|
def test_empty_javascript_code(self):
|
|
"""Test that empty code passes validation."""
|
|
skip_if_js_not_supported()
|
|
empty_code = ""
|
|
cs = CodeString(code=empty_code, language="javascript")
|
|
assert cs.code == empty_code
|
|
|
|
|
|
class TestTypeScriptCodeStringValidation:
|
|
"""Tests for TypeScript CodeString validation."""
|
|
|
|
def test_typescript_string(self):
|
|
"""Test valid TypeScript code string."""
|
|
skip_if_js_not_supported()
|
|
code = CodeString(code="const x: number = 1;", language="typescript")
|
|
assert code.code == "const x: number = 1;"
|
|
|
|
def test_valid_typescript_code(self):
|
|
"""Test that valid TypeScript code passes validation."""
|
|
skip_if_js_not_supported()
|
|
valid_code = "function add(a: number, b: number): number { return a + b; }"
|
|
cs = CodeString(code=valid_code, language="typescript")
|
|
assert cs.code == valid_code
|
|
|
|
def test_typescript_type_assertion_valid(self):
|
|
"""TypeScript type assertions should pass TypeScript validation."""
|
|
skip_if_js_not_supported()
|
|
ts_code = "const value = 4.9 as unknown as number;"
|
|
cs = CodeString(code=ts_code, language="typescript")
|
|
assert cs.code == ts_code
|
|
|
|
def test_typescript_type_assertion_invalid_in_javascript(self):
|
|
"""TypeScript type assertions should FAIL JavaScript validation.
|
|
|
|
This is the critical test - TypeScript syntax like 'as unknown as number'
|
|
should fail when validated as JavaScript.
|
|
"""
|
|
skip_if_js_not_supported()
|
|
from pydantic import ValidationError
|
|
|
|
ts_code = "const value = 4.9 as unknown as number;"
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
CodeString(code=ts_code, language="javascript")
|
|
assert "Invalid Javascript code" in str(exc_info.value)
|
|
|
|
def test_typescript_interface_valid(self):
|
|
"""TypeScript interfaces should pass TypeScript validation."""
|
|
skip_if_js_not_supported()
|
|
ts_code = "interface User { name: string; age: number; }"
|
|
cs = CodeString(code=ts_code, language="typescript")
|
|
assert cs.code == ts_code
|
|
|
|
def test_typescript_interface_invalid_in_javascript(self):
|
|
"""TypeScript interfaces should FAIL JavaScript validation."""
|
|
skip_if_js_not_supported()
|
|
from pydantic import ValidationError
|
|
|
|
ts_code = "interface User { name: string; age: number; }"
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
CodeString(code=ts_code, language="javascript")
|
|
assert "Invalid Javascript code" in str(exc_info.value)
|
|
|
|
def test_typescript_generics_valid(self):
|
|
"""TypeScript generics should pass TypeScript validation."""
|
|
skip_if_js_not_supported()
|
|
ts_code = "function identity<T>(arg: T): T { return arg; }"
|
|
cs = CodeString(code=ts_code, language="typescript")
|
|
assert cs.code == ts_code
|
|
|
|
def test_typescript_generics_invalid_in_javascript(self):
|
|
"""TypeScript generics should FAIL JavaScript validation."""
|
|
skip_if_js_not_supported()
|
|
from pydantic import ValidationError
|
|
|
|
ts_code = "function identity<T>(arg: T): T { return arg; }"
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
CodeString(code=ts_code, language="javascript")
|
|
assert "Invalid Javascript code" in str(exc_info.value)
|
|
|
|
|
|
class TestAiServiceClientJavaScript:
|
|
"""Tests for AiServiceClient with JavaScript/TypeScript - mirrors test_validate_python_code.py."""
|
|
|
|
def test_javascript_generated_candidates_validation(self):
|
|
"""Test that JavaScript candidates are validated correctly."""
|
|
skip_if_js_not_supported()
|
|
ai_service = AiServiceClient()
|
|
|
|
# Invalid JavaScript (missing closing parenthesis)
|
|
code = """```javascript:file.js
|
|
console.log(name
|
|
```"""
|
|
mock_candidates = [{"source_code": code, "explanation": "", "optimization_id": ""}]
|
|
candidates = ai_service._get_valid_candidates(mock_candidates, OptimizedCandidateSource.OPTIMIZE)
|
|
assert len(candidates) == 0
|
|
|
|
# Valid JavaScript
|
|
code = """```javascript:file.js
|
|
console.log('Hello, World!');
|
|
```"""
|
|
mock_candidates = [{"source_code": code, "explanation": "", "optimization_id": ""}]
|
|
candidates = ai_service._get_valid_candidates(mock_candidates, OptimizedCandidateSource.OPTIMIZE)
|
|
assert len(candidates) == 1
|
|
assert candidates[0].source_code.code_strings[0].code == "console.log('Hello, World!');"
|
|
|
|
def test_typescript_generated_candidates_validation(self):
|
|
"""Test that TypeScript candidates are validated correctly."""
|
|
skip_if_js_not_supported()
|
|
ai_service = AiServiceClient()
|
|
|
|
# Valid TypeScript with type annotations
|
|
code = """```typescript:file.ts
|
|
function add(a: number, b: number): number {
|
|
return a + b;
|
|
}
|
|
```"""
|
|
mock_candidates = [{"source_code": code, "explanation": "", "optimization_id": ""}]
|
|
candidates = ai_service._get_valid_candidates(mock_candidates, OptimizedCandidateSource.OPTIMIZE)
|
|
assert len(candidates) == 1
|
|
|
|
def test_typescript_type_assertion_in_candidate(self):
|
|
"""Test that TypeScript type assertions are valid in TS candidates."""
|
|
skip_if_js_not_supported()
|
|
ai_service = AiServiceClient()
|
|
|
|
# TypeScript-specific syntax should be valid
|
|
code = """```typescript:file.ts
|
|
const value = 4.9 as unknown as number;
|
|
```"""
|
|
mock_candidates = [{"source_code": code, "explanation": "", "optimization_id": ""}]
|
|
candidates = ai_service._get_valid_candidates(mock_candidates, OptimizedCandidateSource.OPTIMIZE)
|
|
assert len(candidates) == 1
|
|
|
|
|
|
class TestBackendLanguageParameter:
|
|
"""Tests verifying language parameter flows correctly to backend."""
|
|
|
|
def test_testgen_request_includes_typescript_language(self, tmp_path):
|
|
"""Verify the language parameter is sent as 'typescript' for .ts files."""
|
|
skip_if_js_not_supported()
|
|
from codeflash.discovery.functions_to_optimize import find_all_functions_in_file
|
|
from codeflash.languages import current as lang_current
|
|
|
|
# Set current language to TypeScript
|
|
lang_current._current_language = Language.TYPESCRIPT
|
|
|
|
ts_file = tmp_path / "utils.ts"
|
|
ts_file.write_text("""
|
|
export function add(a: number, b: number): number {
|
|
return a + b;
|
|
}
|
|
""")
|
|
|
|
functions = find_all_functions_in_file(ts_file)
|
|
func = functions[ts_file][0]
|
|
|
|
# Verify function has correct language
|
|
assert func.language == "typescript"
|
|
|
|
ai_client = AiServiceClient()
|
|
captured_payload = None
|
|
|
|
def capture_request(*args, **kwargs):
|
|
nonlocal captured_payload
|
|
if "payload" in kwargs:
|
|
captured_payload = kwargs["payload"]
|
|
elif len(args) > 1:
|
|
captured_payload = args[1]
|
|
# Return a mock response to avoid actual API call
|
|
from unittest.mock import MagicMock
|
|
|
|
mock_response = MagicMock()
|
|
mock_response.status_code = 200
|
|
mock_response.json.return_value = {
|
|
"generated_tests": "// test",
|
|
"instrumented_behavior_tests": "// test",
|
|
"instrumented_perf_tests": "// test",
|
|
}
|
|
return mock_response
|
|
|
|
with patch.object(ai_client, "make_ai_service_request", side_effect=capture_request):
|
|
ai_client.generate_regression_tests(
|
|
source_code_being_tested=ts_file.read_text(),
|
|
function_to_optimize=func,
|
|
helper_function_names=[],
|
|
module_path=ts_file,
|
|
test_module_path=tmp_path / "tests" / "utils.test.ts",
|
|
test_framework="vitest",
|
|
test_timeout=30,
|
|
trace_id="test-language-param-ts",
|
|
test_index=0,
|
|
language="typescript",
|
|
)
|
|
|
|
assert captured_payload is not None
|
|
assert captured_payload.get("language") == "typescript", (
|
|
f"Expected language='typescript', got: {captured_payload.get('language')}"
|
|
)
|
|
|
|
def test_testgen_request_includes_javascript_language(self, tmp_path):
|
|
"""Verify the language parameter is sent as 'javascript' for .js files."""
|
|
skip_if_js_not_supported()
|
|
from codeflash.discovery.functions_to_optimize import find_all_functions_in_file
|
|
from codeflash.languages import current as lang_current
|
|
|
|
# Set current language to JavaScript
|
|
lang_current._current_language = Language.JAVASCRIPT
|
|
|
|
js_file = tmp_path / "utils.js"
|
|
js_file.write_text("""
|
|
function add(a, b) {
|
|
return a + b;
|
|
}
|
|
module.exports = { add };
|
|
""")
|
|
|
|
functions = find_all_functions_in_file(js_file)
|
|
func = functions[js_file][0]
|
|
|
|
# Verify function has correct language
|
|
assert func.language == "javascript"
|
|
|
|
ai_client = AiServiceClient()
|
|
captured_payload = None
|
|
|
|
def capture_request(*args, **kwargs):
|
|
nonlocal captured_payload
|
|
if "payload" in kwargs:
|
|
captured_payload = kwargs["payload"]
|
|
elif len(args) > 1:
|
|
captured_payload = args[1]
|
|
from unittest.mock import MagicMock
|
|
|
|
mock_response = MagicMock()
|
|
mock_response.status_code = 200
|
|
mock_response.json.return_value = {
|
|
"generated_tests": "// test",
|
|
"instrumented_behavior_tests": "// test",
|
|
"instrumented_perf_tests": "// test",
|
|
}
|
|
return mock_response
|
|
|
|
with patch.object(ai_client, "make_ai_service_request", side_effect=capture_request):
|
|
ai_client.generate_regression_tests(
|
|
source_code_being_tested=js_file.read_text(),
|
|
function_to_optimize=func,
|
|
helper_function_names=[],
|
|
module_path=js_file,
|
|
test_module_path=tmp_path / "tests" / "utils.test.js",
|
|
test_framework="jest",
|
|
test_timeout=30,
|
|
trace_id="test-language-param-js",
|
|
test_index=0,
|
|
language="javascript",
|
|
)
|
|
|
|
assert captured_payload is not None
|
|
assert captured_payload.get("language") == "javascript", (
|
|
f"Expected language='javascript', got: {captured_payload.get('language')}"
|
|
)
|