refactor: extract unit tests into separate _tests.rs files
Move inline #[cfg(test)] mod tests { ... } blocks from 22 source files
into dedicated _tests.rs companion files, wired via:
#[cfg(test)]
#[path = "module_tests.rs"]
mod tests;
This keeps implementation-focused source files leaner and more scannable
while preserving full access to private items through `use super::*;`.
Modules extracted:
core: db, note_parser, payloads, project, references, sync_run,
timeline_collect, timeline_expand, timeline_seed
cli: list (55 tests), who (75 tests)
documents: extractor (43 tests), regenerator
embedding: change_detector, chunking
gitlab: graphql (wiremock async tests), transformers/issue
ingestion: dirty_tracker, discussions, issues, mr_diffs
Also adds conflicts_with("explain_score") to the --detail flag in the
who command to prevent mutually exclusive flags from being combined.
All 629 unit tests pass. No behavior changes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
168
src/ingestion/dirty_tracker_tests.rs
Normal file
168
src/ingestion/dirty_tracker_tests.rs
Normal file
@@ -0,0 +1,168 @@
|
||||
use super::*;
|
||||
|
||||
fn setup_db() -> Connection {
|
||||
let conn = Connection::open_in_memory().unwrap();
|
||||
conn.execute_batch("
|
||||
CREATE TABLE dirty_sources (
|
||||
source_type TEXT NOT NULL CHECK (source_type IN ('issue','merge_request','discussion','note')),
|
||||
source_id INTEGER NOT NULL,
|
||||
queued_at INTEGER NOT NULL,
|
||||
attempt_count INTEGER NOT NULL DEFAULT 0,
|
||||
last_attempt_at INTEGER,
|
||||
last_error TEXT,
|
||||
next_attempt_at INTEGER,
|
||||
PRIMARY KEY(source_type, source_id)
|
||||
);
|
||||
CREATE INDEX idx_dirty_sources_next_attempt ON dirty_sources(next_attempt_at);
|
||||
").unwrap();
|
||||
conn
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mark_dirty_inserts() {
|
||||
let conn = setup_db();
|
||||
mark_dirty(&conn, SourceType::Issue, 1).unwrap();
|
||||
|
||||
let count: i64 = conn
|
||||
.query_row("SELECT COUNT(*) FROM dirty_sources", [], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(count, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mark_dirty_tx_inserts() {
|
||||
let mut conn = setup_db();
|
||||
{
|
||||
let tx = conn.transaction().unwrap();
|
||||
mark_dirty_tx(&tx, SourceType::Issue, 1).unwrap();
|
||||
tx.commit().unwrap();
|
||||
}
|
||||
let count: i64 = conn
|
||||
.query_row("SELECT COUNT(*) FROM dirty_sources", [], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(count, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_requeue_resets_backoff() {
|
||||
let conn = setup_db();
|
||||
mark_dirty(&conn, SourceType::Issue, 1).unwrap();
|
||||
record_dirty_error(&conn, SourceType::Issue, 1, "test error").unwrap();
|
||||
|
||||
let attempt: i64 = conn
|
||||
.query_row(
|
||||
"SELECT attempt_count FROM dirty_sources WHERE source_id = 1",
|
||||
[],
|
||||
|r| r.get(0),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(attempt, 1);
|
||||
|
||||
mark_dirty(&conn, SourceType::Issue, 1).unwrap();
|
||||
let attempt: i64 = conn
|
||||
.query_row(
|
||||
"SELECT attempt_count FROM dirty_sources WHERE source_id = 1",
|
||||
[],
|
||||
|r| r.get(0),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(attempt, 0);
|
||||
|
||||
let next_at: Option<i64> = conn
|
||||
.query_row(
|
||||
"SELECT next_attempt_at FROM dirty_sources WHERE source_id = 1",
|
||||
[],
|
||||
|r| r.get(0),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(next_at.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_respects_backoff() {
|
||||
let conn = setup_db();
|
||||
mark_dirty(&conn, SourceType::Issue, 1).unwrap();
|
||||
conn.execute(
|
||||
"UPDATE dirty_sources SET next_attempt_at = 9999999999999 WHERE source_id = 1",
|
||||
[],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let results = get_dirty_sources(&conn).unwrap();
|
||||
assert!(results.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_orders_by_attempt_count() {
|
||||
let conn = setup_db();
|
||||
mark_dirty(&conn, SourceType::Issue, 1).unwrap();
|
||||
conn.execute(
|
||||
"UPDATE dirty_sources SET attempt_count = 2 WHERE source_id = 1",
|
||||
[],
|
||||
)
|
||||
.unwrap();
|
||||
mark_dirty(&conn, SourceType::Issue, 2).unwrap();
|
||||
|
||||
let results = get_dirty_sources(&conn).unwrap();
|
||||
assert_eq!(results.len(), 2);
|
||||
assert_eq!(results[0].1, 2);
|
||||
assert_eq!(results[1].1, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_batch_size_500() {
|
||||
let conn = setup_db();
|
||||
for i in 0..600 {
|
||||
mark_dirty(&conn, SourceType::Issue, i).unwrap();
|
||||
}
|
||||
let results = get_dirty_sources(&conn).unwrap();
|
||||
assert_eq!(results.len(), 500);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clear_removes() {
|
||||
let conn = setup_db();
|
||||
mark_dirty(&conn, SourceType::Issue, 1).unwrap();
|
||||
clear_dirty(&conn, SourceType::Issue, 1).unwrap();
|
||||
|
||||
let count: i64 = conn
|
||||
.query_row("SELECT COUNT(*) FROM dirty_sources", [], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(count, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mark_dirty_note_type() {
|
||||
let conn = setup_db();
|
||||
mark_dirty(&conn, SourceType::Note, 42).unwrap();
|
||||
|
||||
let results = get_dirty_sources(&conn).unwrap();
|
||||
assert_eq!(results.len(), 1);
|
||||
assert_eq!(results[0].0, SourceType::Note);
|
||||
assert_eq!(results[0].1, 42);
|
||||
|
||||
clear_dirty(&conn, SourceType::Note, 42).unwrap();
|
||||
let results = get_dirty_sources(&conn).unwrap();
|
||||
assert!(results.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_drain_loop() {
|
||||
let conn = setup_db();
|
||||
for i in 0..1200 {
|
||||
mark_dirty(&conn, SourceType::Issue, i).unwrap();
|
||||
}
|
||||
|
||||
let mut total = 0;
|
||||
loop {
|
||||
let batch = get_dirty_sources(&conn).unwrap();
|
||||
if batch.is_empty() {
|
||||
break;
|
||||
}
|
||||
for (st, id) in &batch {
|
||||
clear_dirty(&conn, *st, *id).unwrap();
|
||||
}
|
||||
total += batch.len();
|
||||
}
|
||||
assert_eq!(total, 1200);
|
||||
}
|
||||
Reference in New Issue
Block a user