Skip to content

Commit 4d9da40

Browse files
committed
test(769): end-to-end sibling regression + index-format rebuild boundary Refs DeusData#769
Signed-off-by: LA-10 <leenshuhail1@gmail.com>
1 parent 010569f commit 4d9da40

9 files changed

Lines changed: 405 additions & 1 deletion

File tree

‎Makefile.cbm‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,7 @@ TEST_DISCOVER_SRCS = \
559559

560560
TEST_GRAPH_BUFFER_SRCS = tests/test_graph_buffer.c
561561

562-
TEST_PIPELINE_SRCS = tests/test_registry.c tests/test_pipeline.c tests/test_cross_repo.c tests/test_fqn.c tests/test_route_canon.c tests/test_path_alias.c tests/test_configlink.c tests/test_infrascan.c tests/test_worker_pool.c tests/test_parallel.c tests/test_index_resilience.c tests/test_call_reference_contract.c tests/repro/repro_call_scope_usages.c tests/repro/repro_call_argument_usages.c tests/repro/repro_reference_precision.c tests/repro/repro_lexical_binding_precision.c tests/repro/repro_call_argument_matrix_a.c tests/repro/repro_call_argument_matrix_b.c tests/repro/repro_call_node_behaviors.c tests/repro/repro_language_registry.c tests/repro/repro_call_node_manifest.c tests/repro/repro_lsp_ordered_signatures.c tests/repro/repro_lsp_ordered_local.c tests/repro/repro_ts_overload_return_chains.c tests/repro/repro_harness_cleanup.c tests/repro/repro_runner_filter.c
562+
TEST_PIPELINE_SRCS = tests/test_registry.c tests/test_pipeline.c tests/test_cross_repo.c tests/test_fqn.c tests/test_route_canon.c tests/test_path_alias.c tests/test_configlink.c tests/test_infrascan.c tests/test_worker_pool.c tests/test_parallel.c tests/test_index_resilience.c tests/test_index_format.c tests/test_call_reference_contract.c tests/repro/repro_call_scope_usages.c tests/repro/repro_call_argument_usages.c tests/repro/repro_reference_precision.c tests/repro/repro_lexical_binding_precision.c tests/repro/repro_call_argument_matrix_a.c tests/repro/repro_call_argument_matrix_b.c tests/repro/repro_call_node_behaviors.c tests/repro/repro_language_registry.c tests/repro/repro_call_node_manifest.c tests/repro/repro_lsp_ordered_signatures.c tests/repro/repro_lsp_ordered_local.c tests/repro/repro_ts_overload_return_chains.c tests/repro/repro_harness_cleanup.c tests/repro/repro_runner_filter.c
563563

564564
TEST_WATCHER_SRCS = tests/test_watcher.c
565565

