use foundation_db::traits::{DocumentStore, PromotableDocument};
use foundation_db::{SqlDocumentStore, TursoStorage};
use std::sync::Mutex;
use tempfile::TempDir;
static POOL_GUARD: Mutex<Option<foundation_core::valtron::PoolGuard>> = Mutex::new(None);
fn init_valtron() {
let mut guard = POOL_GUARD.lock().unwrap();
if guard.is_none() {
*guard = Some(foundation_core::valtron::initialize_pool(42, None));
}
}
fn make_store() -> (TempDir, SqlDocumentStore<TursoStorage>) {
init_valtron();
let dir = TempDir::new().unwrap();
let url = dir.path().join("docs.db");
let storage = TursoStorage::new(url.to_str().unwrap()).unwrap();
storage.init_schema().unwrap();
(dir, SqlDocumentStore::new(storage))
}
#[derive(serde::Serialize)]
struct Note {
kind: String,
text: String,
}
impl PromotableDocument for Note {
fn record_type(&self) -> Option<String> {
Some(self.kind.clone())
}
fn summary(&self) -> Option<String> {
Some(self.text.clone())
}
}
#[test]
fn append_promotable_round_trips_through_sqlite() {
let (_dir, store) = make_store();
let doc = store
.append_promotable(
"k",
Note {
kind: "observation".into(),
text: "saw a cat".into(),
},
)
.unwrap();
assert_eq!(doc.id.len(), 25, "scru128 doc_id");
assert_eq!(doc.record_type.as_deref(), Some("observation"));
assert_eq!(doc.summary.as_deref(), Some("saw a cat"));
assert_eq!(doc.title, None);
let docs = store.scan_documents("k", 10).unwrap();
assert_eq!(docs.len(), 1);
assert_eq!(docs[0].id, doc.id);
assert_eq!(docs[0].record_type.as_deref(), Some("observation"));
assert_eq!(docs[0].summary.as_deref(), Some("saw a cat"));
assert_eq!(docs[0].title, None);
}
#[test]
fn plain_append_leaves_promoted_columns_null() {
let (_dir, store) = make_store();
store.append("k", serde_json::json!({"n": 1})).unwrap();
let docs = store.scan_documents("k", 10).unwrap();
assert_eq!(docs.len(), 1);
assert_eq!(docs[0].record_type, None);
assert_eq!(docs[0].title, None);
assert_eq!(docs[0].summary, None);
}
#[test]
fn scan_documents_from_is_inclusive_and_ordered_by_doc_id() {
let (_dir, store) = make_store();
let ids: Vec<String> = (0..4)
.map(|n| {
store
.append_promotable(
"k",
Note {
kind: "n".into(),
text: format!("t{n}"),
},
)
.unwrap()
.id
})
.collect();
let docs = store.scan_documents_from("k", &ids[1], 0).unwrap();
assert_eq!(docs.len(), 3);
assert_eq!(docs[0].id, ids[1]);
assert_eq!(docs[0].summary.as_deref(), Some("t1"));
assert_eq!(docs[2].summary.as_deref(), Some("t3"));
let limited = store.scan_documents_from("k", &ids[0], 2).unwrap();
assert_eq!(limited.len(), 2);
}
#[test]
fn scan_documents_is_newest_first() {
let (_dir, store) = make_store();
let mut ids = Vec::new();
for n in 0..3 {
ids.push(
store
.append("k", serde_json::json!({ "n": n }))
.unwrap()
.id,
);
}
let docs = store.scan_documents("k", 10).unwrap();
let got: Vec<&String> = docs.iter().map(|d| &d.id).collect();
assert_eq!(got, vec![&ids[2], &ids[1], &ids[0]]);
}
#[test]
fn migration_runner_applies_all_then_is_idempotent() {
use foundation_db::{MigrationRunner, MIGRATIONS};
init_valtron();
let dir = TempDir::new().unwrap();
let url = dir.path().join("migrate.db");
let storage = TursoStorage::new(url.to_str().unwrap()).unwrap();
let applied = MigrationRunner::new(MIGRATIONS).run(&storage).unwrap();
assert_eq!(applied, MIGRATIONS.len(), "first run applies all migrations");
let reapplied = MigrationRunner::new(MIGRATIONS).run(&storage).unwrap();
assert_eq!(reapplied, 0, "re-run is a no-op (tracked in _migrations)");
let docs = SqlDocumentStore::new(storage);
docs.append("k", serde_json::json!({"n": 1})).unwrap();
assert_eq!(docs.scan_documents("k", 10).unwrap().len(), 1);
}