Skip to content

Commit bca40c6

Browse files
author
Codeflash Bot
committed
Fix: Disable Vitest coverage thresholds for Codeflash-generated tests
**Problem:** When running Codeflash-generated tests with coverage enabled, Vitest would fail with returncode=1 due to project-level coverage thresholds not being met. Generated tests typically cover only a single function (~1-2% of codebase), which fails projects with thresholds like 70% lines/functions configured in their vitest.config.ts. **Root Cause:** In vitest_runner.py line 450, Codeflash was adding --coverage flag without disabling the project's global coverage thresholds. This caused false failures even when all tests passed successfully. **Solution:** Added coverage threshold override flags when coverage is enabled: - --coverage.thresholds.lines=0 - --coverage.thresholds.functions=0 - --coverage.thresholds.statements=0 - --coverage.thresholds.branches=0 These flags disable project-level thresholds, allowing coverage collection without failing the test run. Coverage data is still collected for analysis, but thresholds no longer cause false failures. **Testing:** - Added comprehensive unit tests in test_vitest_coverage_thresholds.py - All 40 existing vitest-related tests pass - Verified with uv run prek (linter + type checker) **Related Issues:** Trace IDs affected: 05a626f3, 932e7799, a145328d, aa9bb63f, d669202e, e6de097a Fixes 6 out of 10 optimization failures in openclaw project.
1 parent 9a2fbc9 commit bca40c6

2 files changed

Lines changed: 193 additions & 1 deletion

File tree

