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 = 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); }