Skip to content

Commit 4150444

Browse files
HumanBean17claude
andcommitted
test(graph): structural guard for pass4 on global tables (#352 #1)
The equivalence test's http_calls_match histogram claimed to guard "missing pass4 on global pass5/6 tables" but could not trip on it: the synthetic corpus has no microservice, so the Feign route is filtered out in pass6 and resolves to 'phantom' in both the incremental and full paths. The assertion was a non-load-bearing consistency check. - Add test_incremental_global_pass_runs_pass4_on_global_tables: a spy on pass4_routes that asserts it runs on a DISTINCT global tables instance (the full-source rebuild) alongside the changed-files tables. Removing the global pass4 call (incremental_rebuild step 6) drops the call count to 1 -> failure. Verified red->green. - Correct the equivalence test's http_calls_match message to state plainly that it is a consistency guard, and point to the structural test for divergence #1. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent d9c6323 commit 4150444

1 file changed

Lines changed: 65 additions & 2 deletions

File tree

tests/test_incremental_graph.py

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,8 +1142,12 @@ def _match_hist(rel: str) -> dict[str, int]:
11421142
f"type roles diverged:\nincremental={incremental_state['roles']}\nfull={full_state['roles']}"
11431143
)
11441144
assert incremental_state["http_calls_match"] == full_state["http_calls_match"], (
1145-
"HTTP_CALLS match histogram diverged (missing pass4 on global pass5/6 tables, #352):\n"
1146-
f"incremental={incremental_state['http_calls_match']}\nfull={full_state['http_calls_match']}"
1145+
"HTTP_CALLS match histogram diverged between incremental and full rebuild (#352):\n"
1146+
f"incremental={incremental_state['http_calls_match']}\nfull={full_state['http_calls_match']}\n"
1147+
"Consistency guard: with this microservice-less corpus the Feign edge resolves to "
1148+
"'phantom' in both paths, so divergence #1 (missing pass4 on the global tables) cannot "
1149+
"trip here -- it is guarded structurally in "
1150+
"test_incremental_global_pass_runs_pass4_on_global_tables."
11471151
)
11481152
assert incremental_state["async_calls_match"] == full_state["async_calls_match"], (
11491153
"ASYNC_CALLS match histogram diverged (#352):\n"
@@ -1155,6 +1159,65 @@ def _match_hist(rel: str) -> dict[str, int]:
11551159
f"full={full_state['calls_callee_declaring_role']}"
11561160
)
11571161

1162+
def test_incremental_global_pass_runs_pass4_on_global_tables(self, tmp_path: Path) -> None:
1163+
"""Structural guard for issue #352 divergence #1: incremental_rebuild must
1164+
run pass4_routes on the GLOBAL pass5/6 tables (the full-source rebuild at
1165+
step 6), not only on the changed-files tables. Without it, Feign
1166+
HTTP_CALLS route linkage drifts from a full rebuild.
1167+
1168+
The equivalence test's http_calls_match histogram cannot trip on this with
1169+
a microservice-less corpus (the route resolves to 'phantom' in both the
1170+
incremental and full paths), so this spy asserts the invariant directly:
1171+
pass4_routes receives a DISTINCT global tables instance alongside the
1172+
changed-files one. Removing the global pass4 call (incremental_rebuild
1173+
step 6) leaves only one tables instance -> failure.
1174+
"""
1175+
import build_ast_graph as bag
1176+
from build_ast_graph import incremental_rebuild
1177+
from _builders import build_ladybug_full_into
1178+
1179+
source_root = tmp_path / "src"
1180+
source_root.mkdir()
1181+
index_dir = tmp_path / "index"
1182+
index_dir.mkdir()
1183+
ladybug_path = index_dir / "code_graph.lbug"
1184+
1185+
(source_root / "Cal.java").write_text("package pkg;\npublic class Cal {}\n", encoding="utf-8")
1186+
(source_root / "Caller.java").write_text("package pkg;\npublic class Caller {}\n", encoding="utf-8")
1187+
build_ladybug_full_into(source_root, ladybug_path)
1188+
# Touch the in-scope file so incremental_rebuild takes the incremental path
1189+
# (not the no-graph fallback) and reaches its global pass5/6 step.
1190+
(source_root / "Caller.java").write_text(
1191+
"package pkg;\npublic class Caller { int _touched; }\n", encoding="utf-8"
1192+
)
1193+
1194+
seen_tables: list = []
1195+
real_pass4 = bag.pass4_routes
1196+
1197+
def spy(tables, *args, **kwargs):
1198+
seen_tables.append(tables)
1199+
return real_pass4(tables, *args, **kwargs)
1200+
1201+
bag.pass4_routes = spy
1202+
try:
1203+
result = incremental_rebuild(source_root, ladybug_path, verbose=False)
1204+
finally:
1205+
bag.pass4_routes = real_pass4
1206+
1207+
assert result.mode == "incremental", f"expected incremental mode, got {result.mode}"
1208+
# pass4 must run on at least two DISTINCT tables instances: the changed-files
1209+
# tables and the global (full-source) tables. Removing the global call leaves
1210+
# only the changed-files call -> this fails.
1211+
assert len(seen_tables) >= 2, (
1212+
f"pass4_routes ran {len(seen_tables)} time(s); expected >=2 (changed-files + global) "
1213+
"-- the global pass5/6 step must run pass4 on the full-source tables (#352 #1)"
1214+
)
1215+
distinct = {id(t) for t in seen_tables}
1216+
assert len(distinct) >= 2, (
1217+
"pass4_routes must run on a DISTINCT global tables instance, not reuse the "
1218+
"changed-files tables -- otherwise out-of-scope routes/EXPOSES are missing (#352 #1)"
1219+
)
1220+
11581221
def test_incremental_refreshes_dependent_role_on_meta_chain_change(
11591222
self, tmp_path: Path
11601223
) -> None:

0 commit comments

Comments
 (0)