All 8 test categories from the task plan, plus a JobRepo subset:
migration — tests/migration.rs: fresh DB after run_migrations
exposes every required §5 table + index.
unit (copy) — tests/asset_writer.rs: copy mode writes file with
mode 0o644 + correct bytes.
unit (ref) — tests/asset_writer.rs: reference mode does not write
file; row records source path.
unit (cs) — tests/asset_writer.rs: tampered checksum returns a
Conflict-flavoured anyhow error.
unit (idem) — tests/idempotency.rs: same put_document twice → 1 row,
doc_version 1→2; tags re-derived.
unit (rb) — tests/idempotency.rs: put_blocks with FK violation
rolls back; pre-existing rows unchanged.
contract — tests/contract_roundtrip.rs: drives kb-parse-md +
kb-normalize + kb-chunk on
fixtures/markdown/code-and-table.md, persists, then
reloads via DocumentStore::get_document /
get_chunk and asserts byte-equal round-trip.
snapshot — tests/ingest_report_snapshot.rs +
snapshots/ingest_report.snapshot.json: pin the wire
JSON form of kb_core::IngestReport for an inline
fixture run.
jobs — tests/jobs.rs: create → progress → finish flow;
error message round-trip; list filters on status/kind.
Drops the unused `serde` direct dep from Cargo.toml; serde_json brings
its own. Dev-deps confirmed via `cargo tree -p kb-store-sqlite --depth 1`
to live only in the dev tree.
91 lines
2.9 KiB
Rust
91 lines
2.9 KiB
Rust
//! `JobRepo` smoke tests: create → progress → finish, list filters.
|
|
|
|
use kb_core::{JobFilter, JobKind, JobRepo, JobStatus};
|
|
use kb_store_sqlite::SqliteStore;
|
|
use serde_json::json;
|
|
|
|
mod common;
|
|
|
|
#[test]
|
|
fn create_then_progress_then_finish() {
|
|
let env = common::TestEnv::new();
|
|
let store = SqliteStore::open(&env.config()).unwrap();
|
|
store.run_migrations().unwrap();
|
|
|
|
let id = store
|
|
.create(JobKind::Ingest, json!({"path": "notes/x.md"}))
|
|
.unwrap();
|
|
// Status starts pending.
|
|
let row = store.list(&JobFilter::default()).unwrap();
|
|
assert_eq!(row.len(), 1);
|
|
assert_eq!(row[0].status, JobStatus::Pending);
|
|
|
|
// First progress flips pending → running.
|
|
store
|
|
.update_progress(&id, json!({"processed": 1, "total": 10}))
|
|
.unwrap();
|
|
let row = store.list(&JobFilter::default()).unwrap();
|
|
assert_eq!(row[0].status, JobStatus::Running);
|
|
assert_eq!(row[0].progress.as_ref().unwrap()["total"], json!(10));
|
|
|
|
// Finish with success.
|
|
store
|
|
.finish(&id, JobStatus::Succeeded, None)
|
|
.unwrap();
|
|
let row = store.list(&JobFilter::default()).unwrap();
|
|
assert_eq!(row[0].status, JobStatus::Succeeded);
|
|
assert!(row[0].finished_at.is_some());
|
|
assert!(row[0].error.is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn finish_with_error_message_is_round_trippable() {
|
|
let env = common::TestEnv::new();
|
|
let store = SqliteStore::open(&env.config()).unwrap();
|
|
store.run_migrations().unwrap();
|
|
|
|
let id = store.create(JobKind::Embed, json!({})).unwrap();
|
|
store
|
|
.finish(&id, JobStatus::Failed, Some("boom: model not pulled"))
|
|
.unwrap();
|
|
|
|
let row = store.list(&JobFilter::default()).unwrap();
|
|
assert_eq!(row[0].status, JobStatus::Failed);
|
|
assert_eq!(
|
|
row[0].error.as_deref(),
|
|
Some("boom: model not pulled"),
|
|
"error message must round-trip"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn list_filters_status_and_kind() {
|
|
let env = common::TestEnv::new();
|
|
let store = SqliteStore::open(&env.config()).unwrap();
|
|
store.run_migrations().unwrap();
|
|
|
|
// Two ingest jobs (one finished succeeded, one pending) + one embed.
|
|
let a = store.create(JobKind::Ingest, json!({"a": 1})).unwrap();
|
|
let _b = store.create(JobKind::Ingest, json!({"b": 1})).unwrap();
|
|
let _c = store.create(JobKind::Embed, json!({"c": 1})).unwrap();
|
|
store.finish(&a, JobStatus::Succeeded, None).unwrap();
|
|
|
|
let by_status_succeeded = store
|
|
.list(&JobFilter {
|
|
status: Some(JobStatus::Succeeded),
|
|
kind: None,
|
|
})
|
|
.unwrap();
|
|
assert_eq!(by_status_succeeded.len(), 1);
|
|
assert_eq!(by_status_succeeded[0].kind, JobKind::Ingest);
|
|
|
|
let by_kind_embed = store
|
|
.list(&JobFilter {
|
|
status: None,
|
|
kind: Some(JobKind::Embed),
|
|
})
|
|
.unwrap();
|
|
assert_eq!(by_kind_embed.len(), 1);
|
|
assert_eq!(by_kind_embed[0].kind, JobKind::Embed);
|
|
}
|