Merge remote-tracking branch 'origin/main' into testgen-review-repair

This commit is contained in:
Kevin Turcios 2026-03-04 04:10:56 -05:00
commit 644ded986f
22 changed files with 1133 additions and 477 deletions

View file

@ -41,11 +41,17 @@ jobs:
uv venv --seed
uv sync
- name: Configure AWS Credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
aws-region: us-east-1
- name: Run Claude Code
id: claude
uses: anthropics/claude-code-action@v1
with:
use_foundry: "true"
use_bedrock: "true"
use_sticky_comment: true
prompt: |
REPO: ${{ github.repository }}
@ -162,12 +168,10 @@ jobs:
2. If found, UPDATE it: `gh api --method PATCH repos/${{ github.repository }}/issues/comments/<ID> -f body="<content>"`
3. If not found, CREATE: `gh pr comment ${{ github.event.pull_request.number }} --body "<content>"`
4. Delete any OTHER claude[bot] comments to clean up duplicates: `gh api repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments --jq '.[] | select(.user.login == "claude[bot]") | .id' | tail -n +2 | xargs -I {} gh api --method DELETE repos/${{ github.repository }}/issues/comments/{}`
claude_args: '--model claude-opus-4-5 --allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*),Bash(gh issue view:*),Bash(gh issue list:*),Bash(gh api:*),Bash(cd django/aiservice*),Bash(uv run prek *),Bash(uv run mypy *),Bash(uv run coverage *),Bash(uv run pytest *),Bash(git status*),Bash(git add *),Bash(git commit *),Bash(git push*),Bash(git diff *),Bash(git checkout *),Read,Glob,Grep,Edit"'
claude_args: '--model us.anthropic.claude-sonnet-4-6 --allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*),Bash(gh issue view:*),Bash(gh issue list:*),Bash(gh api:*),Bash(cd django/aiservice*),Bash(uv run prek *),Bash(uv run mypy *),Bash(uv run coverage *),Bash(uv run pytest *),Bash(git status*),Bash(git add *),Bash(git commit *),Bash(git push*),Bash(git diff *),Bash(git checkout *),Read,Glob,Grep,Edit"'
additional_permissions: |
actions: read
env:
ANTHROPIC_FOUNDRY_API_KEY: ${{ secrets.ANTHROPIC_FOUNDRY_API_KEY }}
ANTHROPIC_FOUNDRY_BASE_URL: ${{ secrets.ANTHROPIC_FOUNDRY_BASE_URL }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
DJANGO_SETTINGS_MODULE: aiservice.settings
@ -217,14 +221,17 @@ jobs:
uv venv --seed
uv sync
- name: Configure AWS Credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
aws-region: us-east-1
- name: Run Claude Code
id: claude
uses: anthropics/claude-code-action@v1
with:
use_foundry: "true"
claude_args: '--model claude-opus-4-5 --allowedTools "Read,Edit,Write,Glob,Grep,Bash(git status*),Bash(git diff*),Bash(git add *),Bash(git commit *),Bash(git push*),Bash(git log*),Bash(git merge*),Bash(git fetch*),Bash(git checkout*),Bash(git branch*),Bash(cd django/aiservice*),Bash(uv run prek *),Bash(prek *),Bash(uv run ruff *),Bash(uv run pytest *),Bash(uv run mypy *),Bash(uv run coverage *),Bash(gh pr comment*),Bash(gh pr view*),Bash(gh pr diff*),Bash(gh pr merge*),Bash(gh pr close*)"'
use_bedrock: "true"
claude_args: '--model us.anthropic.claude-sonnet-4-6 --allowedTools "Read,Edit,Write,Glob,Grep,Bash(git status*),Bash(git diff*),Bash(git add *),Bash(git commit *),Bash(git push*),Bash(git log*),Bash(git merge*),Bash(git fetch*),Bash(git checkout*),Bash(git branch*),Bash(cd django/aiservice*),Bash(uv run prek *),Bash(prek *),Bash(uv run ruff *),Bash(uv run pytest *),Bash(uv run mypy *),Bash(uv run coverage *),Bash(gh pr comment*),Bash(gh pr view*),Bash(gh pr diff*),Bash(gh pr merge*),Bash(gh pr close*)"'
additional_permissions: |
actions: read
env:
ANTHROPIC_FOUNDRY_API_KEY: ${{ secrets.ANTHROPIC_FOUNDRY_API_KEY }}
ANTHROPIC_FOUNDRY_BASE_URL: ${{ secrets.ANTHROPIC_FOUNDRY_BASE_URL }}

View file

@ -42,13 +42,19 @@ jobs:
}
EOF
- name: Configure AWS Credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
aws-region: us-east-1
- name: Run Claude Code
uses: anthropics/claude-code-action@v1
with:
use_foundry: "true"
use_bedrock: "true"
use_sticky_comment: true
allowed_bots: "claude[bot],codeflash-ai[bot]"
claude_args: '--mcp-config /tmp/mcp-config/mcp-servers.json --allowedTools "Read,Glob,Grep,Bash(git diff:*),Bash(git log:*),Bash(git show:*),Bash(wc *),Bash(find *),mcp__serena__*"'
claude_args: '--model us.anthropic.claude-sonnet-4-6 --mcp-config /tmp/mcp-config/mcp-servers.json --allowedTools "Read,Glob,Grep,Bash(git diff:*),Bash(git log:*),Bash(git show:*),Bash(wc *),Bash(find *),mcp__serena__*"'
prompt: |
You are a duplicate code detector with access to Serena semantic code analysis.
@ -108,10 +114,6 @@ jobs:
- Concrete refactoring suggestion
If no significant duplication is found, say so briefly. Do not create issues — just comment on the PR.
env:
ANTHROPIC_FOUNDRY_API_KEY: ${{ secrets.ANTHROPIC_FOUNDRY_API_KEY }}
ANTHROPIC_FOUNDRY_BASE_URL: ${{ secrets.ANTHROPIC_FOUNDRY_BASE_URL }}
- name: Stop Serena
if: always()
run: docker stop serena && docker rm serena || true

View file

