Initial js support in aiservice

This commit is contained in:
misrasaurabh1 2026-01-14 22:15:27 -08:00
parent 4f324b99ec
commit 1a63515f47
34 changed files with 3926 additions and 3127 deletions

View file

@ -0,0 +1,7 @@
"""
Code validators for different programming languages.
"""
from aiservice.validators.javascript_validator import validate_javascript_syntax
__all__ = ["validate_javascript_syntax"]

View file

@ -0,0 +1,155 @@
"""
JavaScript/TypeScript syntax validation.
Uses Node.js for validation when available, with a basic regex fallback.
"""
from __future__ import annotations
import json
import re
import subprocess
from functools import lru_cache
@lru_cache(maxsize=100)
def validate_javascript_syntax(code: str) -> tuple[bool, str | None]:
"""
Validate JavaScript syntax using Node.js.
Args:
code: The JavaScript code to validate
Returns:
A tuple of (is_valid, error_message)
- is_valid: True if the code is syntactically valid
- error_message: None if valid, otherwise the error description
"""
try:
# Use Node.js to parse the code
# We use acorn parser via a small script that reports syntax errors
validation_script = f"""
const acorn = require('acorn');
try {{
acorn.parse({json.dumps(code)}, {{
ecmaVersion: 'latest',
sourceType: 'module',
allowHashBang: true,
allowAwaitOutsideFunction: true,
allowImportExportEverywhere: true,
}});
console.log('VALID');
}} catch (e) {{
console.error(e.message);
process.exit(1);
}}
"""
result = subprocess.run(
["node", "-e", validation_script],
capture_output=True,
text=True,
timeout=5,
)
if result.returncode != 0:
error_msg = result.stderr.strip() or result.stdout.strip()
# Check if the error is due to missing acorn module
if "Cannot find module 'acorn'" in error_msg:
return _fallback_validation(code)
return False, error_msg
return True, None
except subprocess.TimeoutExpired:
return False, "Syntax validation timed out"
except FileNotFoundError:
# Node.js not available, try fallback
return _fallback_validation(code)
except Exception as e:
# Fallback for any other error
return _fallback_validation(code)
def _fallback_validation(code: str) -> tuple[bool, str | None]:
"""
Fallback validation using basic checks when Node.js is not available.
This performs simple bracket matching and basic syntax checks.
"""
try:
# Check for obvious syntax errors
# 1. Bracket matching
brackets = {"(": ")", "[": "]", "{": "}"}
stack = []
in_string = False
string_char = None
escape_next = False
for i, char in enumerate(code):
if escape_next:
escape_next = False
continue
if char == "\\":
escape_next = True
continue
# Handle strings
if char in "'\"`":
if not in_string:
in_string = True
string_char = char
elif char == string_char:
# Check for template literal
if char == "`" or (i + 1 < len(code) and code[i + 1] != string_char):
in_string = False
string_char = None
if in_string:
continue
# Check brackets
if char in brackets:
stack.append((brackets[char], i))
elif char in brackets.values():
if not stack:
return False, f"Unexpected closing bracket '{char}' at position {i}"
expected, _ = stack.pop()
if expected != char:
return False, f"Mismatched bracket '{char}' at position {i}"
if stack:
return False, f"Unclosed bracket '{stack[-1][0]}'"
# 2. Check for common invalid patterns
invalid_patterns = [
(r"function\s*\(\s*\)\s*\{[^}]*\breturn\b[^;]*$", "Missing semicolon after return"),
(r"\bconst\s+\d", "Invalid const declaration - cannot start with number"),
(r"\blet\s+\d", "Invalid let declaration - cannot start with number"),
(r"\bvar\s+\d", "Invalid var declaration - cannot start with number"),
]
for pattern, error_msg in invalid_patterns:
if re.search(pattern, code):
return False, error_msg
return True, None
except Exception as e:
return False, f"Validation error: {e}"
def validate_typescript_syntax(code: str) -> tuple[bool, str | None]:
"""
Validate TypeScript syntax.
Since acorn doesn't support TypeScript type annotations, we use the
fallback validation which checks for basic syntax errors like bracket
matching. For complete TypeScript validation, tsc would be needed.
"""
# Acorn doesn't support TypeScript syntax (type annotations, interfaces, etc.)
# Use the fallback validation which is more lenient
return _fallback_validation(code)

View file

@ -59,7 +59,9 @@ class OptimizeSchema(Schema):
source_code: str
dependency_code: str | None
trace_id: str
python_version: str
python_version: str | None = None # Made optional for multi-language support
language: str = "python" # NEW: language identifier (python, javascript, typescript)
language_version: str | None = None # NEW: e.g., "ES2022", "Node 20", or Python version
experiment_metadata: dict[str, str] | None = None
codeflash_version: str | None = None
current_username: str | None = None

View file

@ -31,6 +31,7 @@ from optimizer.context_utils.optimizer_context import (
)
from optimizer.diff_patches_utils.diff import DiffMethod
from optimizer.models import OptimizedCandidateSource, OptimizeSchema
from optimizer.optimizer_javascript import optimize_javascript
if TYPE_CHECKING:
from openai.types.chat import ChatCompletionMessageParam
@ -281,13 +282,25 @@ def validate_request_data(data: OptimizeSchema, ctx: BaseOptimizerContext) -> tu
async def optimize(
request: AuthenticatedRequest, data: OptimizeSchema
) -> tuple[int, OptimizeResponseSchema | OptimizeErrorResponseSchema]:
# Route based on language
if data.language in ("javascript", "typescript"):
return await optimize_javascript(request, data)
# Default: Python optimization
return await optimize_python(request, data)
async def optimize_python(
request: AuthenticatedRequest, data: OptimizeSchema
) -> tuple[int, OptimizeResponseSchema | OptimizeErrorResponseSchema]:
"""Optimize Python code for performance using LLMs."""
system_prompt = ASYNC_SYSTEM_PROMPT if data.is_async else SYSTEM_PROMPT
user_prompt = ASYNC_USER_PROMPT if data.is_async else USER_PROMPT
ctx: BaseOptimizerContext = BaseOptimizerContext.get_dynamic_context(
system_prompt, user_prompt, data.source_code, DiffMethod.NO_DIFF
)
ph(request.user, "aiservice-optimize-called")
ph(request.user, "aiservice-optimize-called", properties={"language": "python"})
try:
python_version = validate_request_data(data, ctx)

View file

