mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-04 10:48:20 +00:00
test(mcp): give the #1115 store-switch coverage its own test
The store-switch regression for #1115 was added inside tool_list_projects_tree_uses_one_stable_header_and_keeps_json_direct and that test was renamed after it, so the name no longer said what its seven list_projects shape assertions check. Restore that test exactly as it is on main and move the switch loop unchanged into tool_project_discovery_agrees_across_store_switches_issue1115 with its own twelve-project fixture. The new test also checks list_projects before the switches, so it covers both the first-touch index_status/query case and the before/after list agreement the issue asked for. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
1 parent
26cd2608f5
commit
2e7244fe4c
1 file changed
+101
-36
+101
-36
@@ -5914,7 +5914,7 @@ TEST(tool_query_graph_prefix_directory_recovers_rows_beyond_raw_estimate) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST(tool_list_projects_stays_complete_across_store_switches_issue1115) {
|
||||
TEST(tool_list_projects_tree_uses_one_stable_header_and_keeps_json_direct) {
|
||||
char cache[256];
|
||||
snprintf(cache, sizeof(cache), "%s/cbm-list-lean-XXXXXX", cbm_tmpdir());
|
||||
ASSERT_NOT_NULL(cbm_mkdtemp(cache));
|
||||
@@ -5971,38 +5971,6 @@ TEST(tool_list_projects_stays_complete_across_store_switches_issue1115) {
|
||||
bool tree_not_duplicated = tree_response && !strstr(tree_response, "structuredContent");
|
||||
bool page_truthful = page && strstr(page, "projects: 5") && strstr(page, "returned: 5") &&
|
||||
strstr(page, "has_more: true") && strstr(page, "next_offset: 5");
|
||||
bool projects_resolve_after_switches = true;
|
||||
for (int i = PROJECTS - 1; i >= 0; i--) {
|
||||
char project[32];
|
||||
char args[96];
|
||||
snprintf(project, sizeof(project), "lean-project-%02d", i);
|
||||
snprintf(args, sizeof(args), "{\"project\":\"%s\"}", project);
|
||||
|
||||
char *status_response = cbm_mcp_handle_tool(srv, "index_status", args);
|
||||
char *status = extract_text_content(status_response);
|
||||
if (!status || !strstr(status, "ready")) {
|
||||
projects_resolve_after_switches = false;
|
||||
}
|
||||
free(status);
|
||||
free(status_response);
|
||||
|
||||
snprintf(args, sizeof(args), "{\"project\":\"%s\",\"name_pattern\":\"entry\"}",
|
||||
project);
|
||||
char *search_response = cbm_mcp_handle_tool(srv, "search_graph", args);
|
||||
char *search = extract_text_content(search_response);
|
||||
if (!search || !strstr(search, "entry")) {
|
||||
projects_resolve_after_switches = false;
|
||||
}
|
||||
free(search);
|
||||
free(search_response);
|
||||
}
|
||||
char *post_switch_response = cbm_mcp_handle_tool(srv, "list_projects", "{\"limit\":50}");
|
||||
char *post_switch = extract_text_content(post_switch_response);
|
||||
bool list_complete_after_switches =
|
||||
post_switch && strstr(post_switch, "total: 12") && strstr(post_switch, "lean-project-00") &&
|
||||
strstr(post_switch, "lean-project-11");
|
||||
free(post_switch);
|
||||
free(post_switch_response);
|
||||
bool json_direct = false;
|
||||
bool json_structured = json_response && strstr(json_response, "structuredContent");
|
||||
yyjson_doc *doc = json ? yyjson_read(json, strlen(json), 0) : NULL;
|
||||
@@ -6039,13 +6007,109 @@ TEST(tool_list_projects_stays_complete_across_store_switches_issue1115) {
|
||||
ASSERT_TRUE(tree_is_leaner);
|
||||
ASSERT_TRUE(tree_not_duplicated);
|
||||
ASSERT_TRUE(page_truthful);
|
||||
ASSERT_TRUE(projects_resolve_after_switches);
|
||||
ASSERT_TRUE(list_complete_after_switches);
|
||||
ASSERT_TRUE(json_direct);
|
||||
ASSERT_TRUE(json_structured);
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* #1115: with more indexed projects than were resident at once, the
|
||||
* least-recently-used one answered "project not found or not indexed" from
|
||||
* index_status, and list_projects and index_status disagreed in the same
|
||||
* session. The server keeps one project store open and switches it whenever a
|
||||
* call names another project, so twelve projects visited one after another
|
||||
* force a switch on every step and each visit is the first touch of a project
|
||||
* that is not resident. Every project must resolve through index_status and a
|
||||
* query, and list_projects must report all twelve before and after the
|
||||
* switches. */
|
||||
TEST(tool_project_discovery_agrees_across_store_switches_issue1115) {
|
||||
char cache[256];
|
||||
snprintf(cache, sizeof(cache), "%s/cbm-switch-1115-XXXXXX", cbm_tmpdir());
|
||||
ASSERT_NOT_NULL(cbm_mkdtemp(cache));
|
||||
const char *saved_cache = getenv("CBM_CACHE_DIR");
|
||||
char *saved_cache_copy = saved_cache ? strdup(saved_cache) : NULL;
|
||||
cbm_setenv("CBM_CACHE_DIR", cache, 1);
|
||||
|
||||
enum { PROJECTS = 12 };
|
||||
for (int i = 0; i < PROJECTS; i++) {
|
||||
char project[32];
|
||||
char db_path[512];
|
||||
char root_path[512];
|
||||
snprintf(project, sizeof(project), "lean-project-%02d", i);
|
||||
snprintf(db_path, sizeof(db_path), "%s/%s.db", cache, project);
|
||||
snprintf(root_path, sizeof(root_path),
|
||||
"/workspaces/organization/shared/services/lean-project-%02d", i);
|
||||
cbm_store_t *store = cbm_store_open_path(db_path);
|
||||
ASSERT_NOT_NULL(store);
|
||||
ASSERT_EQ(cbm_store_upsert_project(store, project, root_path), CBM_STORE_OK);
|
||||
cbm_node_t node = {.project = project,
|
||||
.label = "Function",
|
||||
.name = "entry",
|
||||
.qualified_name = "shared.module.entry",
|
||||
.file_path = "src/main.c",
|
||||
.start_line = 1,
|
||||
.end_line = 1};
|
||||
ASSERT_GT(cbm_store_upsert_node(store, &node), 0);
|
||||
cbm_store_close(store);
|
||||
}
|
||||
|
||||
cbm_mcp_server_t *srv = cbm_mcp_server_new(NULL);
|
||||
ASSERT_NOT_NULL(srv);
|
||||
char *pre_switch_response = cbm_mcp_handle_tool(srv, "list_projects", "{\"limit\":50}");
|
||||
char *pre_switch = extract_text_content(pre_switch_response);
|
||||
bool list_complete_before_switches = pre_switch && strstr(pre_switch, "total: 12") &&
|
||||
strstr(pre_switch, "lean-project-00") &&
|
||||
strstr(pre_switch, "lean-project-11");
|
||||
free(pre_switch);
|
||||
free(pre_switch_response);
|
||||
|
||||
bool projects_resolve_after_switches = true;
|
||||
for (int i = PROJECTS - 1; i >= 0; i--) {
|
||||
char project[32];
|
||||
char args[96];
|
||||
snprintf(project, sizeof(project), "lean-project-%02d", i);
|
||||
snprintf(args, sizeof(args), "{\"project\":\"%s\"}", project);
|
||||
|
||||
char *status_response = cbm_mcp_handle_tool(srv, "index_status", args);
|
||||
char *status = extract_text_content(status_response);
|
||||
if (!status || !strstr(status, "ready")) {
|
||||
projects_resolve_after_switches = false;
|
||||
}
|
||||
free(status);
|
||||
free(status_response);
|
||||
|
||||
snprintf(args, sizeof(args), "{\"project\":\"%s\",\"name_pattern\":\"entry\"}", project);
|
||||
char *search_response = cbm_mcp_handle_tool(srv, "search_graph", args);
|
||||
char *search = extract_text_content(search_response);
|
||||
if (!search || !strstr(search, "entry")) {
|
||||
projects_resolve_after_switches = false;
|
||||
}
|
||||
free(search);
|
||||
free(search_response);
|
||||
}
|
||||
char *post_switch_response = cbm_mcp_handle_tool(srv, "list_projects", "{\"limit\":50}");
|
||||
char *post_switch = extract_text_content(post_switch_response);
|
||||
bool list_complete_after_switches = post_switch && strstr(post_switch, "total: 12") &&
|
||||
strstr(post_switch, "lean-project-00") &&
|
||||
strstr(post_switch, "lean-project-11");
|
||||
free(post_switch);
|
||||
free(post_switch_response);
|
||||
|
||||
cbm_mcp_server_free(srv);
|
||||
restore_cache_dir(saved_cache_copy);
|
||||
free(saved_cache_copy);
|
||||
for (int i = 0; i < PROJECTS; i++) {
|
||||
char project[32];
|
||||
snprintf(project, sizeof(project), "lean-project-%02d", i);
|
||||
cleanup_project_db(cache, project);
|
||||
}
|
||||
cbm_rmdir(cache);
|
||||
|
||||
ASSERT_TRUE(list_complete_before_switches);
|
||||
ASSERT_TRUE(projects_resolve_after_switches);
|
||||
ASSERT_TRUE(list_complete_after_switches);
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* The stored project root is dynamic identity, not a display-sized field.
|
||||
* Keep a syntactically valid segmented path beyond 1 KiB intact in both wire
|
||||
* encodings; the default detail also routes that same value through branch
|
||||
@@ -25105,7 +25169,8 @@ SUITE(mcp) {
|
||||
RUN_TEST(tool_query_graph_budget_bounds_first_row_and_json_escaping);
|
||||
RUN_TEST(tool_query_graph_prefix_directory_is_lossless_and_json_stays_direct);
|
||||
RUN_TEST(tool_query_graph_prefix_directory_recovers_rows_beyond_raw_estimate);
|
||||
RUN_TEST(tool_list_projects_stays_complete_across_store_switches_issue1115);
|
||||
RUN_TEST(tool_list_projects_tree_uses_one_stable_header_and_keeps_json_direct);
|
||||
RUN_TEST(tool_project_discovery_agrees_across_store_switches_issue1115);
|
||||
RUN_TEST(tool_list_projects_preserves_root_beyond_one_kib);
|
||||
RUN_TEST(tool_index_status_no_project);
|
||||
RUN_TEST(tool_ingest_traces_stores_observed_calls);
|
||||
|
||||
Reference in new issue
Block a user