@ -71,6 +71,8 @@ CRITICAL - Handling Complex Parameter Types:
- If the real class has a builder or factory pattern, use it
- Check if there are concrete implementations you can instantiate directly
The target Java version is {language_version}.
Function to test: {function_name}
"""
@ -101,6 +103,8 @@ CRITICAL - Handling Complex Parameter Types:
- If the real class has a builder or factory pattern, use it
- Check if there are concrete implementations you can instantiate directly
The target Java version is {language_version}.
Function to test: {function_name}
"""
@ -223,6 +227,7 @@ def build_java_prompt(
class_name: str,
package_name: str,
test_framework: str = "junit5",
language_version: str = "17",
) -> tuple[list[ChatCompletionMessageParam], str]:
"""Build the prompt messages for Java test generation.
@ -233,6 +238,7 @@ def build_java_prompt(
class_name: Name of the class containing the function
package_name: Package name for the test class
test_framework: Test framework to use ("junit5" or "junit4")
language_version: Target Java version (e.g., "11", "17", "21")
Returns:
Tuple of (messages, posthog_event_suffix)
@ -248,7 +254,7 @@ def build_java_prompt(
system_message: ChatCompletionMessageParam = {
"role": "system",
"content": system_prompt.format(function_name=function_name),
"content": system_prompt.format(function_name=function_name, language_version=f"Java {language_version}"),
}
user_message: ChatCompletionMessageParam = {
@ -1403,6 +1409,7 @@ async def testgen_java(
class_name=class_name,
package_name=package_name,
test_framework=test_framework,
language_version=data.language_version or "17",
)
# Track costs

View file

@ -0,0 +1,21 @@
{#- Render a JS/TS import statement based on style and module system.
Args:
style: "default", "named", or "namespace"
name: the binding name (class name, function name, or module alias)
path: the module path
module_system: "esm" for ES modules, anything else for CommonJS
-#}
{%- macro js_import(style, name, path, module_system) -%}
{%- if module_system == "esm" -%}
{%- if style == "named" -%} import { {{ name }} } from '{{ path }}';
{%- elif style == "namespace" -%}import * as {{ name }} from '{{ path }}';
{%- else -%} import {{ name }} from '{{ path }}';
{%- endif -%}
{%- else -%}
{%- if style == "named" -%} const { {{ name }} } = require('{{ path }}');
{%- elif style == "namespace" -%}const {{ name }} = require('{{ path }}');
{%- else -%} const {{ name }} = require('{{ path }}');
{%- endif -%}
{%- endif -%}
{%- endmacro -%}

View file

@ -0,0 +1,151 @@
{% extends "mocha_system.md.j2" %}
{% block role_qualifier %} for **asynchronous** code using Mocha and Node.js built-in assert{% endblock %}
{% block task_target %}**async** {{ function_name }}{% endblock %}
{% block task_qualifier %} with proper async/await handling{% endblock %}
{% block async_section %}
<async_requirements>
**CRITICAL: ASYNC TEST REQUIREMENTS**
- The function under test is **asynchronous** - all tests must handle async properly
- Use `async/await` syntax in test functions
- Ensure all promises are awaited
- Test both successful resolution and rejection scenarios
- Handle async timeouts appropriately
</async_requirements>
{% endblock %}
{% block categories %}
**1. Basic Test Cases**:
- **Objective**: To verify the fundamental async functionality of the {{ function_name }} function under normal conditions.
**2. Edge Test Cases**:
- **Objective**: To evaluate the async function's behavior under extreme or unusual conditions, including error handling.
**3. Large Scale Test Cases**:
- **Objective**: To assess the async function's correctness and scalability with larger inputs.
{% endblock %}
{% block determinism_rule %}- **CRITICAL: TESTS MUST BE DETERMINISTIC** - Every test must produce the same pass/fail result on every run against the same code. Never assert on execution timing, elapsed duration, or the relative ordering of async side effects. Only assert on return values, resolved/rejected outcomes, and observable state.
{% endblock %}
{% block extra_instructions %}- **CRITICAL: TEST REJECTION CASES** - Use `await assert.rejects(async () => { ... })` for testing async errors.
{% endblock %}
{% block framework_rules %}
<rules>
**CRITICAL: IMPORT PATH RULES**:
- **NEVER add file extensions (.js, .ts, .tsx) to import paths** - The test framework resolves extensions automatically.
- **WRONG**: `const { fn } = require('../utils.js')` or `const { fn } = require('../utils.ts')`
- **CORRECT**: `const { fn } = require('../utils')`
- The user message provides the exact import statement to use - copy it exactly without modification.
**ASSERTION AND TEST FRAMEWORK: Mocha + node:assert/strict**
This project uses **Mocha**. The ONLY assertion module available is **node:assert/strict** (built into Node.js).
- `describe` and `it` are **Mocha globals** — they require no import.
- Import assert with: `const assert = require('node:assert/strict');`
- Write each test with `it('description', async () => { ... });`
Available assertion methods:
- `assert.strictEqual(actual, expected)` — primitive equality (numbers, strings, booleans)
- `assert.deepStrictEqual(actual, expected)` — object/array deep equality
- `await assert.rejects(async () => { ... })` — verify an async function rejects
- `assert.ok(value)` — verify a value is truthy
- `assert.notStrictEqual(actual, expected)` — verify values are not equal
Example test:
```javascript
const assert = require('node:assert/strict');
const { myFunc } = require('../myModule');
describe('myFunc', () => {
it('should resolve with correct value', async () => {
const result = await myFunc(21);
assert.strictEqual(result, 42);
});
it('should reject on invalid input', async () => {
await assert.rejects(async () => { await myFunc(null); });
});
});
```
</rules>
{% endblock %}
{% block test_signature %}async test using `it('...', async () => ...)`{% endblock %}
{% block md_role_qualifier %} for **asynchronous** code using Mocha and Node.js built-in assert{% endblock %}
{% block md_task_target %}**async** {{ function_name }}{% endblock %}
{% block md_task_qualifier %} with proper async/await handling{% endblock %}
{% block md_async_section %}
### CRITICAL: ASYNC TEST REQUIREMENTS
- The function under test is **asynchronous** - all tests must handle async properly
- Use `async/await` syntax in test functions
- Ensure all promises are awaited
- Test both successful resolution and rejection scenarios
- Handle async timeouts appropriately
{% endblock %}
{% block md_categories %}
**1. Basic Test Cases**:
- **Objective**: To verify the fundamental async functionality of the {{ function_name }} function under normal conditions.
**2. Edge Test Cases**:
- **Objective**: To evaluate the async function's behavior under extreme or unusual conditions, including error handling.
**3. Large Scale Test Cases**:
- **Objective**: To assess the async function's correctness and scalability with larger inputs.
{% endblock %}
{% block md_determinism_rule %}- **CRITICAL: TESTS MUST BE DETERMINISTIC** - Every test must produce the same pass/fail result on every run against the same code. Never assert on execution timing, elapsed duration, or the relative ordering of async side effects. Only assert on return values, resolved/rejected outcomes, and observable state.
{% endblock %}
{% block md_extra_instructions %}- **CRITICAL: TEST REJECTION CASES** - Use `await assert.rejects(async () => { ... })` for testing async errors.
{% endblock %}
{% block md_framework_rules %}
## Import Path Rules
- **NEVER add file extensions (.js, .ts, .tsx) to import paths** - The test framework resolves extensions automatically.
- **WRONG**: `const { fn } = require('../utils.js')` or `const { fn } = require('../utils.ts')`
- **CORRECT**: `const { fn } = require('../utils')`
- The user message provides the exact import statement to use - copy it exactly without modification.
## Assertion and Test Framework: Mocha + node:assert/strict
This project uses **Mocha**. The ONLY assertion module available is **node:assert/strict** (built into Node.js).
- `describe` and `it` are **Mocha globals** — they require no import.
- Import assert with: `const assert = require('node:assert/strict');`
- Write each test with `it('description', async () => { ... });`
Available assertion methods:
- `assert.strictEqual(actual, expected)` — primitive equality (numbers, strings, booleans)
- `assert.deepStrictEqual(actual, expected)` — object/array deep equality
- `await assert.rejects(async () => { ... })` — verify an async function rejects
- `assert.ok(value)` — verify a value is truthy
- `assert.notStrictEqual(actual, expected)` — verify values are not equal
Example test:
```javascript
const assert = require('node:assert/strict');
const { myFunc } = require('../myModule');
describe('myFunc', () => {
it('should resolve with correct value', async () => {
const result = await myFunc(21);
assert.strictEqual(result, 42);
});
it('should reject on invalid input', async () => {
await assert.rejects(async () => { await myFunc(null); });
});
});
```
{% endblock %}
{% block md_test_signature %}async test using `it('...', async () => ...)`{% endblock %}

View file

@ -0,0 +1,69 @@
{% extends "base_system.md.j2" %}
{% block role_qualifier %} for **asynchronous** code{% endblock %}
{% block task_target %}**async** {{ function_name }}{% endblock %}
{% block task_qualifier %} with proper async/await handling{% endblock %}
{% block async_section %}
<async_requirements>
**CRITICAL: ASYNC TEST REQUIREMENTS**
- The function under test is **asynchronous** - all tests must handle async properly
- Use `async/await` syntax in test functions
- Ensure all promises are awaited
- Test both successful resolution and rejection scenarios
- Handle async timeouts appropriately
</async_requirements>
{% endblock %}
{% block categories %}
**1. Basic Test Cases**:
- **Objective**: To verify the fundamental async functionality of the {{ function_name }} function under normal conditions.
**2. Edge Test Cases**:
- **Objective**: To evaluate the async function's behavior under extreme or unusual conditions, including error handling.
**3. Large Scale Test Cases**:
- **Objective**: To assess the async function's correctness and scalability with larger inputs.
{% endblock %}
{% block determinism_rule %}- **CRITICAL: TESTS MUST BE DETERMINISTIC** - Every test must produce the same pass/fail result on every run against the same code. Never assert on execution timing, elapsed duration, or the relative ordering of async side effects. Only assert on return values, resolved/rejected outcomes, and observable state.
{% endblock %}
{% block extra_instructions %}- **CRITICAL: TEST REJECTION CASES** - Use `expect(...).rejects.toThrow()` for testing async errors.
{% endblock %}
{% block test_signature %}async test using `test('...', async () => ...)`{% endblock %}
{% block md_role_qualifier %} for **asynchronous** code{% endblock %}
{% block md_task_target %}**async** {{ function_name }}{% endblock %}
{% block md_task_qualifier %} with proper async/await handling{% endblock %}
{% block md_async_section %}
### CRITICAL: ASYNC TEST REQUIREMENTS
- The function under test is **asynchronous** - all tests must handle async properly
- Use `async/await` syntax in test functions
- Ensure all promises are awaited
- Test both successful resolution and rejection scenarios
- Handle async timeouts appropriately
{% endblock %}
{% block md_categories %}
**1. Basic Test Cases**:
- **Objective**: To verify the fundamental async functionality of the {{ function_name }} function under normal conditions.
**2. Edge Test Cases**:
- **Objective**: To evaluate the async function's behavior under extreme or unusual conditions, including error handling.
**3. Large Scale Test Cases**:
- **Objective**: To assess the async function's correctness and scalability with larger inputs.
{% endblock %}
{% block md_determinism_rule %}- **CRITICAL: TESTS MUST BE DETERMINISTIC** - Every test must produce the same pass/fail result on every run against the same code. Never assert on execution timing, elapsed duration, or the relative ordering of async side effects. Only assert on return values, resolved/rejected outcomes, and observable state.
{% endblock %}
{% block md_extra_instructions %}- **CRITICAL: TEST REJECTION CASES** - Use `expect(...).rejects.toThrow()` for testing async errors.
{% endblock %}
{% block md_test_signature %}async test using `test('...', async () => ...)`{% endblock %}

View file

@ -0,0 +1,134 @@
{% if model_type == "anthropic" %}
<role>
You are Codeflash, a world-class JavaScript/TypeScript developer with an eagle eye for unintended bugs and edge cases. You write careful, accurate unit tests{% block role_qualifier %}{% endblock %}. When asked to reply only with code, you write all of your code in a single markdown code block.
</role>
<task>
Your task is to create comprehensive, high quality test cases for the {% block task_target %}{{ function_name }}{% endblock %} function. These test cases should encompass Basic, Edge, and Large Scale scenarios to ensure the code's robustness, reliability, and scalability{% block task_qualifier %}{% endblock %}.
</task>
{% block async_section %}{% endblock %}
<test_categories>
{% block categories %}
**1. Basic Test Cases**:
- **Objective**: To verify the fundamental functionality of the {{ function_name }} function under normal conditions.
**2. Edge Test Cases**:
- **Objective**: To evaluate the function's behavior under extreme or unusual conditions.
**3. Large Scale Test Cases**:
- **Objective**: To assess the function's performance and scalability with large data samples.
{% endblock %}
</test_categories>
<instructions>
- Implement a comprehensive set of test cases following the guidelines above.
{% block framework_instruction %}- Use the testing framework specified in the user message with `describe`, `test`/`it`, and `expect`.{% endblock %}
- Ensure each test case is well-documented with comments explaining the scenario it covers.
- Pay special attention to edge cases as they often reveal hidden bugs.
- For large-scale tests, focus on the function's efficiency and performance under heavy loads. Avoid loops exceeding 1000 iterations, and keep data structures under 1000 elements.
{% block determinism_rule %}{% endblock %}
- **CRITICAL: DO NOT MOCK THE FUNCTION UNDER TEST** - Never mock, stub, or spy on the {{ function_name }} function itself. You may mock external dependencies (APIs, databases, network calls, file I/O, etc.) if necessary, but the function being tested must execute with its real implementation.
- **CRITICAL: IMPORT FROM REAL MODULES** - Import the function and any related classes/utilities from their actual module paths as shown in the context.
- **CRITICAL: HANDLE ASYNC PROPERLY** - If the function is async, use `async/await` in your tests and ensure all promises are properly awaited.
{% block extra_instructions %}{% endblock %}
</instructions>
{%- block framework_rules %}
<rules>
**CRITICAL: IMPORT PATH RULES**:
- **NEVER add file extensions (.js, .ts, .tsx) to import paths** - The test framework resolves extensions automatically.
- **WRONG**: `import { fn } from '../utils.js'` or `import { fn } from '../utils.ts'`
- **CORRECT**: `import { fn } from '../utils'`
- The user message provides the exact import statement to use - copy it exactly without modification.
**CRITICAL: VITEST IMPORTS REQUIRED**:
- If test_framework is "vitest", you MUST import test functions from 'vitest' since globals are NOT enabled by default:
```javascript
import { describe, test, expect, vi, beforeEach, afterEach } from 'vitest';
```
- For "jest", globals are typically enabled so no import is needed.
**CRITICAL: MOCKING RULES**:
- **USE THE CORRECT MOCK SYNTAX FOR THE SPECIFIED FRAMEWORK**:
- For **Jest**: Use `jest.mock()` and `jest.fn()`
- For **Vitest**: Use `vi.mock()` and `vi.fn()` - NEVER use jest.mock with Vitest!
- Mock calls are HOISTED to the top of the file. NEVER use dynamic expressions - use static string literals only.
- **ALWAYS use static string literals** for mock paths.
- **IMPORTANT**: Check the test framework specified in the user message and use the matching syntax.
</rules>
{%- endblock %}
<output_format>
- Your response MUST be a single markdown code block containing valid JavaScript/TypeScript code.
- Do NOT nest code blocks inside each other.
- The code block MUST contain at least one {% block test_signature %}test using `test()` or `it()`{% endblock %}.
- Follow the exact template structure provided in the user message.
</output_format>
{% else %}
You are Codeflash, a world-class JavaScript/TypeScript developer with an eagle eye for unintended bugs and edge cases. You write careful, accurate unit tests{% block md_role_qualifier %}{% endblock %}. When asked to reply only with code, you write all of your code in a single markdown code block.
## Task
Your task is to create comprehensive, high quality test cases for the {% block md_task_target %}{{ function_name }}{% endblock %} function. These test cases should encompass Basic, Edge, and Large Scale scenarios to ensure the code's robustness, reliability, and scalability{% block md_task_qualifier %}{% endblock %}.
{% block md_async_section %}{% endblock %}
## Test Categories
{% block md_categories %}
**1. Basic Test Cases**:
- **Objective**: To verify the fundamental functionality of the {{ function_name }} function under normal conditions.
**2. Edge Test Cases**:
- **Objective**: To evaluate the function's behavior under extreme or unusual conditions.
**3. Large Scale Test Cases**:
- **Objective**: To assess the function's performance and scalability with large data samples.
{% endblock %}
## Instructions
- Implement a comprehensive set of test cases following the guidelines above.
{% block md_framework_instruction %}- Use the testing framework specified in the user message with `describe`, `test`/`it`, and `expect`.{% endblock %}
- Ensure each test case is well-documented with comments explaining the scenario it covers.
- Pay special attention to edge cases as they often reveal hidden bugs.
- For large-scale tests, focus on the function's efficiency and performance under heavy loads. Avoid loops exceeding 1000 iterations, and keep data structures under 1000 elements.
{% block md_determinism_rule %}{% endblock %}
- **CRITICAL: DO NOT MOCK THE FUNCTION UNDER TEST** - Never mock, stub, or spy on the {{ function_name }} function itself. You may mock external dependencies (APIs, databases, network calls, file I/O, etc.) if necessary, but the function being tested must execute with its real implementation.
- **CRITICAL: IMPORT FROM REAL MODULES** - Import the function and any related classes/utilities from their actual module paths as shown in the context.
- **CRITICAL: HANDLE ASYNC PROPERLY** - If the function is async, use `async/await` in your tests and ensure all promises are properly awaited.
{% block md_extra_instructions %}{% endblock %}
{%- block md_framework_rules %}
## Import Path Rules
- **NEVER add file extensions (.js, .ts, .tsx) to import paths** - The test framework resolves extensions automatically.
- **WRONG**: `import { fn } from '../utils.js'` or `import { fn } from '../utils.ts'`
- **CORRECT**: `import { fn } from '../utils'`
- The user message provides the exact import statement to use - copy it exactly without modification.
## Vitest Imports
- If test_framework is "vitest", you MUST import test functions from 'vitest' since globals are NOT enabled by default:
```javascript
import { describe, test, expect, vi, beforeEach, afterEach } from 'vitest';
```
- For "jest", globals are typically enabled so no import is needed.
## Mocking Rules
- **USE THE CORRECT MOCK SYNTAX FOR THE SPECIFIED FRAMEWORK**:
- For **Jest**: Use `jest.mock()` and `jest.fn()`
- For **Vitest**: Use `vi.mock()` and `vi.fn()` - NEVER use jest.mock with Vitest!
- Mock calls are HOISTED to the top of the file. NEVER use dynamic expressions - use static string literals only.
- **ALWAYS use static string literals** for mock paths.
- **IMPORTANT**: Check the test framework specified in the user message and use the matching syntax.
{%- endblock %}
## Output Format
- Your response MUST be a single markdown code block containing valid JavaScript/TypeScript code.
- Do NOT nest code blocks inside each other.
- The code block MUST contain at least one {% block md_test_signature %}test using `test()` or `it()`{% endblock %}.
- Follow the exact template structure provided in the user message.
{% endif %}

View file

@ -1,59 +0,0 @@
**Role**: You are Codeflash, a world-class JavaScript/TypeScript developer with an eagle eye for unintended bugs and edge cases. You write careful, accurate unit tests for **asynchronous** code. When asked to reply only with code, you write all of your code in a single markdown code block.
**Task** Your task is to create comprehensive, high quality test cases for the **async** {function_name} function. These test cases should encompass Basic, Edge, and Large Scale scenarios to ensure the code's robustness, reliability, and scalability with proper async/await handling.
**CRITICAL: ASYNC TEST REQUIREMENTS**
- The function under test is **asynchronous** - all tests must handle async properly
- Use `async/await` syntax in test functions
- Ensure all promises are awaited
- Test both successful resolution and rejection scenarios
- Handle async timeouts appropriately
**1. Basic Test Cases**:
- **Objective**: To verify the fundamental async functionality of the {function_name} function under normal conditions.
**2. Edge Test Cases**:
- **Objective**: To evaluate the async function's behavior under extreme or unusual conditions, including error handling.
**3. Large Scale Test Cases**:
- **Objective**: To assess the async function's performance and scalability with concurrent operations and large data samples.
**Instructions**:
- Implement a comprehensive set of test cases following the guidelines above.
- Use the testing framework specified in the user message with `describe`, `test`/`it`, and `expect`.
- **ALL test functions must be async**: `test('...', async () => {{ ... }})`
- **ALL calls to the function must be awaited**: `const result = await {function_name}(...)`
- Ensure each test case is well-documented with comments explaining the scenario it covers.
- Pay special attention to edge cases including async error handling.
- For large-scale tests, consider concurrent execution with `Promise.all()`.
- Avoid loops exceeding 1000 iterations, and keep data structures under 1000 elements.
- **CRITICAL: DO NOT MOCK THE FUNCTION UNDER TEST** - Never mock, stub, or spy on the {function_name} function itself.
- **CRITICAL: TEST REJECTION CASES** - Use `expect(...).rejects.toThrow()` for testing async errors.
**CRITICAL: IMPORT PATH RULES**:
- **NEVER add file extensions (.js, .ts, .tsx) to import paths** - The test framework resolves extensions automatically.
- **WRONG**: `import {{ fn }} from '../utils.js'` or `import {{ fn }} from '../utils.ts'`
- **CORRECT**: `import {{ fn }} from '../utils'`
- The user message provides the exact import statement to use - copy it exactly without modification.
**CRITICAL: VITEST IMPORTS REQUIRED**:
- If test_framework is "vitest", you MUST import test functions from 'vitest' since globals are NOT enabled by default:
```javascript
import {{ describe, test, expect, vi, beforeEach, afterEach }} from 'vitest';
```
- For "jest", globals are typically enabled so no import is needed.
**CRITICAL: MOCKING RULES**:
- **USE THE CORRECT MOCK SYNTAX FOR THE SPECIFIED FRAMEWORK**:
- For **Jest**: Use `jest.mock()` and `jest.fn()`
- For **Vitest**: Use `vi.mock()` and `vi.fn()` - NEVER use jest.mock with Vitest!
- Mock calls are HOISTED to the top of the file. NEVER use dynamic expressions - use static string literals only.
- **ALWAYS use static string literals** for mock paths.
- **IMPORTANT**: Check the test framework specified in the user message and use the matching syntax.
**Output Format Requirements**:
- Your response MUST be a single markdown code block containing valid JavaScript/TypeScript code.
- Do NOT nest code blocks inside each other.
- The code block MUST contain at least one async test using `test('...', async () => ...)`.
- Follow the exact template structure provided in the user message.

View file

@ -1,66 +0,0 @@
Using the **{test_framework}** testing framework, write a test suite for the following **ASYNC** JavaScript function.
**IMPORTANT**: You MUST use {test_framework} syntax. If {test_framework} is "vitest", use `vi.mock()` and `vi.fn()`. If {test_framework} is "jest", use `jest.mock()` and `jest.fn()`. Do NOT mix frameworks.
**CRITICAL: This function is ASYNCHRONOUS**
- All test functions MUST be async: `test('...', async () => {{ ... }})`
- All calls to {function_name} MUST be awaited: `await {function_name}(...)`
- Test both successful and error cases for async operations
**Function to Test:**
```javascript
{function_code}
```
**CRITICAL: Use this exact import statement (do not modify the path):**
```javascript
{import_statement}
```
**CRITICAL: VITEST IMPORTS REQUIRED**
If test_framework is "vitest", you MUST import test functions from 'vitest' since globals are NOT enabled:
```javascript
import {{ describe, test, expect, vi, beforeEach, afterEach }} from 'vitest';
```
For "jest", globals are typically enabled so no import is needed.
**Template to Follow:**
```javascript
// vitest imports (REQUIRED for vitest - globals are NOT enabled by default)
import {{ describe, test, expect, vi, beforeEach, afterEach }} from 'vitest';
// function import
{import_statement}
// unit tests
describe('{function_name}', () => {{
// Basic Test Cases
describe('Basic async functionality', () => {{
test('should resolve with correct value', async () => {{
const result = await {function_name}(/* args */);
expect(result).toBe(/* expected */);
}});
}});
// Edge Test Cases
describe('Async edge cases', () => {{
test('should handle async error case', async () => {{
await expect({function_name}(/* invalid args */)).rejects.toThrow();
}});
}});
// Large Scale Test Cases
describe('Concurrent execution tests', () => {{
test('should handle multiple concurrent calls', async () => {{
const results = await Promise.all([
{function_name}(/* args1 */),
{function_name}(/* args2 */),
]);
// assertions
}});
}});
}});
```
{package_comment}
Reply only with code, in a single markdown code block.

View file

@ -1,50 +0,0 @@
**Role**: You are Codeflash, a world-class JavaScript/TypeScript developer with an eagle eye for unintended bugs and edge cases. You write careful, accurate unit tests. When asked to reply only with code, you write all of your code in a single markdown code block.
**Task** Your task is to create comprehensive, high quality test cases for the {function_name} function. These test cases should encompass Basic, Edge, and Large Scale scenarios to ensure the code's robustness, reliability, and scalability. These test cases should *define* the {function_name} function, meaning that the function should pass all the tests, and a function with different external functional behavior should fail them.
**1. Basic Test Cases**:
- **Objective**: To verify the fundamental functionality of the {function_name} function under normal conditions.
**2. Edge Test Cases**:
- **Objective**: To evaluate the function's behavior under extreme or unusual conditions.
**3. Large Scale Test Cases**:
- **Objective**: To assess the function's performance and scalability with large data samples.
**Instructions**:
- Implement a comprehensive set of test cases following the guidelines above.
- Use the testing framework specified in the user message with `describe`, `test`/`it`, and `expect`.
- Ensure each test case is well-documented with comments explaining the scenario it covers.
- Pay special attention to edge cases as they often reveal hidden bugs.
- For large-scale tests, focus on the function's efficiency and performance under heavy loads. Avoid loops exceeding 1000 iterations, and keep data structures under 1000 elements.
- **CRITICAL: DO NOT MOCK THE FUNCTION UNDER TEST** - Never mock, stub, or spy on the {function_name} function itself. You may mock external dependencies (APIs, databases, network calls, file I/O, etc.) if necessary, but the function being tested must execute with its real implementation.
- **CRITICAL: IMPORT FROM REAL MODULES** - Import the function and any related classes/utilities from their actual module paths as shown in the context.
- **CRITICAL: HANDLE ASYNC PROPERLY** - If the function is async, use `async/await` in your tests and ensure all promises are properly awaited.
**CRITICAL: IMPORT PATH RULES**:
- **NEVER add file extensions (.js, .ts, .tsx) to import paths** - The test framework resolves extensions automatically.
- **WRONG**: `import {{fn}} from '../utils.js'` or `import {{fn}} from '../utils.ts'`
- **CORRECT**: `import {{fn}} from '../utils'`
- The user message provides the exact import statement to use - copy it exactly without modification.
**CRITICAL: VITEST IMPORTS REQUIRED**:
- If test_framework is "vitest", you MUST import test functions from 'vitest' since globals are NOT enabled by default:
```javascript
import {{ describe, test, expect, vi, beforeEach, afterEach }} from 'vitest';
```
- For "jest", globals are typically enabled so no import is needed.
**CRITICAL: MOCKING RULES**:
- **USE THE CORRECT MOCK SYNTAX FOR THE SPECIFIED FRAMEWORK**:
- For **Jest**: Use `jest.mock()` and `jest.fn()`
- For **Vitest**: Use `vi.mock()` and `vi.fn()` - NEVER use jest.mock with Vitest!
- Mock calls are HOISTED to the top of the file. NEVER use dynamic expressions - use static string literals only.
- **ALWAYS use static string literals** for mock paths.
- **IMPORTANT**: Check the test framework specified in the user message and use the matching syntax.
**Output Format Requirements**:
- Your response MUST be a single markdown code block containing valid JavaScript/TypeScript code.
- Do NOT nest code blocks inside each other.
- The code block MUST contain at least one test using `test()` or `it()`.
- Follow the exact template structure provided in the user message.

View file

@ -1,56 +0,0 @@
Using the **{test_framework}** testing framework, write a test suite for the following JavaScript function.
**IMPORTANT**: You MUST use {test_framework} syntax. If {test_framework} is "vitest", use `vi.mock()` and `vi.fn()`. If {test_framework} is "jest", use `jest.mock()` and `jest.fn()`. Do NOT mix frameworks.
**Function to Test:**
```javascript
{function_code}
```
**CRITICAL: Use this exact import statement - copy it EXACTLY as shown, do NOT add .js or any file extension:**
```javascript
{import_statement}
```
**CRITICAL: VITEST IMPORTS REQUIRED**
If test_framework is "vitest", you MUST import test functions from 'vitest' since globals are NOT enabled:
```javascript
import {{ describe, test, expect, vi, beforeEach, afterEach }} from 'vitest';
```
For "jest", globals are typically enabled so no import is needed.
**Template to Follow:**
```javascript
// vitest imports (REQUIRED for vitest - globals are NOT enabled by default)
import {{ describe, test, expect, vi, beforeEach, afterEach }} from 'vitest';
// function import
{import_statement}
// unit tests
describe('{function_name}', () => {{
// Basic Test Cases
describe('Basic functionality', () => {{
test('should handle normal input', () => {{
// Test implementation
}});
}});
// Edge Test Cases
describe('Edge cases', () => {{
test('should handle edge case', () => {{
// Test implementation
}});
}});
// Large Scale Test Cases
describe('Performance tests', () => {{
test('should handle large inputs efficiently', () => {{
// Test implementation
}});
}});
}});
```
{package_comment}
Reply only with code, in a single markdown code block.

View file

@ -0,0 +1,97 @@
{% extends "base_system.md.j2" %}
{% block role_qualifier %} using Mocha and Node.js built-in assert{% endblock %}
{% block framework_instruction %}- Write each test with `it('description', () => { ... });` using `describe` blocks for organization.{% endblock %}
{% block framework_rules %}
<rules>
**CRITICAL: IMPORT PATH RULES**:
- **NEVER add file extensions (.js, .ts, .tsx) to import paths** - The test framework resolves extensions automatically.
- **WRONG**: `const { fn } = require('../utils.js')` or `const { fn } = require('../utils.ts')`
- **CORRECT**: `const { fn } = require('../utils')`
- The user message provides the exact import statement to use - copy it exactly without modification.
**ASSERTION AND TEST FRAMEWORK: Mocha + node:assert/strict**
This project uses **Mocha**. The ONLY assertion module available is **node:assert/strict** (built into Node.js).
- `describe` and `it` are **Mocha globals** — they require no import.
- Import assert with: `const assert = require('node:assert/strict');`
- Write each test with `it('description', () => { ... });`
Available assertion methods:
- `assert.strictEqual(actual, expected)` — primitive equality (numbers, strings, booleans)
- `assert.deepStrictEqual(actual, expected)` — object/array deep equality
- `assert.throws(() => { ... })` — verify a function throws an error
- `assert.ok(value)` — verify a value is truthy
- `assert.notStrictEqual(actual, expected)` — verify values are not equal
Example test:
```javascript
const assert = require('node:assert/strict');
const { myFunc } = require('../myModule');
describe('myFunc', () => {
it('should return 42 for valid input', () => {
assert.strictEqual(myFunc(21), 42);
});
it('should return an array', () => {
assert.deepStrictEqual(myFunc(), [1, 2, 3]);
});
it('should throw on invalid input', () => {
assert.throws(() => { myFunc(null); });
});
});
```
</rules>
{% endblock %}
{% block test_signature %}test using `it()`{% endblock %}
{% block md_role_qualifier %} using Mocha and Node.js built-in assert{% endblock %}
{% block md_framework_instruction %}- Write each test with `it('description', () => { ... });` using `describe` blocks for organization.{% endblock %}
{% block md_framework_rules %}
## Import Path Rules
- **NEVER add file extensions (.js, .ts, .tsx) to import paths** - The test framework resolves extensions automatically.
- **WRONG**: `const { fn } = require('../utils.js')` or `const { fn } = require('../utils.ts')`
- **CORRECT**: `const { fn } = require('../utils')`
- The user message provides the exact import statement to use - copy it exactly without modification.
## Assertion and Test Framework: Mocha + node:assert/strict
This project uses **Mocha**. The ONLY assertion module available is **node:assert/strict** (built into Node.js).
- `describe` and `it` are **Mocha globals** — they require no import.
- Import assert with: `const assert = require('node:assert/strict');`
- Write each test with `it('description', () => { ... });`
Available assertion methods:
- `assert.strictEqual(actual, expected)` — primitive equality (numbers, strings, booleans)
- `assert.deepStrictEqual(actual, expected)` — object/array deep equality
- `assert.throws(() => { ... })` — verify a function throws an error
- `assert.ok(value)` — verify a value is truthy
- `assert.notStrictEqual(actual, expected)` — verify values are not equal
Example test:
```javascript
const assert = require('node:assert/strict');
const { myFunc } = require('../myModule');
describe('myFunc', () => {
it('should return 42 for valid input', () => {
assert.strictEqual(myFunc(21), 42);
});
it('should return an array', () => {
assert.deepStrictEqual(myFunc(), [1, 2, 3]);
});
it('should throw on invalid input', () => {
assert.throws(() => { myFunc(null); });
});
});
```
{% endblock %}
{% block md_test_signature %}test using `it()`{% endblock %}

View file

@ -0,0 +1 @@
{% extends "base_system.md.j2" %}

View file

@ -0,0 +1,180 @@
{% from "_macros.md.j2" import js_import %}
{%- if test_framework == "mocha" -%}
Using **Mocha** with **node:assert/strict**, write a test suite for the following {% if is_async %}**ASYNC** {% endif %}JavaScript function.
{% if is_async %}
**CRITICAL: This function is ASYNCHRONOUS**
- All test functions MUST be async: `it('...', async () => { ... })`
- All calls to {{ function_name }} MUST be awaited: `await {{ function_name }}(...)`
- Test both successful and error cases for async operations
{% endif %}
**Function to Test:**
```javascript
{{ function_code }}
```
**CRITICAL: Use this exact import statement{% if not is_async %} - copy it EXACTLY as shown, do NOT add .js or any file extension{% else %} (do not modify the path){% endif %}:**
```javascript
{{ js_import(import_style, import_name, module_path, module_system) }}
```
**Assertion API — use ONLY these methods:**
{%- if is_async %}
- `assert.strictEqual(actual, expected)` — for primitive equality
- `assert.deepStrictEqual(actual, expected)` — for object/array equality
- `await assert.rejects(async () => { ... })` — for async error testing
- `assert.ok(value)` — for truthy checks
{%- else %}
- `assert.strictEqual(actual, expected)` — for primitive equality
- `assert.deepStrictEqual(actual, expected)` — for object/array equality
- `assert.throws(() => { ... })` — for error testing
- `assert.ok(value)` — for truthy checks
{%- endif %}
**Template — follow this structure exactly:**
```javascript
const assert = require('node:assert/strict');
{{ js_import(import_style, import_name, module_path, module_system) }}
describe('{{ function_name }}', () => {
{% if is_async %}
// Basic Test Cases
describe('Basic async functionality', () => {
it('should resolve with correct value', async () => {
const result = await {{ function_name }}(/* args */);
assert.strictEqual(result, /* expected */);
});
});
// Edge Test Cases
describe('Async edge cases', () => {
it('should handle async error case', async () => {
await assert.rejects(
async () => { await {{ function_name }}(/* invalid args */); },
{ name: 'Error' }
);
});
});
// Large Scale Test Cases
describe('Large scale tests', () => {
it('should handle large inputs correctly', async () => {
const result = await {{ function_name }}(/* large input */);
assert.strictEqual(result, /* expected */);
});
});
{% else %}
// Basic Test Cases
describe('Basic functionality', () => {
it('should handle normal input', () => {
assert.strictEqual({{ function_name }}(/* args */), /* expected */);
});
});
// Edge Test Cases
describe('Edge cases', () => {
it('should handle edge case', () => {
assert.strictEqual({{ function_name }}(/* args */), /* expected */);
});
});
// Large Scale Test Cases
describe('Performance tests', () => {
it('should handle large inputs efficiently', () => {
assert.ok({{ function_name }}(/* large args */) !== undefined);
});
});
{% endif %}
});
```
{%- else -%}
Using the **{{ test_framework }}** testing framework, write a test suite for the following {% if is_async %}**ASYNC** {% endif %}JavaScript function.
**IMPORTANT**: You MUST use {{ test_framework }} syntax. If {{ test_framework }} is "vitest", use `vi.mock()` and `vi.fn()`. If {{ test_framework }} is "jest", use `jest.mock()` and `jest.fn()`. Do NOT mix frameworks.
{% if is_async %}
**CRITICAL: This function is ASYNCHRONOUS**
- All test functions MUST be async: `test('...', async () => { ... })`
- All calls to {{ function_name }} MUST be awaited: `await {{ function_name }}(...)`
- Test both successful and error cases for async operations
{% endif %}
**Function to Test:**
```javascript
{{ function_code }}
```
**CRITICAL: Use this exact import statement{% if not is_async %} - copy it EXACTLY as shown, do NOT add .js or any file extension{% else %} (do not modify the path){% endif %}:**
```javascript
{{ js_import(import_style, import_name, module_path, module_system) }}
```
**CRITICAL: VITEST IMPORTS REQUIRED**
If test_framework is "vitest", you MUST import test functions from 'vitest' since globals are NOT enabled:
```javascript
import { describe, test, expect, vi, beforeEach, afterEach } from 'vitest';
```
For "jest", globals are typically enabled so no import is needed.
**Template to Follow:**
```javascript
// vitest imports (REQUIRED for vitest - globals are NOT enabled by default)
import { describe, test, expect, vi, beforeEach, afterEach } from 'vitest';
// function import
{{ js_import(import_style, import_name, module_path, module_system) }}
// unit tests
describe('{{ function_name }}', () => {
{% if is_async %}
// Basic Test Cases
describe('Basic async functionality', () => {
test('should resolve with correct value', async () => {
const result = await {{ function_name }}(/* args */);
expect(result).toBe(/* expected */);
});
});
// Edge Test Cases
describe('Async edge cases', () => {
test('should handle async error case', async () => {
await expect({{ function_name }}(/* invalid args */)).rejects.toThrow();
});
});
// Large Scale Test Cases
describe('Large scale tests', () => {
test('should handle large inputs correctly', async () => {
const result = await {{ function_name }}(/* large input */);
expect(result).toEqual(/* expected */);
});
});
{% else %}
// Basic Test Cases
describe('Basic functionality', () => {
test('should handle normal input', () => {
// Test implementation
});
});
// Edge Test Cases
describe('Edge cases', () => {
test('should handle edge case', () => {
// Test implementation
});
});
// Large Scale Test Cases
describe('Performance tests', () => {
test('should handle large inputs efficiently', () => {
// Test implementation
});
});
{% endif %}
});
```
{%- endif %}
{{ package_comment }}
Reply only with code, in a single markdown code block.

View file

@ -25,6 +25,7 @@ from aiservice.validators.javascript_validator import validate_javascript_syntax
from authapp.auth import AuthenticatedRequest
from core.log_features.log_event import update_optimization_cost
from core.log_features.log_features import log_features
from core.shared.jinja_utils import create_prompt_env
from core.shared.testgen_models import (
TestGenerationFailedError,
TestGenErrorResponseSchema,
@ -41,11 +42,7 @@ _TEST_FUNC_RE = re.compile(r"(?:test|it)\s*\(\s*['\"]")
current_dir = Path(__file__).parent
JS_PROMPTS_DIR = current_dir / "prompts" / "testgen"
# Load JavaScript prompts
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()
_jinja_env = create_prompt_env(JS_PROMPTS_DIR)
# Pattern to extract JavaScript code blocks
JS_PATTERN = re.compile(r"^```(?:javascript|js|typescript|ts)?\s*\n(.*?)\n```", re.MULTILINE | re.DOTALL)
@ -157,58 +154,45 @@ def strip_js_extensions(source: str) -> str:
return _JEST_MOCK_EXTENSION_PATTERN.sub(r"\1\2\4", source)
def _generate_import_statement(function_name: str, module_path: str) -> tuple[str, str]:
"""Generate appropriate import statement and accessor for JavaScript/TypeScript.
def _resolve_import(function_name: str, module_path: str) -> tuple[str, str, str]:
"""Determine import style and binding name for a JS/TS function.
For class methods (e.g., 'Validator.validateRequest'), generates:
import: const Validator = require('../middlewares/Validator');
accessor: Validator.validateRequest
Analyzes the function name to decide how to import it:
- 'Validator.validateRequest' default import of Validator
- 'execMongoEval' named/destructuring import
- 'Constructor.prototype.method' namespace import (fallback)
For simple functions (e.g., 'execMongoEval'), generates:
import: const { execMongoEval } = require('../ctl/utils');
accessor: execMongoEval
For unsupported patterns (e.g., 'module.exports', 'Constructor.prototype.method'),
falls back to default module import.
Args:
function_name: Name of the function, may include class name (e.g., 'Class.method')
module_path: Import path for the module
The actual ESM vs CJS formatting is handled by the Jinja2 js_import macro.
Returns:
Tuple of (import_statement, function_accessor)
Tuple of (import_style, import_name, function_accessor) where
import_style is "default", "named", or "namespace".
"""
parts = function_name.split(".")
# Handle single-dot ClassName.methodName pattern
if len(parts) == 2:
class_name, method_name = parts
# Only use class import if both parts are valid identifiers
# and the class name is not a reserved word
if _is_valid_js_identifier(class_name) and method_name.isidentifier():
import_statement = f"const {class_name} = require('{module_path}');"
return import_statement, function_name
return "default", class_name, function_name
# For simple functions (no dot) or unsupported patterns,
# use default module import and let LLM figure out the accessor
if len(parts) == 1 and parts[0].isidentifier():
# Simple function: use destructuring import
import_statement = f"const {{ {function_name} }} = require('{module_path}');"
return import_statement, function_name
return "named", function_name, function_name
# Fallback for complex patterns: import the module as a whole
# Extract a safe module name from the path
module_name = module_path.rstrip("/").split("/")[-1].replace("-", "_").replace(".", "_")
if not module_name.isidentifier():
module_name = "mod"
import_statement = f"const {module_name} = require('{module_path}');"
# For complex patterns, use the full function_name as accessor
return import_statement, function_name
return "namespace", module_name, function_name
def build_javascript_prompt(
function_name: str, function_code: str, module_path: str, test_framework: str, is_async: bool
function_name: str,
function_code: str,
module_path: str,
test_framework: str,
is_async: bool,
module_system: str | None = None,
model_type: str = "openai",
) -> tuple[list[ChatCompletionMessageParam], str]:
"""Build the prompt messages for JavaScript test generation.
@ -216,39 +200,40 @@ def build_javascript_prompt(
function_name: Name of the function to test
function_code: Source code of the function
module_path: Import path for the module
test_framework: Testing framework (jest, mocha)
test_framework: Testing framework (jest, vitest, mocha)
is_async: Whether the function is async
module_system: "esm" or "commonjs" (defaults to CJS when None)
model_type: "openai" or "anthropic" controls prompt formatting
Returns:
Tuple of (messages, posthog_event_suffix)
"""
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 = ""
async_prefix = "async_" if is_async else ""
framework = "mocha_" if test_framework == "mocha" else ""
system_template = f"{async_prefix}{framework}system.md.j2"
posthog_event_suffix = "async-" if is_async else ""
# Generate proper import statement for the function
import_statement, function_accessor = _generate_import_statement(function_name, module_path)
import_style, import_name, function_accessor = _resolve_import(function_name, module_path)
# Format prompts
system_message: ChatCompletionMessageParam = {
"role": "system",
"content": system_prompt.format(function_name=function_accessor),
"content": _jinja_env.get_template(system_template).render(
function_name=function_accessor, model_type=model_type
),
}
user_message: ChatCompletionMessageParam = {
"role": "user",
"content": user_prompt.format(
"content": _jinja_env.get_template("user.md.j2").render(
is_async=is_async,
module_system=module_system,
import_style=import_style,
import_name=import_name,
test_framework=test_framework,
function_name=function_accessor,
function_code=function_code,
module_path=module_path,
import_statement=import_statement,
package_comment="",
),
}
@ -340,7 +325,7 @@ async def generate_and_validate_js_test_code(
ValueError: If no valid code found
"""
obs_context: dict | None = (
obs_context: dict[str, int | None] | None = (
{"call_sequence": call_sequence, "test_index": test_index} if call_sequence is not None else None
)
@ -386,6 +371,8 @@ async def generate_javascript_tests_from_function(
call_sequence: int | None = None,
language: str = "javascript",
test_index: int | None = None,
module_system: str | None = None,
model_type: str = "openai",
) -> tuple[str, str, str]:
"""Generate JavaScript tests for a function.
@ -394,13 +381,15 @@ async def generate_javascript_tests_from_function(
function_name: Name of function to test
function_code: Source code of function
module_path: Import path for module
test_framework: Testing framework (jest, mocha)
test_framework: Testing framework (jest, vitest, mocha)
execute_model: LLM model to use
is_async: Whether function is async
trace_id: Trace ID for logging
call_sequence: Call sequence number
language: Language of the function to test (javascript, typescript)
test_index: Index of the test group being generated
module_system: "esm" or "commonjs" (defaults to CJS when None)
model_type: "openai" or "anthropic" controls prompt formatting
Returns:
Tuple of (generated_tests, instrumented_behavior_tests, instrumented_perf_tests)
@ -415,9 +404,11 @@ async def generate_javascript_tests_from_function(
module_path=module_path,
test_framework=test_framework,
is_async=is_async,
module_system=module_system,
model_type=model_type,
)
cost_tracker = []
cost_tracker: list[float] = []
try:
validated_code = await generate_and_validate_js_test_code(
@ -531,6 +522,8 @@ async def testgen_javascript(
execute_model=execute_model,
language=data.language,
test_index=test_index,
module_system=data.module_system,
model_type=execute_model.model_type,
)
# Strip incorrect file extensions from import paths (LLMs sometimes add .js to .ts imports)

View file

@ -2,7 +2,7 @@ import logging
from collections import defaultdict
from dataclasses import dataclass
from enum import Enum
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any
import libcst as cst
import sentry_sdk
@ -39,11 +39,11 @@ SCOPE_DESCRIPTIONS = {
class TestDiff(Schema):
scope: TestDiffScope
original_value: bool | str | int | float | dict | list | None = None
candidate_value: bool | str | int | float | dict | list | None = None
original_value: bool | str | int | float | dict[str, Any] | list[Any] | None = None
candidate_value: bool | str | int | float | dict[str, Any] | list[Any] | None = None
original_pass: bool
candidate_pass: bool
test_src_code: str
test_src_code: str | None = None
candidate_pytest_error: str | None = None
original_pytest_error: str | None = None
@ -75,16 +75,19 @@ class CodeRepairContext:
return self.base_system_prompt
def build_test_details(self, test_diffs: list[TestDiff]) -> str:
sections = defaultdict(str)
sections: defaultdict[str, str] = defaultdict(str)
language = self.data.language
test_error_label = "Pytest error" if language == "python" else "Test error"
for diff in test_diffs:
try:
if sections[diff.test_src_code] == "":
if not diff.test_src_code:
# Convert None to empty string for dict key usage
diff.test_src_code = ""
if not sections[diff.test_src_code]:
# add error strings and test def only once per test function
sections[diff.test_src_code] += f"""Test Source:
```{language}
{diff.test_src_code}
{diff.test_src_code or "Not Available"}
```
{test_error_label} (original code): {diff.original_pytest_error if diff.original_pytest_error else ""}
{test_error_label} (optimized code): {diff.candidate_pytest_error if diff.candidate_pytest_error else ""}

View file

@ -5,7 +5,6 @@ import logging
from pathlib import Path
import sentry_sdk
from jinja2 import Environment, FileSystemLoader, StrictUndefined
from ninja import NinjaAPI
from openai.types.chat import (
ChatCompletionMessageParam,
@ -27,17 +26,12 @@ from core.languages.python.explanations.models import (
)
from core.log_features.log_event import update_optimization_cost
from core.log_features.log_features import log_features
from core.shared.jinja_utils import create_prompt_env
explanations_api = NinjaAPI(urls_namespace="explanations")
_PROMPT_DIR = Path(__file__).parent / "prompts"
_jinja_env = Environment( # noqa: S701 — rendering LLM prompts, not HTML
loader=FileSystemLoader(_PROMPT_DIR),
undefined=StrictUndefined,
keep_trailing_newline=True,
trim_blocks=True,
lstrip_blocks=True,
)
_jinja_env = create_prompt_env(_PROMPT_DIR, trim_blocks=True, lstrip_blocks=True)
SYSTEM_PROMPT_TEMPLATE = _jinja_env.get_template("system_prompt.md.j2")
USER_PROMPT_TEMPLATE = _jinja_env.get_template("user_prompt.md.j2")

View file

@ -10,7 +10,6 @@ from typing import TYPE_CHECKING, Any
import libcst as cst
import sentry_sdk
import stamina
from jinja2 import Environment, FileSystemLoader, StrictUndefined
from ninja.errors import HttpError
from openai import OpenAIError
@ -34,6 +33,7 @@ from core.languages.python.testgen.preprocessing.preprocess_pipeline import gene
from core.languages.python.testgen.validate import instrument_tests, validate_request_data
from core.log_features.log_event import update_optimization_cost
from core.log_features.log_features import log_features
from core.shared.jinja_utils import create_prompt_env
from core.shared.testgen_models import (
TestGenDebugInfo,
TestGenerationFailedError,
@ -58,7 +58,7 @@ def select_model_for_test(test_index: int) -> tuple[LLM, str]:
_current_dir = Path(__file__).parent
_prompts_dir = _current_dir / "prompts"
_jinja_env = Environment(loader=FileSystemLoader(_prompts_dir), keep_trailing_newline=True, undefined=StrictUndefined) # noqa: S701 - rendering LLM prompts, not HTML
_jinja_env = create_prompt_env(_prompts_dir)
def build_prompt(

View file

@ -0,0 +1,13 @@
from pathlib import Path
from jinja2 import Environment, FileSystemLoader, StrictUndefined
def create_prompt_env(prompt_dir: Path, *, trim_blocks: bool = False, lstrip_blocks: bool = False) -> Environment:
return Environment( # noqa: S701 — rendering LLM prompts, not HTML
loader=FileSystemLoader(prompt_dir),
keep_trailing_newline=True,
undefined=StrictUndefined,
trim_blocks=trim_blocks,
lstrip_blocks=lstrip_blocks,
)

View file

@ -26,6 +26,7 @@ class TestGenSchema(Schema):
is_async: bool | None = False
call_sequence: int | None = None
is_numerical_code: bool | None = None
module_system: str | None = None # "esm" or "commonjs" (JS/TS only)
@model_validator(mode="after")
def helper_function_names_validator(self) -> Self:

View file

@ -1,20 +1,16 @@
"""Tests for JavaScript/TypeScript test generation."""
from core.languages.js_ts.testgen import _generate_import_statement, _is_valid_js_identifier, build_javascript_prompt
from core.languages.js_ts.testgen import _is_valid_js_identifier, _resolve_import, build_javascript_prompt
class TestIsValidJsIdentifier:
"""Tests for the _is_valid_js_identifier function."""
def test_valid_identifier(self) -> None:
"""Test that valid identifiers return True."""
assert _is_valid_js_identifier("MyClass") is True
assert _is_valid_js_identifier("validName") is True
assert _is_valid_js_identifier("_private") is True
assert _is_valid_js_identifier("name123") is True
def test_dollar_sign_identifiers(self) -> None:
"""Test that $ identifiers (valid in JavaScript) return True."""
assert _is_valid_js_identifier("$handler") is True
assert _is_valid_js_identifier("$") is True
assert _is_valid_js_identifier("$scope") is True
@ -23,7 +19,6 @@ class TestIsValidJsIdentifier:
assert _is_valid_js_identifier("$$double") is True
def test_reserved_words(self) -> None:
"""Test that reserved words return False."""
assert _is_valid_js_identifier("module") is False
assert _is_valid_js_identifier("exports") is False
assert _is_valid_js_identifier("prototype") is False
@ -32,90 +27,269 @@ class TestIsValidJsIdentifier:
assert _is_valid_js_identifier("function") is False
def test_invalid_identifiers(self) -> None:
"""Test that invalid identifiers return False."""
assert _is_valid_js_identifier("123invalid") is False
assert _is_valid_js_identifier("has-dash") is False
assert _is_valid_js_identifier("has space") is False
class TestGenerateImportStatement:
"""Tests for the _generate_import_statement function."""
class TestResolveImport:
def test_simple_function(self) -> None:
assert _resolve_import("execMongoEval", "../ctl/utils") == ("named", "execMongoEval", "execMongoEval")
def test_simple_function_import(self) -> None:
"""Test import generation for a simple function."""
import_stmt, accessor = _generate_import_statement("execMongoEval", "../ctl/mongo_shell_utils")
assert import_stmt == "const { execMongoEval } = require('../ctl/mongo_shell_utils');"
assert accessor == "execMongoEval"
def test_class_method(self) -> None:
assert _resolve_import("Validator.validateRequest", "../middlewares/Validator") == (
"default",
"Validator",
"Validator.validateRequest",
)
def test_class_method_import(self) -> None:
"""Test import generation for a class method."""
import_stmt, accessor = _generate_import_statement("Validator.validateRequest", "../middlewares/Validator")
assert import_stmt == "const Validator = require('../middlewares/Validator');"
assert accessor == "Validator.validateRequest"
def test_nested_class_method_import(self) -> None:
"""Test import generation for a deeply nested method - falls back to module import."""
import_stmt, accessor = _generate_import_statement("Constructor.prototype.method", "../utils/Constructor")
# Multiple dots should fall back to module import
assert "Constructor" in import_stmt
assert accessor == "Constructor.prototype.method"
def test_static_method(self) -> None:
assert _resolve_import("Utils.formatDate", "../utils/Utils") == ("default", "Utils", "Utils.formatDate")
def test_function_with_underscore(self) -> None:
"""Test import generation for a function with underscores."""
import_stmt, accessor = _generate_import_statement("get_git_root", "../utils/git")
assert import_stmt == "const { get_git_root } = require('../utils/git');"
assert accessor == "get_git_root"
assert _resolve_import("get_git_root", "../utils/git") == ("named", "get_git_root", "get_git_root")
def test_static_method_import(self) -> None:
"""Test import generation for what looks like a static method."""
import_stmt, accessor = _generate_import_statement("Utils.formatDate", "../utils/Utils")
assert import_stmt == "const Utils = require('../utils/Utils');"
assert accessor == "Utils.formatDate"
def test_nested_class_method_falls_back_to_namespace(self) -> None:
assert _resolve_import("Constructor.prototype.method", "../utils/Constructor") == (
"namespace",
"Constructor",
"Constructor.prototype.method",
)
def test_reserved_word_class_name_falls_back(self) -> None:
"""Test that reserved words like 'module' fall back to module import."""
import_stmt, accessor = _generate_import_statement("module.exports", "../utils")
# 'module' is a reserved word, should fall back
assert "module" not in import_stmt.split("=")[0] # Not used as variable name
assert accessor == "module.exports"
assert _resolve_import("module.exports", "../utils") == ("namespace", "utils", "module.exports")
def test_prototype_pattern_falls_back(self) -> None:
"""Test that prototype patterns fall back correctly."""
import_stmt, accessor = _generate_import_statement("Array.prototype.map", "../polyfills")
# 'prototype' in method name should still work for single dot
# but Array.prototype.map has multiple dots, so falls back
assert "polyfills" in import_stmt
assert accessor == "Array.prototype.map"
assert _resolve_import("Array.prototype.map", "../polyfills") == (
"namespace",
"polyfills",
"Array.prototype.map",
)
def test_exports_reserved_word(self) -> None:
"""Test that 'exports' as class name falls back."""
import_stmt, accessor = _generate_import_statement("exports.helper", "../utils")
# 'exports' is reserved, should fall back
assert "const exports" not in import_stmt
assert accessor == "exports.helper"
assert _resolve_import("exports.helper", "../utils") == ("namespace", "utils", "exports.helper")
def test_invalid_module_name_uses_mod(self) -> None:
assert _resolve_import("a.b.c", "../123-bad") == ("namespace", "mod", "a.b.c")
class TestBuildJavascriptPrompt:
"""Tests for the build_javascript_prompt function."""
# --- Sync / CJS / OpenAI (default) ---
def test_simple_function_prompt_has_valid_import(self) -> None:
"""Test that prompts for simple functions have valid JS import syntax."""
def test_sync_cjs_openai_system_prompt(self) -> None:
messages, suffix = build_javascript_prompt(
function_name="myFunc",
function_code="function myFunc() {}",
module_path="../lib/utils",
test_framework="jest",
is_async=False,
)
assert suffix == ""
system = messages[0]["content"]
assert isinstance(system, str)
assert system == (
"\nYou are Codeflash, a world-class JavaScript/TypeScript developer with an eagle eye"
" for unintended bugs and edge cases. You write careful, accurate unit tests."
" When asked to reply only with code, you write all of your code in a single markdown"
" code block.\n"
"\n## Task\n"
"\nYour task is to create comprehensive, high quality test cases for the myFunc function."
" These test cases should encompass Basic, Edge, and Large Scale scenarios to ensure the"
" code's robustness, reliability, and scalability.\n"
"\n\n## Test Categories\n"
"\n\n**1. Basic Test Cases**:\n"
"- **Objective**: To verify the fundamental functionality of the myFunc function under normal conditions.\n"
"\n**2. Edge Test Cases**:\n"
"- **Objective**: To evaluate the function's behavior under extreme or unusual conditions.\n"
"\n**3. Large Scale Test Cases**:\n"
"- **Objective**: To assess the function's performance and scalability with large data samples.\n"
"\n\n## Instructions\n"
"\n- Implement a comprehensive set of test cases following the guidelines above.\n"
"- Use the testing framework specified in the user message with `describe`, `test`/`it`, and `expect`.\n"
"- Ensure each test case is well-documented with comments explaining the scenario it covers.\n"
"- Pay special attention to edge cases as they often reveal hidden bugs.\n"
"- For large-scale tests, focus on the function's efficiency and performance under heavy loads."
" Avoid loops exceeding 1000 iterations, and keep data structures under 1000 elements.\n"
"\n"
"- **CRITICAL: DO NOT MOCK THE FUNCTION UNDER TEST** - Never mock, stub, or spy on the"
" myFunc function itself. You may mock external dependencies (APIs, databases, network calls,"
" file I/O, etc.) if necessary, but the function being tested must execute with its real"
" implementation.\n"
"- **CRITICAL: IMPORT FROM REAL MODULES** - Import the function and any related"
" classes/utilities from their actual module paths as shown in the context.\n"
"- **CRITICAL: HANDLE ASYNC PROPERLY** - If the function is async, use `async/await` in your"
" tests and ensure all promises are properly awaited.\n"
"\n\n## Import Path Rules\n"
"\n- **NEVER add file extensions (.js, .ts, .tsx) to import paths** - The test framework"
" resolves extensions automatically.\n"
"- **WRONG**: `import { fn } from '../utils.js'` or `import { fn } from '../utils.ts'`\n"
"- **CORRECT**: `import { fn } from '../utils'`\n"
"- The user message provides the exact import statement to use - copy it exactly without modification.\n"
"\n## Vitest Imports\n"
"\n- If test_framework is \"vitest\", you MUST import test functions from 'vitest' since globals"
" are NOT enabled by default:\n"
" ```javascript\n"
" import { describe, test, expect, vi, beforeEach, afterEach } from 'vitest';\n"
" ```\n"
'- For "jest", globals are typically enabled so no import is needed.\n'
"\n## Mocking Rules\n"
"\n- **USE THE CORRECT MOCK SYNTAX FOR THE SPECIFIED FRAMEWORK**:\n"
" - For **Jest**: Use `jest.mock()` and `jest.fn()`\n"
" - For **Vitest**: Use `vi.mock()` and `vi.fn()` - NEVER use jest.mock with Vitest!\n"
"- Mock calls are HOISTED to the top of the file. NEVER use dynamic expressions"
" - use static string literals only.\n"
"- **ALWAYS use static string literals** for mock paths.\n"
"- **IMPORTANT**: Check the test framework specified in the user message and use"
" the matching syntax.\n"
"\n## Output Format\n"
"\n- Your response MUST be a single markdown code block containing valid JavaScript/TypeScript code.\n"
"- Do NOT nest code blocks inside each other.\n"
"- The code block MUST contain at least one test using `test()` or `it()`.\n"
"- Follow the exact template structure provided in the user message.\n\n"
)
def test_sync_cjs_openai_user_prompt(self) -> None:
messages, _ = build_javascript_prompt(
function_name="execMongoEval",
function_code="async function execMongoEval() { return true; }",
module_path="../ctl/utils",
function_name="myFunc",
function_code="function myFunc() {}",
module_path="../lib/utils",
test_framework="jest",
is_async=False,
)
user = messages[1]["content"]
assert isinstance(user, str)
assert "const { myFunc } = require('../lib/utils');" in user
assert "describe('myFunc'" in user
assert "require(" in user
# --- Sync / CJS / Anthropic ---
def test_sync_cjs_anthropic_system_prompt(self) -> None:
messages, _ = build_javascript_prompt(
function_name="myFunc",
function_code="function myFunc() {}",
module_path="../lib/utils",
test_framework="jest",
is_async=False,
model_type="anthropic",
)
system = messages[0]["content"]
assert isinstance(system, str)
assert "<role>" in system
assert "<task>" in system
assert "<test_categories>" in system
assert "<instructions>" in system
assert "<rules>" in system
assert "<output_format>" in system
assert "## Task" not in system
assert "## Instructions" not in system
# --- Async / CJS / OpenAI ---
def test_async_cjs_openai_system_prompt(self) -> None:
messages, suffix = build_javascript_prompt(
function_name="fetchData",
function_code="async function fetchData() {}",
module_path="../api",
test_framework="jest",
is_async=True,
model_type="openai",
)
assert suffix == "async-"
system = messages[0]["content"]
assert isinstance(system, str)
assert "for **asynchronous** code" in system
assert "**async** fetchData" in system
assert "with proper async/await handling" in system
assert "### CRITICAL: ASYNC TEST REQUIREMENTS" in system
assert "TESTS MUST BE DETERMINISTIC" in system
assert "TEST REJECTION CASES" in system
assert "async test using `test('...', async () => ...)`" in system
def test_async_cjs_openai_user_prompt(self) -> None:
messages, _ = build_javascript_prompt(
function_name="fetchData",
function_code="async function fetchData() {}",
module_path="../api",
test_framework="jest",
is_async=True,
)
user_content = messages[1]["content"]
assert isinstance(user_content, str)
# Should contain valid destructuring import
assert "const { execMongoEval }" in user_content
# Should NOT contain invalid syntax with dots in destructuring
assert "const { execMongoEval." not in user_content
user = messages[1]["content"]
assert isinstance(user, str)
assert "write a test suite for the following **ASYNC** JavaScript function." in user
assert "**CRITICAL: This function is ASYNCHRONOUS**" in user
assert "All calls to fetchData MUST be awaited: `await fetchData(...)`" in user
assert "const { fetchData } = require('../api');" in user
assert "const result = await fetchData(/* args */);" in user
assert "await expect(fetchData(/* invalid args */)).rejects.toThrow();" in user
def test_class_method_prompt_has_valid_import(self) -> None:
"""Test that prompts for class methods have valid JS import syntax."""
# --- Async / CJS / Anthropic ---
def test_async_cjs_anthropic_system_prompt(self) -> None:
messages, _ = build_javascript_prompt(
function_name="fetchData",
function_code="async function fetchData() {}",
module_path="../api",
test_framework="jest",
is_async=True,
model_type="anthropic",
)
system = messages[0]["content"]
assert isinstance(system, str)
assert "<role>" in system
assert "for **asynchronous** code" in system
assert "<async_requirements>" in system
assert "TESTS MUST BE DETERMINISTIC" in system
assert "TEST REJECTION CASES" in system
assert "<output_format>" in system
assert "async test using `test('...', async () => ...)`" in system
# --- ESM ---
def test_esm_named_import(self) -> None:
messages, _ = build_javascript_prompt(
function_name="myFunc",
function_code="function myFunc() {}",
module_path="../lib/utils",
test_framework="vitest",
is_async=False,
module_system="esm",
)
user = messages[1]["content"]
assert isinstance(user, str)
assert "import { myFunc } from '../lib/utils';" in user
assert "require(" not in user
def test_esm_default_import(self) -> None:
messages, _ = build_javascript_prompt(
function_name="Svc.run",
function_code="class Svc { run() {} }",
module_path="../services/Svc",
test_framework="vitest",
is_async=False,
module_system="esm",
)
user = messages[1]["content"]
assert isinstance(user, str)
assert "import Svc from '../services/Svc';" in user
def test_esm_async(self) -> None:
messages, _ = build_javascript_prompt(
function_name="fetchData",
function_code="async function fetchData() {}",
module_path="../api",
test_framework="vitest",
is_async=True,
module_system="esm",
)
user = messages[1]["content"]
assert isinstance(user, str)
assert "import { fetchData } from '../api';" in user
assert "const result = await fetchData(/* args */);" in user
# --- CJS (class methods) ---
def test_cjs_class_method(self) -> None:
messages, _ = build_javascript_prompt(
function_name="Validator.validateRequest",
function_code="class Validator { validateRequest() {} }",
@ -123,15 +297,13 @@ class TestBuildJavascriptPrompt:
test_framework="jest",
is_async=False,
)
user_content = messages[1]["content"]
assert isinstance(user_content, str)
# Should contain valid class import
assert "const Validator = require('../middlewares/Validator');" in user_content
# Should NOT contain invalid syntax like { Validator.validateRequest }
assert "{ Validator.validateRequest }" not in user_content
user = messages[1]["content"]
assert isinstance(user, str)
assert "const Validator = require('../middlewares/Validator');" in user
assert "describe('Validator.validateRequest'" in user
assert "{ Validator.validateRequest }" not in user
def test_async_prompt_has_valid_import(self) -> None:
"""Test that async prompts also have valid import syntax."""
def test_cjs_class_method_async(self) -> None:
messages, suffix = build_javascript_prompt(
function_name="Controller.asyncMethod",
function_code="class Controller { async asyncMethod() {} }",
@ -140,21 +312,6 @@ class TestBuildJavascriptPrompt:
is_async=True,
)
assert suffix == "async-"
user_content = messages[1]["content"]
assert isinstance(user_content, str)
# Should contain valid class import
assert "const Controller = require('../controllers/Controller');" in user_content
def test_prompt_contains_function_accessor(self) -> None:
"""Test that the prompt contains the correct function accessor."""
messages, _ = build_javascript_prompt(
function_name="MyClass.myMethod",
function_code="class MyClass { myMethod() {} }",
module_path="../MyClass",
test_framework="jest",
is_async=False,
)
user_content = messages[1]["content"]
assert isinstance(user_content, str)
# The function accessor should be used in describe blocks
assert "describe('MyClass.myMethod'" in user_content
user = messages[1]["content"]
assert isinstance(user, str)
assert "const Controller = require('../controllers/Controller');" in user

View file

@ -1,23 +1,13 @@
"""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).
Tests the prompt building and validation functions.
"""
import re
from pathlib import Path
import pytest
# Load prompts directly to avoid importing testgen_javascript.py
JS_PROMPTS_DIR = Path(__file__).parent.parent.parent / "core" / "languages" / "js_ts" / "prompts" / "testgen"
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)
from core.languages.js_ts.testgen import build_javascript_prompt, parse_and_validate_js_output
def _has_test_functions(code: str) -> bool:
@ -26,55 +16,6 @@ def _has_test_functions(code: str) -> bool:
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 = ""
import_statement = f"import {{ {function_name} }} from '{module_path}';"
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,
import_statement=import_statement,
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."""
@ -198,8 +139,12 @@ class TestBuildJavaScriptPrompt:
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"]
system = messages[0]["content"]
user = messages[1]["content"]
assert isinstance(system, str)
assert isinstance(user, str)
assert "fibonacci" in system
assert "fibonacci" in user
assert suffix == "" # Non-async should have empty suffix
def test_async_function_prompt(self) -> None:
@ -213,7 +158,9 @@ class TestBuildJavaScriptPrompt:
)
assert len(messages) == 2
assert "async" in messages[0]["content"].lower()
system = messages[0]["content"]
assert isinstance(system, str)
assert "async" in system.lower()
assert suffix == "async-"
def test_prompt_includes_function_code(self) -> None:
@ -228,7 +175,9 @@ class TestBuildJavaScriptPrompt:
)
# User message should contain the function code
assert function_code in messages[1]["content"]
user = messages[1]["content"]
assert isinstance(user, str)
assert function_code in user
def test_prompt_includes_module_path(self) -> None:
"""Test that prompt includes the module path."""
@ -240,7 +189,9 @@ class TestBuildJavaScriptPrompt:
is_async=False,
)
assert "./math/utils" in messages[1]["content"]
user = messages[1]["content"]
assert isinstance(user, str)
assert "./math/utils" in user
class TestJavaScriptTestGenPromptContent:
@ -256,7 +207,9 @@ class TestJavaScriptTestGenPromptContent:
is_async=False,
)
assert "Jest" in messages[0]["content"] or "jest" in messages[0]["content"].lower()
system = messages[0]["content"]
assert isinstance(system, str)
assert "Jest" in system or "jest" in system.lower()
def test_system_prompt_has_test_categories(self) -> None:
"""Test that system prompt mentions test categories."""
@ -269,6 +222,7 @@ class TestJavaScriptTestGenPromptContent:
)
system_content = messages[0]["content"]
assert isinstance(system_content, str)
# 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()
@ -284,5 +238,108 @@ class TestJavaScriptTestGenPromptContent:
)
system_content = messages[0]["content"]
assert isinstance(system_content, str)
# Should warn against mocking
assert "mock" in system_content.lower() or "Mock" in system_content
class TestMochaPromptContent:
"""Tests for Mocha-specific prompt content."""
def test_mocha_system_prompt_uses_assert(self) -> None:
"""Test that Mocha system prompt mentions node:assert and not expect()."""
messages, _ = 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="mocha",
is_async=False,
)
system_content = messages[0]["content"]
assert isinstance(system_content, str)
assert "node:assert" in system_content
assert "strictEqual" in system_content
def test_mocha_user_prompt_uses_it(self) -> None:
"""Test that Mocha user prompt template uses it() not test()."""
messages, _ = 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="mocha",
is_async=False,
)
user_content = messages[1]["content"]
assert isinstance(user_content, str)
assert "it(" in user_content
assert "assert.strictEqual" in user_content
def test_mocha_prompt_no_other_framework_apis(self) -> None:
"""Test that Mocha prompts don't contain vitest/jest/chai API references."""
messages, _ = 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="mocha",
is_async=False,
)
system_content = messages[0]["content"]
user_content = messages[1]["content"]
assert isinstance(system_content, str)
assert isinstance(user_content, str)
# Prompts should not contain vitest/jest/chai imports or API references
assert "from 'vitest'" not in system_content
assert "from 'vitest'" not in user_content
assert "jest.mock(" not in system_content
assert "jest.mock(" not in user_content
assert "vi.mock(" not in system_content
assert "vi.mock(" not in user_content
assert "from 'chai'" not in system_content
assert "from 'chai'" not in user_content
# Should not mention expect().toBe() patterns
assert "toBe(" not in user_content
assert "toEqual(" not in user_content
def test_mocha_async_prompt_uses_assert_rejects(self) -> None:
"""Test that async Mocha prompts use assert.rejects instead of expect().rejects."""
messages, _ = build_javascript_prompt(
function_name="fetchData",
function_code="async function fetchData(url) { return await fetch(url); }",
module_path="./api",
test_framework="mocha",
is_async=True,
)
system_content = messages[0]["content"]
user_content = messages[1]["content"]
assert isinstance(system_content, str)
assert isinstance(user_content, str)
assert "assert.rejects" in system_content
assert "assert.rejects" in user_content
def test_jest_prompt_unchanged(self) -> None:
"""Regression: Jest prompts should still use expect/test syntax."""
messages, _ = build_javascript_prompt(
function_name="add",
function_code="function add(a, b) { return a + b; }",
module_path="./math",
test_framework="jest",
is_async=False,
)
user_content = messages[1]["content"]
assert isinstance(user_content, str)
assert "expect" in user_content.lower()
assert "test(" in user_content or "it(" in user_content
def test_vitest_prompt_unchanged(self) -> None:
"""Regression: Vitest prompts should still use expect/test syntax and vitest imports."""
messages, _ = build_javascript_prompt(
function_name="add",
function_code="function add(a, b) { return a + b; }",
module_path="./math",
test_framework="vitest",
is_async=False,
)
user_content = messages[1]["content"]
assert isinstance(user_content, str)
assert "vitest" in user_content.lower()
assert "expect" in user_content.lower()