@ -0,0 +1,403 @@
"""
JavaScript/TypeScript code optimizer module.
This module handles optimization requests for JavaScript and TypeScript code.
"""
from __future__ import annotations
import asyncio
import logging
import re
import uuid
from pathlib import Path
from typing import TYPE_CHECKING, Any
import sentry_sdk
from ninja.errors import HttpError
from openai.types.chat import ChatCompletionSystemMessageParam, ChatCompletionUserMessageParam
from aiservice.analytics.posthog import ph
from aiservice.common_utils import validate_trace_id
from aiservice.env_specific import debug_log_sensitive_data, debug_log_sensitive_data_from_callable
from aiservice.llm import LLM, OPTIMIZE_MODEL, calculate_llm_cost, call_llm
from aiservice.validators.javascript_validator import validate_javascript_syntax, validate_typescript_syntax
from authapp.auth import AuthenticatedRequest
from authapp.user import get_user_by_id
from log_features.log_event import get_or_create_optimization_event
from log_features.log_features import log_features
from optimizer.config import MAX_OPTIMIZER_CALLS, get_model_distribution
from optimizer.context_utils.optimizer_context import (
OptimizeErrorResponseSchema,
OptimizeResponseItemSchema,
OptimizeResponseSchema,
)
from optimizer.models import OptimizedCandidateSource, OptimizeSchema
from optimizer.prompts import get_system_prompt, get_user_prompt
if TYPE_CHECKING:
from openai.types.chat import ChatCompletionMessageParam
# Pattern to extract code blocks from LLM response
JS_CODE_PATTERN = re.compile(
r"```(?:javascript|js|typescript|ts)(?::[^\n]*)?\s*\n(.*?)```",
re.MULTILINE | re.DOTALL,
)
def extract_code_and_explanation(content: str) -> tuple[str, str]:
"""
Extract code and explanation from LLM response.
Args:
content: The raw LLM response content
Returns:
Tuple of (code, explanation)
"""
match = JS_CODE_PATTERN.search(content)
if match:
code = match.group(1).strip()
# Explanation is everything before the code block
explanation_end = match.start()
explanation = content[:explanation_end].strip()
return code, explanation
# No code block found, return empty code
return "", content
async def optimize_javascript_code_single(
user_id: str,
source_code: str,
trace_id: str,
dependency_code: str | None = None,
optimize_model: LLM = OPTIMIZE_MODEL,
language_version: str = "ES2022",
is_async: bool = False,
call_sequence: int | None = None,
) -> tuple[OptimizeResponseItemSchema | None, float | None, str]:
"""
Optimize JavaScript/TypeScript code using LLMs.
Args:
user_id: The user ID making the request
source_code: The source code to optimize
trace_id: The trace ID for logging
dependency_code: Optional dependency code for context
optimize_model: The LLM model to use
language_version: Target JS/TS version (e.g., "ES2022")
is_async: Whether the code is async
call_sequence: Call sequence number for tracking
Returns:
Tuple of (optimization_result, llm_cost, model_name)
"""
logging.info("/optimize: Optimizing JavaScript code.")
debug_log_sensitive_data(f"Optimizing JavaScript code for user {user_id}:\n{source_code}")
# Get language-appropriate prompts
language = "javascript" # TypeScript uses same prompts
system_prompt = get_system_prompt(language, is_async)
user_prompt = get_user_prompt(language, is_async)
# Format prompts
system_prompt = system_prompt.format(language_version=language_version)
user_prompt = user_prompt.format(source_code=f"```javascript\n{source_code}\n```")
if dependency_code:
user_prompt = f"Dependencies (read-only):\n```javascript\n{dependency_code}\n```\n\n{user_prompt}"
obs_context: dict[str, Any] | None = {"call_sequence": call_sequence} if call_sequence is not None else None
messages: list[ChatCompletionMessageParam] = [
ChatCompletionSystemMessageParam(role="system", content=system_prompt),
ChatCompletionUserMessageParam(role="user", content=user_prompt),
]
try:
output = await call_llm(
llm=optimize_model,
messages=messages,
call_type="optimization",
trace_id=trace_id,
user_id=user_id,
python_version=language_version, # Reusing python_version field for language version
context=obs_context,
)
except Exception as e:
logging.exception("LLM Code Generation error in JavaScript optimizer")
sentry_sdk.capture_exception(e)
debug_log_sensitive_data(f"Failed to generate code for source:\n{source_code}")
return None, None, optimize_model.name
llm_cost = calculate_llm_cost(output.raw_response, optimize_model)
debug_log_sensitive_data(f"LLM optimization response:\n{output.raw_response.model_dump_json(indent=2)}")
if output.raw_response.usage is not None:
ph(
user_id,
"aiservice-optimize-openai-usage",
properties={"model": optimize_model.name, "usage": output.raw_response.usage.json(), "language": language},
)
# Extract code and explanation from response
optimized_code, explanation = extract_code_and_explanation(output.content)
if not optimized_code:
sentry_sdk.capture_message("No code block found in JavaScript optimization response")
debug_log_sensitive_data(f"No code found in response for source:\n{source_code}")
return None, llm_cost, optimize_model.name
# Validate the generated code
is_valid, error = validate_javascript_syntax(optimized_code)
if not is_valid:
sentry_sdk.capture_message(f"Invalid JavaScript generated: {error}")
debug_log_sensitive_data(f"Invalid code generated:\n{optimized_code}\nError: {error}")
return None, llm_cost, optimize_model.name
# Check that the code is actually different from the original
if _normalize_code(optimized_code) == _normalize_code(source_code):
debug_log_sensitive_data("Generated code identical to original")
return None, llm_cost, optimize_model.name
optimization_id = str(uuid.uuid4())
result = OptimizeResponseItemSchema(
source_code=optimized_code,
explanation=explanation,
optimization_id=optimization_id,
)
return result, llm_cost, optimize_model.name
def _normalize_code(code: str) -> str:
"""
Normalize code for comparison (remove comments and whitespace).
"""
# Remove single-line comments
code = re.sub(r"//.*$", "", code, flags=re.MULTILINE)
# Remove multi-line comments
code = re.sub(r"/\*.*?\*/", "", code, flags=re.DOTALL)
# Normalize whitespace
code = " ".join(code.split())
return code
async def optimize_javascript_code(
user_id: str,
source_code: str,
trace_id: str,
dependency_code: str | None = None,
language_version: str = "ES2022",
is_async: bool = False,
n_candidates: int = 0,
) -> tuple[list[OptimizeResponseItemSchema], float, dict[str, str]]:
"""
Run parallel optimizations with multiple models.
Args:
user_id: The user ID making the request
source_code: The source code to optimize
trace_id: The trace ID for logging
dependency_code: Optional dependency code for context
language_version: Target JS/TS version
is_async: Whether the code is async
n_candidates: Number of optimization candidates to generate
Returns:
Tuple of (optimization_results, total_cost, optimization_models)
"""
tasks: list[asyncio.Task[tuple[OptimizeResponseItemSchema | None, float | None, str]]] = []
call_sequence = 1
if n_candidates == 0:
return [], 0.0, {}
async with asyncio.TaskGroup() as tg:
for model, num_calls in get_model_distribution(n_candidates, MAX_OPTIMIZER_CALLS):
for _ in range(num_calls):
task = tg.create_task(
optimize_javascript_code_single(
user_id=user_id,
source_code=source_code,
trace_id=trace_id,
dependency_code=dependency_code,
optimize_model=model,
language_version=language_version,
is_async=is_async,
call_sequence=call_sequence,
)
)
tasks.append(task)
call_sequence += 1
# Collect results
optimization_results: list[OptimizeResponseItemSchema] = []
total_cost = 0.0
optimization_models: dict[str, str] = {}
seen_code: set[str] = set()
for task in tasks:
result, cost, model_name = task.result()
if cost:
total_cost += cost
if result is not None:
# Deduplicate by normalized code
normalized = _normalize_code(result.source_code)
if normalized not in seen_code:
seen_code.add(normalized)
optimization_results.append(result)
optimization_models[result.optimization_id] = model_name
return optimization_results, total_cost, optimization_models
def validate_javascript_request_data(data: OptimizeSchema) -> None:
"""
Validate JavaScript/TypeScript optimization request data.
Args:
data: The request data
Raises:
HttpError: If validation fails
"""
if not data.source_code:
raise HttpError(400, "Source code cannot be empty.")
if not validate_trace_id(data.trace_id):
raise HttpError(400, "Invalid trace ID. Please provide a valid UUIDv4.")
# Validate syntax based on language
if data.language == "typescript":
is_valid, error = validate_typescript_syntax(data.source_code)
lang_name = "TypeScript"
else:
is_valid, error = validate_javascript_syntax(data.source_code)
lang_name = "JavaScript"
if not is_valid:
raise HttpError(400, f"Invalid source code. It is not valid {lang_name}: {error}")
async def optimize_javascript(
request: AuthenticatedRequest, data: OptimizeSchema
) -> tuple[int, OptimizeResponseSchema | OptimizeErrorResponseSchema]:
"""
Main endpoint handler for JavaScript/TypeScript optimization.
Args:
request: The authenticated request
data: The optimization request data
Returns:
Tuple of (status_code, response)
"""
language = data.language
ph(request.user, "aiservice-optimize-called", properties={"language": language})
try:
validate_javascript_request_data(data)
except HttpError as e:
e.add_note(f"JavaScript optimizer request validation error: {e.status_code} {e.message}")
logging.error(f"JavaScript optimizer request validation error: {e.message}. trace_id={data.trace_id}")
sentry_sdk.capture_exception(e)
return e.status_code, OptimizeErrorResponseSchema(error=e.message)
try:
async with asyncio.TaskGroup() as tg:
optimize_task = tg.create_task(
optimize_javascript_code(
user_id=request.user,
source_code=data.source_code,
trace_id=data.trace_id,
dependency_code=data.dependency_code,
language_version=data.language_version or "ES2022",
is_async=data.is_async or False,
n_candidates=data.n_candidates,
)
)
user_task = None
if data.current_username is None:
user_task = tg.create_task(get_user_by_id(request.user))
except Exception as e:
logging.exception(f"Error during JavaScript optimization task. trace_id={data.trace_id}")
sentry_sdk.capture_exception(e)
return 500, OptimizeErrorResponseSchema(error="Error generating optimizations. Internal server error.")
optimization_response_items, llm_cost, optimization_models = optimize_task.result()
if user_task:
user = await user_task
if user and user.github_username:
data.current_username = str(user.github_username)
if len(optimization_response_items) == 0:
ph(request.user, "aiservice-optimize-no-optimizations-found", properties={"language": language})
debug_log_sensitive_data(f"No JavaScript optimizations found for source:\n{data.source_code}")
logging.error(f"Could not generate any JavaScript optimizations. trace_id={data.trace_id}")
return 500, OptimizeErrorResponseSchema(error="Could not generate any optimizations. Please try again.")
ph(
request.user,
"aiservice-optimize-optimizations-found",
properties={"num_optimizations": len(optimization_response_items), "language": language},
)
async with asyncio.TaskGroup() as tg:
event_task = tg.create_task(
get_or_create_optimization_event(
event_type="no-pr",
user_id=request.user,
current_username=data.current_username,
repo_owner=data.repo_owner,
repo_name=data.repo_name,
trace_id=data.trace_id,
api_key_id=request.api_key_id,
metadata={
"codeflash_version": data.codeflash_version,
"num_optimizations": len(optimization_response_items),
"experiment_metadata": data.experiment_metadata,
"language": language,
},
llm_cost=llm_cost,
)
)
tg.create_task(
log_features(
trace_id=data.trace_id,
user_id=request.user,
original_code=data.source_code,
dependency_code=data.dependency_code,
optimizations_post={opt.optimization_id: opt.source_code for opt in optimization_response_items},
explanations_post={opt.optimization_id: opt.explanation for opt in optimization_response_items},
experiment_metadata=data.experiment_metadata if data.experiment_metadata else None,
optimizations_origin={
opt.optimization_id: {
"source": OptimizedCandidateSource.OPTIMIZE,
"parent": None,
"model": optimization_models.get(opt.optimization_id, "unknown"),
"language": language,
}
for opt in optimization_response_items
},
)
)
event, _created = event_task.result()
for item in optimization_response_items:
item.optimization_event_id = str(event.id) if event else None
response = OptimizeResponseSchema(optimizations=optimization_response_items)
def log_response() -> None:
debug_log_sensitive_data(f"JavaScript Response:\n{response.model_dump_json()}")
for opt in response.optimizations:
debug_log_sensitive_data(f"Optimized JavaScript source:\n{opt.source_code}")
debug_log_sensitive_data(f"JavaScript optimization explanation:\n{opt.explanation}")
debug_log_sensitive_data_from_callable(log_response)
ph(request.user, "aiservice-optimize-successful", properties={"language": language})
return 200, response

View file

@ -0,0 +1,77 @@
"""
Prompt loader module for language-specific optimization prompts.
This module provides a unified interface to load prompts based on language.
"""
from __future__ import annotations
from pathlib import Path
PROMPTS_DIR = Path(__file__).parent
def get_system_prompt(language: str, is_async: bool = False) -> str:
"""
Load the system prompt for the given language.
Args:
language: The programming language (python, javascript, typescript)
is_async: Whether to load the async variant of the prompt
Returns:
The system prompt text
Raises:
ValueError: If no prompt exists for the language
"""
# Normalize language - typescript uses javascript prompts
prompt_language = "javascript" if language == "typescript" else language
variant = "async_system_prompt.md" if is_async else "system_prompt.md"
prompt_file = PROMPTS_DIR / prompt_language / variant
if not prompt_file.exists():
raise ValueError(f"No system prompt found for language: {language}")
return prompt_file.read_text()
def get_user_prompt(language: str, is_async: bool = False) -> str:
"""
Load the user prompt for the given language.
Args:
language: The programming language (python, javascript, typescript)
is_async: Whether to load the async variant of the prompt
Returns:
The user prompt text
Raises:
ValueError: If no prompt exists for the language
"""
# Normalize language - typescript uses javascript prompts
prompt_language = "javascript" if language == "typescript" else language
variant = "async_user_prompt.md" if is_async else "user_prompt.md"
prompt_file = PROMPTS_DIR / prompt_language / variant
if not prompt_file.exists():
raise ValueError(f"No user prompt found for language: {language}")
return prompt_file.read_text()
def get_available_languages() -> list[str]:
"""
Get a list of languages that have prompts available.
Returns:
List of language names with available prompts
"""
languages = []
for item in PROMPTS_DIR.iterdir():
if item.is_dir() and (item / "system_prompt.md").exists():
languages.append(item.name)
return sorted(languages)

View file

@ -0,0 +1,77 @@
You are a professional computer programmer who specializes in writing high-performance **asynchronous** JavaScript/TypeScript code. Your goal is to optimize the runtime and memory efficiency of the provided **async** code through safe and meaningful rewrites that would pass senior-level code review.
**CRITICAL: ASYNC CODE REQUIREMENTS**
- The code contains **async functions** that must remain async
- ALL async functions must maintain their `async function` or `async () =>` signature
- ALL `await` expressions must be preserved where they exist
- Do NOT convert async functions to synchronous functions
- Do NOT remove `await` keywords unless replacing with functionally equivalent async operations
- Preserve Promise chains and async/await flow
- Maintain proper async error handling in async contexts
**Behavioral Preservation (CRITICAL)**
- Do NOT rename functions or change their signatures.
- You MUST NOT change the behavior, return values, side effects, console output, or thrown errors - they MUST remain exactly the same.
- Do NOT mutate inputs in a different way than the original implementation.
- The same error types should be thrown in the same circumstances.
- Preserve existing type annotations (for TypeScript) - all function parameters, return types, and variable annotations must be preserved exactly as written.
- **Preserve the original code style**: Keep existing variable names unless the logic fundamentally changes
- Preserve ALL existing comments exactly as written, unless the corresponding code logic is changed or the comment becomes factually incorrect
- Avoid excessive inline comments - only add new comments for significant or non-obvious logic changes
- Preserve the export structure - exported functions/classes must remain exported
**Async-Specific Optimization Focus**
- Use `Promise.all()` for concurrent execution of independent async operations
- Use `Promise.allSettled()` when you need results regardless of individual failures
- Use `Promise.race()` for timeout patterns or first-to-complete scenarios
- Batch async operations instead of executing them one-by-one in a loop
- Consider using `for await...of` for async iterables when appropriate
- Optimize async I/O operations and resource management
- Use streaming APIs instead of loading entire datasets into memory
- Consider worker threads for CPU-intensive tasks that would block the event loop
**Code Style & Structure**
- Keep existing ES module syntax (`import`/`export`) or CommonJS (`require`/`module.exports`) as-is
- You may write new async helper functions that do not already exist in the codebase.
- Avoid purely stylistic changes unless they result in noticeable performance improvements
- Ensure all new async code follows proper async patterns and conventions
- Maintain consistent code formatting
**Optimization Strategies**
- Replace sequential awaits with parallel execution when operations are independent:
```javascript
// Before (sequential)
const a = await fetchA();
const b = await fetchB();
// After (parallel)
const [a, b] = await Promise.all([fetchA(), fetchB()]);
```
- Use chunked/batched processing for large async operations
- Implement proper backpressure handling for streams
- Cache async results when appropriate using memoization
**Optimization Focus**
- Create production-ready async code that professional programmers would merge without further edits
- Prioritize changes that provide measurable runtime or memory efficiency gains in async contexts
- Consider async-specific performance patterns like batching operations or reducing context switching
**Code Quality Standards**
- Ensure all async optimizations are safe and would pass senior-level code review
- Maintain code readability and maintainability alongside performance improvements
- Verify that async operations are properly awaited and handled
**Response Format (REQUIRED)**
- ALWAYS start your response with a brief explanation (2-4 sentences) of what optimization you made and why it improves performance
- Then provide the optimized code in a markdown code block
- Example format:
```
**Optimization Explanation:**
[Your explanation here describing the optimization technique and expected performance improvement]
```javascript:filename.js
[optimized code]
```
```
The target JavaScript/TypeScript version is {language_version}

