Skip to content

feat(qa): targeted test command templates + full test discovery in project index#402

Merged
OBenner merged 2 commits into
developfrom
feat/targeted-test-commands
Jul 12, 2026
Merged

feat(qa): targeted test command templates + full test discovery in project index#402
OBenner merged 2 commits into
developfrom
feat/targeted-test-commands

Conversation

@OBenner

@OBenner OBenner commented Jul 6, 2026

Copy link
Copy Markdown
Owner

Summary

Roadmap phase 4 — QA iteration economics. Two connected gaps:

  1. project_index.json (what coder/QA prompts actually read) had almost no test info. ServiceAnalyzer._detect_testing() recognized only Vitest/Jest/pytest and never emitted test_command. Now it delegates to TestDiscovery (all ecosystems from feat(qa): test discovery parity for JVM, .NET, native, and scripting-tail languages #388) and emits testing, e2e_testing, test_command, coverage_command, and targeted_test_command per service.

  2. No way to run a subset of tests. For compiled stacks a full-suite run per QA-fix iteration is expensive. TestDiscovery now produces a targeted_command template with a {target} placeholder:

Style Examples
path-based pytest {target}, npx jest {target}, go test {target}, mix test {target}
filter-based mvn test -Dtest={target}, ./gradlew test --tests {target}, dotnet test --filter "{target}", ctest -R {target}

The Gradle template keeps the wrapper when the project ships one. Frameworks without a reliable targeted form (zig/stack/cabal/make) yield None rather than a guess.

QA agents pick the template up from project_index.json — the LLM does the file→target mapping, deterministic code stays honest.

Testing

tests/test_discovery.py: 72 passed (5 new). New tests/test_service_analyzer_testing.py: 4 passed. Ruff check + format clean.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Improved automatic detection of unit and end-to-end testing frameworks.
    • Added test, coverage, and targeted test commands when available.
    • Added framework-specific templates for running tests against a selected target.
    • Standardized detected framework names for consistent results.
  • Bug Fixes
    • Added fallback detection for projects where advanced test discovery is unavailable.
  • Tests
    • Expanded coverage across Python, JavaScript, Gradle, CTest, and projects without configured tests.

…oject index

Roadmap phase 4 (iteration economics): the coder/QA prompts read
per-service test commands from project_index.json, but
ServiceAnalyzer._detect_testing() only knew Vitest/Jest/pytest and
never emitted test_command at all.

- TestDiscovery: new targeted_command template per framework
  ({target} = path for pytest/jest/go-style runners, name/filter for
  mvn/gradle/dotnet/ctest). Exposed on the result and in to_dict().
- ServiceAnalyzer: _detect_testing now delegates to TestDiscovery,
  emitting testing, e2e_testing, test_command, coverage_command, and
  targeted_test_command for every supported ecosystem, so QA fixers
  can run single tests instead of full suites in compiled stacks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Oleg Miagkov <mrobenner@gmail.com>
@cursor

cursor Bot commented Jul 6, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Testing analysis now uses TestDiscovery for framework and command metadata, including targeted test commands. Fallback detection remains available, framework names use lowercase values, and new tests cover discovery serialization and multiple testing ecosystems.

Changes

Testing discovery integration

Layer / File(s) Summary
Targeted test command discovery
apps/backend/analysis/test_discovery.py, tests/test_discovery.py
TestDiscoveryResult stores targeted command templates, discovery selects supported framework commands, serialization includes the field, and tests cover pytest, Gradle, CTest, Zig, and output serialization.
ServiceAnalyzer testing metadata
apps/backend/analysis/analyzers/service_analyzer.py, tests/test_service_analyzer_testing.py, tests/test_debug_assistant.py
ServiceAnalyzer consumes discovered frameworks and commands, retains fallback detection, uses lowercase framework names, and tests Python, Gradle, JavaScript, and unconfigured services.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ServiceAnalyzer
  participant TestDiscovery
  participant TestDiscoveryResult
  ServiceAnalyzer->>TestDiscovery: discover service path
  TestDiscovery->>TestDiscoveryResult: populate frameworks and commands
  TestDiscovery-->>ServiceAnalyzer: return discovery result
  ServiceAnalyzer->>ServiceAnalyzer: record testing, e2e_testing, and command fields
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly captures the main change: targeted test command templates and broader test discovery integration.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/targeted-test-commands

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

ServiceAnalyzer now reports framework names via TestDiscovery, which
uses canonical lowercase identifiers (vitest, playwright) instead of
display names (Vitest, Playwright).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Oleg Miagkov <mrobenner@gmail.com>
@sonarqubecloud

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/backend/analysis/analyzers/service_analyzer.py`:
- Around line 216-235: Broaden the exception handling around
TestDiscovery().discover(self.path) in the analyzer’s testing-detection block to
also handle filesystem-related OSError failures, preserving the existing
fallback behavior when discovery cannot run. Keep unrelated exceptions out of
scope unless required by the surrounding error-handling convention.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6297f263-0407-4574-9a36-ef3cc8a387a6

📥 Commits

Reviewing files that changed from the base of the PR and between a6f6538 and 75535e4.

📒 Files selected for processing (5)
  • apps/backend/analysis/analyzers/service_analyzer.py
  • apps/backend/analysis/test_discovery.py
  • tests/test_debug_assistant.py
  • tests/test_discovery.py
  • tests/test_service_analyzer_testing.py

Comment on lines +216 to +235
try:
from analysis.test_discovery import TestDiscovery

discovery = TestDiscovery().discover(self.path)
unit_frameworks = [f.name for f in discovery.frameworks if f.type != "e2e"]
e2e_frameworks = [f.name for f in discovery.frameworks if f.type == "e2e"]

if unit_frameworks:
self.analysis["testing"] = unit_frameworks[0]
if e2e_frameworks:
self.analysis["e2e_testing"] = e2e_frameworks[0]
if discovery.test_command:
self.analysis["test_command"] = discovery.test_command
if discovery.coverage_command:
self.analysis["coverage_command"] = discovery.coverage_command
if discovery.targeted_command:
# Template with {target}: path for pytest/jest/go-style
# runners, test name/filter for mvn/gradle/dotnet/ctest
self.analysis["targeted_test_command"] = discovery.targeted_command
except ImportError:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Consider broadening the exception guard for robustness.

Only ImportError is caught, but TestDiscovery().discover(self.path) could raise other exceptions (e.g., OSError from Path.resolve() or glob() on restricted paths). This would crash the entire analyze() call rather than falling back to minimal detection. The discovery code handles most file/JSON errors internally, so the risk is low, but wrapping the discover() call in a broader guard (or adding OSError) would improve resilience.

As per path instructions, check for proper error handling and security considerations.

♻️ Proposed refactor
         try:
             from analysis.test_discovery import TestDiscovery
 
             discovery = TestDiscovery().discover(self.path)
-        except ImportError:
+        except (ImportError, OSError):
             # Fall back to the previous minimal detection
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try:
from analysis.test_discovery import TestDiscovery
discovery = TestDiscovery().discover(self.path)
unit_frameworks = [f.name for f in discovery.frameworks if f.type != "e2e"]
e2e_frameworks = [f.name for f in discovery.frameworks if f.type == "e2e"]
if unit_frameworks:
self.analysis["testing"] = unit_frameworks[0]
if e2e_frameworks:
self.analysis["e2e_testing"] = e2e_frameworks[0]
if discovery.test_command:
self.analysis["test_command"] = discovery.test_command
if discovery.coverage_command:
self.analysis["coverage_command"] = discovery.coverage_command
if discovery.targeted_command:
# Template with {target}: path for pytest/jest/go-style
# runners, test name/filter for mvn/gradle/dotnet/ctest
self.analysis["targeted_test_command"] = discovery.targeted_command
except ImportError:
try:
from analysis.test_discovery import TestDiscovery
discovery = TestDiscovery().discover(self.path)
unit_frameworks = [f.name for f in discovery.frameworks if f.type != "e2e"]
e2e_frameworks = [f.name for f in discovery.frameworks if f.type == "e2e"]
if unit_frameworks:
self.analysis["testing"] = unit_frameworks[0]
if e2e_frameworks:
self.analysis["e2e_testing"] = e2e_frameworks[0]
if discovery.test_command:
self.analysis["test_command"] = discovery.test_command
if discovery.coverage_command:
self.analysis["coverage_command"] = discovery.coverage_command
if discovery.targeted_command:
# Template with {target}: path for pytest/jest/go-style
# runners, test name/filter for mvn/gradle/dotnet/ctest
self.analysis["targeted_test_command"] = discovery.targeted_command
except (ImportError, OSError):
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/backend/analysis/analyzers/service_analyzer.py` around lines 216 - 235,
Broaden the exception handling around TestDiscovery().discover(self.path) in the
analyzer’s testing-detection block to also handle filesystem-related OSError
failures, preserving the existing fallback behavior when discovery cannot run.
Keep unrelated exceptions out of scope unless required by the surrounding
error-handling convention.

Source: Path instructions

@OBenner OBenner merged commit e873cab into develop Jul 12, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant