Skip to content

Commit 96ebaf4

Browse files
radimclaude
andcommitted
test: cover reload_schema history-vs-json precedence
Adds two regression tests for mcp::DryRunServer::reload_schema: prefers history.db over schema.json (so reloads don't clobber planner/activity stats already cached), and falls back to schema.json when history.db has no row for the configured key. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 71bd0e5 commit 96ebaf4

1 file changed

Lines changed: 93 additions & 0 deletions

File tree

crates/dry_run_cli/src/mcp/server_tests.rs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,3 +420,96 @@ async fn rebuild_after_refresh_without_history_uses_inline_only() {
420420
assert_eq!(bundle.activity_by_node.len(), 1);
421421
assert!(bundle.activity_by_node.contains_key("primary"));
422422
}
423+
424+
// Regression: reload_schema must prefer history.db over schema.json so
425+
// planner/activity stats survive a reload. Before this fix, reload_schema
426+
// only read schema.json and wrapped it stats-less via wrap_schema_only,
427+
// clobbering history-derived stats already in the in-memory cache.
428+
#[tokio::test]
429+
async fn reload_schema_prefers_history_over_json() {
430+
let dir = tempfile::TempDir::new().unwrap();
431+
let store = HistoryStore::open(&dir.path().join("history.db")).unwrap();
432+
let key = SnapshotKey {
433+
project_id: dry_run_core::history::ProjectId("test".into()),
434+
database_id: dry_run_core::history::DatabaseId("test-db".into()),
435+
};
436+
437+
let schema = test_snapshot();
438+
let schema_hash = schema.content_hash.clone();
439+
SnapshotStore::put(&store, &key, &schema)
440+
.await
441+
.expect("seed schema");
442+
store
443+
.put_activity_stats(
444+
&key,
445+
&make_activity_row(&schema_hash, "primary", "primary-h1"),
446+
)
447+
.await
448+
.expect("seed primary activity");
449+
450+
let json_path = dir.path().join("schema.json");
451+
std::fs::write(&json_path, serde_json::to_string(&schema).unwrap()).unwrap();
452+
453+
// Server starts with a stats-less snapshot in cache (mimicking a server
454+
// that booted before history.db was populated). schema_candidates points
455+
// at the JSON fallback. with_history wires up the primary source.
456+
let server = DryRunServer::from_annotated_with_db(
457+
crate::mcp::wrap_schema_only(test_snapshot()),
458+
None,
459+
LintConfig::default(),
460+
None,
461+
"test",
462+
vec![json_path],
463+
)
464+
.with_history(store, Some(key));
465+
466+
let result = server.reload_schema().await.expect("reload_schema");
467+
let text = format!("{:?}", result.content.first().unwrap());
468+
assert!(
469+
text.contains("history.db"),
470+
"reload_schema should report loading from history.db, got: {text}"
471+
);
472+
473+
let annotated = server.schema.read().await.clone().unwrap();
474+
assert!(
475+
annotated.activity_by_node.contains_key("primary"),
476+
"reload_schema should preserve primary activity from history.db"
477+
);
478+
}
479+
480+
// Regression: when history.db has no entry for the configured key,
481+
// reload_schema must still load from schema.json (DDL-only fallback).
482+
#[tokio::test]
483+
async fn reload_schema_falls_back_to_schema_json_when_history_empty() {
484+
let dir = tempfile::TempDir::new().unwrap();
485+
let store = HistoryStore::open(&dir.path().join("history.db")).unwrap();
486+
let key = SnapshotKey {
487+
project_id: dry_run_core::history::ProjectId("test".into()),
488+
database_id: dry_run_core::history::DatabaseId("test-db".into()),
489+
};
490+
491+
let schema = test_snapshot();
492+
let json_path = dir.path().join("schema.json");
493+
std::fs::write(&json_path, serde_json::to_string(&schema).unwrap()).unwrap();
494+
495+
let server = DryRunServer::from_annotated_with_db(
496+
crate::mcp::wrap_schema_only(test_snapshot()),
497+
None,
498+
LintConfig::default(),
499+
None,
500+
"test",
501+
vec![json_path.clone()],
502+
)
503+
.with_history(store, Some(key));
504+
505+
let result = server.reload_schema().await.expect("reload_schema");
506+
let text = format!("{:?}", result.content.first().unwrap());
507+
assert!(
508+
text.contains(&format!("{}", json_path.display())),
509+
"reload_schema should report loading from the schema.json path, got: {text}"
510+
);
511+
512+
let annotated = server.schema.read().await.clone().unwrap();
513+
assert!(annotated.planner.is_none());
514+
assert!(annotated.activity_by_node.is_empty());
515+
}

0 commit comments

Comments
 (0)