mirror of
https://github.com/codeflash-ai/codeflash-internal.git
synced 2026-05-04 18:25:18 +00:00
# Pull Request Checklist ## Description - [ ] **Breaking Changes**: Document any breaking changes (if applicable) - [ ] **Description of PR**: Clear and concise description of what this PR accomplishes - [ ] **Related Issues**: Link to any related issues or tickets ## Testing - [ ] **Test cases Attached**: All relevant test cases have been added/updated - [ ] **Manual Testing**: Manual testing completed for the changes ## Monitoring & Debugging - [ ] **Logging in place**: Appropriate logging has been added for debugging user issues - [ ] **Sentry will be able to catch errors**: Error handling ensures Sentry can capture and report errors - [ ] **Avoid Dev based/Prisma logging**: No development-only or Prisma-specific logging in production code ## Configuration - [ ] **Env variables newly added**: Any new environment variables are documented in .env.example file or mentioned in description --- ## Additional Notes <!-- Add any additional context, screenshots, or notes for reviewers here --> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> Co-authored-by: HeshamHM28 <HeshamMohamedFathy@outlook.com> Co-authored-by: Ubuntu <ubuntu@ip-172-31-39-200.ec2.internal> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: Kevin Turcios <turcioskevinr@gmail.com> Co-authored-by: Kevin Turcios <106575910+KRRT7@users.noreply.github.com>
287 lines
9.4 KiB
Python
287 lines
9.4 KiB
Python
"""Tests for JavaScript test generation module.
|
|
|
|
Tests the prompt building and validation functions without importing
|
|
the full testgen module (to avoid LLM dependencies in tests).
|
|
"""
|
|
|
|
import re
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
# Load prompts directly to avoid importing testgen_javascript.py
|
|
current_dir = Path(__file__).parent.parent.parent / "core" / "languages" / "python" / "testgen"
|
|
JS_PROMPTS_DIR = current_dir / "prompts" / "javascript"
|
|
|
|
JS_EXECUTE_SYSTEM_PROMPT = (JS_PROMPTS_DIR / "execute_system_prompt.md").read_text()
|
|
JS_EXECUTE_USER_PROMPT = (JS_PROMPTS_DIR / "execute_user_prompt.md").read_text()
|
|
JS_EXECUTE_ASYNC_SYSTEM_PROMPT = (JS_PROMPTS_DIR / "execute_async_system_prompt.md").read_text()
|
|
JS_EXECUTE_ASYNC_USER_PROMPT = (JS_PROMPTS_DIR / "execute_async_user_prompt.md").read_text()
|
|
|
|
JS_PATTERN = re.compile(r"^```(?:javascript|js|typescript|ts)?\s*\n(.*?)\n```", re.MULTILINE | re.DOTALL)
|
|
|
|
|
|
def _has_test_functions(code: str) -> bool:
|
|
"""Check if the code contains Jest test functions."""
|
|
test_pattern = r"(?:test|it)\s*\(\s*['\"]"
|
|
return bool(re.search(test_pattern, code))
|
|
|
|
|
|
def build_javascript_prompt(
|
|
function_name: str, function_code: str, module_path: str, test_framework: str, is_async: bool
|
|
) -> tuple[list[dict[str, str]], str]:
|
|
"""Build the prompt messages for JavaScript test generation."""
|
|
if is_async:
|
|
system_prompt = JS_EXECUTE_ASYNC_SYSTEM_PROMPT
|
|
user_prompt = JS_EXECUTE_ASYNC_USER_PROMPT
|
|
posthog_event_suffix = "async-"
|
|
else:
|
|
system_prompt = JS_EXECUTE_SYSTEM_PROMPT
|
|
user_prompt = JS_EXECUTE_USER_PROMPT
|
|
posthog_event_suffix = ""
|
|
|
|
system_message = {"role": "system", "content": system_prompt.format(function_name=function_name)}
|
|
|
|
user_message = {
|
|
"role": "user",
|
|
"content": user_prompt.format(
|
|
test_framework=test_framework,
|
|
function_name=function_name,
|
|
function_code=function_code,
|
|
module_path=module_path,
|
|
package_comment="",
|
|
),
|
|
}
|
|
|
|
messages = [system_message, user_message]
|
|
return messages, posthog_event_suffix
|
|
|
|
|
|
def parse_and_validate_js_output(response_content: str) -> str:
|
|
"""Parse and validate the LLM response for JavaScript code."""
|
|
if "```" not in response_content:
|
|
raise ValueError("LLM response did not contain a code block.")
|
|
|
|
pattern_res = JS_PATTERN.search(response_content)
|
|
if not pattern_res:
|
|
raise ValueError("No JavaScript code block found in the LLM response.")
|
|
|
|
code = pattern_res.group(1).strip()
|
|
|
|
if not _has_test_functions(code):
|
|
raise ValueError("Generated code does not contain any test functions.")
|
|
|
|
return code
|
|
|
|
|
|
class TestHasTestFunctions:
|
|
"""Tests for detecting Jest test functions in code."""
|
|
|
|
def test_detects_test_function(self) -> None:
|
|
"""Test that test() calls are detected."""
|
|
code = """
|
|
test('should add numbers', () => {
|
|
expect(add(1, 2)).toBe(3);
|
|
});
|
|
"""
|
|
assert _has_test_functions(code) is True
|
|
|
|
def test_detects_it_function(self) -> None:
|
|
"""Test that it() calls are detected."""
|
|
code = """
|
|
it('should add numbers', () => {
|
|
expect(add(1, 2)).toBe(3);
|
|
});
|
|
"""
|
|
assert _has_test_functions(code) is True
|
|
|
|
def test_detects_test_in_describe(self) -> None:
|
|
"""Test that tests inside describe blocks are detected."""
|
|
code = """
|
|
describe('Calculator', () => {
|
|
test('should add', () => {
|
|
expect(add(1, 2)).toBe(3);
|
|
});
|
|
});
|
|
"""
|
|
assert _has_test_functions(code) is True
|
|
|
|
def test_no_tests_returns_false(self) -> None:
|
|
"""Test that code without tests returns false."""
|
|
code = """
|
|
function add(a, b) {
|
|
return a + b;
|
|
}
|
|
"""
|
|
assert _has_test_functions(code) is False
|
|
|
|
def test_empty_code_returns_false(self) -> None:
|
|
"""Test that empty code returns false."""
|
|
assert _has_test_functions("") is False
|
|
|
|
def test_describe_only_returns_false(self) -> None:
|
|
"""Test that describe without tests returns false."""
|
|
code = """
|
|
describe('Calculator', () => {
|
|
// No tests yet
|
|
});
|
|
"""
|
|
assert _has_test_functions(code) is False
|
|
|
|
|
|
class TestParseAndValidateJsOutput:
|
|
"""Tests for parsing and validating LLM output."""
|
|
|
|
def test_extracts_javascript_code(self) -> None:
|
|
"""Test extracting code from JavaScript code block."""
|
|
response = """Here are the tests:
|
|
|
|
```javascript
|
|
test('should work', () => {
|
|
expect(true).toBe(true);
|
|
});
|
|
```
|
|
"""
|
|
code = parse_and_validate_js_output(response)
|
|
|
|
assert "test('should work'" in code
|
|
assert "expect(true)" in code
|
|
|
|
def test_extracts_js_code(self) -> None:
|
|
"""Test extracting code from js code block."""
|
|
response = """```js
|
|
test('basic', () => {
|
|
expect(1).toBe(1);
|
|
});
|
|
```"""
|
|
code = parse_and_validate_js_output(response)
|
|
|
|
assert "test('basic'" in code
|
|
|
|
def test_raises_on_no_code_block(self) -> None:
|
|
"""Test that missing code block raises ValueError."""
|
|
response = "This response has no code block."
|
|
|
|
with pytest.raises(ValueError) as exc_info:
|
|
parse_and_validate_js_output(response)
|
|
|
|
assert "code block" in str(exc_info.value).lower()
|
|
|
|
def test_raises_on_no_tests(self) -> None:
|
|
"""Test that code without tests raises ValueError."""
|
|
response = """```javascript
|
|
function add(a, b) {
|
|
return a + b;
|
|
}
|
|
```"""
|
|
|
|
with pytest.raises(ValueError) as exc_info:
|
|
parse_and_validate_js_output(response)
|
|
|
|
assert "test" in str(exc_info.value).lower()
|
|
|
|
|
|
class TestBuildJavaScriptPrompt:
|
|
"""Tests for building JavaScript prompts."""
|
|
|
|
def test_sync_function_prompt(self) -> None:
|
|
"""Test building prompt for sync function."""
|
|
messages, suffix = build_javascript_prompt(
|
|
function_name="fibonacci",
|
|
function_code="function fibonacci(n) { return n <= 1 ? n : fibonacci(n-1) + fibonacci(n-2); }",
|
|
module_path="./fibonacci",
|
|
test_framework="jest",
|
|
is_async=False,
|
|
)
|
|
|
|
assert len(messages) == 2
|
|
assert messages[0]["role"] == "system"
|
|
assert messages[1]["role"] == "user"
|
|
assert "fibonacci" in messages[0]["content"]
|
|
assert "fibonacci" in messages[1]["content"]
|
|
assert suffix == "" # Non-async should have empty suffix
|
|
|
|
def test_async_function_prompt(self) -> None:
|
|
"""Test building prompt for async function."""
|
|
messages, suffix = build_javascript_prompt(
|
|
function_name="fetchData",
|
|
function_code="async function fetchData(url) { return await fetch(url); }",
|
|
module_path="./api",
|
|
test_framework="jest",
|
|
is_async=True,
|
|
)
|
|
|
|
assert len(messages) == 2
|
|
assert "async" in messages[0]["content"].lower()
|
|
assert suffix == "async-"
|
|
|
|
def test_prompt_includes_function_code(self) -> None:
|
|
"""Test that prompt includes the function source code."""
|
|
function_code = "function add(a, b) { return a + b; }"
|
|
messages, _ = build_javascript_prompt(
|
|
function_name="add",
|
|
function_code=function_code,
|
|
module_path="./math",
|
|
test_framework="jest",
|
|
is_async=False,
|
|
)
|
|
|
|
# User message should contain the function code
|
|
assert function_code in messages[1]["content"]
|
|
|
|
def test_prompt_includes_module_path(self) -> None:
|
|
"""Test that prompt includes the module path."""
|
|
messages, _ = build_javascript_prompt(
|
|
function_name="add",
|
|
function_code="function add(a, b) { return a + b; }",
|
|
module_path="./math/utils",
|
|
test_framework="jest",
|
|
is_async=False,
|
|
)
|
|
|
|
assert "./math/utils" in messages[1]["content"]
|
|
|
|
|
|
class TestJavaScriptTestGenPromptContent:
|
|
"""Tests for JavaScript test generation prompt content."""
|
|
|
|
def test_system_prompt_mentions_jest(self) -> None:
|
|
"""Test that system prompt mentions Jest framework."""
|
|
messages, _ = build_javascript_prompt(
|
|
function_name="test",
|
|
function_code="function test() {}",
|
|
module_path="./test",
|
|
test_framework="jest",
|
|
is_async=False,
|
|
)
|
|
|
|
assert "Jest" in messages[0]["content"] or "jest" in messages[0]["content"].lower()
|
|
|
|
def test_system_prompt_has_test_categories(self) -> None:
|
|
"""Test that system prompt mentions test categories."""
|
|
messages, _ = build_javascript_prompt(
|
|
function_name="test",
|
|
function_code="function test() {}",
|
|
module_path="./test",
|
|
test_framework="jest",
|
|
is_async=False,
|
|
)
|
|
|
|
system_content = messages[0]["content"]
|
|
# Should mention different test categories
|
|
assert "Basic" in system_content or "basic" in system_content.lower()
|
|
assert "Edge" in system_content or "edge" in system_content.lower()
|
|
|
|
def test_system_prompt_warns_against_mocking(self) -> None:
|
|
"""Test that system prompt warns against mocking the function under test."""
|
|
messages, _ = build_javascript_prompt(
|
|
function_name="test",
|
|
function_code="function test() {}",
|
|
module_path="./test",
|
|
test_framework="jest",
|
|
is_async=False,
|
|
)
|
|
|
|
system_content = messages[0]["content"]
|
|
# Should warn against mocking
|
|
assert "mock" in system_content.lower() or "Mock" in system_content
|