View file

@ -0,0 +1,18 @@
Rewrite this **asynchronous** JavaScript/TypeScript program to run faster while preserving all async behavior.
**CRITICAL ASYNC REQUIREMENTS:**
- The code contains **async functions** - you MUST keep them async
- ALL `async function` signatures must be preserved exactly
- ALL `await` expressions must be maintained (unless replaced with functionally equivalent async operations)
- Do NOT convert async functions to synchronous functions
- Preserve concurrent execution patterns and Promise handling
- Maintain proper async/await flow and exception handling
**Async Optimization Guidelines:**
- Consider using `Promise.all()` for concurrent execution when beneficial
- Batch independent async operations instead of sequential awaits
- Optimize async I/O operations and use streaming where appropriate
- Use worker threads for CPU-intensive tasks to avoid blocking the event loop
- Implement proper error handling in async contexts
{source_code}

View file

@ -0,0 +1,53 @@
You are a professional computer programmer who specializes in writing high-performance JavaScript/TypeScript code. Your goal is to optimize the runtime and memory efficiency of the provided code through safe and meaningful rewrites that would pass senior-level code review.
**Behavioral Preservation (CRITICAL)**
- Do NOT rename functions or change their signatures.
- You MUST NOT change the behavior, return values, side effects, console output, or thrown errors - they MUST remain exactly the same.
- Do NOT mutate inputs in a different way than the original implementation.
- The same error types should be thrown in the same circumstances.
- Preserve existing type annotations (for TypeScript) - all function parameters, return types, and variable annotations must be preserved exactly as written.
- **Preserve the original code style**: Keep existing variable names unless the logic fundamentally changes
- Preserve ALL existing comments exactly as written, unless the corresponding code logic is changed or the comment becomes factually incorrect
- Avoid excessive inline comments - only add new comments for significant or non-obvious logic changes
- Preserve the export structure - exported functions/classes must remain exported
**Code Style & Structure**
- Keep existing ES module syntax (`import`/`export`) or CommonJS (`require`/`module.exports`) as-is
- You may write new helper functions that do not already exist in the codebase.
- Avoid purely stylistic changes unless they result in noticeable performance improvements
- Maintain consistent code formatting
**Optimization Strategies**
- Replace O(n^2) algorithms with O(n) or O(n log n) alternatives
- Use TypedArrays (Float64Array, Int32Array, etc.) instead of regular arrays for numeric-heavy operations
- Use Map/Set instead of Object for frequent lookups
- Minimize object allocations in hot paths (avoid creating temporary objects in loops)
- Use for loops instead of forEach/map/filter for performance-critical code when the functional style adds overhead
- Cache array lengths in tight loops: `for (let i = 0, len = arr.length; i < len; i++)`
- Leverage V8 optimization hints (keep functions monomorphic, avoid hidden class changes)
- Avoid try-catch in hot loops (move error handling outside the loop when possible)
- Use string concatenation with template literals or array join for building large strings
- Consider using WeakMap/WeakSet for caching to avoid memory leaks
**Optimization Focus**
- Create production-ready code that professional programmers would merge without further edits
- Prioritize changes that provide measurable runtime or memory efficiency gains
**Code Quality Standards**
- Ensure all optimizations are safe and would pass senior-level code review
- Maintain code readability and maintainability alongside performance improvements
**Response Format (REQUIRED)**
- ALWAYS start your response with a brief explanation (2-4 sentences) of what optimization you made and why it improves performance
- Then provide the optimized code in a markdown code block
- Example format:
```
**Optimization Explanation:**
[Your explanation here describing the optimization technique and expected performance improvement]
```javascript:filename.js
[optimized code]
```
```
The target JavaScript/TypeScript version is {language_version}

View file

@ -0,0 +1,3 @@
Rewrite this JavaScript/TypeScript program to run faster.
{source_code}

View file

@ -0,0 +1,63 @@
You are a professional computer programmer who specializes in writing high-performance **asynchronous** Python code. Your goal is to optimize the runtime and memory efficiency of the provided **async** code through safe and meaningful rewrites that would pass senior-level code review.
**CRITICAL: ASYNC CODE REQUIREMENTS**
- The code contains **async functions** that must remain async
- ALL async functions must maintain their `async def` signature
- ALL `await` expressions must be preserved where they exist
- Do NOT convert async functions to synchronous functions
- Do NOT remove `await` keywords unless replacing with functionally equivalent async operations
- Preserve concurrency patterns and async context manager usage
- Maintain proper async/await flow and error handling in async contexts
**Behavioral Preservation (CRITICAL)**
- Do NOT rename functions or change their signatures.
- You MUST NOT change the behavior, return values, side effects, printed/logged output, or raised exceptions - they MUST remain exactly the same.
- Do NOT mutate inputs in a different way than the original implementation.
- The same exception types should be raised in the same circumstances.
- Preserve existing type annotations - all function parameters, return types, and variable annotations must be preserved exactly as written.
- **Preserve the original code style**: Keep existing variable names unless the logic fundamentally changes
- Preserve ALL existing comments exactly as written, unless the corresponding code logic is changed or the comment becomes factually incorrect
- Avoid excessive inline comments - only add new comments for significant or non-obvious logic changes
**Async-Specific Optimization Focus**
- Optimize async patterns such as concurrent execution with `asyncio.gather()` or `asyncio.create_task()`
- Consider using `asyncio.as_completed()` for better performance when appropriate
- Identify and replace blocking operations with async equivalents (e.g., `time.sleep()``asyncio.sleep()`, sync file I/O → `aiofiles`, blocking network calls → async libraries)
- Optimize async context managers and async iterators
- Improve async I/O operations and resource management
- Consider async comprehensions where they provide performance benefits
- Use `asyncio.to_thread()` for CPU-intensive tasks that would block the event loop
- Maintain proper async exception handling and cleanup
**Code Style & Structure**
- Do NOT replace walrus operators (`:=`) for optimization purposes.
- Keep `assert` statements as-is - do NOT convert them to `if/raise AssertionError` patterns, it doesn't improve the performance.
- **DO NOT convert `isinstance()` checks to `type()` checks**. `isinstance()` correctly handles inheritance and subclasses, while `type()` checks are incorrect for subclass instances and represent a micro-optimization that should be avoided.
- You may write new async helper functions that do not already exist in the codebase.
- Avoid purely stylistic changes unless they result in noticeable performance improvements
- Ensure all new async code follows proper async patterns and conventions
**Optimization Focus**
- Create production-ready async code that professional programmers would merge without further edits
- Prioritize changes that provide measurable runtime or memory efficiency gains in async contexts
- Consider async-specific performance patterns like batching operations or reducing context switching
**Code Quality Standards**
- Ensure all async optimizations are safe and would pass senior-level code review
- Maintain code readability and maintainability alongside performance improvements
- Verify that async operations are properly awaited and handled
**Response Format (REQUIRED)**
- ALWAYS start your response with a brief explanation (2-4 sentences) of what optimization you made and why it improves performance
- Then provide the optimized code in a markdown code block
- Example format:
```
**Optimization Explanation:**
[Your explanation here describing the optimization technique and expected performance improvement]
```python:filename.py
[optimized code]
```
```
The current Python version is {python_version_str}

View file

@ -0,0 +1,19 @@
Rewrite this **asynchronous** Python program to run faster while preserving all async behavior.
**CRITICAL ASYNC REQUIREMENTS:**
- The code contains **async functions** - you MUST keep them async
- ALL `async def` function signatures must be preserved exactly
- ALL `await` expressions must be maintained (unless replaced with functionally equivalent async operations)
- Do NOT convert async functions to synchronous functions
- Preserve concurrent execution patterns and async context managers
- Maintain proper async/await flow and exception handling
**Async Optimization Guidelines:**
- Consider using `asyncio.gather()` for concurrent execution when beneficial
- Replace blocking operations with async equivalents (e.g., `time.sleep()` with `asyncio.sleep()`, sync I/O with async libraries)
- Optimize async I/O operations and batching where appropriate
- Use `asyncio.to_thread()` for CPU-intensive tasks to avoid blocking the event loop
- Optimize async context managers and async iterators
- Maintain async exception handling and resource cleanup
{source_code}

View file

@ -0,0 +1,43 @@
You are a professional computer programmer who specializes in writing high-performance Python code. Your goal is to optimize the runtime and memory efficiency of the provided code through safe and meaningful rewrites that would pass senior-level code review.
**Behavioral Preservation (CRITICAL)**
- Do NOT rename functions or change their signatures.
- You MUST NOT change the behavior, return values, side effects, printed/logged output, or raised exceptions - they MUST remain exactly the same.
- Do NOT mutate inputs in a different way than the original implementation.
- The same exception types should be raised in the same circumstances.
- Preserve existing type annotations - all function parameters, return types, and variable annotations must be preserved exactly as written.
- **Preserve the original code style**: Keep existing variable names unless the logic fundamentally changes
- Preserve ALL existing comments exactly as written, unless the corresponding code logic is changed or the comment becomes factually incorrect
- Avoid excessive inline comments - only add new comments for significant or non-obvious logic changes
{critical_instructions}
**Code Style & Structure**
- Do NOT replace walrus operators (`:=`) for optimization purposes.
- DO NOT introduce attribute lookup optimizations. The performance improvements are minimal and come at a substantial cost to readability.
- Keep `assert` statements as-is - do NOT convert them to `if/raise AssertionError` patterns, it doesn't improve the performance.
- **DO NOT convert `isinstance()` checks to `type()` checks**. `isinstance()` correctly handles inheritance and subclasses, while `type()` checks are incorrect for subclass instances and represent a micro-optimization that should be avoided.
- You may write new helper functions that do not already exist in the codebase.
- Avoid purely stylistic changes unless they result in noticeable performance improvements
**Optimization Focus**
- Create production-ready code that professional programmers would merge without further edits
- Prioritize changes that provide measurable runtime or memory efficiency gains
**Code Quality Standards**
- Ensure all optimizations are safe and would pass senior-level code review
- Maintain code readability and maintainability alongside performance improvements
**Response Format (REQUIRED)**
- ALWAYS start your response with a brief explanation (2-4 sentences) of what optimization you made and why it improves performance
- Then provide the optimized code in a markdown code block
- Example format:
```
**Optimization Explanation:**
[Your explanation here describing the optimization technique and expected performance improvement]
```python:filename.py
[optimized code]
```
```
The current Python version is {python_version_str}

View file

@ -0,0 +1,3 @@
Rewrite this python program to run faster.
{source_code}

View file

