#include "test_framework.h" #include "test_helpers.h" #include "foundation/compat.h" #include "foundation/mem_core.h" #include "mcp/architecture_jobs.h" #include #include #include #include static int64_t projection_node(cbm_store_t *store, const char *label, const char *qn, const char *file, bool entry) { cbm_node_t n = {.project = "projection", .label = label, .name = qn, .qualified_name = qn, .file_path = file, .start_line = 10, .end_line = 20, .properties_json = entry ? "{\"is_entry_point\":true}" : "{}"}; return cbm_store_upsert_node(store, &n); } static int64_t projection_edge(cbm_store_t *store, int64_t source, int64_t target, const char *type) { cbm_edge_t e = { .project = "projection", .source_id = source, .target_id = target, .type = type}; return cbm_store_insert_edge(store, &e); } static cbm_store_t *projection_store(void) { cbm_store_t *store = cbm_store_open_memory(); if (store) cbm_store_upsert_project(store, "projection", "/tmp/projection"); return store; } static yyjson_val *projection_field(yyjson_doc *doc, const char *field) { return yyjson_obj_get(yyjson_doc_get_root(doc), field); } TEST(projection_accounts_for_isolated_nodes_and_files) { cbm_store_t *store = projection_store(); ASSERT_NOT_NULL(store); projection_node(store, "Project", "projection", "", false); projection_node(store, "Folder", "projection.folder", "folder", false); projection_node(store, "File", "projection.alone.c", "alone.c", false); projection_node(store, "Module", "projection.alone", "alone.c", false); projection_node(store, "Function", "projection.alone.work", "alone.c", false); projection_node(store, "File", "projection.notes.md", "notes.md", false); char *json = NULL; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", NULL, &json), CBM_STORE_OK); yyjson_doc *doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); yyjson_val *totals = projection_field(doc, "totals"); ASSERT_EQ(yyjson_get_int(yyjson_obj_get(totals, "nodes")), 6); ASSERT_EQ(yyjson_get_int(yyjson_obj_get(totals, "accounted_nodes")), 4); ASSERT_EQ(yyjson_get_int(yyjson_obj_get(totals, "structural_nodes")), 2); ASSERT_EQ(yyjson_get_int(yyjson_obj_get(totals, "files")), 2); ASSERT_TRUE(yyjson_get_bool(projection_field(doc, "complete"))); ASSERT_EQ(yyjson_arr_size(projection_field(doc, "components")), 2); ASSERT_EQ(yyjson_arr_size(projection_field(doc, "dependencies")), 0); yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); cbm_store_close(store); PASS(); } TEST(projection_preserves_typed_edges_and_contiguous_paths) { cbm_store_t *store = projection_store(); ASSERT_NOT_NULL(store); projection_node(store, "Package", "projection.a", "", false); projection_node(store, "Package", "projection.b", "", false); projection_node(store, "Package", "projection.c", "", false); int64_t a = projection_node(store, "Function", "projection.a.start", "a.c", true); int64_t b1 = projection_node(store, "Function", "projection.b.one", "b.c", false); int64_t b2 = projection_node(store, "Function", "projection.b.two", "b.c", false); int64_t c = projection_node(store, "Function", "projection.c.end", "c.c", false); int64_t first = projection_edge(store, a, b1, "CALLS"); projection_edge(store, b2, c, "CALLS"); projection_edge(store, b1, b2, "CALL_REFERENCE"); char *json = NULL; cbm_architecture_projection_options_t opts = {.entry_node_id = a}; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", &opts, &json), CBM_STORE_OK); yyjson_doc *doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); yyjson_val *paths = projection_field(doc, "paths"); ASSERT_EQ(yyjson_arr_size(paths), 1); yyjson_val *path = yyjson_arr_get(paths, 0); yyjson_val *nodes = yyjson_obj_get(path, "nodes"), *edges = yyjson_obj_get(path, "edges"); ASSERT_EQ(yyjson_arr_size(nodes), 2); ASSERT_EQ(yyjson_arr_size(edges), 1); ASSERT_EQ(yyjson_get_sint(yyjson_obj_get(yyjson_arr_get(nodes, 1), "id")), b1); ASSERT_EQ(yyjson_get_sint(yyjson_obj_get(yyjson_arr_get(edges, 0), "id")), first); ASSERT_EQ(yyjson_arr_size(projection_field(doc, "dependencies")), 2); yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); /* A real binding changes the answer; projected component edges alone did not. */ projection_edge(store, b1, b2, "CALLS"); ASSERT_EQ(cbm_store_architecture_projection(store, "projection", &opts, &json), CBM_STORE_OK); doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); nodes = yyjson_obj_get(yyjson_arr_get(projection_field(doc, "paths"), 0), "nodes"); ASSERT_EQ(yyjson_arr_size(nodes), 4); ASSERT_EQ(yyjson_get_sint(yyjson_obj_get(yyjson_arr_get(nodes, 3), "id")), c); yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); cbm_store_close(store); PASS(); } TEST(projection_reports_dependency_cycles_without_fabricated_execution) { cbm_store_t *store = projection_store(); ASSERT_NOT_NULL(store); projection_node(store, "Package", "projection.a", "", false); projection_node(store, "Package", "projection.b", "", false); projection_node(store, "Package", "projection.c", "", false); int64_t a = projection_node(store, "Function", "projection.a.run", "a.c", true); int64_t b = projection_node(store, "Function", "projection.b.run", "b.c", false); int64_t c = projection_node(store, "Function", "projection.c.run", "c.c", false); projection_edge(store, a, b, "IMPORTS"); projection_edge(store, b, c, "IMPLEMENTS"); projection_edge(store, c, a, "USAGE"); char *json = NULL; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", NULL, &json), CBM_STORE_OK); yyjson_doc *doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); yyjson_val *cycles = projection_field(doc, "cycles"); ASSERT_EQ(yyjson_arr_size(cycles), 1); ASSERT_EQ(yyjson_arr_size(yyjson_obj_get(yyjson_arr_get(cycles, 0), "component_ids")), 3); ASSERT_EQ(yyjson_arr_size(projection_field(doc, "paths")), 0); yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); cbm_architecture_projection_options_t opts = {.max_components = 2}; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", &opts, &json), CBM_STORE_OK); doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); ASSERT_EQ(yyjson_arr_size(projection_field(doc, "cycles")), 0); ASSERT_EQ(yyjson_get_int(yyjson_obj_get(projection_field(doc, "totals"), "cycles")), 1); ASSERT_EQ(yyjson_get_int(yyjson_obj_get(projection_field(doc, "limits"), "omitted_cycles")), 1); yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); cbm_store_close(store); PASS(); } TEST(projection_graph_limits_abstain_instead_of_sampling) { cbm_store_t *store = projection_store(); ASSERT_NOT_NULL(store); int64_t a = projection_node(store, "Function", "projection.a", "a.c", true); int64_t b = projection_node(store, "Function", "projection.b", "b.c", false); projection_edge(store, a, b, "CALLS"); projection_edge(store, b, a, "CALLS"); cbm_architecture_projection_options_t opts = {.max_nodes = 1}; char *json = NULL; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", &opts, &json), CBM_STORE_OK); yyjson_doc *doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); ASSERT_STR_EQ(yyjson_get_str(projection_field(doc, "status")), "limited"); ASSERT_FALSE(yyjson_get_bool(projection_field(doc, "complete"))); ASSERT_EQ(yyjson_arr_size(projection_field(doc, "components")), 0); ASSERT_EQ(yyjson_get_int(yyjson_obj_get(projection_field(doc, "totals"), "nodes")), 2); yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); opts.max_nodes = 0; opts.max_edges = 1; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", &opts, &json), CBM_STORE_OK); doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); ASSERT_STR_EQ(yyjson_get_str(projection_field(doc, "status")), "limited"); ASSERT_EQ(yyjson_arr_size(projection_field(doc, "components")), 0); yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); cbm_store_close(store); PASS(); } static bool projection_cancel(void *context) { (void)context; return true; } TEST(projection_cancellation_deadline_and_store_reuse) { cbm_store_t *store = projection_store(); ASSERT_NOT_NULL(store); projection_node(store, "Function", "projection.a", "a.c", true); char *json = NULL; cbm_architecture_projection_options_t opts = {.cancel = projection_cancel}; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", &opts, &json), CBM_STORE_CANCELLED); ASSERT_NULL(json); opts.cancel = NULL; opts.deadline_ms = cbm_now_ms() - 1; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", &opts, &json), CBM_STORE_SCAN_LIMIT); ASSERT_NULL(json); ASSERT_EQ(cbm_store_architecture_projection(store, "projection", NULL, &json), CBM_STORE_OK); ASSERT_NOT_NULL(json); cbm_free(CBM_MEM_CLASS_OTHER, json); ASSERT_EQ(sqlite3_get_autocommit(cbm_store_get_db(store)), 1); cbm_store_close(store); PASS(); } TEST(projection_display_caps_preserve_total_accounting) { cbm_store_t *store = projection_store(); ASSERT_NOT_NULL(store); projection_node(store, "File", "projection.a.c", "a.c", false); projection_node(store, "File", "projection.b.c", "b.c", false); projection_node(store, "File", "projection.c.c", "c.c", false); cbm_architecture_projection_options_t opts = {.max_components = 1}; char *json = NULL; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", &opts, &json), CBM_STORE_OK); yyjson_doc *doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); ASSERT_EQ(yyjson_arr_size(projection_field(doc, "components")), 1); ASSERT_EQ(yyjson_get_int(yyjson_obj_get(projection_field(doc, "totals"), "components")), 3); ASSERT_EQ(yyjson_get_int(yyjson_obj_get(projection_field(doc, "totals"), "accounted_nodes")), 3); ASSERT_EQ(yyjson_get_int(yyjson_obj_get(projection_field(doc, "limits"), "omitted_components")), 2); ASSERT_FALSE(yyjson_get_bool(projection_field(doc, "complete"))); yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); cbm_store_close(store); PASS(); } TEST(projection_unknown_project_and_empty_project) { cbm_store_t *store = projection_store(); ASSERT_NOT_NULL(store); char *json = NULL; ASSERT_EQ(cbm_store_architecture_projection(store, "absent", NULL, &json), CBM_STORE_NOT_FOUND); ASSERT_NULL(json); ASSERT_EQ(cbm_store_architecture_projection(store, "projection", NULL, &json), CBM_STORE_OK); yyjson_doc *doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); ASSERT_TRUE(yyjson_get_bool(projection_field(doc, "complete"))); ASSERT_EQ(yyjson_arr_size(projection_field(doc, "components")), 0); yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); projection_node(store, "Project", "projection", "", false); projection_node(store, "Folder", "projection.folder", "folder", false); ASSERT_EQ(cbm_store_architecture_projection(store, "projection", NULL, &json), CBM_STORE_OK); doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); ASSERT_TRUE(yyjson_get_bool(projection_field(doc, "complete"))); ASSERT_EQ(yyjson_arr_size(projection_field(doc, "components")), 0); ASSERT_EQ(yyjson_get_int(yyjson_obj_get(projection_field(doc, "totals"), "structural_nodes")), 2); yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); cbm_store_close(store); PASS(); } TEST(projection_paths_depth_cap_is_explicit) { cbm_store_t *store = projection_store(); ASSERT_NOT_NULL(store); int64_t nodes[5]; for (int i = 0; i < 5; i++) { char qn[48]; snprintf(qn, sizeof(qn), "projection.n%d", i); nodes[i] = projection_node(store, "Function", qn, "chain.c", i == 0); if (i) projection_edge(store, nodes[i - 1], nodes[i], "CALLS"); } cbm_architecture_projection_options_t opts = {.max_depth = 2}; char *json = NULL; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", &opts, &json), CBM_STORE_OK); yyjson_doc *doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); ASSERT_TRUE( yyjson_get_bool(yyjson_obj_get(projection_field(doc, "limits"), "paths_truncated"))); yyjson_val *path = yyjson_arr_get(projection_field(doc, "paths"), 0); ASSERT_EQ(yyjson_arr_size(yyjson_obj_get(path, "edges")), 2); yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); cbm_store_close(store); PASS(); } TEST(projection_channel_relationships_are_typed_not_fake_calls) { cbm_store_t *store = projection_store(); ASSERT_NOT_NULL(store); int64_t producer = projection_node(store, "Function", "projection.producer", "send.c", true); int64_t consumer = projection_node(store, "Function", "projection.consumer", "receive.c", false); int64_t channel = projection_node(store, "Channel", "projection.orders", "", false); projection_edge(store, producer, channel, "EMITS"); projection_edge(store, consumer, channel, "LISTENS_ON"); projection_edge(store, producer, consumer, "DATA_FLOWS"); projection_edge(store, producer, consumer, "SIMILAR_TO"); char *json = NULL; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", NULL, &json), CBM_STORE_OK); yyjson_doc *doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); ASSERT_EQ(yyjson_arr_size(projection_field(doc, "dependencies")), 3); ASSERT_EQ(yyjson_arr_size(projection_field(doc, "paths")), 0); ASSERT_EQ(yyjson_get_int(yyjson_obj_get(projection_field(doc, "totals"), "unmodeled_edges")), 1); yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); cbm_store_close(store); PASS(); } TEST(projection_test_callers_cannot_absorb_source_components) { cbm_store_t *store = projection_store(); ASSERT_NOT_NULL(store); projection_node(store, "Module", "projection.store", "src/store.c", false); int64_t production = projection_node(store, "Function", "projection.store.read", "src/store.c", false); for (int file = 0; file < 2; file++) { char module[64], path[64]; snprintf(module, sizeof(module), "projection.test%d", file); snprintf(path, sizeof(path), "tests/check%d.c", file); projection_node(store, "Module", module, path, false); for (int i = 0; i < 3; i++) { char qn[80]; snprintf(qn, sizeof(qn), "%s.check%d", module, i); int64_t test = projection_node(store, "Function", qn, path, false); projection_edge(store, test, production, "CALLS"); } } char *json = NULL; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", NULL, &json), CBM_STORE_OK); yyjson_doc *doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); yyjson_val *parts = projection_field(doc, "components"); ASSERT_EQ(yyjson_arr_size(parts), 3); int non_test = 0, tests = 0; for (size_t i = 0; i < yyjson_arr_size(parts); i++) { yyjson_val *part = yyjson_arr_get(parts, i); const char *role = yyjson_get_str(yyjson_obj_get(part, "role")); ASSERT_NOT_NULL(role); if (!strcmp(role, "non_test")) { non_test++; ASSERT_EQ(yyjson_get_int(yyjson_obj_get(part, "member_count")), 2); } else { tests++; ASSERT_EQ(yyjson_get_int(yyjson_obj_get(part, "member_count")), 4); } } ASSERT_EQ(non_test, 1); ASSERT_EQ(tests, 2); ASSERT_EQ(yyjson_get_int(yyjson_obj_get(projection_field(doc, "totals"), "accounted_nodes")), 10); yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); cbm_store_close(store); PASS(); } TEST(projection_indexed_embedded_test_has_separate_membership) { cbm_store_t *store = projection_store(); ASSERT_NOT_NULL(store); int64_t type = projection_node(store, "Class", "projection.Store", "src/store.rs", false); int64_t method = projection_node(store, "Method", "projection.Store.read", "src/store.rs", false); cbm_node_t test = {.project = "projection", .label = "Method", .name = "embedded_check", .qualified_name = "projection.Store.embedded_check", .file_path = "src/store.rs", .properties_json = "{\"is_test\":true}"}; int64_t embedded = cbm_store_upsert_node(store, &test); projection_edge(store, type, method, "DEFINES_METHOD"); projection_edge(store, type, embedded, "DEFINES_METHOD"); projection_edge(store, embedded, method, "CALLS"); char *json = NULL; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", NULL, &json), CBM_STORE_OK); yyjson_doc *doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); yyjson_val *parts = projection_field(doc, "components"); ASSERT_EQ(yyjson_arr_size(parts), 2); bool found = false; for (size_t i = 0; i < yyjson_arr_size(parts); i++) { yyjson_val *part = yyjson_arr_get(parts, i); if (!strcmp(yyjson_get_str(yyjson_obj_get(part, "role")), "test")) { found = true; ASSERT_EQ(yyjson_get_int(yyjson_obj_get(part, "member_count")), 1); ASSERT_STR_EQ(yyjson_get_str(yyjson_obj_get(part, "role_basis")), "indexed_is_test"); } } ASSERT_TRUE(found); yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); cbm_store_close(store); PASS(); } TEST(projection_shallow_leaves_do_not_hide_deeper_behavior) { cbm_store_t *store = projection_store(); ASSERT_NOT_NULL(store); int64_t entry = projection_node(store, "Function", "projection.main", "main.c", true); /* These precede the meaningful branch in both node and edge order. */ for (int i = 0; i < 13; i++) { char qn[64]; snprintf(qn, sizeof(qn), "projection.utility%d", i); int64_t leaf = projection_node(store, "Function", qn, "utility.c", false); projection_edge(store, entry, leaf, "CALLS"); } int64_t a = projection_node(store, "Function", "projection.dispatch", "dispatch.c", false); int64_t b = projection_node(store, "Function", "projection.query", "query.c", false); int64_t end = projection_node(store, "Function", "projection.persist", "store.c", false); projection_edge(store, entry, a, "CALLS"); projection_edge(store, a, b, "CALLS"); projection_edge(store, b, end, "CALLS"); char *json = NULL; cbm_architecture_projection_options_t opts = {.entry_node_id = entry}; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", &opts, &json), CBM_STORE_OK); yyjson_doc *doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); yyjson_val *paths = projection_field(doc, "paths"); ASSERT_EQ(yyjson_arr_size(paths), 12); yyjson_val *first = yyjson_arr_get(paths, 0); ASSERT_EQ(yyjson_arr_size(yyjson_obj_get(first, "edges")), 3); yyjson_val *nodes = yyjson_obj_get(first, "nodes"); ASSERT_EQ(yyjson_get_sint(yyjson_obj_get(yyjson_arr_get(nodes, 3), "id")), end); ASSERT_TRUE( yyjson_get_bool(yyjson_obj_get(projection_field(doc, "limits"), "paths_truncated"))); yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); cbm_store_close(store); PASS(); } TEST(projection_behavior_preserves_first_hop_diversity) { cbm_store_t *store = projection_store(); ASSERT_NOT_NULL(store); int64_t entry = projection_node(store, "Function", "projection.main", "main.c", true); int64_t a = projection_node(store, "Function", "projection.a", "a.c", false); int64_t a1 = projection_node(store, "Function", "projection.a1", "a.c", false); int64_t a2 = projection_node(store, "Function", "projection.a2", "a.c", false); int64_t b = projection_node(store, "Function", "projection.b", "b.c", false); int64_t b1 = projection_node(store, "Function", "projection.b1", "b.c", false); projection_edge(store, entry, a, "CALLS"); projection_edge(store, a, a1, "CALLS"); projection_edge(store, a, a2, "CALLS"); projection_edge(store, entry, b, "CALLS"); projection_edge(store, b, b1, "CALLS"); char *json = NULL; cbm_architecture_projection_options_t opts = {.entry_node_id = entry, .max_paths = 2}; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", &opts, &json), CBM_STORE_OK); yyjson_doc *doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); yyjson_val *paths = projection_field(doc, "paths"); ASSERT_EQ(yyjson_arr_size(paths), 2); yyjson_val *first = yyjson_obj_get(yyjson_arr_get(paths, 0), "nodes"); yyjson_val *second = yyjson_obj_get(yyjson_arr_get(paths, 1), "nodes"); int64_t hop1 = yyjson_get_sint(yyjson_obj_get(yyjson_arr_get(first, 1), "id")); int64_t hop2 = yyjson_get_sint(yyjson_obj_get(yyjson_arr_get(second, 1), "id")); ASSERT_TRUE((hop1 == a && hop2 == b) || (hop1 == b && hop2 == a)); yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); cbm_store_close(store); PASS(); } TEST(projection_corridor_preserves_joins_cycles_and_callsite_evidence) { cbm_store_t *store = projection_store(); ASSERT_NOT_NULL(store); int64_t a = projection_node(store, "Function", "projection.a", "src/api/main.c", true); int64_t b = projection_node(store, "Function", "projection.b", "src/engine/b.c", false); int64_t c = projection_node(store, "Function", "projection.c", "src/engine/c.c", false); int64_t d = projection_node(store, "Function", "projection.d", "src/store/d.c", false); int64_t ignored = projection_node(store, "Function", "projection.callback", "src/events/e.c", false); cbm_edge_t evidence = { .project = "projection", .source_id = a, .target_id = b, .type = "CALLS", .properties_json = "{\"line\":42,\"strategy\":\"lsp_direct\",\"confidence\":0.95,\"candidates\":1}"}; int64_t ab = cbm_store_insert_edge(store, &evidence); projection_edge(store, a, c, "CALLS"); projection_edge(store, b, d, "CALLS"); projection_edge(store, c, d, "CALLS"); projection_edge(store, b, c, "CALLS"); projection_edge(store, c, b, "CALLS"); projection_edge(store, a, ignored, "CALL_REFERENCE"); projection_edge(store, ignored, d, "EMITS"); cbm_architecture_projection_options_t opts = { .entry_node_id = a, .target_node_id = d, .max_depth = 4}; char *json = NULL; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", &opts, &json), CBM_STORE_OK); yyjson_doc *doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); yyjson_val *view = projection_field(doc, "behavior"), *nodes = yyjson_obj_get(view, "nodes"), *edges = yyjson_obj_get(view, "edges"); ASSERT_TRUE(yyjson_get_bool(yyjson_obj_get(view, "complete"))); ASSERT_TRUE(yyjson_get_bool(yyjson_obj_get(view, "reachable"))); ASSERT_EQ(yyjson_arr_size(nodes), 4); ASSERT_EQ(yyjson_arr_size(edges), 6); int into_d = 0; bool found_evidence = false; for (size_t i = 0; i < yyjson_arr_size(edges); i++) { yyjson_val *edge = yyjson_arr_get(edges, i); if (yyjson_get_sint(yyjson_obj_get(edge, "target_id")) == d) into_d++; if (yyjson_get_sint(yyjson_obj_get(edge, "id")) == ab) { found_evidence = true; yyjson_val *site = yyjson_obj_get(edge, "callsite"), *resolution = yyjson_obj_get(edge, "resolution"); ASSERT_EQ(yyjson_get_sint(yyjson_obj_get(site, "line")), 42); ASSERT_STR_EQ(yyjson_get_str(yyjson_obj_get(site, "file_path")), "src/api/main.c"); ASSERT_STR_EQ(yyjson_get_str(yyjson_obj_get(resolution, "strategy")), "lsp_direct"); ASSERT_EQ(yyjson_get_sint(yyjson_obj_get(resolution, "candidates")), 1); } } ASSERT_EQ(into_d, 2); ASSERT_TRUE(found_evidence); yyjson_val *cycles = yyjson_obj_get(view, "cycles"); ASSERT_EQ(yyjson_arr_size(cycles), 1); ASSERT_EQ(yyjson_arr_size(yyjson_obj_get(yyjson_arr_get(cycles, 0), "node_ids")), 2); yyjson_val *paths = projection_field(doc, "paths"); ASSERT_TRUE(yyjson_arr_size(paths) >= 2); for (size_t i = 0; i < yyjson_arr_size(paths); i++) { yyjson_val *path = yyjson_arr_get(paths, i), *pn = yyjson_obj_get(path, "nodes"), *pe = yyjson_obj_get(path, "edges"); ASSERT_EQ(yyjson_arr_size(pn), yyjson_arr_size(pe) + 1); ASSERT_EQ(yyjson_get_sint(yyjson_obj_get(yyjson_arr_get(pn, 0), "id")), a); ASSERT_EQ( yyjson_get_sint(yyjson_obj_get(yyjson_arr_get(pn, yyjson_arr_size(pn) - 1), "id")), d); ASSERT_TRUE(yyjson_arr_size(pe) <= 4); for (size_t j = 0; j < yyjson_arr_size(pe); j++) { yyjson_val *edge = yyjson_arr_get(pe, j); ASSERT_EQ(yyjson_get_sint(yyjson_obj_get(edge, "source_id")), yyjson_get_sint(yyjson_obj_get(yyjson_arr_get(pn, j), "id"))); ASSERT_EQ(yyjson_get_sint(yyjson_obj_get(edge, "target_id")), yyjson_get_sint(yyjson_obj_get(yyjson_arr_get(pn, j + 1), "id"))); } } yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); opts.target_node_id = ignored; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", &opts, &json), CBM_STORE_OK); doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); view = projection_field(doc, "behavior"); ASSERT_FALSE(yyjson_get_bool(yyjson_obj_get(view, "reachable"))); ASSERT_TRUE(yyjson_get_bool(yyjson_obj_get(view, "corridor_complete"))); ASSERT_EQ(yyjson_arr_size(yyjson_obj_get(view, "nodes")), 0); ASSERT_EQ(yyjson_arr_size(projection_field(doc, "paths")), 0); yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); cbm_store_close(store); PASS(); } TEST(projection_corridor_caps_abstain_without_hiding_joins) { cbm_store_t *store = projection_store(); ASSERT_NOT_NULL(store); int64_t a = projection_node(store, "Function", "projection.a", "a.c", true); int64_t b = projection_node(store, "Function", "projection.b", "b.c", false); int64_t d = projection_node(store, "Function", "projection.d", "d.c", false); projection_edge(store, a, b, "CALLS"); projection_edge(store, b, d, "CALLS"); cbm_architecture_projection_options_t opts = { .entry_node_id = a, .target_node_id = d, .max_corridor_nodes = 2}; char *json = NULL; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", &opts, &json), CBM_STORE_OK); yyjson_doc *doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); yyjson_val *view = projection_field(doc, "behavior"); ASSERT_FALSE(yyjson_get_bool(yyjson_obj_get(view, "complete"))); ASSERT_FALSE(yyjson_get_bool(yyjson_obj_get(view, "corridor_complete"))); ASSERT_EQ(yyjson_get_int(yyjson_obj_get(yyjson_obj_get(view, "totals"), "corridor_nodes")), 3); ASSERT_EQ(yyjson_arr_size(yyjson_obj_get(view, "nodes")), 0); ASSERT_EQ(yyjson_arr_size(yyjson_obj_get(view, "edges")), 0); ASSERT_EQ(yyjson_arr_size(projection_field(doc, "paths")), 0); yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); opts.target_node_id = 0; opts.max_targets = 1; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", &opts, &json), CBM_STORE_OK); doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); view = projection_field(doc, "behavior"); ASSERT_EQ(yyjson_arr_size(yyjson_obj_get(view, "reachable_targets")), 1); ASSERT_EQ(yyjson_get_int(yyjson_obj_get(yyjson_obj_get(view, "totals"), "omitted_targets")), 1); ASSERT_FALSE(yyjson_get_bool(yyjson_obj_get(view, "complete"))); yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); cbm_store_close(store); PASS(); } TEST(projection_overview_accounts_for_components_beyond_legacy_cap) { cbm_store_t *store = projection_store(); ASSERT_NOT_NULL(store); for (int i = 0; i < 300; i++) { char qn[80], file[80]; snprintf(qn, sizeof(qn), "projection.part%d", i); snprintf(file, sizeof(file), "packages/part%d/file.c", i); projection_node(store, "File", qn, file, false); } int64_t entry = projection_node(store, "Function", "projection.entry", "src/main.c", true); int64_t target = projection_node(store, "Function", "projection.target", "src/service.c", false); projection_edge(store, entry, target, "CALLS"); cbm_architecture_projection_options_t opts = {.max_components = 1, .entry_node_id = entry}; char *json = NULL; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", &opts, &json), CBM_STORE_OK); yyjson_doc *doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); yyjson_val *overview = projection_field(doc, "overview"), *groups = yyjson_obj_get(overview, "groups"), *parts = yyjson_obj_get(overview, "components"); ASSERT_TRUE(yyjson_get_bool(yyjson_obj_get(overview, "complete"))); ASSERT_EQ(yyjson_arr_size(projection_field(doc, "components")), 1); ASSERT_EQ(yyjson_arr_size(parts), 302); ASSERT_TRUE(yyjson_arr_size(groups) <= 64); ASSERT_EQ(yyjson_get_int(yyjson_obj_get(overview, "grouping_depth")), 1); size_t membership = 0; int members = 0; for (size_t i = 0; i < yyjson_arr_size(groups); i++) { yyjson_val *group = yyjson_arr_get(groups, i); membership += yyjson_arr_size(yyjson_obj_get(group, "component_ids")); members += yyjson_get_int(yyjson_obj_get(group, "member_count")); } ASSERT_EQ(membership, 302); ASSERT_EQ(members, 302); yyjson_val *symbol = yyjson_arr_get(yyjson_obj_get(projection_field(doc, "behavior"), "reachable_targets"), 0); const char *group_id = yyjson_get_str(yyjson_obj_get(symbol, "group_id")); ASSERT_NOT_NULL(group_id); char stable[128]; snprintf(stable, sizeof(stable), "%s", group_id); yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); opts.target_node_id = target; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", &opts, &json), CBM_STORE_OK); doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); symbol = yyjson_arr_get(yyjson_obj_get(projection_field(doc, "behavior"), "reachable_targets"), 0); ASSERT_STR_EQ(yyjson_get_str(yyjson_obj_get(symbol, "group_id")), stable); ASSERT_EQ(yyjson_arr_size(yyjson_obj_get(projection_field(doc, "overview"), "components")), 302); yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); cbm_store_close(store); PASS(); } TEST(projection_aggregate_cycle_does_not_invent_symbol_cycle) { cbm_store_t *store = projection_store(); ASSERT_NOT_NULL(store); int64_t a = projection_node(store, "Function", "projection.a", "src/api/a.c", true); int64_t b = projection_node(store, "Function", "projection.b", "src/store/b.c", false); int64_t c = projection_node(store, "Function", "projection.c", "src/api/c.c", false); projection_edge(store, a, b, "CALLS"); projection_edge(store, b, c, "CALLS"); cbm_architecture_projection_options_t opts = {.entry_node_id = a, .target_node_id = c}; char *json = NULL; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", &opts, &json), CBM_STORE_OK); yyjson_doc *doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); ASSERT_EQ(yyjson_arr_size(yyjson_obj_get(projection_field(doc, "overview"), "groups")), 2); ASSERT_EQ(yyjson_arr_size(yyjson_obj_get(projection_field(doc, "overview"), "connections")), 2); ASSERT_EQ(yyjson_arr_size(yyjson_obj_get(projection_field(doc, "behavior"), "cycles")), 0); yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); cbm_store_close(store); PASS(); } TEST(projection_provenance_budget_counts_utf8_bytes) { cbm_store_t *store = projection_store(); ASSERT_NOT_NULL(store); int64_t a = projection_node(store, "Function", "projection.a", "a.c", true); int64_t b = projection_node(store, "Function", "projection.b", "b.c", false); int64_t d = projection_node(store, "Function", "projection.d", "d.c", false); char properties[19000]; size_t used = (size_t)snprintf(properties, sizeof(properties), "{\"line\":42,\"strategy\":\"unique_name\",\"ignored\":\""); for (int i = 0; i < 9000; i++) { properties[used++] = (char)0xc3; properties[used++] = (char)0xa9; } memcpy(properties + used, "\"}", 3); ASSERT_TRUE(strlen(properties) > 16384); cbm_edge_t edge = {.project = "projection", .source_id = a, .target_id = b, .type = "CALLS", .properties_json = properties}; int64_t oversized = cbm_store_insert_edge(store, &edge); edge.source_id = b; edge.target_id = d; edge.properties_json = "{\"line\":24}"; int64_t normal = cbm_store_insert_edge(store, &edge); cbm_architecture_projection_options_t opts = {.entry_node_id = a, .target_node_id = d}; char *json = NULL; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", &opts, &json), CBM_STORE_OK); yyjson_doc *doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); yyjson_val *edges = yyjson_obj_get(projection_field(doc, "behavior"), "edges"); ASSERT_EQ(yyjson_arr_size(edges), 2); for (size_t i = 0; i < yyjson_arr_size(edges); i++) { yyjson_val *value = yyjson_arr_get(edges, i); int64_t id = yyjson_get_sint(yyjson_obj_get(value, "id")); if (id == oversized) { ASSERT_NULL(yyjson_obj_get(value, "callsite")); ASSERT_NULL(yyjson_obj_get(value, "resolution")); } else { ASSERT_EQ(id, normal); ASSERT_EQ(yyjson_get_int(yyjson_obj_get(yyjson_obj_get(value, "callsite"), "line")), 24); } } yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); cbm_store_close(store); PASS(); } TEST(projection_example_budgets_preserve_full_aggregate_counts) { cbm_store_t *store = projection_store(); ASSERT_NOT_NULL(store); projection_node(store, "Package", "projection.a", "a.c", false); projection_node(store, "Package", "projection.b", "b.c", false); for (int i = 0; i < 5; i++) { char source[64], target[64]; snprintf(source, sizeof(source), "projection.a.caller%d", i); snprintf(target, sizeof(target), "projection.b.callee%d", i); int64_t a = projection_node(store, "Function", source, "a.c", i == 0); int64_t b = projection_node(store, "Function", target, "b.c", false); projection_edge(store, a, b, "CALLS"); } char *json = NULL; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", NULL, &json), CBM_STORE_OK); yyjson_doc *doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); yyjson_val *parts = projection_field(doc, "components"), *deps = projection_field(doc, "dependencies"); ASSERT_EQ(yyjson_arr_size(parts), 2); ASSERT_EQ(yyjson_arr_size(deps), 1); yyjson_val *dependency = yyjson_arr_get(deps, 0); ASSERT_EQ(yyjson_get_int(yyjson_obj_get(dependency, "count")), 5); ASSERT_EQ(yyjson_arr_size(yyjson_obj_get(dependency, "witnesses")), 1); ASSERT_EQ(yyjson_get_int(yyjson_obj_get(dependency, "omitted_witnesses")), 4); yyjson_val *overview = projection_field(doc, "overview"); ASSERT_TRUE(yyjson_get_bool(yyjson_obj_get(overview, "complete"))); yyjson_val *all = yyjson_obj_get(overview, "components"), *groups = yyjson_obj_get(overview, "groups"); ASSERT_EQ(yyjson_arr_size(all), 2); ASSERT_EQ(yyjson_arr_size(groups), 1); for (size_t i = 0; i < yyjson_arr_size(all); i++) { yyjson_val *part = yyjson_arr_get(all, i); ASSERT_EQ(yyjson_get_int(yyjson_obj_get(part, "member_count")), 6); ASSERT_EQ(yyjson_arr_size(yyjson_obj_get(part, "representatives")), 1); ASSERT_EQ(yyjson_get_int(yyjson_obj_get(part, "omitted_members")), 5); } yyjson_val *group = yyjson_arr_get(groups, 0); ASSERT_EQ(yyjson_arr_size(yyjson_obj_get(group, "component_ids")), 2); ASSERT_EQ(yyjson_get_int(yyjson_obj_get(group, "member_count")), 12); ASSERT_EQ(yyjson_arr_size(yyjson_obj_get(group, "representatives")), 1); ASSERT_EQ(yyjson_get_int(yyjson_obj_get(group, "omitted_representatives")), 1); yyjson_val *connection = yyjson_arr_get(yyjson_obj_get(overview, "connections"), 0); ASSERT_EQ(yyjson_get_int(yyjson_obj_get(connection, "count")), 5); ASSERT_EQ(yyjson_arr_size(yyjson_obj_get(connection, "witnesses")), 1); ASSERT_EQ(yyjson_get_int(yyjson_obj_get(connection, "omitted_witnesses")), 4); yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); cbm_store_close(store); PASS(); } static int64_t projection_node_properties(cbm_store_t *store, const char *qn, const char *file, const char *properties) { cbm_node_t node = {.project = "projection", .label = "Function", .name = qn, .qualified_name = qn, .file_path = file, .start_line = 10, .end_line = 20, .properties_json = properties}; return cbm_store_upsert_node(store, &node); } static int projection_count_metadata_queries(unsigned kind, void *context, void *statement, void *detail) { (void)detail; if (kind == SQLITE_TRACE_STMT) { const char *sql = sqlite3_sql(statement); if (sql && strstr(sql, "FROM nodes WHERE id=?1")) (*(int *)context)++; } return 0; } /* Validate every duplicated occurrence, not just the first path or witness. */ static int projection_check_contracts(yyjson_val *value, int64_t symbol_id, int64_t edge_id, int *symbols, int *edges) { if (yyjson_is_obj(value)) { if (yyjson_obj_get(value, "qualified_name") && yyjson_get_sint(yyjson_obj_get(value, "id")) == symbol_id) { (*symbols)++; ASSERT_STR_EQ(yyjson_get_str(yyjson_obj_get(value, "signature")), "save(value: Value) -> Result"); ASSERT_STR_EQ(yyjson_get_str(yyjson_obj_get(value, "return_type")), "Result"); yyjson_val *parameters = yyjson_obj_get(value, "parameters"); ASSERT_NOT_NULL(parameters); ASSERT_EQ(yyjson_get_sint(yyjson_obj_get(parameters, "count")), 1); ASSERT_STR_EQ(yyjson_get_str(yyjson_arr_get(yyjson_obj_get(parameters, "names"), 0)), "value"); ASSERT_STR_EQ(yyjson_get_str(yyjson_arr_get(yyjson_obj_get(parameters, "types"), 0)), "Value"); } if ((yyjson_obj_get(value, "source_id") && yyjson_get_sint(yyjson_obj_get(value, "id")) == edge_id) || yyjson_get_sint(yyjson_obj_get(value, "edge_id")) == edge_id) { (*edges)++; yyjson_val *arguments = yyjson_obj_get(value, "arguments"); ASSERT_EQ(yyjson_arr_size(arguments), 1); yyjson_val *argument = yyjson_arr_get(arguments, 0); ASSERT_EQ(yyjson_get_sint(yyjson_obj_get(argument, "i")), 7); ASSERT_STR_EQ(yyjson_get_str(yyjson_obj_get(argument, "e")), "input[\"key\"]"); ASSERT_STR_EQ(yyjson_get_str(yyjson_obj_get(argument, "v")), "line\nvalue"); ASSERT_NULL(yyjson_obj_get(argument, "keyword")); ASSERT_EQ(yyjson_get_sint(yyjson_obj_get(value, "argument_limit")), 8); ASSERT_TRUE(yyjson_is_bool(yyjson_obj_get(value, "arguments_complete"))); ASSERT_FALSE(yyjson_get_bool(yyjson_obj_get(value, "arguments_complete"))); ASSERT_EQ(yyjson_get_sint(yyjson_obj_get(yyjson_obj_get(value, "callsite"), "line")), 42); } size_t index, maximum; yyjson_val *key, *child; yyjson_obj_foreach(value, index, maximum, key, child) if (projection_check_contracts(child, symbol_id, edge_id, symbols, edges)) return 1; } else if (yyjson_is_arr(value)) { size_t index, maximum; yyjson_val *child; yyjson_arr_foreach(value, index, maximum, child) if (projection_check_contracts(child, symbol_id, edge_id, symbols, edges)) return 1; } return 0; } TEST(projection_behavior_evidence_is_opt_in_and_shared_across_surfaces) { cbm_store_t *store = projection_store(); ASSERT_NOT_NULL(store); projection_node(store, "Package", "projection.a", "", false); projection_node(store, "Package", "projection.b", "", false); int64_t a = projection_node(store, "Function", "projection.a.start", "src/api/start.c", true); int64_t b = projection_node_properties( store, "projection.b.save", "src/store/save.c", "{\"signature\":\"save(value: Value) -> Result\",\"return_type\":\"Result\"," "\"param_names\":[\"value\"],\"param_types\":[\"Value\"],\"param_count\":1}"); cbm_edge_t evidence = {.project = "projection", .source_id = a, .target_id = b, .type = "CALLS", .properties_json = "{\"line\":42,\"args\":[{\"i\":7,\"e\":\"input[\\\"key\\\"]\",\"v\":" "\"line\\nvalue\",\"keyword\":\"value\"}]}"}; int64_t call = cbm_store_insert_edge(store, &evidence); ASSERT_TRUE(call > 0); int queries = 0; sqlite3_trace_v2(cbm_store_get_db(store), SQLITE_TRACE_STMT, projection_count_metadata_queries, &queries); cbm_architecture_projection_options_t opts = {.entry_node_id = a, .target_node_id = b}; char *json = NULL; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", &opts, &json), CBM_STORE_OK); ASSERT_NULL(strstr(json, "\"signature\"")); ASSERT_NULL(strstr(json, "\"return_type\"")); ASSERT_NULL(strstr(json, "\"parameters\"")); ASSERT_NULL(strstr(json, "\"arguments\"")); ASSERT_NULL(strstr(json, "\"argument_limit\"")); ASSERT_EQ(queries, 0); cbm_free(CBM_MEM_CLASS_OTHER, json); opts.include_behavior_evidence = true; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", &opts, &json), CBM_STORE_OK); yyjson_doc *doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); int symbols = 0, edges = 0; ASSERT_EQ(projection_check_contracts(yyjson_doc_get_root(doc), b, call, &symbols, &edges), 0); ASSERT_TRUE(symbols >= 3); ASSERT_TRUE(edges >= 4); ASSERT_TRUE(queries > 0); ASSERT_TRUE(queries <= 4); /* cache once per displayed indexed node */ yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); queries = 0; opts.include_behavior_evidence = false; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", &opts, &json), CBM_STORE_OK); ASSERT_NULL(strstr(json, "\"return_type\"")); ASSERT_NULL(strstr(json, "\"arguments\"")); ASSERT_EQ(queries, 0); cbm_free(CBM_MEM_CLASS_OTHER, json); sqlite3_trace_v2(cbm_store_get_db(store), 0, NULL, NULL); cbm_store_close(store); PASS(); } TEST(projection_argument_validation_keeps_exact_edge_and_callsite_identity) { cbm_store_t *store = projection_store(); ASSERT_NOT_NULL(store); int64_t a = projection_node(store, "Function", "projection.a", "a.c", true); int64_t b = projection_node(store, "Function", "projection.b", "b.c", false); int64_t target = projection_node(store, "Function", "projection.target", "target.c", false); projection_edge(store, a, b, "CALLS"); cbm_edge_t evidence = { .project = "projection", .source_id = a, .target_id = target, .type = "CALLS", .properties_json = "{\"line\":42,\"args\":[{\"i\":0,\"e\":\"first\",\"v\":\"exact\"}," "{\"i\":-1,\"e\":\"negative\"},{\"i\":2},{\"i\":3,\"e\":42}," "{\"i\":4,\"e\":\"keep\",\"v\":false},{\"i\":5,\"e\":\"actual\"}," "{\"i\":6.5,\"e\":\"float\"},{\"i\":7,\"e\":\"last\"},{\"i\":8,\"e\":\"ninth\"}]}"}; int64_t first = cbm_store_insert_edge(store, &evidence); ASSERT_TRUE(first > 0); evidence.source_id = b; evidence.properties_json = "{\"line\":84,\"args\":[{\"i\":0,\"e\":\"different\"}]}"; int64_t second = cbm_store_insert_edge(store, &evidence); ASSERT_TRUE(second > 0); cbm_architecture_projection_options_t opts = { .entry_node_id = a, .target_node_id = target, .include_behavior_evidence = true}; char *json = NULL; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", &opts, &json), CBM_STORE_OK); yyjson_doc *doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); yyjson_val *edges = yyjson_obj_get(projection_field(doc, "behavior"), "edges"); int seen = 0; for (size_t i = 0; i < yyjson_arr_size(edges); i++) { yyjson_val *value = yyjson_arr_get(edges, i), *args = yyjson_obj_get(value, "arguments"); int64_t id = yyjson_get_sint(yyjson_obj_get(value, "id")); if (id == first) { seen++; ASSERT_EQ(yyjson_arr_size(args), 4); int indexes[] = {0, 4, 5, 7}; for (size_t at = 0; at < 4; at++) ASSERT_EQ(yyjson_get_sint(yyjson_obj_get(yyjson_arr_get(args, at), "i")), indexes[at]); ASSERT_NULL(yyjson_obj_get(yyjson_arr_get(args, 1), "v")); ASSERT_STR_EQ(yyjson_get_str(yyjson_obj_get(yyjson_arr_get(args, 3), "e")), "last"); ASSERT_EQ(yyjson_get_sint(yyjson_obj_get(yyjson_obj_get(value, "callsite"), "line")), 42); } else if (id == second) { seen++; ASSERT_EQ(yyjson_arr_size(args), 1); ASSERT_STR_EQ(yyjson_get_str(yyjson_obj_get(yyjson_arr_get(args, 0), "e")), "different"); ASSERT_EQ(yyjson_get_sint(yyjson_obj_get(yyjson_obj_get(value, "callsite"), "line")), 84); ASSERT_STR_EQ( yyjson_get_str(yyjson_obj_get(yyjson_obj_get(value, "callsite"), "file_path")), "b.c"); } } ASSERT_EQ(seen, 2); ASSERT_NULL(strstr(json, "ninth")); yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); cbm_store_close(store); PASS(); } TEST(projection_missing_corrupt_and_noncall_arguments_remain_unknown) { const char *properties[] = {"{}", "{\"args\":\"corrupt\"}", "{\"args\":null}", "{\"args\":{}}", "{\"args\":[]}"}; for (size_t i = 0; i < sizeof(properties) / sizeof(properties[0]); i++) { cbm_store_t *store = projection_store(); ASSERT_NOT_NULL(store); int64_t a = projection_node(store, "Function", "projection.a", "a.c", true); int64_t b = projection_node_properties( store, "projection.b", "b.c", "{\"signature\":42,\"return_type\":\"Known\",\"param_count\":1,\"param_names\":[\"x\"]," "\"param_types\":[false]}"); cbm_edge_t evidence = {.project = "projection", .source_id = a, .target_id = b, .type = "CALLS", .properties_json = properties[i]}; ASSERT_TRUE(cbm_store_insert_edge(store, &evidence) > 0); cbm_architecture_projection_options_t opts = { .entry_node_id = a, .target_node_id = b, .include_behavior_evidence = true}; char *json = NULL; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", &opts, &json), CBM_STORE_OK); yyjson_doc *doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); yyjson_val *edge = yyjson_arr_get(yyjson_obj_get(projection_field(doc, "behavior"), "edges"), 0); if (i == 4) { ASSERT_TRUE(yyjson_is_arr(yyjson_obj_get(edge, "arguments"))); ASSERT_EQ(yyjson_arr_size(yyjson_obj_get(edge, "arguments")), 0); ASSERT_FALSE(yyjson_get_bool(yyjson_obj_get(edge, "arguments_complete"))); } else ASSERT_NULL(yyjson_obj_get(edge, "arguments")); ASSERT_NULL(strstr(json, "\"signature\"")); ASSERT_NULL(strstr(json, "\"parameters\"")); ASSERT_NOT_NULL(strstr(json, "\"return_type\":\"Known\"")); yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); cbm_store_close(store); } cbm_store_t *store = projection_store(); ASSERT_NOT_NULL(store); int64_t a = projection_node(store, "Function", "projection.a", "a.c", true); int64_t b = projection_node(store, "Function", "projection.b", "b.c", false); cbm_edge_t evidence = {.project = "projection", .source_id = a, .target_id = b, .type = "HTTP_CALLS", .properties_json = "{\"args\":[{\"i\":0,\"e\":\"not_a_CALLS_property\"}]}"}; ASSERT_TRUE(cbm_store_insert_edge(store, &evidence) > 0); cbm_architecture_projection_options_t opts = { .entry_node_id = a, .target_node_id = b, .include_behavior_evidence = true}; char *json = NULL; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", &opts, &json), CBM_STORE_OK); ASSERT_NULL(strstr(json, "\"arguments\"")); cbm_free(CBM_MEM_CLASS_OTHER, json); cbm_store_close(store); PASS(); } TEST(projection_oversized_evidence_is_omitted_without_truncating_values) { cbm_store_t *store = projection_store(); ASSERT_NOT_NULL(store); int64_t a = projection_node(store, "Function", "projection.a", "a.c", true); char signature[4098], expression[1026], node_properties[4300], edge_properties[2300]; memset(signature, 's', sizeof(signature) - 1); signature[sizeof(signature) - 1] = '\0'; memset(expression, 'e', sizeof(expression) - 1); expression[sizeof(expression) - 1] = '\0'; snprintf(node_properties, sizeof(node_properties), "{\"signature\":\"%s\",\"return_type\":\"Preserved\"}", signature); int64_t b = projection_node_properties(store, "projection.b", "b.c", node_properties); snprintf(edge_properties, sizeof(edge_properties), "{\"args\":[{\"i\":0,\"e\":\"%s\"},{\"i\":1,\"e\":\"kept\",\"v\":\"%s\"}]}", expression, expression); cbm_edge_t evidence = {.project = "projection", .source_id = a, .target_id = b, .type = "CALLS", .properties_json = edge_properties}; ASSERT_TRUE(cbm_store_insert_edge(store, &evidence) > 0); cbm_architecture_projection_options_t opts = { .entry_node_id = a, .target_node_id = b, .include_behavior_evidence = true}; char *json = NULL; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", &opts, &json), CBM_STORE_OK); yyjson_doc *doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); ASSERT_NULL(strstr(json, "\"signature\"")); ASSERT_NOT_NULL(strstr(json, "\"return_type\":\"Preserved\"")); yyjson_val *edge = yyjson_arr_get(yyjson_obj_get(projection_field(doc, "behavior"), "edges"), 0); yyjson_val *args = yyjson_obj_get(edge, "arguments"); ASSERT_EQ(yyjson_arr_size(args), 1); yyjson_val *arg = yyjson_arr_get(args, 0); ASSERT_EQ(yyjson_get_sint(yyjson_obj_get(arg, "i")), 1); ASSERT_STR_EQ(yyjson_get_str(yyjson_obj_get(arg, "e")), "kept"); ASSERT_NULL(yyjson_obj_get(arg, "v")); ASSERT_FALSE(yyjson_get_bool(yyjson_obj_get(edge, "arguments_complete"))); yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); cbm_store_close(store); PASS(); } TEST(projection_repeated_behavior_evidence_preserves_base_graph_under_budget) { cbm_store_t *store = projection_store(); ASSERT_NOT_NULL(store); enum { components = 80 }; int64_t symbols[components]; char signature[4097], expression[1025], properties[4400], arguments[1200]; memset(signature, 's', sizeof(signature) - 1); signature[sizeof(signature) - 1] = '\0'; memset(expression, 'e', sizeof(expression) - 1); expression[sizeof(expression) - 1] = '\0'; snprintf(properties, sizeof(properties), "{\"signature\":\"%s\",\"return_type\":\"Result\",\"is_entry_point\":true}", signature); snprintf(arguments, sizeof(arguments), "{\"line\":42,\"args\":[{\"i\":0,\"e\":\"%s\"}]}", expression); for (int i = 0; i < components; i++) { char package[80], name[100], file[100]; snprintf(package, sizeof(package), "projection.package%02d", i); snprintf(name, sizeof(name), "%s.function", package); snprintf(file, sizeof(file), "src/package%02d/function.c", i); ASSERT_TRUE(projection_node(store, "Package", package, "", false) > 0); symbols[i] = projection_node_properties(store, name, file, properties); ASSERT_TRUE(symbols[i] > 0); } /* Thousands of witnesses repeat a small indexed declaration set. Its uncapped mutable JSON plus serialized copy exceeded the shared budget. */ for (int from = 0; from < components; from++) for (int to = 0; to < components; to++) { if (from == to) continue; cbm_edge_t edge = {.project = "projection", .source_id = symbols[from], .target_id = symbols[to], .type = "CALLS", .properties_json = arguments}; ASSERT_TRUE(cbm_store_insert_edge(store, &edge) > 0); } cbm_architecture_projection_options_t opts = { .entry_node_id = symbols[0], .target_node_id = symbols[1], .max_depth = 1, .max_paths = 1}; char *baseline_json = NULL, *enriched_json = NULL; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", &opts, &baseline_json), CBM_STORE_OK); yyjson_doc *baseline = yyjson_read(baseline_json, strlen(baseline_json), 0); ASSERT_NOT_NULL(baseline); ASSERT_EQ(yyjson_arr_size(projection_field(baseline, "components")), components); opts.include_behavior_evidence = true; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", &opts, &enriched_json), CBM_STORE_OK); yyjson_doc *enriched = yyjson_read(enriched_json, strlen(enriched_json), 0); ASSERT_NOT_NULL(enriched); const char *collections[] = {"components", "dependencies", "entrypoints", "paths"}; for (size_t i = 0; i < sizeof(collections) / sizeof(collections[0]); i++) ASSERT_EQ(yyjson_arr_size(projection_field(enriched, collections[i])), yyjson_arr_size(projection_field(baseline, collections[i]))); ASSERT_EQ( yyjson_get_sint(yyjson_obj_get(projection_field(enriched, "totals"), "accounted_nodes")), yyjson_get_sint(yyjson_obj_get(projection_field(baseline, "totals"), "accounted_nodes"))); ASSERT_NULL(strstr(enriched_json, "The architecture response exceeded its memory budget")); yyjson_val *path = yyjson_arr_get(projection_field(enriched, "paths"), 0); yyjson_val *source = yyjson_arr_get(yyjson_obj_get(path, "nodes"), 0); ASSERT_STR_EQ(yyjson_get_str(yyjson_obj_get(source, "signature")), signature); yyjson_val *edge = yyjson_arr_get(yyjson_obj_get(path, "edges"), 0); yyjson_val *argument = yyjson_arr_get(yyjson_obj_get(edge, "arguments"), 0); ASSERT_STR_EQ(yyjson_get_str(yyjson_obj_get(argument, "e")), expression); ASSERT_NOT_NULL(strstr(enriched_json, "Optional behavior evidence was omitted")); yyjson_doc_free(baseline); yyjson_doc_free(enriched); cbm_free(CBM_MEM_CLASS_OTHER, baseline_json); cbm_free(CBM_MEM_CLASS_OTHER, enriched_json); cbm_store_close(store); PASS(); } /* Shaped like Django 5.2.7 (12,700 mostly single-symbol components, far below * the node and edge budgets): one full overview entry per component used to * exceed the 32 MB response budget, so the whole projection came back * limited and empty. The overview now lists a bounded, ranked subset and * still accounts for every component through its groups and totals. A group * whose components all rank last, like Django's @no-source, still gets its * own share of entries so that it can be opened. */ TEST(projection_many_small_components_stay_within_response_budget) { cbm_store_t *store = projection_store(); ASSERT_NOT_NULL(store); enum { bulk = 14000, late = 20, parts = bulk + late, entries = 4096, share = 16 }; char padding[161]; memset(padding, 'x', sizeof(padding) - 1); padding[sizeof(padding) - 1] = '\0'; ASSERT_EQ(cbm_store_begin(store), CBM_STORE_OK); for (int i = 0; i < bulk; i++) { char qn[256], file[256]; snprintf(qn, sizeof(qn), "projection.area%d.%s%05d.Part", i % 8, padding, i); snprintf(file, sizeof(file), "src/area%d/%.*s%05d.py", i % 8, 120, padding, i); ASSERT_TRUE(projection_node(store, "Class", qn, file, false) > 0); } /* Equal rank otherwise, so the later ids of this group rank last. */ for (int i = 0; i < late; i++) { char qn[64], file[64]; snprintf(qn, sizeof(qn), "projection.late.Part%02d", i); snprintf(file, sizeof(file), "late/part%02d.py", i); ASSERT_TRUE(projection_node(store, "Class", qn, file, false) > 0); } ASSERT_EQ(cbm_store_commit(store), CBM_STORE_OK); char *json = NULL; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", NULL, &json), CBM_STORE_OK); ASSERT_NULL(strstr(json, "exceeded its memory budget")); /* architecture_jobs.c rejects results above 8 MiB. */ ASSERT_TRUE(strlen(json) < 8 * 1024 * 1024); yyjson_doc *doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); ASSERT_STR_EQ(yyjson_get_str(projection_field(doc, "status")), "ready"); ASSERT_EQ(yyjson_get_int(yyjson_obj_get(projection_field(doc, "totals"), "components")), parts); yyjson_val *displayed = projection_field(doc, "components"); ASSERT_EQ(yyjson_arr_size(displayed), 256); yyjson_val *overview = projection_field(doc, "overview"), *listed = yyjson_obj_get(overview, "components"), *groups = yyjson_obj_get(overview, "groups"); size_t listed_count = yyjson_arr_size(listed); ASSERT_EQ(listed_count, entries); ASSERT_FALSE(yyjson_get_bool(yyjson_obj_get(overview, "complete"))); ASSERT_EQ(yyjson_get_int( yyjson_obj_get(yyjson_obj_get(overview, "limits"), "omitted_component_entries")), parts - entries); ASSERT_EQ(yyjson_get_int(yyjson_obj_get(yyjson_obj_get(overview, "totals"), "components")), parts); /* Every displayed component keeps its overview entry. */ for (size_t i = 0; i < yyjson_arr_size(displayed); i++) { const char *id = yyjson_get_str(yyjson_obj_get(yyjson_arr_get(displayed, i), "id")); bool found = false; for (size_t j = 0; j < listed_count && !found; j++) found = !strcmp(id, yyjson_get_str(yyjson_obj_get(yyjson_arr_get(listed, j), "id"))); ASSERT_TRUE(found); } size_t membership = 0; int members = 0; for (size_t i = 0; i < yyjson_arr_size(groups); i++) { yyjson_val *group = yyjson_arr_get(groups, i); yyjson_val *ids = yyjson_obj_get(group, "component_ids"); membership += yyjson_arr_size(ids); members += yyjson_get_int(yyjson_obj_get(group, "member_count")); /* Equal ranks fall back to component order, so each group must list * a prefix of its ids, and at least its share of them. */ const char *group_id = yyjson_get_str(yyjson_obj_get(group, "id")); size_t count = yyjson_arr_size(ids), shown = 0, at, maximum; yyjson_val *entry; yyjson_arr_foreach(listed, at, maximum, entry) { if (strcmp(group_id, yyjson_get_str(yyjson_obj_get(entry, "group_id")))) continue; ASSERT_TRUE(shown < count); ASSERT_STR_EQ(yyjson_get_str(yyjson_obj_get(entry, "id")), yyjson_get_str(yyjson_arr_get(ids, shown))); shown++; } ASSERT_TRUE(shown >= (count < share ? count : (size_t)share)); if (!strcmp(yyjson_get_str(yyjson_obj_get(group, "label")), "late")) ASSERT_EQ(shown, share); } ASSERT_EQ(membership, parts); ASSERT_EQ(members, parts); yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); cbm_store_close(store); PASS(); } static yyjson_val *projection_component_of(yyjson_doc *doc, const char *qn) { yyjson_val *parts = projection_field(doc, "components"); for (size_t i = 0; i < yyjson_arr_size(parts); i++) { yyjson_val *part = yyjson_arr_get(parts, i); yyjson_val *rep = yyjson_arr_get(yyjson_obj_get(part, "representatives"), 0); if (!strcmp(yyjson_get_str(yyjson_obj_get(rep, "qualified_name")), qn)) return part; } return NULL; } TEST(projection_python_test_files_follow_name_conventions) { cbm_store_t *store = projection_store(); ASSERT_NOT_NULL(store); projection_node(store, "Class", "projection.app.tests.ViewTests", "app/tests.py", false); projection_node(store, "Function", "projection.app.models_test.check", "app/models_test.py", false); projection_node(store, "Function", "projection.app.conftest.fixture", "app/conftest.py", false); projection_node(store, "Function", "projection.check", "test/check.c", false); /* A top-level test/ is a test suite, also for helpers without a test name. */ projection_node(store, "Function", "projection.test.helpers.build", "test/helpers.py", false); projection_node(store, "Function", "projection.app.views.index", "app/views.py", false); /* django/test/ ships Django's testing API. The extractor flags its * functions from the directory alone; the class carries no flag. */ int64_t client = projection_node(store, "Class", "projection.django.test.client.Client", "django/test/client.py", false); cbm_node_t method = {.project = "projection", .label = "Method", .name = "get", .qualified_name = "projection.django.test.client.Client.get", .file_path = "django/test/client.py", .properties_json = "{\"is_test\":true}"}; int64_t get = cbm_store_upsert_node(store, &method); projection_edge(store, client, get, "DEFINES_METHOD"); char *json = NULL; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", NULL, &json), CBM_STORE_OK); yyjson_doc *doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); const char *tests[] = {"projection.app.tests.ViewTests", "projection.app.models_test.check", "projection.app.conftest.fixture", "projection.check", "projection.test.helpers.build"}; for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { yyjson_val *part = projection_component_of(doc, tests[i]); ASSERT_NOT_NULL(part); ASSERT_STR_EQ(yyjson_get_str(yyjson_obj_get(part, "role")), "test"); ASSERT_STR_EQ(yyjson_get_str(yyjson_obj_get(part, "role_basis")), "test_path_convention"); } yyjson_val *views = projection_component_of(doc, "projection.app.views.index"); ASSERT_NOT_NULL(views); ASSERT_STR_EQ(yyjson_get_str(yyjson_obj_get(views, "role")), "non_test"); /* One role per file: the class and its flagged method stay one part. */ yyjson_val *api = projection_component_of(doc, "projection.django.test.client.Client"); ASSERT_NOT_NULL(api); ASSERT_STR_EQ(yyjson_get_str(yyjson_obj_get(api, "role")), "non_test"); ASSERT_EQ(yyjson_get_int(yyjson_obj_get(api, "member_count")), 2); ASSERT_EQ(yyjson_arr_size(projection_field(doc, "components")), 7); yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); cbm_store_close(store); PASS(); } /* Django views are never named main; their HANDLES edge is the entry evidence. * Test-client calls like self.client.get("/probe/") are indexed as route * registrations, so a route that only test code registers proves nothing. */ TEST(projection_route_handlers_are_entry_points) { cbm_store_t *store = projection_store(); ASSERT_NOT_NULL(store); int64_t route = projection_node(store, "Route", "projection.__route__GET__/orders", "", false); int64_t view = projection_node(store, "Function", "projection.shop.views.orders", "shop/views.py", false); int64_t method = projection_node(store, "Method", "projection.shop.api.Orders.get", "shop/api.py", false); int64_t fixture = projection_node(store, "Function", "projection.tests.urls.orders", "tests/urls.py", false); int64_t query = projection_node(store, "Function", "projection.shop.query.orders", "shop/query.py", false); projection_node(store, "Function", "projection.shop.views.unrouted", "shop/views.py", false); int64_t cart_route = projection_node(store, "Route", "projection.__route__GET__/cart", "", false); int64_t urls = projection_node(store, "Function", "projection.shop.urls.register", "shop/urls.py", false); int64_t cart = projection_node(store, "Function", "projection.shop.views.cart", "shop/views.py", false); int64_t probe_route = projection_node(store, "Route", "projection.__route__GET__/probe", "", false); int64_t probe = projection_node(store, "Method", "projection.tests.test_views.Tests.test_probe", "tests/test_views.py", false); int64_t text = projection_node(store, "Method", "projection.shop.models.Order.text", "shop/models.py", false); /* Created last, so only the ranking can put the indexed entry first. */ int64_t launcher = projection_node(store, "Function", "projection.manage.main", "manage.py", true); int64_t setup = projection_node(store, "Function", "projection.manage.setup", "manage.py", false); projection_edge(store, view, route, "HANDLES"); projection_edge(store, method, route, "HANDLES"); projection_edge(store, fixture, route, "HANDLES"); projection_edge(store, view, query, "CALLS"); projection_edge(store, urls, cart_route, "CALLS"); projection_edge(store, cart, cart_route, "HANDLES"); projection_edge(store, probe, probe_route, "CALLS"); projection_edge(store, text, probe_route, "HANDLES"); projection_edge(store, launcher, setup, "CALLS"); char *json = NULL; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", NULL, &json), CBM_STORE_OK); yyjson_doc *doc = yyjson_read(json, strlen(json), 0); ASSERT_NOT_NULL(doc); yyjson_val *entries = projection_field(doc, "entrypoints"); const int64_t expected[] = {launcher, view, method, cart}; ASSERT_EQ(yyjson_arr_size(entries), sizeof(expected) / sizeof(expected[0])); for (size_t i = 0; i < sizeof(expected) / sizeof(expected[0]); i++) ASSERT_EQ(yyjson_get_sint(yyjson_obj_get(yyjson_arr_get(entries, i), "id")), expected[i]); ASSERT_EQ(yyjson_get_int(yyjson_obj_get(projection_field(doc, "totals"), "entrypoints")), 4); yyjson_val *paths = projection_field(doc, "paths"); ASSERT_EQ(yyjson_arr_size(paths), 2); yyjson_val *first = yyjson_arr_get(paths, 0), *second = yyjson_arr_get(paths, 1); ASSERT_EQ(yyjson_get_sint(yyjson_obj_get(first, "entrypoint_id")), launcher); ASSERT_EQ(yyjson_get_sint(yyjson_obj_get(second, "entrypoint_id")), view); ASSERT_EQ( yyjson_get_sint(yyjson_obj_get(yyjson_arr_get(yyjson_obj_get(second, "nodes"), 1), "id")), query); yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); cbm_store_close(store); PASS(); } TEST(projection_response_memory_returns_to_caller_baseline) { cbm_store_t *store = projection_store(); ASSERT_NOT_NULL(store); projection_node(store, "File", "projection.a.c", "a.c", false); int64_t entry = projection_node(store, "Function", "projection.start", "a.c", true); int64_t target = projection_node(store, "Function", "projection.finish", "b.c", false); projection_edge(store, entry, target, "CALLS"); cbm_architecture_projection_options_t options = {.entry_node_id = entry, .include_behavior_evidence = true}; size_t blocks = cbm_mem_class_live_blocks(CBM_MEM_CLASS_OTHER); size_t bytes = cbm_mem_class_live_bytes(CBM_MEM_CLASS_OTHER); for (int limited = 0; limited < 2; limited++) { options.max_nodes = limited ? 1 : 0; char *json = NULL; ASSERT_EQ(cbm_store_architecture_projection(store, "projection", &options, &json), CBM_STORE_OK); ASSERT_NOT_NULL(json); ASSERT_EQ(cbm_mem_class_live_blocks(CBM_MEM_CLASS_OTHER), blocks + 1); ASSERT_EQ(cbm_mem_class_live_bytes(CBM_MEM_CLASS_OTHER), bytes + cbm_mem_usable_size(json)); cbm_free(CBM_MEM_CLASS_OTHER, json); ASSERT_EQ(cbm_mem_class_live_blocks(CBM_MEM_CLASS_OTHER), blocks); ASSERT_EQ(cbm_mem_class_live_bytes(CBM_MEM_CLASS_OTHER), bytes); } cbm_store_close(store); PASS(); } TEST(projection_completed_job_memory_returns_to_caller_baseline) { char temp[256] = "/tmp/cbm_projection_memory_XXXXXX"; ASSERT_NOT_NULL(cbm_mkdtemp(temp)); char path[1024]; snprintf(path, sizeof(path), "%s/index.db", temp); cbm_store_t *store = cbm_store_open_path(path); ASSERT_NOT_NULL(store); ASSERT_EQ(cbm_store_upsert_project(store, "projection", temp), CBM_STORE_OK); projection_node(store, "Function", "projection.entry", "entry.c", true); /* A live caller allocation makes a lost worker delta observable even when * the shared counters would otherwise saturate at zero on cache release. */ void *sentinel = cbm_alloc(CBM_MEM_CLASS_OTHER, 64); ASSERT_NOT_NULL(sentinel); size_t before_blocks = cbm_mem_class_live_blocks(CBM_MEM_CLASS_OTHER); size_t before_bytes = cbm_mem_class_live_bytes(CBM_MEM_CLASS_OTHER); cbm_architecture_jobs_t *jobs = cbm_architecture_jobs_new(); ASSERT_NOT_NULL(jobs); bool ready = false, cached = false; uint64_t deadline = cbm_now_ms() + 10000; do { char *json = cbm_architecture_jobs_request(jobs, store, "projection", 0); yyjson_doc *doc = json ? yyjson_read(json, strlen(json), 0) : NULL; const char *status = doc ? yyjson_get_str(projection_field(doc, "status")) : NULL; ready = status && strcmp(status, "ready") == 0; bool pending = status && strcmp(status, "pending") == 0; yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); if (!pending) break; cbm_usleep(1000); } while (cbm_now_ms() < deadline); if (ready) { char *json = cbm_architecture_jobs_request(jobs, store, "projection", 0); yyjson_doc *doc = json ? yyjson_read(json, strlen(json), 0) : NULL; const char *status = doc ? yyjson_get_str(projection_field(doc, "status")) : NULL; cached = status && strcmp(status, "ready") == 0; yyjson_doc_free(doc); cbm_free(CBM_MEM_CLASS_OTHER, json); } cbm_architecture_jobs_free(jobs); /* joins before the caller frees retained output */ size_t after_blocks = cbm_mem_class_live_blocks(CBM_MEM_CLASS_OTHER); size_t after_bytes = cbm_mem_class_live_bytes(CBM_MEM_CLASS_OTHER); cbm_free(CBM_MEM_CLASS_OTHER, sentinel); cbm_store_close(store); th_rmtree(temp); ASSERT_TRUE(ready); ASSERT_TRUE(cached); ASSERT_EQ(after_blocks, before_blocks); ASSERT_EQ(after_bytes, before_bytes); PASS(); } SUITE(architecture_projection) { RUN_TEST(projection_completed_job_memory_returns_to_caller_baseline); RUN_TEST(projection_response_memory_returns_to_caller_baseline); RUN_TEST(projection_accounts_for_isolated_nodes_and_files); RUN_TEST(projection_preserves_typed_edges_and_contiguous_paths); RUN_TEST(projection_reports_dependency_cycles_without_fabricated_execution); RUN_TEST(projection_graph_limits_abstain_instead_of_sampling); RUN_TEST(projection_cancellation_deadline_and_store_reuse); RUN_TEST(projection_display_caps_preserve_total_accounting); RUN_TEST(projection_unknown_project_and_empty_project); RUN_TEST(projection_paths_depth_cap_is_explicit); RUN_TEST(projection_channel_relationships_are_typed_not_fake_calls); RUN_TEST(projection_test_callers_cannot_absorb_source_components); RUN_TEST(projection_indexed_embedded_test_has_separate_membership); RUN_TEST(projection_shallow_leaves_do_not_hide_deeper_behavior); RUN_TEST(projection_behavior_preserves_first_hop_diversity); RUN_TEST(projection_corridor_preserves_joins_cycles_and_callsite_evidence); RUN_TEST(projection_corridor_caps_abstain_without_hiding_joins); RUN_TEST(projection_overview_accounts_for_components_beyond_legacy_cap); RUN_TEST(projection_aggregate_cycle_does_not_invent_symbol_cycle); RUN_TEST(projection_provenance_budget_counts_utf8_bytes); RUN_TEST(projection_example_budgets_preserve_full_aggregate_counts); RUN_TEST(projection_behavior_evidence_is_opt_in_and_shared_across_surfaces); RUN_TEST(projection_argument_validation_keeps_exact_edge_and_callsite_identity); RUN_TEST(projection_missing_corrupt_and_noncall_arguments_remain_unknown); RUN_TEST(projection_oversized_evidence_is_omitted_without_truncating_values); RUN_TEST(projection_repeated_behavior_evidence_preserves_base_graph_under_budget); RUN_TEST(projection_many_small_components_stay_within_response_budget); RUN_TEST(projection_python_test_files_follow_name_conventions); RUN_TEST(projection_route_handlers_are_entry_points); }