diff --git a/crates/kebab-app/tests/ingest_file.rs b/crates/kebab-app/tests/ingest_file.rs new file mode 100644 index 0000000..b70fa4d --- /dev/null +++ b/crates/kebab-app/tests/ingest_file.rs @@ -0,0 +1,89 @@ +//! Integration: kebab_app::ingest_file_with_config copies external file +//! to _external/, ingests as single asset, idempotent on second call. + +use std::fs; + +use kebab_config::Config; + +#[test] +fn ingest_file_copies_external_md_and_reports_new() { + let dir = tempfile::tempdir().unwrap(); + let workspace = dir.path().join("notes"); + let data = dir.path().join("data"); + fs::create_dir_all(&workspace).unwrap(); + fs::create_dir_all(&data).unwrap(); + + let mut cfg = Config::defaults(); + cfg.workspace.root = workspace.to_string_lossy().into_owned(); + cfg.storage.data_dir = data.to_string_lossy().into_owned(); + cfg.models.embedding.provider = "none".to_string(); + cfg.models.embedding.dimensions = 0; + + // Source file outside the workspace. + let external_src = dir.path().join("source.md"); + fs::write(&external_src, "# Hello\n\nbody.").unwrap(); + + let report = kebab_app::ingest_file_with_config(cfg.clone(), &external_src).unwrap(); + assert_eq!(report.scanned, 1, "{report:?}"); + assert_eq!(report.new, 1, "{report:?}"); + assert_eq!(report.unchanged, 0, "{report:?}"); + + // _external/ dir created, file copied with hash prefix. + let ext_dir = workspace.join("_external"); + assert!(ext_dir.is_dir()); + let entries: Vec<_> = fs::read_dir(&ext_dir) + .unwrap() + .filter_map(|e| e.ok()) + .collect(); + assert_eq!(entries.len(), 1, "exactly one file in _external/"); + let name = entries[0].file_name().to_string_lossy().into_owned(); + assert!(name.ends_with(".md")); + + // .kebabignore has _external/ line. + let ki = fs::read_to_string(workspace.join(".kebabignore")).unwrap(); + assert!(ki.lines().any(|l| l.trim() == "_external/")); +} + +#[test] +fn ingest_file_idempotent_on_second_call() { + let dir = tempfile::tempdir().unwrap(); + let workspace = dir.path().join("notes"); + let data = dir.path().join("data"); + fs::create_dir_all(&workspace).unwrap(); + fs::create_dir_all(&data).unwrap(); + + let mut cfg = Config::defaults(); + cfg.workspace.root = workspace.to_string_lossy().into_owned(); + cfg.storage.data_dir = data.to_string_lossy().into_owned(); + cfg.models.embedding.provider = "none".to_string(); + cfg.models.embedding.dimensions = 0; + + let src = dir.path().join("doc.md"); + fs::write(&src, "# A\n\nbody.").unwrap(); + + let r1 = kebab_app::ingest_file_with_config(cfg.clone(), &src).unwrap(); + assert_eq!(r1.new, 1); + + let r2 = kebab_app::ingest_file_with_config(cfg.clone(), &src).unwrap(); + assert_eq!(r2.new, 0, "{r2:?}"); + assert_eq!(r2.unchanged, 1, "{r2:?}"); +} + +#[test] +fn ingest_file_errors_on_missing_path() { + let dir = tempfile::tempdir().unwrap(); + let workspace = dir.path().join("notes"); + let data = dir.path().join("data"); + fs::create_dir_all(&workspace).unwrap(); + fs::create_dir_all(&data).unwrap(); + + let mut cfg = Config::defaults(); + cfg.workspace.root = workspace.to_string_lossy().into_owned(); + cfg.storage.data_dir = data.to_string_lossy().into_owned(); + cfg.models.embedding.provider = "none".to_string(); + cfg.models.embedding.dimensions = 0; + + let nonexistent = dir.path().join("nope.md"); + let err = kebab_app::ingest_file_with_config(cfg, &nonexistent).unwrap_err(); + assert!(err.to_string().contains("does not exist"), "{err}"); +}