use adk_managed::state_store::{Durability, InMemoryManagedStateStore, ManagedStateStore};
use adk_managed::types::SessionStatus;
use adk_managed::{CheckpointManager, RunState};
use std::sync::Arc;
fn advanced_state() -> RunState {
RunState {
seq: 12,
pending_tool_ids: vec!["call-7".to_string()],
status: SessionStatus::Running,
}
}
fn idle_event() -> adk_managed::types::SessionEvent {
adk_managed::types::SessionEvent::StatusIdle { seq: 12, stop_reason: None, usage: None }
}
#[tokio::test]
async fn a_manager_without_a_store_flushes_without_error() {
let mut manager = CheckpointManager::new("session-1".to_string());
manager.checkpoint(idle_event(), advanced_state());
assert!(manager.store().is_none());
assert!(manager.flush().await.is_ok());
}
#[tokio::test]
async fn a_flushed_snapshot_is_visible_in_the_store() {
let store = Arc::new(InMemoryManagedStateStore::new());
let mut manager =
CheckpointManager::new("session-1".to_string()).with_store(store.clone() as Arc<_>);
manager.checkpoint(idle_event(), advanced_state());
manager.flush().await.expect("flush");
let stored = store.load("session-1").await.unwrap().expect("snapshot must be stored");
assert_eq!(stored.run_state.seq, 12);
assert_eq!(stored.run_state.pending_tool_ids, vec!["call-7".to_string()]);
assert_eq!(stored.events.len(), 1);
}
#[tokio::test]
async fn a_checkpoint_is_not_in_the_store_until_flushed() {
let store = Arc::new(InMemoryManagedStateStore::new());
let mut manager =
CheckpointManager::new("session-1".to_string()).with_store(store.clone() as Arc<_>);
manager.checkpoint(idle_event(), advanced_state());
assert!(
store.load("session-1").await.unwrap().is_none(),
"a local checkpoint is not a store write"
);
}
#[tokio::test]
async fn restore_rebuilds_a_manager_from_the_store() {
let store: Arc<dyn ManagedStateStore> = Arc::new(InMemoryManagedStateStore::new());
let mut original =
CheckpointManager::new("session-1".to_string()).with_store(Arc::clone(&store));
original.checkpoint(idle_event(), advanced_state());
original.flush().await.expect("flush");
let restored = CheckpointManager::restore("session-1".to_string(), Arc::clone(&store))
.await
.expect("restore");
assert_eq!(restored.run_state().seq, 12);
assert_eq!(restored.events().len(), 1);
assert_eq!(restored.session_id(), "session-1");
}
#[tokio::test]
async fn restoring_an_unknown_session_yields_an_empty_manager() {
let store: Arc<dyn ManagedStateStore> = Arc::new(InMemoryManagedStateStore::new());
let restored = CheckpointManager::restore("never-seen".to_string(), store)
.await
.expect("restore must not fail on a missing session");
assert_eq!(restored.run_state().seq, 0);
assert!(restored.events().is_empty());
}
#[tokio::test]
async fn the_shipped_store_reports_that_it_does_not_survive_process_loss() {
let store = InMemoryManagedStateStore::new();
assert_eq!(store.durability(), Durability::ProcessLocal);
assert!(!store.durability().survives_process_loss());
}
#[tokio::test]
async fn a_separate_store_instance_sees_nothing_which_is_the_restart_case() {
let first: Arc<dyn ManagedStateStore> = Arc::new(InMemoryManagedStateStore::new());
let mut manager =
CheckpointManager::new("session-1".to_string()).with_store(Arc::clone(&first));
manager.checkpoint(idle_event(), advanced_state());
manager.flush().await.expect("flush");
let second: Arc<dyn ManagedStateStore> = Arc::new(InMemoryManagedStateStore::new());
let restored =
CheckpointManager::restore("session-1".to_string(), second).await.expect("restore");
assert_eq!(
restored.run_state().seq,
0,
"process-local state does not cross a process boundary"
);
assert!(restored.events().is_empty());
}