#![allow(clippy::unwrap_used, clippy::expect_used)]
use super::*;
use tempfile::TempDir;
#[test]
fn sync_state_roundtrip() {
let dir = TempDir::new().unwrap();
let conn = open_db(&dir.path().join("test.db")).unwrap();
assert_eq!(get_last_synced_id(&conn).unwrap(), 0);
set_last_synced_id(&conn, 42).unwrap();
assert_eq!(get_last_synced_id(&conn).unwrap(), 42);
set_last_synced_id(&conn, 100).unwrap();
assert_eq!(get_last_synced_id(&conn).unwrap(), 100);
}
#[test]
fn get_events_since_filters_correctly() {
let dir = TempDir::new().unwrap();
let conn = open_db(&dir.path().join("test.db")).unwrap();
for i in 0..5 {
conn.execute(
"INSERT INTO events (timestamp, command, filter_name, input_bytes, output_bytes,
input_tokens_est, output_tokens_est, filter_time_ms, exit_code)
VALUES ('2026-01-01T00:00:00Z', 'git status', NULL, 1000, 200, 250, 50, 10, 0)",
[],
)
.unwrap();
let _ = i;
}
let events = get_events_since(&conn, 2).unwrap();
assert_eq!(events.len(), 3);
assert!(events[0].id > 2);
assert!(events.iter().all(|e| e.id > 2));
}
#[test]
fn get_events_since_respects_limit_500() {
let dir = TempDir::new().unwrap();
let conn = open_db(&dir.path().join("test.db")).unwrap();
for _ in 0..600 {
conn.execute(
"INSERT INTO events
(timestamp, command, filter_name, input_bytes, output_bytes,
input_tokens_est, output_tokens_est, filter_time_ms, exit_code)
VALUES
('2026-01-01T00:00:00Z', 'git status', NULL,
1000, 200, 250, 50, 10, 0)",
[],
)
.unwrap();
}
let events = get_events_since(&conn, 0).unwrap();
assert_eq!(
events.len(),
500,
"get_events_since must return at most 500 events"
);
let ids: Vec<i64> = events.iter().map(|e| e.id).collect();
assert!(
ids.windows(2).all(|w| w[0] < w[1]),
"events must be in ascending ID order"
);
let last_id = *ids.last().unwrap(); let rest = get_events_since(&conn, last_id).unwrap();
assert_eq!(
rest.len(),
100,
"second call should return the remaining 100 events"
);
assert!(
rest.iter().all(|e| e.id > last_id),
"second batch must only contain events with id > {last_id}"
);
}
#[test]
fn get_events_since_includes_null_filter_names() {
let dir = TempDir::new().unwrap();
let conn = open_db(&dir.path().join("test.db")).unwrap();
conn.execute(
"INSERT INTO events
(timestamp, command, filter_name, input_bytes, output_bytes,
input_tokens_est, output_tokens_est, filter_time_ms, exit_code)
VALUES
('2026-01-01T00:00:00Z', 'echo hello', NULL, 100, 50, 25, 12, 5, 0)",
[],
)
.unwrap();
let events = get_events_since(&conn, 0).unwrap();
assert_eq!(events.len(), 1);
assert_eq!(
events[0].filter_name, None,
"NULL filter_name should deserialise as None"
);
}
#[test]
fn filter_hash_stored_and_retrieved() {
let dir = TempDir::new().unwrap();
let conn = open_db(&dir.path().join("test.db")).unwrap();
let hash = "a".repeat(64); let ev = build_event(
"git status",
Some("git status"),
Some(&hash),
400,
200,
400,
10,
0,
false,
);
assert_eq!(ev.filter_hash.as_deref(), Some(hash.as_str()));
record_event(&conn, &ev).expect("record");
let events = get_events_since(&conn, 0).unwrap();
assert_eq!(events.len(), 1);
assert_eq!(
events[0].filter_hash.as_deref(),
Some(hash.as_str()),
"filter_hash must survive the roundtrip through the DB"
);
}
#[test]
fn filter_hash_none_stored_as_null() {
let dir = TempDir::new().unwrap();
let conn = open_db(&dir.path().join("test.db")).unwrap();
let ev = build_event("echo hi", None, None, 100, 50, 100, 5, 0, false);
assert!(ev.filter_hash.is_none());
record_event(&conn, &ev).expect("record");
let events = get_events_since(&conn, 0).unwrap();
assert_eq!(events.len(), 1);
assert_eq!(
events[0].filter_hash, None,
"None filter_hash should be stored as NULL and round-trip as None"
);
}
#[test]
fn last_synced_at_roundtrip() {
let dir = TempDir::new().unwrap();
let conn = open_db(&dir.path().join("test.db")).unwrap();
assert_eq!(get_last_synced_at(&conn).unwrap(), None);
set_last_synced_at(&conn, "2026-02-26T12:00:00Z").unwrap();
assert_eq!(
get_last_synced_at(&conn).unwrap().as_deref(),
Some("2026-02-26T12:00:00Z")
);
set_last_synced_at(&conn, "2026-02-27T08:00:00Z").unwrap();
assert_eq!(
get_last_synced_at(&conn).unwrap().as_deref(),
Some("2026-02-27T08:00:00Z")
);
}
#[test]
fn pending_count_zero_when_empty() {
let dir = TempDir::new().unwrap();
let conn = open_db(&dir.path().join("test.db")).unwrap();
assert_eq!(get_pending_count(&conn).unwrap(), 0);
}
#[test]
fn pending_count_with_unsynced_events() {
let dir = TempDir::new().unwrap();
let conn = open_db(&dir.path().join("test.db")).unwrap();
for _ in 0..5 {
let ev = build_event("cmd", Some("f"), None, 400, 100, 400, 5, 0, false);
record_event(&conn, &ev).unwrap();
}
assert_eq!(get_pending_count(&conn).unwrap(), 5);
set_last_synced_id(&conn, 3).unwrap();
assert_eq!(get_pending_count(&conn).unwrap(), 2);
set_last_synced_id(&conn, 5).unwrap();
assert_eq!(get_pending_count(&conn).unwrap(), 0);
}