chore: import upstream snapshot with attribution
OpenSSF Scorecard / scorecard (push) Failing after 0s
DCO / dco (push) Failing after 0s
CodeQL SAST / analyze (push) Failing after 1s
Deploy Pages / deploy (push) Failing after 1s

This commit is contained in:
wehub-resource-sync
2026-07-13 12:28:05 +08:00
commit 41cb1c0170
1830 changed files with 38276124 additions and 0 deletions
+173
View File
@@ -0,0 +1,173 @@
/*
* repro_issue480.c — Reproduce-first case for OPEN bug #480.
*
* Issue: #480 — "trace_path returns empty for all functions despite
* traversable CALLS edges (v0.8.1, macOS arm64)"
*
* Root cause (identified by maintainer DeusData + reporter halindrome):
* handle_trace_call_path() calls cbm_store_find_nodes_by_name() to locate
* the start node for BFS. On the affected build, the name-to-node lookup
* returns node_count == 0 for EVERY function name — even names that the
* graph clearly contains (confirmed by query_graph Cypher returning the same
* function with 58 inbound CALLS edges). The fallback to
* cbm_store_find_node_by_qn() also returns nothing, so the handler exits
* with a "function not found" error OR (when the node IS found by name)
* the BFS start-node id does not match any edge endpoint stored in the
* graph, so cbm_store_bfs() returns visited_count == 0 and the "callers"
* / "callees" JSON arrays are serialised empty.
*
* The split: query_graph Cypher (direct SQL) traverses the same edges
* correctly, while trace_path (BFS via start-node id) yields nothing.
* This isolates the bug to trace_path's own start-node lookup or to how
* the resolved node id is passed to cbm_store_bfs(), NOT to edge creation.
*
* Expected (correct) behaviour:
* After indexing a two-function Python file where caller() calls callee(),
* trace_path for "callee" with direction="inbound" must return a non-empty
* "callers" array that contains a node named "caller".
*
* Actual (buggy) behaviour:
* trace_path returns {"function":"callee","direction":"inbound","callers":[]}
* — an empty "callers" array — even though CALLS edges exist in the graph
* and are walkable via query_graph.
*
* Why RED on current code:
* The precondition assertion (CALLS edges > 0) passes because edge creation
* is correct. The subsequent assertion that resp contains the string
* "\"caller\"" (the caller function's name embedded in the callers array)
* FAILS because cbm_store_bfs() finds no hops from the resolved start node.
*
* How this isolates the traversal bug from an extraction bug:
* If CALLS edges were the problem, rh_count_edges(store, …, "CALLS") would
* return 0 and the ASSERT_GT precondition would fire RED — visibly flagging
* an extraction failure instead. By asserting the precondition GREEN and
* the trace_path result RED, we prove the edges exist and the fault lies
* exclusively in trace_path's traversal layer.
*
* Fix location (not implemented here):
* cbm_store_find_nodes_by_name() or cbm_store_bfs() in
* src/store/store.c — the node id returned by name lookup must match
* the source/target ids stored in the edges table.
*/
#include <foundation/compat.h>
#include "test_framework.h"
#include "repro_harness.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
/* ── Fixture ────────────────────────────────────────────────────────────────
*
* Two Python functions in one file:
*
* def callee():
* return 42
*
* def caller():
* return callee()
*
* Python has proven reliable CALLS extraction (test_extraction.c:python_calls
* asserts calls.count > 0 for a simpler fixture; the integration suite's
* main.py fixture yields CALLS edges that are visible via query_graph).
* caller() → callee() is a simple, unambiguous intra-file call: the extractor
* sees exactly one callee() call expression inside caller(), so the graph
* must have ≥ 1 CALLS edge after indexing.
*/
static const RFile k_files[] = {
{
"main.py",
"def callee():\n"
" return 42\n"
"\n"
"def caller():\n"
" return callee()\n"
}
};
/* ─────────────────────────────────────────────────────────────────────────
* repro_issue480_trace_path_nonempty_with_calls
*
* Precondition (must be GREEN to prove this is a traversal bug):
* rh_count_edges(store, project, "CALLS") > 0
*
* The failing assertion (RED on buggy code):
* The "callers" array in the trace_path response is non-empty and contains
* the string "caller" (the name of the caller function).
* ───────────────────────────────────────────────────────────────────────── */
TEST(repro_issue480_trace_path_nonempty_with_calls) {
RProj lp;
cbm_store_t *store = rh_index_files(&lp, k_files,
(int)(sizeof(k_files) / sizeof(k_files[0])));
ASSERT_NOT_NULL(store);
/* ── Precondition: extraction must have produced ≥ 1 CALLS edge ──────
* If this fires RED, the fixture or language has an extraction bug —
* that is a different problem from #480. Switch to a different
* language fixture (e.g. Go utils.go with Multiply→Add) in that case. */
int calls_count = rh_count_edges(store, lp.project, "CALLS");
ASSERT_GT(calls_count, 0);
/* ── Invoke trace_path for "callee" with direction="inbound" ─────────
*
* Args match the trace_path schema (required: function_name, project):
* function_name — bare name "callee"; also tested by the reporter with
* the fully-qualified name, both yield empty on buggy code
* project — lp.project (derived from tmpdir by cbm_project_name_from_path)
* direction — "inbound": ask for callers of callee()
* depth — 2: enough to reach one hop (caller → callee)
*
* Expected response shape (correct):
* {"function":"callee","direction":"inbound","callers":[{"name":"caller",...},...]}
*
* Buggy response shape:
* {"function":"callee","direction":"inbound","callers":[]}
* (or: {"error":"function not found",...} if the name lookup fails entirely)
*/
char args[512];
snprintf(args, sizeof(args),
"{\"function_name\":\"callee\","
"\"project\":\"%s\","
"\"direction\":\"inbound\","
"\"depth\":2}",
lp.project);
char *resp = cbm_mcp_handle_tool(lp.srv, "trace_path", args);
ASSERT_NOT_NULL(resp);
/* The response must NOT be a "function not found" error.
* If the name lookup itself fails, this fires first and pinpoints the
* start-node lookup as the breakage site. */
ASSERT_NULL(strstr(resp, "function not found"));
/* The response is the MCP tool-result envelope
* {"content":[{"type":"text","text":"<inner trace_path json>"}]}
* so the inner json is embedded as a STRING value and its quotes are
* backslash-escaped: the "callers" key appears as \"callers\" in the
* serialized response. Match the escaped form — the project's own
* passing trace_path tests (test_incremental.c, via resp_has_key) do the
* same. (The earlier unescaped strstr could never match a correctly
* escaped MCP envelope, which is why this repro was mis-targeted.)
*
* The "callers" key must appear (always emitted for inbound). */
ASSERT_NOT_NULL(strstr(resp, "\\\"callers\\\""));
/* The "callers" array must be NON-EMPTY. WHY RED on the #480 bug:
* cbm_store_bfs() returning 0 hops serialises \"callers\":[] (no caller
* QN in the response), so BOTH the empty-array guard and the caller-QN
* assertion fire RED. We assert the caller's qualified-name tail
* "main.caller" (unambiguous vs the callee "main.callee", and immune to
* escaping) so a populated, correctly-named caller hop is required. */
ASSERT_NULL(strstr(resp, "\\\"callers\\\":[]")); /* empty array = traversal bug */
ASSERT_NOT_NULL(strstr(resp, "main.caller")); /* caller QN in results */
free(resp);
rh_cleanup(&lp, store);
PASS();
}
/* ── Suite ─────────────────────────────────────────────────────────────── */
SUITE(repro_issue480) {
RUN_TEST(repro_issue480_trace_path_nonempty_with_calls);
}