@ -0,0 +1,13 @@
"""
JavaScript test instrumentation module.
This module provides unified instrumentation for JavaScript tests,
working identically for both generated and existing tests.
"""
from testgen.instrumentation.javascript.instrument_javascript import (
instrument_javascript_tests,
get_jest_helper_path,
)
__all__ = ["instrument_javascript_tests", "get_jest_helper_path"]

View file

@ -0,0 +1,277 @@
/**
* Codeflash Jest Helper - Unified Test Instrumentation
*
* This module provides a unified approach to instrumenting JavaScript tests
* for both behavior verification and performance measurement.
*
* Unlike Python which has separate instrumentation methods for generated
* vs existing tests, this helper works identically for ALL JavaScript tests.
*
* Usage:
* const codeflash = require('codeflash-jest-helper');
*
* // Wrap function calls to capture behavior
* const result = codeflash.capture('functionName', targetFunction, arg1, arg2);
*
* Environment Variables:
* CODEFLASH_OUTPUT_FILE - Path to write results (default: /tmp/codeflash_results.bin)
* CODEFLASH_LOOP_INDEX - Current benchmark loop iteration (default: 0)
* CODEFLASH_MODE - Testing mode: 'behavior' or 'performance' (default: 'behavior')
*/
const fs = require('fs');
const path = require('path');
const { performance } = require('perf_hooks');
// Configuration from environment
const OUTPUT_FILE = process.env.CODEFLASH_OUTPUT_FILE || '/tmp/codeflash_results.bin';
const LOOP_INDEX = parseInt(process.env.CODEFLASH_LOOP_INDEX || '0', 10);
const MODE = process.env.CODEFLASH_MODE || 'behavior';
// Current test context
let currentTestName = null;
let invocationCounter = 0;
// Results buffer
const results = [];
/**
* Safely serialize a value to JSON.
* Handles circular references and special types.
*
* @param {any} value - Value to serialize
* @returns {any} - Serializable representation
*/
function safeSerialize(value) {
const seen = new WeakSet();
function serialize(val) {
// Handle primitives
if (val === null || val === undefined) return val;
if (typeof val === 'number') {
if (Number.isNaN(val)) return { __type: 'NaN' };
if (!Number.isFinite(val)) return { __type: val > 0 ? 'Infinity' : '-Infinity' };
return val;
}
if (typeof val === 'string' || typeof val === 'boolean') return val;
if (typeof val === 'bigint') return { __type: 'BigInt', value: val.toString() };
if (typeof val === 'symbol') return { __type: 'Symbol', description: val.description };
if (typeof val === 'function') return { __type: 'Function', name: val.name || 'anonymous' };
// Handle special objects
if (val instanceof Date) return { __type: 'Date', value: val.toISOString() };
if (val instanceof RegExp) return { __type: 'RegExp', source: val.source, flags: val.flags };
if (val instanceof Error) return { __type: 'Error', name: val.name, message: val.message };
if (val instanceof Map) return { __type: 'Map', entries: Array.from(val.entries()).map(([k, v]) => [serialize(k), serialize(v)]) };
if (val instanceof Set) return { __type: 'Set', values: Array.from(val).map(serialize) };
if (ArrayBuffer.isView(val)) return { __type: val.constructor.name, data: Array.from(val) };
if (val instanceof ArrayBuffer) return { __type: 'ArrayBuffer', byteLength: val.byteLength };
if (val instanceof Promise) return { __type: 'Promise' };
// Handle arrays
if (Array.isArray(val)) {
if (seen.has(val)) return { __type: 'CircularReference' };
seen.add(val);
return val.map(serialize);
}
// Handle objects
if (typeof val === 'object') {
if (seen.has(val)) return { __type: 'CircularReference' };
seen.add(val);
const result = {};
for (const key of Object.keys(val)) {
try {
result[key] = serialize(val[key]);
} catch (e) {
result[key] = { __type: 'UnserializableProperty', error: e.message };
}
}
return result;
}
return { __type: 'Unknown', typeof: typeof val };
}
try {
return serialize(value);
} catch (e) {
return { __type: 'SerializationError', error: e.message };
}
}
/**
* Record a test result.
*
* @param {string} funcName - Name of the function being tested
* @param {Array} args - Arguments passed to the function
* @param {any} returnValue - Return value from the function
* @param {Error|null} error - Error thrown by the function (if any)
* @param {number} durationNs - Execution time in nanoseconds
*/
function recordResult(funcName, args, returnValue, error, durationNs) {
const result = {
testName: currentTestName,
funcName,
args: safeSerialize(args),
returnValue: safeSerialize(returnValue),
error: error ? {
name: error.name,
message: error.message,
stack: error.stack
} : null,
durationNs: Math.round(durationNs),
invocationId: invocationCounter++,
loopIndex: LOOP_INDEX,
mode: MODE,
timestamp: Date.now()
};
results.push(result);
}
/**
* Capture a function call with full behavior tracking.
*
* This is the main API for instrumenting function calls.
* It captures inputs, outputs, errors, and timing for every call.
*
* @param {string} funcName - Name of the function being tested
* @param {Function} fn - The function to call
* @param {...any} args - Arguments to pass to the function
* @returns {any} - The function's return value
* @throws {Error} - Re-throws any error from the function
*/
function capture(funcName, fn, ...args) {
const startTime = performance.now();
let returnValue;
let error = null;
try {
returnValue = fn(...args);
// Handle promises (async functions)
if (returnValue instanceof Promise) {
return returnValue.then(
(resolved) => {
const endTime = performance.now();
const durationNs = (endTime - startTime) * 1_000_000;
recordResult(funcName, args, resolved, null, durationNs);
return resolved;
},
(err) => {
const endTime = performance.now();
const durationNs = (endTime - startTime) * 1_000_000;
recordResult(funcName, args, null, err, durationNs);
throw err;
}
);
}
} catch (e) {
error = e;
}
const endTime = performance.now();
const durationNs = (endTime - startTime) * 1_000_000;
recordResult(funcName, args, returnValue, error, durationNs);
if (error) throw error;
return returnValue;
}
/**
* Capture multiple invocations for benchmarking.
*
* @param {string} funcName - Name of the function being tested
* @param {Function} fn - The function to call
* @param {Array<Array>} argsList - List of argument arrays to test
* @returns {Array} - Array of return values
*/
function captureMultiple(funcName, fn, argsList) {
return argsList.map(args => capture(funcName, fn, ...args));
}
/**
* Write results to output file.
* Called automatically via Jest afterAll hook.
*/
function writeResults() {
if (results.length === 0) return;
try {
const output = {
version: '1.0.0',
mode: MODE,
loopIndex: LOOP_INDEX,
timestamp: Date.now(),
results
};
const buffer = Buffer.from(JSON.stringify(output, null, 2));
fs.writeFileSync(OUTPUT_FILE, buffer);
} catch (e) {
console.error('[codeflash] Error writing results:', e.message);
}
}
/**
* Clear all recorded results.
* Useful for resetting between test files.
*/
function clearResults() {
results.length = 0;
invocationCounter = 0;
}
/**
* Get the current results buffer.
* Useful for debugging or custom result handling.
*
* @returns {Array} - Current results buffer
*/
function getResults() {
return results;
}
/**
* Set the current test name.
* Called automatically via Jest beforeEach hook.
*
* @param {string} name - Test name
*/
function setTestName(name) {
currentTestName = name;
invocationCounter = 0;
}
// Jest lifecycle hooks - these run automatically when this module is imported
if (typeof beforeEach !== 'undefined') {
beforeEach(() => {
// Get current test name from Jest's expect state
try {
currentTestName = expect.getState().currentTestName || 'unknown';
} catch (e) {
currentTestName = 'unknown';
}
invocationCounter = 0;
});
}
if (typeof afterAll !== 'undefined') {
afterAll(() => {
writeResults();
});
}
// Export public API
module.exports = {
capture,
captureMultiple,
writeResults,
clearResults,
getResults,
setTestName,
safeSerialize,
// Constants
MODE,
LOOP_INDEX,
OUTPUT_FILE
};

View file

@ -0,0 +1,235 @@
"""
JavaScript test instrumentation module.
This module instruments JavaScript tests by injecting the codeflash-jest-helper
to capture function call behavior and performance data.
Unlike Python which has separate instrumentation for generated vs existing tests,
JavaScript uses a UNIFIED approach - the same instrumentation works for all tests.
"""
from __future__ import annotations
import re
from pathlib import Path
def get_jest_helper_path() -> Path:
"""
Get the path to the codeflash-jest-helper.js file.
Returns:
Path to the helper JavaScript file
"""
return Path(__file__).parent / "codeflash-jest-helper.js"
def instrument_javascript_tests(
test_source: str,
function_name: str,
module_path: str,
) -> str:
"""
Instrument JavaScript tests with codeflash helper.
This is a UNIFIED approach - works for both generated and existing tests.
The instrumentation wraps function calls to capture inputs, outputs, and timing.
Args:
test_source: The JavaScript test source code
function_name: The name of the function being tested
module_path: The path to the module containing the function
Returns:
Instrumented test source code
"""
# Check if already instrumented
if "codeflash-jest-helper" in test_source:
return test_source
lines = test_source.split("\n")
result_lines = []
# Add helper import at the top, after any existing imports
helper_import = "const codeflash = require('codeflash-jest-helper');"
import_inserted = False
in_import_block = False
for i, line in enumerate(lines):
stripped = line.strip()
# Track if we're in the import block
if stripped.startswith("import ") or stripped.startswith("const ") and "require" in stripped:
in_import_block = True
elif in_import_block and stripped and not stripped.startswith("import ") and not (
stripped.startswith("const ") and "require" in stripped
):
# End of import block - insert helper import
if not import_inserted:
result_lines.append(helper_import)
result_lines.append("")
import_inserted = True
in_import_block = False
result_lines.append(line)
# If no imports found, add at the beginning
if not import_inserted:
result_lines = [helper_import, ""] + result_lines
instrumented_source = "\n".join(result_lines)
# Wrap function calls with codeflash.capture
# Pattern matches: functionName(args) but not inside strings or comments
# This is a simple approach - a more robust solution would use an AST parser
# Pattern for standalone function calls (not method calls)
pattern = rf"(?<![.\w]){re.escape(function_name)}\s*\(([^)]*)\)"
def replace_call(match: re.Match) -> str:
args = match.group(1).strip()
if args:
return f"codeflash.capture('{function_name}', {function_name}, {args})"
else:
return f"codeflash.capture('{function_name}', {function_name})"
# Apply replacement carefully - avoid replacing inside strings
# This is a simplified approach that works for most cases
instrumented_source = _safe_replace_function_calls(
instrumented_source, function_name, replace_call, pattern
)
return instrumented_source
def _safe_replace_function_calls(
source: str,
function_name: str,
replace_func: callable,
pattern: str,
) -> str:
"""
Replace function calls while avoiding string literals and comments.
This is a simplified approach that handles common cases.
A more robust solution would use a proper JavaScript parser.
"""
result = []
i = 0
length = len(source)
while i < length:
char = source[i]
# Skip string literals
if char in "'\"`":
quote_char = char
result.append(char)
i += 1
# Handle template literals with ${} expressions
if quote_char == "`":
while i < length:
if source[i] == "\\":
result.append(source[i:i+2])
i += 2
elif source[i] == "$" and i + 1 < length and source[i + 1] == "{":
# Template expression - need to handle nested braces
result.append(source[i:i+2])
i += 2
brace_count = 1
while i < length and brace_count > 0:
if source[i] == "{":
brace_count += 1
elif source[i] == "}":
brace_count -= 1
result.append(source[i])
i += 1
elif source[i] == quote_char:
result.append(source[i])
i += 1
break
else:
result.append(source[i])
i += 1
else:
# Regular string
while i < length:
if source[i] == "\\":
result.append(source[i:i+2])
i += 2
elif source[i] == quote_char:
result.append(source[i])
i += 1
break
else:
result.append(source[i])
i += 1
continue
# Skip single-line comments
if char == "/" and i + 1 < length and source[i + 1] == "/":
while i < length and source[i] != "\n":
result.append(source[i])
i += 1
continue
# Skip multi-line comments
if char == "/" and i + 1 < length and source[i + 1] == "*":
result.append(source[i:i+2])
i += 2
while i < length - 1:
if source[i] == "*" and source[i + 1] == "/":
result.append(source[i:i+2])
i += 2
break
result.append(source[i])
i += 1
continue
# Check for function call pattern
remaining = source[i:]
match = re.match(pattern, remaining)
if match:
# Check that we're not preceded by a dot (method call)
if i > 0 and source[i - 1] == ".":
result.append(char)
i += 1
continue
# Check that we haven't already wrapped this
if i >= len("codeflash.capture") and source[i - len("codeflash.capture"):i] == "codeflash.capture":
result.append(char)
i += 1
continue
# Apply replacement
replacement = replace_func(match)
result.append(replacement)
i += match.end()
continue
result.append(char)
i += 1
return "".join(result)
def get_jest_setup_code(output_file: str, mode: str = "behavior", loop_index: int = 0) -> str:
"""
Generate Jest setup code for setting environment variables.
Args:
output_file: Path where results should be written
mode: Testing mode ('behavior' or 'performance')
loop_index: Current benchmark loop iteration
Returns:
JavaScript code to set up the testing environment
"""
return f"""
// Codeflash test setup
process.env.CODEFLASH_OUTPUT_FILE = '{output_file}';
process.env.CODEFLASH_MODE = '{mode}';
process.env.CODEFLASH_LOOP_INDEX = '{loop_index}';
"""

