# Task 8 — ItemRepo with deduping insert + FTS5 BM25 search ## Verification ### cargo test -p news-store ```text $ cargo test -p news-store Compiling news-store v0.1.0 (/home/connor/docs/projects/news/crates/news-store) Finished `test` profile [unoptimized + debuginfo] target(s) in 1m 06s Running unittests src/lib.rs (target/debug/deps/news_store-c92024802100563b) running 10 tests test items::tests::insert_if_new_dedupes_on_source_and_link ... ok test sources::tests::seed_default_sources_is_idempotent ... ok test sources::tests::list_enabled_returns_the_three_seeded_sources ... ok test items::tests::search_fts_no_match_returns_empty_vec ... ok test tests::init_db_runs_migrations ... ok test tests::raw_items_insert_is_reflected_in_fts_index ... ok test tests::migrating_an_already_migrated_pool_is_a_no_op ... ok test tests::raw_items_delete_removes_row_from_fts_index ... ok test sources::tests::set_enabled_false_excludes_source_from_list_enabled ... ok test items::tests::search_fts_returns_matching_items_ranked ... ok test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.03s Doc-tests news_store running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ``` ### cargo fmt --all --check ```text $ cargo fmt --all --check (no output) ``` ### cargo clippy --workspace --all-targets -- -D warnings ```text $ cargo clippy --workspace --all-targets -- -D warnings Checking news-store v0.1.0 (/home/connor/docs/projects/news/crates/news-store) Finished `dev` profile [unoptimized + debuginfo] target(s) in 14.36s ``` ## Files changed - `crates/news-store/Cargo.toml` — added `chrono = { workspace = true }` for RFC3339 timestamp handling. - `crates/news-store/src/lib.rs` — added `pub mod items` and re-exported `ItemRepo` + `InsertOutcome`. - `crates/news-store/src/items.rs` — new file containing `ItemRepo`, `InsertOutcome`, `insert_if_new`, `search_fts`, and tests. ## Test coverage - `items::insert_if_new_dedupes_on_source_and_link`: inserts the same `(source_id, link)` twice; first returns `Inserted`, second returns `AlreadyExists`; `raw_items` holds exactly 1 row. - `items::tests::search_fts_returns_matching_items_ranked`: 3 distinct items, search for "election" returns the matching title. - `items::tests::search_fts_no_match_returns_empty_vec`: search for "zzz_no_match" returns an empty `Vec` (not an error).