test(client): Plan 2 — registry + indexer integration coverage#16
Conversation
Code Review — Plan 2: registry + indexer integration coverageOverall this is a high-quality test PR. The tests are well-structured, cover the right behavioral invariants, and the namespace-isolation checks in every query test are exactly the right thing to pin. A few things worth addressing: Correctness issues
The planning doc (Task 4) expected
// Current (weak):
assert!(rows[1].modified_at >= rows[0].modified_at);
// Better:
assert!(rows[1].modified_at > rows[0].modified_at,
"mtime must advance after >=1s sleep; if equal, the sleep was too short");If CI ever runs this under heavy load and the kernel rounds both timestamps to the same second, Minor style nitsRedundant sort after ordered query ( let mut rows: Vec<FileRecord> = file_records::table
...
.order(file_records::id.asc())
.load(conn)
.unwrap();
rows.sort_by_key(|r| r.id); // ← redundant; Diesel already returns these in id orderThe
fs_tx
.clone() // ← clone just to get a mutable owner
.send(Ok(Vec::new()))
.await
.expect("push synthetic debounce event");Declare let (mut fs_tx, fs_rx) = mpsc::channel::<...>(8);
// ...
fs_tx.send(Ok(Vec::new())).await.expect("...");Pointless The // Current:
let result = timeout(...).await.expect("...").expect("...");
let _ = result; // discards ()
// Simpler:
timeout(...).await.expect("...").expect("...");Missing edge cases (worth adding to a later plan)
Planning document artifact
Summary
The core coverage is solid. I'd suggest fixing the |
Plan 2 of the library test coverage series. Adapts design spec §2 to the actual registry API surface (create, update_jid, delete, non_deleted, updated_locally, latest_jid) and pins append-only delete + no-UpdateForm indexer semantics. 9 tasks covering client/tests/registry_tests.rs, client/tests/indexer_tests.rs, client/tests/indexer_run_tests.rs.
…ry_tests Satisfies clippy::bool_assert_comparison under -D warnings on the new file. Deviation from plan text (plan used assert_eq!(x, true/false)); behavior unchanged.
…split size/mtime)
…owaway fs_tx clone - Tighten the modified-file assertion to strict `>` on modified_at. The 1100ms sleep exists specifically so whole-second mtimes are distinct; >= would silently pass if truncate_to_seconds ever collapsed them. - Drop `fs_tx.clone()` by making fs_tx `mut`. The original clone existed only to get a mutable owner for SinkExt::send; marking the local mut is simpler and avoids the allocation.
18b1e3b to
9857bfa
Compare
|
Thanks for the review. Addressed in Fixed:
Already resolved before review: Deferred to later plans (explicitly noted in review as "worth adding to a later plan"): Plus the plan-docs-in-tree policy question — same answer as on PR #15. Force-pushed after rebasing onto the updated Plan 1 branch so PR #15's fixes come along. |
Code Review — Plan 2: registry + indexer integration coverageOverall this is a solid test-coverage PR. The tests use real SQLite (no mocks), exercise the public API surface faithfully, and the namespace-isolation checks in every query test are especially good. A few things worth discussing before merge. Correctness issues1. Plan doc has a wrong assertion (doc inconsistency, not a code bug)
// Plan doc says:
assert_eq!(paths, vec![("a.cook", 11), ("b.cook", 20)]);The actual test correctly asserts The plan also has a weaker assertion in Task 7 Step 4: // Plan doc uses a weak disjunction:
assert!(rows[0].size != rows[1].size || rows[0].modified_at != rows[1].modified_at);The real test improves on this with two separate 2. Untested edge case: deleted-but-unsynced files in Looking at // File is deleted locally and not yet synced — should it appear in updated_locally?
let tombstone = registry::delete(conn, &vec![sample_delete(&live)]).unwrap();
let pending = registry::updated_locally(conn, 1).unwrap();
// Assert whichever is the intended behaviorThis is also a good guard against a future refactor accidentally dropping the delete-sync path. Code quality nits3. Redundant let rows: Vec<FileRecord> = file_records::table
// ...
.order(file_records::id.asc()) // already sorted
.load(conn)
.unwrap();
rows.sort_by_key(|r| r.id); // redundantNo harm, but it signals doubt about the query's ordering guarantee. Either trust the 4. registry::create(conn, &vec![sample_create("a.cook", 42, 1)]).unwrap();The production signature is Plan document questionThe What's working well
Summary: The two substantive points are (1) the untested delete-sync path in |
Summary
client/tests/registry_tests.rs— 9 integration tests pinning every publicregistryfn (create,update_jid,delete,non_deleted,updated_locally,latest_jid) against a real SQLite tempdir pool, including append-only delete semantics, latest-per-path queries, namespace isolation, andNotFound-on-emptylatest_jid.client/tests/indexer_tests.rs— 7 filesystem-driven tests forcheck_index_oncecovering new / no-op / modified (append-not-UPDATE) / deleted / ineligible-filter+recursion / dotfile allowlist / symlink-skip paths.client/tests/indexer_run_tests.rs— 2 multi-thread async tests forindexer::runcovering initial-scan + event-driven re-scan + graceful cancel, plus a pre-cancelled-token fast-exit.No production code changed. All 18 new tests plus the whole
cooklang-sync-clientsuite stay green; clippy is clean on the new files.Stacked on #15 (Plan 1) — base branch is
test-coverage-plan-1, notmain. Once Plan 1 merges, this PR retargets tomainautomatically.Plan 2 behavior pins (load-bearing for later refactors)
registry::deleteis append-only: asserting two rows exist after delete, newest withdeleted=true— guards thenon_deletedquery'smax(id)subselect.indexer::check_index_onceappends a newCreateFormon modified files (noUpdateFormpath): asserts exactly 2 rows exist for the path after mutation.registry::latest_jidreturnsdiesel::result::Error::NotFound(notOk(0)) when no row has a jid: callers must handle this distinction deliberately.modified_atequality: modified-file tests wait ≥1.1s before rewriting sotruncate_to_secondsproduces a distinguishable value.Test plan
cargo test -p cooklang-sync-client --test registry_tests— 9/9 passcargo test -p cooklang-sync-client --test indexer_tests— 7/7 passcargo test -p cooklang-sync-client --test indexer_run_tests— 2/2 passcargo test -p cooklang-sync-client(full suite) — all greencargo clippy -p cooklang-sync-client --tests --no-deps -- -D warnings— no new warnings on the three new filesgit diff --statonclient/src/is empty for this delta)Follow-up plans (out of scope here)
client/src/remote.rs(wiremock) +client/src/syncer.rsauth,chunk_id,metadata,notification, request/response)create_serversmoke)