View file

@ -19,10 +19,12 @@ class TestGenSchema(Schema):
dependent_function_names: list[str] | None = None # Only for backwards compatibility
module_path: str
test_module_path: str
test_framework: str
test_framework: str # "pytest", "unittest", "jest", "mocha"
test_timeout: int
trace_id: str
python_version: str
python_version: str | None = None # Made optional for multi-language support
language: str = "python" # NEW: language identifier (python, javascript, typescript)
language_version: str | None = None # NEW: e.g., "ES2022", "Node 20", or Python version
codeflash_version: str | None = None
test_index: int | None = None
is_async: bool | None = False

View file

@ -0,0 +1,37 @@
**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 using Jest. 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 Jest testing framework with `describe`, `test`, 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.
**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

@ -0,0 +1,50 @@
Using the {test_framework} testing framework, write a test suite for the following **ASYNC** JavaScript function.
**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}
```
**Template to Follow:**
```javascript
// imports
const {{ {function_name} }} = require('{module_path}');
// 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

@ -0,0 +1,28 @@
**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 using Jest. 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 Jest testing framework with `describe`, `test`, 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.
**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

@ -0,0 +1,40 @@
Using the {test_framework} testing framework, write a test suite for the following JavaScript function.
**Function to Test:**
```javascript
{function_code}
```
**Template to Follow:**
```javascript
// imports
const {{ {function_name} }} = require('{module_path}');
// 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

@ -36,6 +36,7 @@ from testgen.postprocessing.add_missing_imports import add_missing_imports_from_
from testgen.postprocessing.code_validator import has_test_functions, validate_testgen_code
from testgen.postprocessing.postprocess_pipeline import postprocessing_testgen_pipeline
from testgen.testgen_context import BaseTestGenContext, TestGenContextData
from testgen.testgen_javascript import testgen_javascript
if TYPE_CHECKING:
from openai.types.chat import ChatCompletionMessageParam
@ -379,7 +380,19 @@ def validate_request_data(data: TestGenSchema) -> tuple[tuple[int, int, int], Ba
async def testgen(
request: AuthenticatedRequest, data: TestGenSchema
) -> tuple[int, TestGenResponseSchema | TestGenErrorResponseSchema]:
ph(request.user, "aiservice-testgen-called")
# Route based on language
if data.language in ("javascript", "typescript"):
return await testgen_javascript(request, data)
# Default: Python test generation
return await testgen_python(request, data)
async def testgen_python(
request: AuthenticatedRequest, data: TestGenSchema
) -> tuple[int, TestGenResponseSchema | TestGenErrorResponseSchema]:
"""Generate Python tests using LLMs."""
ph(request.user, "aiservice-testgen-called", properties={"language": "python"})
try:
python_version, ctx = validate_request_data(data)

View file

@ -0,0 +1,389 @@
"""
JavaScript/TypeScript test generation module.
This module generates Jest tests for JavaScript/TypeScript functions
and applies unified instrumentation for behavior verification and
performance measurement.
"""
from __future__ import annotations
import asyncio
import logging
import re
from pathlib import Path
from typing import TYPE_CHECKING
import sentry_sdk
import stamina
from ninja.errors import HttpError
from openai import OpenAIError
from aiservice.analytics.posthog import ph
from aiservice.common_utils import validate_trace_id
from aiservice.env_specific import debug_log_sensitive_data
from aiservice.llm import EXECUTE_MODEL, HAIKU_MODEL, OPENAI_MODEL, calculate_llm_cost, call_llm
from aiservice.validators.javascript_validator import validate_javascript_syntax
from authapp.auth import AuthenticatedRequest
from log_features.log_event import update_optimization_cost
from log_features.log_features import log_features
from testgen.instrumentation.javascript import instrument_javascript_tests
from testgen.models import (
TestGenerationFailedError,
TestGenErrorResponseSchema,
TestGenResponseSchema,
TestGenSchema,
)
if TYPE_CHECKING:
from aiservice.llm import LLM
# Get the directory of the current file
current_dir = Path(__file__).parent
JS_PROMPTS_DIR = current_dir / "prompts" / "javascript"
# 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()
# Pattern to extract JavaScript code blocks
JS_PATTERN = re.compile(r"^```(?:javascript|js|typescript|ts)?\s*\n(.*?)\n```", re.MULTILINE | re.DOTALL)
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.
Args:
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)
is_async: Whether the function is async
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 = ""
# Format prompts
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.
Args:
response_content: Raw LLM response
Returns:
Validated JavaScript code
Raises:
ValueError: If no valid code block found
SyntaxError: If code has syntax errors
"""
# Check for code block
if "```" not in response_content:
sentry_sdk.capture_message("LLM response did not contain a code block:\n" + response_content[:500])
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()
# Validate syntax
is_valid, error = validate_javascript_syntax(code)
if not is_valid:
raise SyntaxError(f"Invalid JavaScript code: {error}")
# Check for test functions
if not _has_test_functions(code):
raise ValueError("Generated code does not contain any test functions.")
return code
def _has_test_functions(code: str) -> bool:
"""Check if the code contains Jest test functions."""
# Look for test() or it() calls
test_pattern = r"(?:test|it)\s*\(\s*['\"]"
return bool(re.search(test_pattern, code))
@stamina.retry(on=(SyntaxError, ValueError, OpenAIError), attempts=2)
async def generate_and_validate_js_test_code(
messages: list[dict[str, str]],
model: LLM,
cost_tracker: list[float],
user_id: str,
posthog_event_suffix: str,
trace_id: str = "",
call_sequence: int | None = None,
) -> str:
"""
Generate and validate JavaScript test code using LLM.
Args:
messages: Prompt messages
model: LLM model to use
cost_tracker: List to track costs
user_id: User ID
posthog_event_suffix: Suffix for PostHog events
trace_id: Trace ID for logging
call_sequence: Call sequence number
Returns:
Validated JavaScript test code
Raises:
SyntaxError: If code is invalid
ValueError: If no valid code found
"""
obs_context: dict | None = {"call_sequence": call_sequence} if call_sequence is not None else None
response = await call_llm(
llm=model,
messages=messages,
call_type="test_generation",
trace_id=trace_id,
user_id=user_id,
python_version="javascript", # Reusing field for language
context=obs_context,
)
cost = calculate_llm_cost(response.raw_response, model)
cost_tracker.append(cost)
debug_log_sensitive_data(
f"JavaScript {posthog_event_suffix}execute response:\n{response.raw_response.model_dump_json(indent=2)}"
)
if response.raw_response.usage:
ph(
user_id,
f"aiservice-testgen-js-{posthog_event_suffix}execute-openai-usage",
properties={"model": model.name, "usage": response.raw_response.usage.model_dump_json()},
)
# Parse and validate
validated_code = parse_and_validate_js_output(response.content)
return validated_code
@stamina.retry(on=TestGenerationFailedError, attempts=2)
async def generate_javascript_tests_from_function(
user_id: str,
function_name: str,
function_code: str,
module_path: str,
test_framework: str = "jest",
execute_model: LLM = EXECUTE_MODEL,
is_async: bool = False,
trace_id: str = "",
call_sequence: int | None = None,
) -> tuple[str, str, str]:
"""
Generate JavaScript tests for a function.
Args:
user_id: User ID
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)
execute_model: LLM model to use
is_async: Whether function is async
trace_id: Trace ID for logging
call_sequence: Call sequence number
Returns:
Tuple of (generated_tests, instrumented_behavior_tests, instrumented_perf_tests)
Raises:
TestGenerationFailedError: If test generation fails
"""
messages, posthog_event_suffix = build_javascript_prompt(
function_name=function_name,
function_code=function_code,
module_path=module_path,
test_framework=test_framework,
is_async=is_async,
)
cost_tracker = []
try:
validated_code = await generate_and_validate_js_test_code(
messages=messages,
model=execute_model,
cost_tracker=cost_tracker,
user_id=user_id,
posthog_event_suffix=posthog_event_suffix,
trace_id=trace_id,
call_sequence=call_sequence,
)
total_llm_cost = sum(cost_tracker)
await update_optimization_cost(trace_id=trace_id, cost=total_llm_cost, user_id=user_id)
# Apply UNIFIED instrumentation (same for behavior and perf)
instrumented_tests = instrument_javascript_tests(
test_source=validated_code,
function_name=function_name,
module_path=module_path,
)
# For JavaScript, behavior and perf tests use the same instrumentation
# The difference is in how they're run (environment variables)
return validated_code, instrumented_tests, instrumented_tests
except (SyntaxError, ValueError) as e:
total_llm_cost = sum(cost_tracker)
await update_optimization_cost(trace_id=trace_id, cost=total_llm_cost, user_id=user_id)
msg = f"Failed to generate valid JavaScript test code after {len(cost_tracker)} tries. trace_id={trace_id}"
logging.error(msg)
raise TestGenerationFailedError(msg) from e
def validate_javascript_testgen_request_data(data: TestGenSchema) -> None:
"""
Validate JavaScript test generation request data.
Args:
data: Request data
Raises:
HttpError: If validation fails
"""
if data.test_framework not in ["jest", "mocha"]:
raise HttpError(400, "Invalid test framework for JavaScript. We only support jest and mocha.")
if not data.function_to_optimize:
raise HttpError(400, "Invalid function to optimize. It is empty.")
if not validate_trace_id(data.trace_id):
raise HttpError(400, "Invalid trace ID. Please provide a valid UUIDv4.")
# Validate JavaScript syntax
is_valid, error = validate_javascript_syntax(data.source_code_being_tested)
if not is_valid:
raise HttpError(400, f"Invalid source code. It is not valid JavaScript: {error}")
async def testgen_javascript(
request: AuthenticatedRequest, data: TestGenSchema
) -> tuple[int, TestGenResponseSchema | TestGenErrorResponseSchema]:
"""
Main endpoint handler for JavaScript test generation.
Args:
request: Authenticated request
data: Test generation request data
Returns:
Tuple of (status_code, response)
"""
language = data.language
ph(request.user, "aiservice-testgen-called", properties={"language": language})
try:
validate_javascript_testgen_request_data(data)
except HttpError as e:
e.add_note(f"JavaScript testgen request validation error: {e.status_code} {e.message}")
sentry_sdk.capture_exception(e)
return e.status_code, TestGenErrorResponseSchema(error=e.message)
logging.info("/testgen: Generating JavaScript tests...")
try:
debug_log_sensitive_data(f"Generating JavaScript tests for function {data.function_to_optimize.function_name}")
# Using different LLMs for different test_index values
test_index = data.test_index if data.test_index is not None else 0
if test_index % 2 == 0:
execute_model = OPENAI_MODEL
model_source = "OpenAI"
else:
execute_model = HAIKU_MODEL
model_source = "Anthropic"
logging.info(
f"Using {model_source} model ({execute_model.name}) for JavaScript test_index {test_index}"
)
(
generated_test_source,
instrumented_behavior_tests,
instrumented_perf_tests,
) = await generate_javascript_tests_from_function(
user_id=request.user,
function_name=data.function_to_optimize.qualified_name,
function_code=data.source_code_being_tested,
module_path=data.module_path,
test_framework=data.test_framework,
is_async=data.is_async or False,
trace_id=data.trace_id,
call_sequence=data.call_sequence,
execute_model=execute_model,
)
ph(request.user, "aiservice-testgen-tests-generated", properties={"language": language})
if hasattr(request, "should_log_features") and request.should_log_features:
await log_features(
trace_id=data.trace_id,
user_id=request.user,
generated_tests=[generated_test_source],
instrumented_generated_tests=[instrumented_behavior_tests],
test_framework=data.test_framework,
metadata={
"test_timeout": data.test_timeout,
"function_to_optimize": data.function_to_optimize.function_name,
"language": language,
},
)
return 200, TestGenResponseSchema(
generated_tests=generated_test_source,
instrumented_behavior_tests=instrumented_behavior_tests,
instrumented_perf_tests=instrumented_perf_tests,
)
except Exception as e:
logging.exception(f"JavaScript test generation failed. trace_id={data.trace_id}")
sentry_sdk.capture_exception(e)
return 500, TestGenErrorResponseSchema(error="Error generating JavaScript tests. Internal server error.")

