Skip to content

fix: read TESTED_BY edges in the parser's direction everywhere#598

Open
SHudici wants to merge 1 commit into
tirth8205:mainfrom
SHudici:fix/tested-by-direction
Open

fix: read TESTED_BY edges in the parser's direction everywhere#598
SHudici wants to merge 1 commit into
tirth8205:mainfrom
SHudici:fix/tested-by-direction

Conversation

@SHudici

@SHudici SHudici commented Jul 3, 2026

Copy link
Copy Markdown

Problem

The parser emits TESTED_BY edges as production → test (source = the code under test, target = the test function). Several consumers read the edge in the opposite direction, so they silently return nothing:

  • query_graph(pattern="tests_for") looked for incoming edges on the production node and returned the wrong endpoint.
  • graph.get_transitive_tests queried target_qualified = ? for the production node in all three lookup blocks (direct, bare-name fallback, transitive).
  • changes.py counted "tested" nodes from incoming edges → tests_for context in change reviews was empty.
  • enrich.py's Tests block and refactor.py's test-reference augmentation read the reverse direction as well.

Some call sites already read the correct direction (build.py, analysis.py, review.py, communities.py), which is how the inconsistency stayed invisible: half the features worked, half quietly returned empty results.

Fix

Read TESTED_BY by source_qualified (production) and take target_qualified (test) everywhere, matching the parser. Test seeds that encoded the inverted direction were flipped to the parser's real output, and two end-to-end regression tests pin the direction:

  • test_tested_by_edges_point_from_production_to_test (parser level)
  • TestQueryGraphTestsFor (tool level, with a test name that deliberately defeats the name-matching fallback so only the edge lookup can find it)

Notes

resolve_bare_call_targets upgrades bare CALLS targets to qualified names but does not touch TESTED_BY sources that inherited a bare name from an unresolved cross-file call. refactor.py keeps its plausibility-checked bare-name fallback for exactly that case. Two natural follow-up PRs if you want them: extending bare-name resolution to TESTED_BY, and a canonical GraphStore.get_tests_of() accessor so the direction knowledge lives in one place instead of each consumer's SQL.

Testing

Full suite: 1412 passed / 0 failed on Windows 11; the new regression tests fail on main and pass here.

🤖 Generated with Claude Code

The parser emits TESTED_BY as production -> test (parser.py, all five
language blocks): source is the tested symbol, target is the test.
Half the consumers read the edge the other way around (test -> production),
so they silently returned nothing on real graphs:

- tools/query.py tests_for: queried incoming edges of the production
  node and returned edge sources. Real graphs never have TESTED_BY
  edges targeting production code, so the edge-based path always came
  up empty and only the test_<name> naming fallback ever fired.
- graph.py get_transitive_tests (direct, bare-name fallback, and
  transitive blocks): same inversion, so direct and transitive test
  coverage was always empty. This also silently zeroed the test
  coverage factor in compute_risk_score, which calls it.
- graph.py load_flow_adjacency: has_tested_by collected edge targets
  (the tests) instead of sources (the tested code), so flow
  criticality's tested_count was always 0.
- changes.py test-gap detection: checked incoming edges, so every
  changed function was flagged as a test gap regardless of coverage.
- enrich.py: the Tests: context line never listed anything.
- refactor.py dead-code detection: has_test_refs looked for incoming
  TESTED_BY edges and bare-name matches on the target column; both are
  impossible under the emitted direction. Test references are the
  node's outgoing TESTED_BY edges (with a bare-source fallback, since
  the edge source inherits an unresolved CALLS target name).

Consumers that already read the emitted direction (risk_index in
tools/build.py, knowledge gaps and suggested questions in analysis.py,
review context in tools/review.py) are unchanged.

Test seeds in test_changes, test_enrich, test_graph and
test_integration_v2 seeded the inverted direction to match the
inverted consumers; they now seed what the parser emits. New
regression tests pin the direction at both ends: the parser must emit
production -> test (test_parser), and tests_for must find a test whose
name defeats the naming-convention fallback, via the edge alone
(test_tools).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant