use adk_graph::state::Checkpoint;
use serde_json::json;
#[test]
fn a_checkpoint_without_the_new_fields_still_loads() {
let stored = json!({
"thread_id": "thread-1",
"checkpoint_id": "cp-1",
"state": { "value": 7 },
"step": 3,
"pending_nodes": ["next"],
"metadata": {},
"created_at": "2026-07-01T12:00:00Z"
});
let checkpoint: Checkpoint =
serde_json::from_value(stored).expect("an older checkpoint must still load");
assert_eq!(checkpoint.thread_id, "thread-1");
assert_eq!(checkpoint.step, 3);
assert_eq!(checkpoint.pending_nodes, vec!["next".to_string()]);
assert_eq!(checkpoint.cleared_interrupt, None);
assert!(checkpoint.attempts.is_empty());
assert!(checkpoint.child_ledger.is_empty());
}
#[test]
fn empty_bookkeeping_is_not_serialized() {
let checkpoint = Checkpoint::new("thread-1", Default::default(), 0, vec!["first".to_string()]);
let encoded = serde_json::to_value(&checkpoint).expect("serialize");
assert!(encoded.get("cleared_interrupt").is_none());
assert!(encoded.get("attempts").is_none());
assert!(encoded.get("child_ledger").is_none());
}
#[test]
fn bookkeeping_survives_a_round_trip() {
let mut checkpoint =
Checkpoint::new("thread-1", Default::default(), 1, vec!["gated".to_string()]);
checkpoint.cleared_interrupt = Some("gated".to_string());
checkpoint.attempts.insert("flaky".to_string(), 2);
checkpoint.child_ledger.insert("parent/child@1".to_string(), json!({ "ok": true }));
let encoded = serde_json::to_string(&checkpoint).expect("serialize");
let decoded: Checkpoint = serde_json::from_str(&encoded).expect("deserialize");
assert_eq!(decoded.cleared_interrupt.as_deref(), Some("gated"));
assert_eq!(decoded.attempts.get("flaky"), Some(&2));
assert_eq!(
decoded.child_ledger.get("parent/child@1").and_then(|v| v.get("ok")),
Some(&json!(true))
);
}
#[cfg(feature = "sqlite")]
#[tokio::test]
async fn the_sqlite_backend_persists_the_bookkeeping() {
use adk_graph::checkpoint::{Checkpointer, SqliteCheckpointer};
let checkpointer =
SqliteCheckpointer::new("sqlite::memory:").await.expect("open the checkpointer");
let mut checkpoint =
Checkpoint::new("thread-1", Default::default(), 4, vec!["gated".to_string()]);
checkpoint.cleared_interrupt = Some("gated".to_string());
checkpoint.attempts.insert("flaky".to_string(), 3);
checkpoint.child_ledger.insert("parent/child@1".to_string(), json!("done"));
checkpointer.save(&checkpoint).await.expect("save");
let loaded = checkpointer.load("thread-1").await.expect("load").expect("a checkpoint");
assert_eq!(loaded.cleared_interrupt.as_deref(), Some("gated"));
assert_eq!(loaded.attempts.get("flaky"), Some(&3));
assert_eq!(loaded.child_ledger.get("parent/child@1"), Some(&json!("done")));
}