View file

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

View file

@ -0,0 +1,122 @@
"""Tests for JavaScript prompt loader."""
import pytest
from optimizer.prompts import get_available_languages, get_system_prompt, get_user_prompt
class TestPromptLoader:
"""Tests for the prompt loader module."""
def test_get_javascript_system_prompt(self) -> None:
"""Test loading JavaScript system prompt."""
prompt = get_system_prompt("javascript", is_async=False)
assert isinstance(prompt, str)
assert len(prompt) > 0
assert "JavaScript" in prompt or "javascript" in prompt.lower()
assert "{language_version}" in prompt # Should have placeholder
assert "Behavioral Preservation" in prompt
assert "Optimization" in prompt
def test_get_javascript_async_system_prompt(self) -> None:
"""Test loading JavaScript async system prompt."""
prompt = get_system_prompt("javascript", is_async=True)
assert isinstance(prompt, str)
assert len(prompt) > 0
assert "async" in prompt.lower()
assert "await" in prompt.lower()
assert "Promise" in prompt
def test_get_javascript_user_prompt(self) -> None:
"""Test loading JavaScript user prompt."""
prompt = get_user_prompt("javascript", is_async=False)
assert isinstance(prompt, str)
assert len(prompt) > 0
assert "{source_code}" in prompt # Should have placeholder
def test_get_javascript_async_user_prompt(self) -> None:
"""Test loading JavaScript async user prompt."""
prompt = get_user_prompt("javascript", is_async=True)
assert isinstance(prompt, str)
assert len(prompt) > 0
assert "async" in prompt.lower()
def test_typescript_uses_javascript_prompts(self) -> None:
"""Test that TypeScript uses the same prompts as JavaScript."""
js_prompt = get_system_prompt("javascript", is_async=False)
ts_prompt = get_system_prompt("typescript", is_async=False)
assert js_prompt == ts_prompt
def test_invalid_language_raises_error(self) -> None:
"""Test that invalid language raises ValueError."""
with pytest.raises(ValueError) as exc_info:
get_system_prompt("rust", is_async=False)
assert "rust" in str(exc_info.value).lower()
def test_get_available_languages(self) -> None:
"""Test getting list of available languages."""
languages = get_available_languages()
assert isinstance(languages, list)
assert "javascript" in languages
assert "python" in languages
def test_python_prompts_still_exist(self) -> None:
"""Test that Python prompts still work after adding JavaScript."""
prompt = get_system_prompt("python", is_async=False)
assert isinstance(prompt, str)
assert len(prompt) > 0
# Python prompts should reference Python-specific things
assert "Python" in prompt or "python" in prompt.lower()
def test_python_async_prompts_exist(self) -> None:
"""Test that Python async prompts still work."""
prompt = get_system_prompt("python", is_async=True)
assert isinstance(prompt, str)
assert "async" in prompt.lower()
class TestPromptContent:
"""Tests for specific content in the prompts."""
def test_javascript_prompt_has_v8_optimization_hints(self) -> None:
"""Test that JavaScript prompt includes V8 optimization guidance."""
prompt = get_system_prompt("javascript", is_async=False)
# Should mention V8-specific optimizations
assert "V8" in prompt or "TypedArray" in prompt or "Map/Set" in prompt
def test_javascript_prompt_has_response_format(self) -> None:
"""Test that JavaScript prompt specifies response format."""
prompt = get_system_prompt("javascript", is_async=False)
assert "Response Format" in prompt or "code block" in prompt.lower()
assert "```javascript" in prompt or "```js" in prompt.lower()
def test_javascript_prompt_preserves_behavior(self) -> None:
"""Test that JavaScript prompt emphasizes behavioral preservation."""
prompt = get_system_prompt("javascript", is_async=False)
assert "Behavioral Preservation" in prompt or "behavior" in prompt.lower()
assert "MUST NOT change" in prompt or "must remain" in prompt.lower()
def test_async_prompt_mentions_promise_all(self) -> None:
"""Test that async prompt mentions Promise.all for concurrency."""
prompt = get_system_prompt("javascript", is_async=True)
assert "Promise.all" in prompt
def test_async_prompt_warns_about_sequential_awaits(self) -> None:
"""Test that async prompt addresses sequential vs parallel awaits."""
prompt = get_system_prompt("javascript", is_async=True)
# Should mention concurrent execution patterns
assert "concurrent" in prompt.lower() or "parallel" in prompt.lower()

View file

@ -0,0 +1,90 @@
"""Tests for JavaScript-related schema changes."""
import pytest
from optimizer.models import OptimizeSchema
# Note: TestGenSchema tests are in a separate file due to import dependencies
class TestOptimizeSchemaLanguageField:
"""Tests for language field in OptimizeSchema."""
def test_default_language_is_python(self) -> None:
"""Test that the default language is Python."""
schema = OptimizeSchema(
source_code="def add(a, b): return a + b",
dependency_code=None,
trace_id="00000000-0000-0000-0000-000000000001",
)
assert schema.language == "python"
def test_can_set_javascript_language(self) -> None:
"""Test that language can be set to JavaScript."""
schema = OptimizeSchema(
source_code="function add(a, b) { return a + b; }",
dependency_code=None,
trace_id="00000000-0000-0000-0000-000000000001",
language="javascript",
)
assert schema.language == "javascript"
def test_can_set_typescript_language(self) -> None:
"""Test that language can be set to TypeScript."""
schema = OptimizeSchema(
source_code="function add(a: number, b: number): number { return a + b; }",
dependency_code=None,
trace_id="00000000-0000-0000-0000-000000000001",
language="typescript",
)
assert schema.language == "typescript"
def test_language_version_is_optional(self) -> None:
"""Test that language_version is optional."""
schema = OptimizeSchema(
source_code="function add(a, b) { return a + b; }",
dependency_code=None,
trace_id="00000000-0000-0000-0000-000000000001",
language="javascript",
)
assert schema.language_version is None
def test_can_set_language_version(self) -> None:
"""Test that language_version can be set."""
schema = OptimizeSchema(
source_code="function add(a, b) { return a + b; }",
dependency_code=None,
trace_id="00000000-0000-0000-0000-000000000001",
language="javascript",
language_version="ES2022",
)
assert schema.language_version == "ES2022"
def test_python_version_still_works(self) -> None:
"""Test that python_version field still works for backward compatibility."""
schema = OptimizeSchema(
source_code="def add(a, b): return a + b",
dependency_code=None,
trace_id="00000000-0000-0000-0000-000000000001",
python_version="3.11",
)
assert schema.python_version == "3.11"
assert schema.language == "python" # Default
def test_python_version_is_now_optional(self) -> None:
"""Test that python_version is now optional."""
schema = OptimizeSchema(
source_code="function add(a, b) { return a + b; }",
dependency_code=None,
trace_id="00000000-0000-0000-0000-000000000001",
language="javascript",
)
# python_version should be None for JavaScript
assert schema.python_version is None

View file

