use crate::db::Database;
use crate::db::repository::PendingRequestRepository;
use rusqlite::params;
async fn backdate(db: &Database, id: uuid::Uuid, created_secs_ago: i64, updated_secs_ago: i64) {
let id_s = id.to_string();
db.pool()
.get()
.await
.unwrap()
.interact(move |conn| {
conn.execute(
"UPDATE pending_requests SET created_at = unixepoch() - ?1, \
updated_at = unixepoch() - ?2 WHERE id = ?3",
params![created_secs_ago, updated_secs_ago, id_s],
)
})
.await
.unwrap()
.unwrap();
}
#[tokio::test]
async fn thirty_minute_old_turn_is_still_resumed() {
let db = Database::connect_in_memory().await.unwrap();
db.run_migrations().await.unwrap();
let repo = PendingRequestRepository::new(db.pool().clone());
let id = uuid::Uuid::new_v4();
let session_id = uuid::Uuid::new_v4();
repo.insert(id, session_id, "long agentic turn", "tui", None)
.await
.unwrap();
backdate(&db, id, 1800, 1800).await;
let interrupted = repo.get_interrupted().await.unwrap();
assert_eq!(
interrupted.len(),
1,
"a 30-minute-old in-flight turn must resume, not be purged"
);
assert_eq!(interrupted[0].session_id, session_id.to_string());
}
#[tokio::test]
async fn day_old_debris_is_purged() {
let db = Database::connect_in_memory().await.unwrap();
db.run_migrations().await.unwrap();
let repo = PendingRequestRepository::new(db.pool().clone());
let id = uuid::Uuid::new_v4();
repo.insert(id, uuid::Uuid::new_v4(), "crash debris", "tui", None)
.await
.unwrap();
backdate(&db, id, 100_000, 100_000).await;
let interrupted = repo.get_interrupted().await.unwrap();
assert!(
interrupted.is_empty(),
"rows idle for over 24h are crash debris and must be cleared"
);
}
#[tokio::test]
async fn touch_keeps_a_very_long_turn_alive() {
let db = Database::connect_in_memory().await.unwrap();
db.run_migrations().await.unwrap();
let repo = PendingRequestRepository::new(db.pool().clone());
let id = uuid::Uuid::new_v4();
repo.insert(id, uuid::Uuid::new_v4(), "multi-hour turn", "tui", None)
.await
.unwrap();
backdate(&db, id, 200_000, 200_000).await;
repo.touch(id).await.unwrap();
let interrupted = repo.get_interrupted().await.unwrap();
assert_eq!(
interrupted.len(),
1,
"a touched (recently active) turn must survive the debris cap"
);
}
#[tokio::test]
async fn channel_scoped_query_has_same_age_semantics() {
let db = Database::connect_in_memory().await.unwrap();
db.run_migrations().await.unwrap();
let repo = PendingRequestRepository::new(db.pool().clone());
let old_turn = uuid::Uuid::new_v4();
repo.insert(
old_turn,
uuid::Uuid::new_v4(),
"old telegram turn",
"telegram",
Some("123"),
)
.await
.unwrap();
backdate(&db, old_turn, 1800, 1800).await;
let debris = uuid::Uuid::new_v4();
repo.insert(
debris,
uuid::Uuid::new_v4(),
"telegram debris",
"telegram",
Some("123"),
)
.await
.unwrap();
backdate(&db, debris, 100_000, 100_000).await;
let rows = repo.get_interrupted_for_channel("telegram").await.unwrap();
assert_eq!(rows.len(), 1);
assert_eq!(rows[0].id, old_turn.to_string());
}