#![cfg(feature = "sqlite")]
use adk_session::SqliteSessionService;
use adk_session::service::{CreateRequest, DeleteRequest, GetRequest, ListRequest, SessionService};
use sqlx::{Row, SqlitePool};
#[tokio::test]
async fn fresh_db_migrate_creates_tables_and_registry() {
let svc = SqliteSessionService::new("sqlite::memory:").await.unwrap();
let v = svc.schema_version().await.unwrap();
assert_eq!(v, 0, "schema_version should be 0 before migrate");
svc.migrate().await.unwrap();
let v = svc.schema_version().await.unwrap();
assert_eq!(v, 1, "schema_version should be 1 after migrate");
}
#[tokio::test]
async fn migrate_twice_is_idempotent() {
let svc = SqliteSessionService::new("sqlite::memory:").await.unwrap();
svc.migrate().await.unwrap();
let v1 = svc.schema_version().await.unwrap();
svc.migrate().await.unwrap();
let v2 = svc.schema_version().await.unwrap();
assert_eq!(v1, v2, "schema_version should not change on second migrate");
assert_eq!(v2, 1);
}
#[tokio::test]
async fn crud_works_after_migration() {
let svc = SqliteSessionService::new("sqlite::memory:").await.unwrap();
svc.migrate().await.unwrap();
let session = svc
.create(CreateRequest {
app_name: "myapp".into(),
user_id: "user1".into(),
session_id: Some("sess-1".into()),
state: Default::default(),
})
.await
.unwrap();
assert_eq!(session.id(), "sess-1");
let fetched = svc
.get(GetRequest {
app_name: "myapp".into(),
user_id: "user1".into(),
session_id: "sess-1".into(),
num_recent_events: None,
after: None,
})
.await
.unwrap();
assert_eq!(fetched.id(), "sess-1");
let list = svc
.list(ListRequest {
app_name: "myapp".into(),
user_id: "user1".into(),
limit: None,
offset: None,
})
.await
.unwrap();
assert_eq!(list.len(), 1);
svc.delete(DeleteRequest {
app_name: "myapp".into(),
user_id: "user1".into(),
session_id: "sess-1".into(),
})
.await
.unwrap();
let after_delete = svc
.get(GetRequest {
app_name: "myapp".into(),
user_id: "user1".into(),
session_id: "sess-1".into(),
num_recent_events: None,
after: None,
})
.await;
assert!(after_delete.is_err(), "get after delete should fail");
}
#[tokio::test]
async fn baseline_detection_on_preexisting_tables() {
let pool = SqlitePool::connect("sqlite::memory:").await.unwrap();
sqlx::query(
"CREATE TABLE sessions (\
app_name TEXT NOT NULL, \
user_id TEXT NOT NULL, \
session_id TEXT NOT NULL, \
state TEXT NOT NULL, \
created_at TEXT NOT NULL, \
updated_at TEXT NOT NULL, \
PRIMARY KEY (app_name, user_id, session_id)\
)",
)
.execute(&pool)
.await
.unwrap();
sqlx::query(
"INSERT INTO sessions (app_name, user_id, session_id, state, created_at, updated_at) \
VALUES ('app', 'u1', 's1', '{}', '2025-01-01', '2025-01-01')",
)
.execute(&pool)
.await
.unwrap();
let svc = SqliteSessionService::from_pool(pool);
svc.migrate().await.unwrap();
let v = svc.schema_version().await.unwrap();
assert_eq!(v, 1, "baseline should be recorded as v1");
let row =
sqlx::query("SELECT COUNT(*) AS cnt FROM sessions").fetch_one(svc.pool()).await.unwrap();
let count: i64 = row.try_get("cnt").unwrap();
assert_eq!(count, 1, "pre-existing data should be preserved");
}
#[tokio::test]
async fn registry_records_are_complete() {
let svc = SqliteSessionService::new("sqlite::memory:").await.unwrap();
svc.migrate().await.unwrap();
let row = sqlx::query(
"SELECT version, description, applied_at FROM _adk_session_migrations WHERE version = 1",
)
.fetch_one(svc.pool())
.await
.unwrap();
let version: i64 = row.try_get("version").unwrap();
let description: String = row.try_get("description").unwrap();
let applied_at: String = row.try_get("applied_at").unwrap();
assert_eq!(version, 1);
assert!(!description.is_empty(), "description should not be empty");
assert!(!applied_at.is_empty(), "applied_at should not be empty");
}