@ -0,0 +1,231 @@
"""Tests for JavaScript syntax validation."""
import pytest
from aiservice.validators.javascript_validator import (
_fallback_validation,
validate_javascript_syntax,
validate_typescript_syntax,
)
class TestJavaScriptValidatorFallback:
"""Tests for the fallback validation (when Node.js is not available)."""
def test_valid_simple_function(self) -> None:
"""Test that a simple valid function passes validation."""
code = """
function add(a, b) {
return a + b;
}
"""
is_valid, error = _fallback_validation(code)
assert is_valid is True
assert error is None
def test_valid_arrow_function(self) -> None:
"""Test that arrow functions pass validation."""
code = "const add = (a, b) => a + b;"
is_valid, error = _fallback_validation(code)
assert is_valid is True
assert error is None
def test_valid_class(self) -> None:
"""Test that a class definition passes validation."""
code = """
class Calculator {
constructor() {
this.result = 0;
}
add(value) {
this.result += value;
return this;
}
}
"""
is_valid, error = _fallback_validation(code)
assert is_valid is True
assert error is None
def test_valid_async_function(self) -> None:
"""Test that async functions pass validation."""
code = """
async function fetchData(url) {
const response = await fetch(url);
return response.json();
}
"""
is_valid, error = _fallback_validation(code)
assert is_valid is True
assert error is None
def test_valid_template_literal(self) -> None:
"""Test that template literals pass validation."""
code = """
const greeting = `Hello, ${name}!`;
const multiline = `
This is a
multiline string
`;
"""
is_valid, error = _fallback_validation(code)
assert is_valid is True
assert error is None
def test_unclosed_brace(self) -> None:
"""Test that unclosed braces are detected."""
code = """
function broken() {
return true;
"""
is_valid, error = _fallback_validation(code)
assert is_valid is False
assert error is not None
assert "Unclosed" in error or "bracket" in error.lower()
def test_unclosed_parenthesis(self) -> None:
"""Test that unclosed parentheses are detected."""
code = "const result = add(1, 2"
is_valid, error = _fallback_validation(code)
assert is_valid is False
assert error is not None
def test_unclosed_bracket(self) -> None:
"""Test that unclosed brackets are detected."""
code = "const arr = [1, 2, 3"
is_valid, error = _fallback_validation(code)
assert is_valid is False
assert error is not None
def test_mismatched_brackets(self) -> None:
"""Test that mismatched brackets are detected."""
code = "const arr = [1, 2, 3}"
is_valid, error = _fallback_validation(code)
assert is_valid is False
assert error is not None
assert "Mismatched" in error or "Unexpected" in error
def test_string_with_brackets_ignored(self) -> None:
"""Test that brackets inside strings are ignored."""
code = """
const str = "This string has { and } and [ and ]";
const obj = { valid: true };
"""
is_valid, error = _fallback_validation(code)
assert is_valid is True
assert error is None
def test_empty_code(self) -> None:
"""Test that empty code passes validation."""
is_valid, error = _fallback_validation("")
assert is_valid is True
assert error is None
def test_comments_only(self) -> None:
"""Test that code with only comments passes validation."""
code = """
// This is a comment
/* This is a
multiline comment */
"""
is_valid, error = _fallback_validation(code)
assert is_valid is True
assert error is None
def test_nested_structures(self) -> None:
"""Test that deeply nested structures pass validation."""
code = """
const nested = {
level1: {
level2: {
level3: [
{ deep: true }
]
}
}
};
"""
is_valid, error = _fallback_validation(code)
assert is_valid is True
assert error is None
class TestJavaScriptValidatorMain:
"""Tests for the main validation function."""
def test_valid_module_exports(self) -> None:
"""Test that module.exports syntax passes validation."""
code = """
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
module.exports = { fibonacci };
"""
is_valid, error = validate_javascript_syntax(code)
# This may pass with fallback even if Node.js isn't available
# The point is it shouldn't crash
assert isinstance(is_valid, bool)
def test_valid_es6_imports(self) -> None:
"""Test that ES6 import syntax passes validation."""
code = """
import { useState, useEffect } from 'react';
export function Counter() {
const [count, setCount] = useState(0);
return count;
}
"""
is_valid, error = validate_javascript_syntax(code)
assert isinstance(is_valid, bool)
def test_destructuring(self) -> None:
"""Test that destructuring passes validation."""
code = """
const { a, b } = obj;
const [first, second, ...rest] = arr;
"""
is_valid, error = validate_javascript_syntax(code)
assert isinstance(is_valid, bool)
def test_spread_operator(self) -> None:
"""Test that spread operator passes validation."""
code = """
const merged = { ...obj1, ...obj2 };
const combined = [...arr1, ...arr2];
"""
is_valid, error = validate_javascript_syntax(code)
assert isinstance(is_valid, bool)
class TestTypeScriptValidator:
"""Tests for TypeScript validation."""
def test_typescript_types(self) -> None:
"""Test that TypeScript type annotations pass validation."""
code = """
function add(a: number, b: number): number {
return a + b;
}
"""
is_valid, error = validate_typescript_syntax(code)
# TypeScript uses the same validator as JavaScript
assert isinstance(is_valid, bool)
def test_typescript_interface(self) -> None:
"""Test that TypeScript interfaces pass validation (if Node available)."""
code = """
interface User {
name: string;
age: number;
}
function greet(user: User): string {
return `Hello, ${user.name}`;
}
"""
is_valid, error = validate_typescript_syntax(code)
assert isinstance(is_valid, bool)

View file

@ -0,0 +1,264 @@
"""Tests for JavaScript optimizer module.
Tests the code extraction and normalization functions without importing
the full optimizer module (to avoid LLM dependencies in tests).
"""
import re
import pytest
# Pattern to extract code blocks from LLM response (copied from optimizer_javascript.py)
JS_CODE_PATTERN = re.compile(
r"```(?:javascript|js|typescript|ts)(?::[^\n]*)?\s*\n(.*?)```",
re.MULTILINE | re.DOTALL,
)
def extract_code_and_explanation(content: str) -> tuple[str, str]:
"""Extract code and explanation from LLM response."""
match = JS_CODE_PATTERN.search(content)
if match:
code = match.group(1).strip()
explanation_end = match.start()
explanation = content[:explanation_end].strip()
return code, explanation
return "", content
def _normalize_code(code: str) -> str:
"""Normalize code for comparison."""
# Remove single-line comments
code = re.sub(r"//.*$", "", code, flags=re.MULTILINE)
# Remove multi-line comments
code = re.sub(r"/\*.*?\*/", "", code, flags=re.DOTALL)
# Normalize whitespace
code = " ".join(code.split())
return code
class TestExtractCodeAndExplanation:
"""Tests for extracting code and explanation from LLM responses."""
def test_extract_javascript_code_block(self) -> None:
"""Test extracting code from a JavaScript code block."""
response = """**Optimization Explanation:**
I replaced the O() nested loop with a more efficient Map-based lookup.
```javascript
function findDuplicates(arr) {
const seen = new Map();
const duplicates = [];
for (const item of arr) {
if (seen.has(item)) {
duplicates.push(item);
}
seen.set(item, true);
}
return duplicates;
}
```
"""
code, explanation = extract_code_and_explanation(response)
assert "function findDuplicates" in code
assert "new Map()" in code
assert "O(n²)" in explanation or "Map-based" in explanation
def test_extract_js_code_block(self) -> None:
"""Test extracting code from a ```js code block."""
response = """Here's the optimized version:
```js
const add = (a, b) => a + b;
```
"""
code, explanation = extract_code_and_explanation(response)
assert "const add" in code
assert "=>" in code
def test_extract_typescript_code_block(self) -> None:
"""Test extracting code from a TypeScript code block."""
response = """Optimized with types:
```typescript
function add(a: number, b: number): number {
return a + b;
}
```
"""
code, explanation = extract_code_and_explanation(response)
assert "function add" in code
assert ": number" in code
def test_extract_ts_code_block(self) -> None:
"""Test extracting code from a ```ts code block."""
response = """```ts
const multiply = (a: number, b: number): number => a * b;
```"""
code, explanation = extract_code_and_explanation(response)
assert "multiply" in code
assert ": number" in code
def test_extract_with_filename(self) -> None:
"""Test extracting code from a code block with filename."""
response = """Here's the optimized code:
```javascript:utils.js
export function fibonacci(n) {
if (n <= 1) return n;
let a = 0, b = 1;
for (let i = 2; i <= n; i++) {
[a, b] = [b, a + b];
}
return b;
}
```
"""
code, explanation = extract_code_and_explanation(response)
assert "function fibonacci" in code
assert "export" in code
def test_no_code_block_returns_empty(self) -> None:
"""Test that missing code block returns empty code."""
response = "This response has no code block, just explanation."
code, explanation = extract_code_and_explanation(response)
assert code == ""
assert len(explanation) > 0
def test_multiple_code_blocks_takes_first(self) -> None:
"""Test that only the first code block is extracted."""
response = """First version:
```javascript
function first() { return 1; }
```
Alternative version:
```javascript
function second() { return 2; }
```
"""
code, explanation = extract_code_and_explanation(response)
assert "first" in code
assert "second" not in code
class TestNormalizeCode:
"""Tests for code normalization for comparison."""
def test_normalize_removes_single_line_comments(self) -> None:
"""Test that single-line comments are removed."""
code = """
function add(a, b) {
// This is a comment
return a + b; // inline comment
}
"""
normalized = _normalize_code(code)
assert "//" not in normalized
assert "This is a comment" not in normalized
assert "inline comment" not in normalized
def test_normalize_removes_multiline_comments(self) -> None:
"""Test that multi-line comments are removed."""
code = """
/* This is a
multi-line comment */
function add(a, b) {
return a + b;
}
"""
normalized = _normalize_code(code)
assert "/*" not in normalized
assert "*/" not in normalized
assert "multi-line" not in normalized
def test_normalize_whitespace(self) -> None:
"""Test that whitespace is normalized."""
code1 = """
function add(a, b) {
return a + b;
}
"""
code2 = "function add(a, b) { return a + b; }"
assert _normalize_code(code1) == _normalize_code(code2)
def test_identical_code_normalizes_same(self) -> None:
"""Test that identical code normalizes to the same string."""
code1 = "function add(a, b) { return a + b; }"
code2 = "function add(a, b) { return a + b; }"
assert _normalize_code(code1) == _normalize_code(code2)
def test_different_code_normalizes_different(self) -> None:
"""Test that different code normalizes to different strings."""
code1 = "function add(a, b) { return a + b; }"
code2 = "function subtract(a, b) { return a - b; }"
assert _normalize_code(code1) != _normalize_code(code2)
def test_normalize_preserves_string_contents(self) -> None:
"""Test that string contents are preserved."""
code = 'const msg = "Hello // not a comment";'
normalized = _normalize_code(code)
# The string content should be preserved
# (though this is a simplified test - real implementation
# might handle this differently)
assert "Hello" in normalized
def test_normalize_empty_code(self) -> None:
"""Test that empty code normalizes to empty string."""
assert _normalize_code("") == ""
assert _normalize_code(" ") == ""
assert _normalize_code("\n\n\n") == ""
class TestCodeDeduplication:
"""Tests for code deduplication logic."""
def test_same_code_different_comments_equal(self) -> None:
"""Test that same code with different comments is considered equal."""
code1 = """
// Version 1
function add(a, b) {
return a + b;
}
"""
code2 = """
// Version 2 - optimized
function add(a, b) {
return a + b; // sum
}
"""
assert _normalize_code(code1) == _normalize_code(code2)
def test_same_code_different_whitespace_equal(self) -> None:
"""Test that same code with different whitespace is considered equal."""
code1 = "function add(a, b) { return a + b; }"
code2 = """
function add(a, b) {
return a + b;
}
"""
assert _normalize_code(code1) == _normalize_code(code2)
def test_semantically_different_code_not_equal(self) -> None:
"""Test that semantically different code is not equal."""
code1 = "function add(a, b) { return a + b; }"
code2 = "function add(a, b) { return a - b; }" # Different operation
assert _normalize_code(code1) != _normalize_code(code2)

View file