codeflash/languages/javascript/vitest_runner.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,21 @@ def run_vitest_behavioral_tests(
447447
# Pre-creating an empty directory may cause vitest to delete it
448448
logger.debug(f"Coverage will be written to: {coverage_dir}")
449449

450-
vitest_cmd.extend(["--coverage", "--coverage.reporter=json", f"--coverage.reportsDirectory={coverage_dir}"])
450+
vitest_cmd.extend(
451+
[
452+
"--coverage",
453+
"--coverage.reporter=json",
454+
f"--coverage.reportsDirectory={coverage_dir}",
455+
# Disable project-level coverage thresholds to prevent false failures.
456+
# Codeflash-generated tests typically cover only a single function (~1-2% of codebase),
457+
# which would fail projects with thresholds like 70% lines/functions configured
458+
# in their vitest.config.ts.
459+
"--coverage.thresholds.lines=0",
460+
"--coverage.thresholds.functions=0",
461+
"--coverage.thresholds.statements=0",
462+
"--coverage.thresholds.branches=0",
463+
]
464+
)
451465
# Note: Removed --coverage.enabled=true (redundant) and --coverage.all false
452466
# The version mismatch between vitest and @vitest/coverage-v8 can cause
453467
# issues with coverage flag parsing. Let vitest use default settings.
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
"""Test that Vitest coverage thresholds are disabled for Codeflash tests.
2+
3+
When running Codeflash-generated tests with coverage enabled, we must disable
4+
the project's global coverage thresholds to prevent false failures. Generated
5+
tests typically cover only a single function (~1-2% of the codebase), which
6+
would fail projects with thresholds like 70% lines/functions.
7+
8+
Related issue: Trace IDs 05a626f3, 932e7799, a145328d, aa9bb63f, d669202e, e6de097a
9+
"""
10+
11+
from __future__ import annotations
12+
13+
import subprocess
14+
import tempfile
15+
from pathlib import Path
16+
from unittest.mock import MagicMock, patch
17+
18+
import pytest
19+
20+
from codeflash.languages.javascript.vitest_runner import run_vitest_behavioral_tests
21+
from codeflash.models.models import TestFile, TestFiles
22+
from codeflash.models.test_type import TestType
23+
24+
25+
class TestVitestCoverageThresholds:
26+
"""Tests for disabling coverage thresholds in Vitest commands."""
27+
28+
def test_coverage_thresholds_disabled_when_coverage_enabled(self) -> None:
29+
"""Should add coverage threshold flags to disable project thresholds."""
30+
with tempfile.TemporaryDirectory() as tmp_dir:
31+
tmp_path = Path(tmp_dir)
32+
33+
# Create mock test file
34+
test_file_path = tmp_path / "test.test.ts"
35+
test_file_path.write_text("test('mock', () => {})")
36+
37+
# Create mock project structure
38+
(tmp_path / "package.json").write_text('{"name": "test-project"}')
39+
(tmp_path / "node_modules" / "codeflash").mkdir(parents=True)
40+
(tmp_path / "node_modules" / "@vitest" / "coverage-v8").mkdir(parents=True)
41+
42+
# Create TestFiles object
43+
test_files = TestFiles(
44+
test_files=[
45+
TestFile(
46+
instrumented_behavior_file_path=test_file_path,
47+
benchmarking_file_path=None,
48+
original_file_path=None,
49+
test_type=TestType.GENERATED_REGRESSION,
50+
)
51+
]
52+
)
53+
54+
# Mock subprocess.run to capture the command
55+
captured_cmd = []
56+
57+
def mock_run(cmd, **kwargs):
58+
captured_cmd.extend(cmd)
59+
# Create a minimal JUnit XML file
60+
result_file = None
61+
for i, arg in enumerate(cmd):
62+
if arg.startswith("--outputFile.junit="):
63+
result_file = Path(arg.split("=", 1)[1])
64+
break
65+
66+
if result_file:
67+
result_file.parent.mkdir(parents=True, exist_ok=True)
68+
result_file.write_text('<?xml version="1.0"?><testsuites><testsuite tests="1" failures="0"></testsuite></testsuites>')
69+
70+
return subprocess.CompletedProcess(
71+
args=cmd,
72+
returncode=0,
73+
stdout="✓ test.test.ts (1 test)",
74+
stderr="",
75+
)
76+
77+
with patch("codeflash.languages.javascript.vitest_runner.subprocess.run", side_effect=mock_run):
78+
with patch("codeflash.languages.javascript.vitest_runner.get_run_tmp_file") as mock_tmp:
79+
# Mock temp file location
80+
mock_tmp.side_effect = lambda p: tmp_path / p
81+
82+
# Run with coverage enabled
83+
run_vitest_behavioral_tests(
84+
test_paths=test_files,
85+
test_env={},
86+
cwd=tmp_path,
87+
timeout=60,
88+
project_root=tmp_path,
89+
enable_coverage=True,
90+
candidate_index=0,
91+
)
92+
93+
# Verify that coverage threshold flags were added
94+
cmd_str = " ".join(captured_cmd)
95+
96+
# The fix should add these flags to disable thresholds
97+
assert "--coverage.thresholds.lines=0" in captured_cmd, (
98+
f"Missing --coverage.thresholds.lines=0 in command: {cmd_str}"
99+
)
100+
assert "--coverage.thresholds.functions=0" in captured_cmd, (
101+
f"Missing --coverage.thresholds.functions=0 in command: {cmd_str}"
102+
)
103+
assert "--coverage.thresholds.statements=0" in captured_cmd, (
104+
f"Missing --coverage.thresholds.statements=0 in command: {cmd_str}"
105+
)
106+
assert "--coverage.thresholds.branches=0" in captured_cmd, (
107+
f"Missing --coverage.thresholds.branches=0 in command: {cmd_str}"
108+
)
109+
110+
def test_coverage_command_structure_with_thresholds_disabled(self) -> None:
111+
"""Verify the full command structure when coverage is enabled."""
112+
with tempfile.TemporaryDirectory() as tmp_dir:
113+
tmp_path = Path(tmp_dir)
114+
115+
# Create mock setup
116+
test_file_path = tmp_path / "test.test.ts"
117+
test_file_path.write_text("test('mock', () => {})")
118+
(tmp_path / "package.json").write_text('{"name": "test-project"}')
119+
(tmp_path / "node_modules" / "codeflash").mkdir(parents=True)
120+
(tmp_path / "node_modules" / "@vitest" / "coverage-v8").mkdir(parents=True)
121+
122+
test_files = TestFiles(
123+
test_files=[
124+
TestFile(
125+
instrumented_behavior_file_path=test_file_path,
126+
benchmarking_file_path=None,
127+
original_file_path=None,
128+
test_type=TestType.GENERATED_REGRESSION,
129+
)
130+
]
131+
)
132+
133+
captured_cmd = []
134+
135+
def mock_run(cmd, **kwargs):
136+
captured_cmd.extend(cmd)
137+
result_file = None
138+
for arg in cmd:
139+
if arg.startswith("--outputFile.junit="):
140+
result_file = Path(arg.split("=", 1)[1])
141+
break
142+
143+
if result_file:
144+
result_file.parent.mkdir(parents=True, exist_ok=True)
145+
result_file.write_text('<?xml version="1.0"?><testsuites><testsuite tests="1" failures="0"></testsuite></testsuites>')
146+
147+
return subprocess.CompletedProcess(
148+
args=cmd, returncode=0, stdout="✓ test", stderr=""
149+
)
150+
151+
with patch("codeflash.languages.javascript.vitest_runner.subprocess.run", side_effect=mock_run):
152+
with patch("codeflash.languages.javascript.vitest_runner.get_run_tmp_file") as mock_tmp:
153+
mock_tmp.side_effect = lambda p: tmp_path / p
154+
155+
run_vitest_behavioral_tests(
156+
test_paths=test_files,
157+
test_env={},
158+
cwd=tmp_path,
159+
timeout=60,
160+
project_root=tmp_path,
161+
enable_coverage=True,
162+
candidate_index=0,
163+
)
164+
165+
# Verify command contains both coverage flags AND threshold overrides
166+
assert "--coverage" in captured_cmd
167+
assert "--coverage.reporter=json" in captured_cmd
168+
assert any("--coverage.reportsDirectory=" in arg for arg in captured_cmd)
169+
170+
# And the threshold overrides
171+
assert "--coverage.thresholds.lines=0" in captured_cmd
172+
assert "--coverage.thresholds.functions=0" in captured_cmd
173+
assert "--coverage.thresholds.statements=0" in captured_cmd
174+
assert "--coverage.thresholds.branches=0" in captured_cmd
175+
176+
177+
if __name__ == "__main__":
178+
pytest.main([__file__, "-v"])

0 commit comments

Comments
 (0)