test(cli): integration tests for --trace + schema breakdowns (fb-37)
Also fixes App::search_with_opts trace branch to use NoopRetriever for SearchMode::Lexical, removing the embeddings requirement when the user only wants lexical-mode trace.
This commit is contained in:
57
crates/kebab-cli/tests/wire_schema_breakdowns.rs
Normal file
57
crates/kebab-cli/tests/wire_schema_breakdowns.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
//! p9-fb-37: integration tests for `kebab schema --json` extended stats.
|
||||
|
||||
mod common;
|
||||
|
||||
use serde_json::Value;
|
||||
use std::fs;
|
||||
use std::process::Command;
|
||||
|
||||
fn run_schema(cfg: &std::path::Path) -> Value {
|
||||
let bin = env!("CARGO_BIN_EXE_kebab");
|
||||
let out = Command::new(bin)
|
||||
.args(["--config", cfg.to_str().unwrap(), "schema", "--json"])
|
||||
.output()
|
||||
.expect("run kebab schema");
|
||||
assert!(
|
||||
out.status.success(),
|
||||
"schema failed: stderr={}",
|
||||
String::from_utf8_lossy(&out.stderr)
|
||||
);
|
||||
serde_json::from_slice(&out.stdout).expect("valid JSON")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn schema_stats_includes_breakdowns_on_fresh_corpus() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let (cfg, workspace, _data) = common::write_config(dir.path(), 0);
|
||||
// Run a no-op ingest to bring up migrations + create the SQLite file.
|
||||
fs::write(workspace.join("placeholder.md"), "# placeholder\n").unwrap();
|
||||
common::ingest(&cfg, &workspace);
|
||||
|
||||
let v = run_schema(&cfg);
|
||||
let stats = &v["stats"];
|
||||
let m = stats["media_breakdown"].as_object().unwrap();
|
||||
assert_eq!(m.len(), 5, "5 media keys padded");
|
||||
for k in &["markdown", "pdf", "image", "audio", "other"] {
|
||||
assert!(m[*k].is_number(), "media[{k}] is integer");
|
||||
}
|
||||
assert!(stats["lang_breakdown"].is_object());
|
||||
assert!(stats["index_bytes"]["sqlite"].is_number());
|
||||
assert!(stats["index_bytes"]["lancedb"].is_number());
|
||||
assert!(stats["stale_doc_count"].is_number());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn schema_stats_breakdowns_after_ingest() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let (cfg, workspace, _data) = common::write_config(dir.path(), 0);
|
||||
fs::write(workspace.join("a.md"), "---\nlang: en\n---\nhello\n").unwrap();
|
||||
fs::write(workspace.join("b.md"), "---\nlang: ko\n---\n안녕\n").unwrap();
|
||||
common::ingest(&cfg, &workspace);
|
||||
|
||||
let v = run_schema(&cfg);
|
||||
let stats = &v["stats"];
|
||||
assert_eq!(stats["media_breakdown"]["markdown"], 2);
|
||||
assert!(stats["lang_breakdown"].is_object());
|
||||
assert!(stats["index_bytes"]["sqlite"].as_u64().unwrap() > 0);
|
||||
}
|
||||
58
crates/kebab-cli/tests/wire_search_trace.rs
Normal file
58
crates/kebab-cli/tests/wire_search_trace.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
//! p9-fb-37: integration tests for `kebab search --trace --json`.
|
||||
|
||||
mod common;
|
||||
|
||||
use serde_json::Value;
|
||||
use std::fs;
|
||||
|
||||
#[test]
|
||||
fn search_trace_json_includes_trace_block() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let (cfg, workspace, _data) = common::write_config(dir.path(), 0);
|
||||
fs::write(workspace.join("doc1.md"), "# Title\n\nrust async hello\n").unwrap();
|
||||
common::ingest(&cfg, &workspace);
|
||||
|
||||
let (stdout, _stderr) = common::run_search_with_args(
|
||||
&cfg,
|
||||
&["--mode", "lexical", "--trace", "--json", "rust"],
|
||||
);
|
||||
let v: Value = serde_json::from_str(stdout.trim()).expect("valid JSON");
|
||||
assert_eq!(v["schema_version"], "search_response.v1");
|
||||
assert!(v["trace"].is_object(), "trace block present");
|
||||
assert!(v["trace"]["timing"].is_object());
|
||||
assert!(v["trace"]["timing"]["total_ms"].is_number());
|
||||
assert!(v["trace"]["lexical"].is_array());
|
||||
assert!(v["trace"]["vector"].is_array());
|
||||
assert!(v["trace"]["rrf_inputs"].is_array());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn search_without_trace_omits_trace_field() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let (cfg, workspace, _data) = common::write_config(dir.path(), 0);
|
||||
fs::write(workspace.join("doc1.md"), "# Title\n\nrust async hello\n").unwrap();
|
||||
common::ingest(&cfg, &workspace);
|
||||
|
||||
let (stdout, _stderr) = common::run_search_with_args(
|
||||
&cfg,
|
||||
&["--mode", "lexical", "--json", "rust"],
|
||||
);
|
||||
let v: Value = serde_json::from_str(stdout.trim()).expect("valid JSON");
|
||||
assert!(v.get("trace").is_none(), "trace field absent without --trace");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn search_trace_lexical_mode_vector_list_empty() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let (cfg, workspace, _data) = common::write_config(dir.path(), 0);
|
||||
fs::write(workspace.join("doc1.md"), "# Title\n\nrust async hello\n").unwrap();
|
||||
common::ingest(&cfg, &workspace);
|
||||
|
||||
let (stdout, _stderr) = common::run_search_with_args(
|
||||
&cfg,
|
||||
&["--mode", "lexical", "--trace", "--json", "rust"],
|
||||
);
|
||||
let v: Value = serde_json::from_str(stdout.trim()).expect("valid JSON");
|
||||
assert_eq!(v["trace"]["vector"].as_array().unwrap().len(), 0);
|
||||
assert_eq!(v["trace"]["timing"]["vector_ms"], 0);
|
||||
}
|
||||
Reference in New Issue
Block a user