‎src/mcp/mcp.c‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8343,6 +8343,9 @@ static char *handle_index_repository(cbm_mcp_server_t *srv, const char *args) {
83438343
srv, doc, root, project_name, repo_path, persistence, p, excluded_dirs, excluded_count,
83448344
file_errors, file_error_count, has_logfile ? logfile_path : NULL);
83458345
yyjson_mut_obj_add_str(doc, root, "status", degraded ? "degraded" : "indexed");
8346+
if (cbm_pipeline_had_format_migration(p)) {
8347+
yyjson_mut_obj_add_bool(doc, root, "format_migration", true);
8348+
}
83468349
} else {
83478350
yyjson_mut_obj_add_str(doc, root, "status", "error");
83488351
yyjson_mut_obj_add_str(doc, root, "hint",

‎src/pipeline/pipeline.c‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,10 @@ struct cbm_pipeline {
190190
int committed_nodes;
191191
int committed_edges;
192192

193+
/* #769: set when a stale-format index was routed through the one-time
194+
* full rebuild, so the MCP response can surface the migration. */
195+
bool format_migration;
196+
193197
/* ADR (project_summaries) captured before a full-reindex DB delete, so it
194198
* can be restored after the rebuild. NULL when no ADR existed. Issue #516. */
195199
char *saved_adr;
@@ -224,6 +228,10 @@ void cbm_pipeline_set_pkgmap(CBMHashTable *map) {
224228
g_pkgmap = map;
225229
}
226230

231+
bool cbm_pipeline_had_format_migration(const cbm_pipeline_t *p) {
232+
return p && p->format_migration;
233+
}
234+
227235
/* ── Timing helper ──────────────────────────────────────────────── */
228236

229237
static double elapsed_ms(struct timespec start) {
@@ -1393,6 +1401,24 @@ static int try_incremental_or_delete_db(cbm_pipeline_t *p, cbm_file_info_t *file
13931401
free(db_path);
13941402
return CBM_PIPELINE_FORCE_FULL_REINDEX;
13951403
}
1404+
1405+
cbm_store_t *fmt_store = cbm_store_open_path_query(db_path);
1406+
int fmt = 0;
1407+
if (fmt_store) {
1408+
cbm_store_get_format_version(fmt_store, &fmt);
1409+
cbm_store_close(fmt_store);
1410+
}
1411+
if (fmt != CBM_INDEX_FORMAT_VERSION) {
1412+
cbm_log_info("pipeline.route", "path", "format_change_reindex", "stored_format",
1413+
itoa_buf(fmt));
1414+
p->format_migration = true;
1415+
int adr_rc = capture_existing_adr(p, db_path);
1416+
(void)cbm_unlink(db_path);
1417+
(void)cbm_remove_db_sidecars(db_path);
1418+
free(db_path);
1419+
return adr_rc != 0 ? adr_rc : CBM_PIPELINE_FORCE_FULL_REINDEX;
1420+
}
1421+
13961422
cbm_log_info("pipeline.route", "path", "incremental_manifest");
13971423
int rc = cbm_pipeline_run_incremental(p, db_path, files, file_count, baseline_manifest,
13981424
baseline_count, force_full_on_mismatch);
@@ -1747,6 +1773,11 @@ int cbm_pipeline_publish_staged(char *stage_path, const cbm_pipeline_generation_
17471773
ok = cbm_store_adr_store(store, generation->project, generation->adr_content) ==
17481774
CBM_STORE_OK;
17491775
}
1776+
1777+
if (ok) {
1778+
ok = cbm_store_set_format_version(store, CBM_INDEX_FORMAT_VERSION) == CBM_STORE_OK;
1779+
}
1780+
17501781
cbm_log_info("publish.timing", "block", "writes", "elapsed_ms",
17511782
itoa_buf((int)elapsed_ms(t_pub)));
17521783
cbm_clock_gettime(CLOCK_MONOTONIC, &t_pub);

‎src/pipeline/pipeline.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ int cbm_pipeline_get_mode(const cbm_pipeline_t *p);
9393
* to NULL/0 when p is NULL or nothing was excluded. Do not free. */
9494
void cbm_pipeline_get_excluded(const cbm_pipeline_t *p, char ***out, int *count);
9595

96+
bool cbm_pipeline_had_format_migration(const cbm_pipeline_t *p);
97+
9698
/* Committed node/edge counts captured at dump time (-1 when dump did not run).
9799
* Nodes are the #334 plausibility-gate axis; edges are informational only. */
98100
void cbm_pipeline_get_committed_counts(const cbm_pipeline_t *p, int *nodes, int *edges);

‎src/store/store.c‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3512,6 +3512,36 @@ int cbm_store_list_files(cbm_store_t *s, const char *project, char ***out, int *
35123512
return CBM_STORE_OK;
35133513
}
35143514

3515+
/* ── Index format version (PRAGMA user_version) ─────────────────── */
3516+
3517+
int cbm_store_get_format_version(cbm_store_t *s, int *out) {
3518+
if (!s || !s->db || !out) {
3519+
return CBM_STORE_ERR;
3520+
}
3521+
*out = 0;
3522+
sqlite3_stmt *stmt = NULL;
3523+
if (sqlite3_prepare_v2(s->db, "PRAGMA user_version;", CBM_NOT_FOUND, &stmt, NULL) !=
3524+
SQLITE_OK) {
3525+
return CBM_STORE_ERR;
3526+
}
3527+
if (sqlite3_step(stmt) == SQLITE_ROW) {
3528+
*out = sqlite3_column_int(stmt, 0);
3529+
}
3530+
sqlite3_finalize(stmt);
3531+
return CBM_STORE_OK;
3532+
}
3533+
3534+
/* PRAGMA values cannot be bound; format the integer constant in. Writes, so
3535+
* only call on a read-write connection (never from configure_pragmas). */
3536+
int cbm_store_set_format_version(cbm_store_t *s, int version) {
3537+
if (!s || !s->db) {
3538+
return CBM_STORE_ERR;
3539+
}
3540+
char sql[ST_BUF_64];
3541+
snprintf(sql, sizeof(sql), "PRAGMA user_version = %d;", version);
3542+
return exec_sql(s, sql);
3543+
}
3544+
35153545
/* ── Node neighbor names ──────────────────────────────────────── */
35163546

35173547
static int query_neighbor_names(sqlite3 *db, const char *sql, int64_t node_id, int limit,

‎src/store/store.h‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ typedef struct cbm_store cbm_store_t;
2323
#define CBM_STORE_OK 0
2424
#define CBM_STORE_ERR (-1)
2525
#define CBM_STORE_NOT_FOUND (-2)
26+
#define CBM_INDEX_FORMAT_VERSION 1
2627

2728
/* ── Data structures ────────────────────────────────────────────── */
2829

@@ -92,6 +93,13 @@ void cbm_store_node_degree(cbm_store_t *s, int64_t node_id, int *in_deg, int *ou
9293
* Returns CBM_STORE_OK or CBM_STORE_ERR. */
9394
int cbm_store_list_files(cbm_store_t *s, const char *project, char ***out, int *count);
9495

96+
/* Persisted index-format identity. Bump when a change alters the QN scheme
97+
* or node identity of an already-written graph, so an old DB is routed
98+
* through the full-reindex path instead of producing a mixed graph.
99+
* 1 = File QNs keep the file extension (#769). */
100+
int cbm_store_get_format_version(cbm_store_t *s, int *out);
101+
int cbm_store_set_format_version(cbm_store_t *s, int version);
102+
95103
/* Get caller/callee names for a node (CALLS/HTTP_CALLS/ASYNC_CALLS edges).
96104
* Returns 0 on success. Caller must free each out_callers[i]/out_callees[i]
97105
* and the arrays themselves. */

‎tests/test_fqn.c‎

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,77 @@ TEST(fqn_compute_basic_rs) {
8989
PASS();
9090
}
9191

92+
TEST(fqn_compute_file_sibling_distinct) {
93+
char *ts = cbm_pipeline_fqn_compute("proj", "app/badge/badge.component.ts", "__file__");
94+
char *html = cbm_pipeline_fqn_compute("proj", "app/badge/badge.component.html", "__file__");
95+
char *scss = cbm_pipeline_fqn_compute("proj", "app/badge/badge.component.scss", "__file__");
96+
ASSERT_NOT_NULL(ts);
97+
ASSERT_NOT_NULL(html);
98+
ASSERT_NOT_NULL(scss);
99+
ASSERT_STR_NEQ(ts, html);
100+
ASSERT_STR_NEQ(ts, scss);
101+
ASSERT_STR_NEQ(html, scss);
102+
free(ts);
103+
free(html);
104+
free(scss);
105+
PASS();
106+
}
107+
108+
TEST(fqn_compute_symbol_still_strips) {
109+
ASSERT_FQN(cbm_pipeline_fqn_compute("proj", "app/badge/badge.component.ts", "BadgeComponent"),
110+
"proj.app.badge.badge.component.BadgeComponent");
111+
PASS();
112+
}
113+
114+
TEST(fqn_module_siblings_still_share) {
115+
ASSERT_FQN(cbm_pipeline_fqn_module("proj", "app/badge/badge.component.ts"),
116+
"proj.app.badge.badge.component");
117+
ASSERT_FQN(cbm_pipeline_fqn_module("proj", "app/badge/badge.component.html"),
118+
"proj.app.badge.badge.component");
119+
PASS();
120+
}
121+
122+
/* #769: exact __file__ QNs for component siblings. */
123+
TEST(fqn_compute_file_sibling_exact) {
124+
ASSERT_FQN(cbm_pipeline_fqn_compute("proj", "app/badge/badge.component.ts", "__file__"),
125+
"proj.app.badge.badge.component.ts.__file__");
126+
ASSERT_FQN(cbm_pipeline_fqn_compute("proj", "app/badge/badge.component.html", "__file__"),
127+
"proj.app.badge.badge.component.html.__file__");
128+
ASSERT_FQN(cbm_pipeline_fqn_compute("proj", "app/badge/badge.component.scss", "__file__"),
129+
"proj.app.badge.badge.component.scss.__file__");
130+
PASS();
131+
}
132+
133+
TEST(fqn_compute_file_multi_extension) {
134+
ASSERT_FQN(cbm_pipeline_fqn_compute("proj", "app/badge/badge.spec.ts", "__file__"),
135+
"proj.app.badge.badge.spec.ts.__file__");
136+
ASSERT_FQN(cbm_pipeline_fqn_compute("proj", "archive.tar.gz", "__file__"),
137+
"proj.archive.tar.gz.__file__");
138+
PASS();
139+
}
140+
141+
TEST(fqn_compute_file_no_extension) {
142+
ASSERT_FQN(cbm_pipeline_fqn_compute("proj", "Makefile", "__file__"),
143+
"proj.Makefile.__file__");
144+
ASSERT_FQN(cbm_pipeline_fqn_compute("proj", "LICENSE", "__file__"),
145+
"proj.LICENSE.__file__");
146+
PASS();
147+
}
148+
149+
TEST(fqn_compute_file_dotfile) {
150+
ASSERT_FQN(cbm_pipeline_fqn_compute("proj", ".gitignore", "__file__"),
151+
"proj..gitignore.__file__");
152+
ASSERT_FQN(cbm_pipeline_fqn_compute("proj", "config/.env", "__file__"),
153+
"proj.config..env.__file__");
154+
PASS();
155+
}
156+
157+
TEST(fqn_compute_file_index_ts) {
158+
ASSERT_FQN(cbm_pipeline_fqn_compute("proj", "app/index.ts", "__file__"),
159+
"proj.app.index.ts.__file__");
160+
PASS();
161+
}
162+
92163
/* ── Nested paths ─────────────────────────────────────────────── */
93164

94165
TEST(fqn_compute_nested_two_levels) {
@@ -595,6 +666,14 @@ SUITE(fqn) {
595666
RUN_TEST(fqn_file_qn_distinguishes_same_stem_header_source_issue964);
596667
RUN_TEST(fqn_module_qn_still_strips_extension);
597668
RUN_TEST(fqn_compute_basic_rs);
669+
RUN_TEST(fqn_compute_file_sibling_distinct);
670+
RUN_TEST(fqn_compute_symbol_still_strips);
671+
RUN_TEST(fqn_module_siblings_still_share);
672+
RUN_TEST(fqn_compute_file_sibling_exact);
673+
RUN_TEST(fqn_compute_file_multi_extension);
674+
RUN_TEST(fqn_compute_file_no_extension);
675+
RUN_TEST(fqn_compute_file_dotfile);
676+
RUN_TEST(fqn_compute_file_index_ts);
598677

599678
/* fqn_compute: nested paths */
600679
RUN_TEST(fqn_compute_nested_two_levels);

0 commit comments

Comments
 (0)