@ -0,0 +1,294 @@
"""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 / "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

View file

@ -0,0 +1,327 @@
"""Tests for JavaScript test instrumentation."""
from pathlib import Path
import pytest
from testgen.instrumentation.javascript.instrument_javascript import (
get_jest_helper_path,
get_jest_setup_code,
instrument_javascript_tests,
)
class TestJestHelperPath:
"""Tests for Jest helper path resolution."""
def test_helper_path_exists(self) -> None:
"""Test that the Jest helper file exists."""
helper_path = get_jest_helper_path()
assert helper_path.exists()
assert helper_path.is_file()
assert helper_path.name == "codeflash-jest-helper.js"
def test_helper_path_is_javascript(self) -> None:
"""Test that the helper file has .js extension."""
helper_path = get_jest_helper_path()
assert helper_path.suffix == ".js"
def test_helper_contains_capture_function(self) -> None:
"""Test that the helper contains the capture function."""
helper_path = get_jest_helper_path()
content = helper_path.read_text()
assert "function capture" in content
assert "module.exports" in content
class TestJestSetupCode:
"""Tests for Jest setup code generation."""
def test_generates_environment_variables(self) -> None:
"""Test that setup code sets environment variables."""
setup = get_jest_setup_code("/tmp/results.bin", "behavior", 0)
assert "CODEFLASH_OUTPUT_FILE" in setup
assert "/tmp/results.bin" in setup
assert "CODEFLASH_MODE" in setup
assert "behavior" in setup
assert "CODEFLASH_LOOP_INDEX" in setup
def test_performance_mode(self) -> None:
"""Test setup code for performance mode."""
setup = get_jest_setup_code("/tmp/results.bin", "performance", 5)
assert "performance" in setup
assert "'5'" in setup or "5" in setup
class TestInstrumentJavaScriptTests:
"""Tests for the main instrumentation function."""
def test_adds_helper_import(self) -> None:
"""Test that the codeflash helper import is added."""
test_source = """
const { fibonacci } = require('./fibonacci');
describe('fibonacci', () => {
test('should return 0 for n=0', () => {
expect(fibonacci(0)).toBe(0);
});
});
"""
result = instrument_javascript_tests(
test_source=test_source,
function_name="fibonacci",
module_path="./fibonacci",
)
assert "codeflash-jest-helper" in result
assert "const codeflash = require" in result
def test_does_not_duplicate_import(self) -> None:
"""Test that import is not duplicated if already present."""
test_source = """
const codeflash = require('codeflash-jest-helper');
const { fibonacci } = require('./fibonacci');
describe('fibonacci', () => {
test('should return 0 for n=0', () => {
expect(codeflash.capture('fibonacci', fibonacci, 0)).toBe(0);
});
});
"""
result = instrument_javascript_tests(
test_source=test_source,
function_name="fibonacci",
module_path="./fibonacci",
)
# Should only have one import
assert result.count("codeflash-jest-helper") == 1
def test_wraps_function_calls(self) -> None:
"""Test that function calls are wrapped with codeflash.capture."""
test_source = """
const { add } = require('./math');
describe('add', () => {
test('should add two numbers', () => {
const result = add(1, 2);
expect(result).toBe(3);
});
});
"""
result = instrument_javascript_tests(
test_source=test_source,
function_name="add",
module_path="./math",
)
assert "codeflash.capture('add', add, 1, 2)" in result
def test_wraps_function_without_args(self) -> None:
"""Test wrapping function calls without arguments."""
test_source = """
const { getVersion } = require('./utils');
test('should return version', () => {
expect(getVersion()).toBe('1.0.0');
});
"""
result = instrument_javascript_tests(
test_source=test_source,
function_name="getVersion",
module_path="./utils",
)
assert "codeflash.capture('getVersion', getVersion)" in result
def test_preserves_test_structure(self) -> None:
"""Test that the overall test structure is preserved."""
test_source = """
const { sort } = require('./sort');
describe('sort', () => {
describe('basic cases', () => {
test('should sort empty array', () => {
expect(sort([])).toEqual([]);
});
test('should sort single element', () => {
expect(sort([1])).toEqual([1]);
});
});
});
"""
result = instrument_javascript_tests(
test_source=test_source,
function_name="sort",
module_path="./sort",
)
# Test structure should be preserved
assert "describe('sort'" in result
assert "describe('basic cases'" in result
assert "test('should sort empty array'" in result
assert "test('should sort single element'" in result
def test_handles_es6_imports(self) -> None:
"""Test that ES6 imports are handled correctly."""
test_source = """
import { fibonacci } from './fibonacci';
describe('fibonacci', () => {
test('base case', () => {
expect(fibonacci(0)).toBe(0);
});
});
"""
result = instrument_javascript_tests(
test_source=test_source,
function_name="fibonacci",
module_path="./fibonacci",
)
# Helper should be added
assert "codeflash-jest-helper" in result
# Original import should be preserved
assert "import { fibonacci }" in result
def test_does_not_wrap_method_calls(self) -> None:
"""Test that method calls on objects are not wrapped."""
test_source = """
const { Calculator } = require('./calc');
test('should add', () => {
const calc = new Calculator();
expect(calc.add(1, 2)).toBe(3);
});
"""
result = instrument_javascript_tests(
test_source=test_source,
function_name="add",
module_path="./calc",
)
# Method calls should NOT be wrapped (they have a dot before them)
# Only standalone function calls should be wrapped
assert "calc.add" in result # Method call preserved
# But not wrapped (no codeflash.capture around method call)
def test_handles_multiple_calls_same_test(self) -> None:
"""Test handling multiple function calls in the same test."""
test_source = """
const { process } = require('./processor');
test('should process multiple inputs', () => {
const result1 = process('a');
const result2 = process('b');
const result3 = process('c');
expect([result1, result2, result3]).toEqual(['A', 'B', 'C']);
});
"""
result = instrument_javascript_tests(
test_source=test_source,
function_name="process",
module_path="./processor",
)
# All three calls should be wrapped
assert result.count("codeflash.capture('process'") == 3
def test_handles_async_functions(self) -> None:
"""Test instrumentation of async function calls."""
test_source = """
const { fetchData } = require('./api');
test('should fetch data', async () => {
const data = await fetchData('http://example.com');
expect(data).toBeDefined();
});
"""
result = instrument_javascript_tests(
test_source=test_source,
function_name="fetchData",
module_path="./api",
)
# Should wrap the async function call
assert "codeflash.capture('fetchData'" in result
class TestInstrumentationEdgeCases:
"""Tests for edge cases in instrumentation."""
def test_function_name_in_string_not_wrapped(self) -> None:
"""Test that function names in strings are not wrapped."""
test_source = """
const { add } = require('./math');
test('should call add function', () => {
console.log('Calling add function');
expect(add(1, 2)).toBe(3);
});
"""
result = instrument_javascript_tests(
test_source=test_source,
function_name="add",
module_path="./math",
)
# The string should remain unchanged
assert "'Calling add function'" in result
# But the actual call should be wrapped
assert "codeflash.capture('add', add, 1, 2)" in result
def test_function_name_in_comment_not_wrapped(self) -> None:
"""Test that function names in comments are not wrapped."""
test_source = """
const { multiply } = require('./math');
test('should multiply', () => {
// multiply is the function we're testing
const result = multiply(2, 3);
expect(result).toBe(6);
});
"""
result = instrument_javascript_tests(
test_source=test_source,
function_name="multiply",
module_path="./math",
)
# The actual call should be wrapped
assert "codeflash.capture('multiply', multiply, 2, 3)" in result
def test_empty_test_file(self) -> None:
"""Test handling of empty test file."""
result = instrument_javascript_tests(
test_source="",
function_name="test",
module_path="./test",
)
# Should add the helper import even to empty file
assert "codeflash-jest-helper" in result
def test_complex_arguments(self) -> None:
"""Test handling of complex function arguments."""
test_source = """
const { process } = require('./processor');
test('should process', () => {
const result = process({ key: 'value' }, [1, 2, 3], () => true);
expect(result).toBeDefined();
});
"""
result = instrument_javascript_tests(
test_source=test_source,
function_name="process",
module_path="./processor",
)
# Should handle complex arguments
assert "codeflash.capture('process'" in result

View file

@ -0,0 +1,151 @@
"""Tests for the codeflash-jest-helper.js module structure."""
from pathlib import Path
import pytest
from testgen.instrumentation.javascript.instrument_javascript import get_jest_helper_path
class TestJestHelperStructure:
"""Tests for the structure and content of the Jest helper module."""
@pytest.fixture
def helper_content(self) -> str:
"""Load the Jest helper content."""
helper_path = get_jest_helper_path()
return helper_path.read_text()
def test_exports_capture_function(self, helper_content: str) -> None:
"""Test that the helper exports a capture function."""
assert "function capture" in helper_content
assert "module.exports" in helper_content
# Check that capture is exported
assert "capture" in helper_content.split("module.exports")[1]
def test_has_safe_serialize(self, helper_content: str) -> None:
"""Test that the helper has a safeSerialize function for handling complex objects."""
assert "safeSerialize" in helper_content or "safe" in helper_content.lower()
def test_handles_promises(self, helper_content: str) -> None:
"""Test that the helper handles Promise returns for async functions."""
assert "Promise" in helper_content
assert ".then" in helper_content or "instanceof Promise" in helper_content
def test_records_timing(self, helper_content: str) -> None:
"""Test that the helper records timing information."""
assert "performance" in helper_content.lower()
assert "duration" in helper_content.lower() or "time" in helper_content.lower()
def test_captures_errors(self, helper_content: str) -> None:
"""Test that the helper captures and re-throws errors."""
assert "error" in helper_content.lower()
assert "catch" in helper_content
assert "throw" in helper_content
def test_uses_environment_variables(self, helper_content: str) -> None:
"""Test that the helper reads from environment variables."""
assert "process.env" in helper_content
assert "CODEFLASH" in helper_content
def test_has_write_results_function(self, helper_content: str) -> None:
"""Test that there's a function to write results to file."""
assert "writeResults" in helper_content or "write" in helper_content.lower()
assert "fs" in helper_content # Uses fs module
def test_has_jest_hooks(self, helper_content: str) -> None:
"""Test that the helper includes Jest lifecycle hooks."""
# Should reference beforeEach and/or afterAll hooks
assert "beforeEach" in helper_content or "afterAll" in helper_content
def test_has_invocation_tracking(self, helper_content: str) -> None:
"""Test that the helper tracks invocation order."""
assert "invocation" in helper_content.lower()
def test_handles_special_types(self, helper_content: str) -> None:
"""Test that the helper handles special JavaScript types."""
# Should handle at least some special types
special_types = ["Date", "Map", "Set", "Symbol", "BigInt", "Error"]
handled = sum(1 for t in special_types if t in helper_content)
assert handled >= 3 # Should handle at least 3 special types
def test_handles_circular_references(self, helper_content: str) -> None:
"""Test that the helper handles circular references."""
assert "Circular" in helper_content or "seen" in helper_content.lower()
def test_valid_javascript_syntax(self, helper_content: str) -> None:
"""Test that the helper has valid JavaScript syntax (basic check)."""
# Basic bracket matching
brackets = {"(": ")", "[": "]", "{": "}"}
stack = []
in_string = False
string_char = None
for char in helper_content:
if char in "'\"`" and not in_string:
in_string = True
string_char = char
elif char == string_char and in_string:
in_string = False
string_char = None
elif not in_string:
if char in brackets:
stack.append(brackets[char])
elif char in brackets.values():
if stack and stack[-1] == char:
stack.pop()
# Should have balanced brackets (allowing for some slack due to strings)
assert len(stack) < 5 # Some tolerance for complex string handling
class TestJestHelperExports:
"""Tests for what the Jest helper exports."""
@pytest.fixture
def helper_content(self) -> str:
"""Load the Jest helper content."""
helper_path = get_jest_helper_path()
return helper_path.read_text()
def test_exports_section_exists(self, helper_content: str) -> None:
"""Test that there's a module.exports section."""
assert "module.exports" in helper_content
def test_exports_capture(self, helper_content: str) -> None:
"""Test that capture is exported."""
exports_section = helper_content.split("module.exports")[-1]
assert "capture" in exports_section
def test_exports_multiple_functions(self, helper_content: str) -> None:
"""Test that multiple utility functions are exported."""
exports_section = helper_content.split("module.exports")[-1]
# Should export multiple things
export_count = exports_section.count(",")
assert export_count >= 2 # At least 3 exports
class TestJestHelperDocumentation:
"""Tests for documentation in the Jest helper."""
@pytest.fixture
def helper_content(self) -> str:
"""Load the Jest helper content."""
helper_path = get_jest_helper_path()
return helper_path.read_text()
def test_has_jsdoc_comments(self, helper_content: str) -> None:
"""Test that the helper has JSDoc comments."""
# Should have at least some JSDoc-style comments
assert "/**" in helper_content
assert "@param" in helper_content or "@returns" in helper_content
def test_has_usage_instructions(self, helper_content: str) -> None:
"""Test that the helper has usage instructions."""
# Should have some usage guidance
assert "Usage" in helper_content or "require" in helper_content
def test_documents_environment_variables(self, helper_content: str) -> None:
"""Test that environment variables are documented."""
assert "CODEFLASH_OUTPUT_FILE" in helper_content
assert "CODEFLASH_MODE" in helper_content or "MODE" in helper_content

3173
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -19,5 +19,8 @@
"@secretlint/secretlint-rule-preset-recommend": "^9.0.0",
"eslint": "^9.14.0",
"secretlint": "^9.0.0"
},
"dependencies": {
"acorn": "^8.15.0"
}
}