//! Snapshot test pinning the JSON wire form of `kebab_core::IngestReport` //! for an inline fixture run. The store crate doesn't (yet) write //! IngestReports — that's `kb-app`'s job — but the wire schema lives in //! `kb-core`, and we want a determinism pin that fails loudly if the //! shape drifts. //! //! Set `UPDATE_SNAPSHOTS=1` to re-bake the baseline. use std::path::PathBuf; use kebab_core::{ AssetId, ChunkerVersion, DocumentId, IngestItem, IngestItemKind, IngestReport, ParserVersion, SourceScope, WorkspacePath, }; use serde_json::Value; fn baseline_path() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("snapshots") .join("ingest_report.snapshot.json") } fn fixture_report() -> IngestReport { IngestReport { scope: SourceScope { root: PathBuf::from("/home/u/KB"), include: vec!["**/*.md".into()], exclude: vec![".git/**".into()], }, scanned: 3, new: 2, updated: 1, skipped: 0, unchanged: 0, errors: 0, duration_ms: 187, skipped_by_extension: std::collections::BTreeMap::new(), skipped_gitignore: 0, skipped_kebabignore: 0, skipped_builtin_blacklist: 0, skipped_generated: 0, skipped_size_exceeded: 0, skip_examples: kebab_core::SkipExamples::default(), purged_deleted_files: 0, items: Some(vec![ IngestItem { kind: IngestItemKind::New, doc_id: Some(DocumentId("a".repeat(32))), doc_path: WorkspacePath::new("notes/alpha.md".into()).unwrap(), asset_id: Some(AssetId("a".repeat(32))), byte_len: Some(1234), block_count: Some(7), chunk_count: Some(3), parser_version: Some(ParserVersion("md-frontmatter-v2".into())), chunker_version: Some(ChunkerVersion("md-heading-v1".into())), warnings: vec![], pdf_ocr_pages: None, pdf_ocr_ms_total: None, error: None, }, IngestItem { kind: IngestItemKind::Updated, doc_id: Some(DocumentId("b".repeat(32))), doc_path: WorkspacePath::new("notes/beta.md".into()).unwrap(), asset_id: Some(AssetId("b".repeat(32))), byte_len: Some(2048), block_count: Some(12), chunk_count: Some(5), parser_version: Some(ParserVersion("md-frontmatter-v2".into())), chunker_version: Some(ChunkerVersion("md-heading-v1".into())), warnings: vec!["malformed frontmatter".into()], pdf_ocr_pages: None, pdf_ocr_ms_total: None, error: None, }, ]), } } #[test] fn ingest_report_wire_form_is_stable() { let report = fixture_report(); let actual = serde_json::to_value(&report).unwrap(); let baseline = match std::fs::read_to_string(baseline_path()) { Ok(s) => s, Err(_) if std::env::var("UPDATE_SNAPSHOTS").is_ok() => { std::fs::create_dir_all(baseline_path().parent().unwrap()).unwrap(); let pretty = serde_json::to_string_pretty(&actual).unwrap(); std::fs::write(baseline_path(), format!("{pretty}\n")).unwrap(); return; } Err(e) => panic!( "missing baseline {}; run with UPDATE_SNAPSHOTS=1: {e}", baseline_path().display() ), }; let expected: Value = serde_json::from_str(&baseline).unwrap(); if actual != expected { if std::env::var("UPDATE_SNAPSHOTS").is_ok() { let pretty = serde_json::to_string_pretty(&actual).unwrap(); std::fs::write(baseline_path(), format!("{pretty}\n")).unwrap(); return; } let pretty = serde_json::to_string_pretty(&actual).unwrap(); panic!( "ingest_report snapshot drift\n\ --- expected ({}) ---\n{baseline}\n\ --- actual ---\n{pretty}", baseline_path().display() ); } }