use gradatum_admin::BackfillArgs;
use gradatum_index::SqliteIndex;
use tempfile::TempDir;
async fn setup_root() -> (std::path::PathBuf, TempDir) {
let tmp = TempDir::new().expect("TempDir");
let root = tmp.path().to_path_buf();
std::fs::create_dir_all(root.join("db")).expect("mkdir db");
std::fs::create_dir_all(root.join("vault/.gradatum")).expect("mkdir vault/.gradatum");
let index_path = root.join("vault/.gradatum/index.db");
SqliteIndex::open(&index_path)
.await
.expect("SqliteIndex::open pour init schéma");
let queue_path = root.join("db/queue.sqlite");
gradatum_queue::SqliteQueue::new(&queue_path)
.await
.expect("SqliteQueue::new pour init schéma");
(root, tmp)
}
fn insert_note(conn: &rusqlite::Connection, note_id: &str, vault_id: &str, body_text: &str) {
let hash = vec![0u8; 32];
conn.execute(
"INSERT INTO notes (
id, vault_id, locus, section, status, schema_version,
author_kind, author_id, author_display_name,
created, updated, status_changed, status_reason,
content_hash, version, body_text, integrity_signature, extra_json, tags
) VALUES (?1, ?2, NULL, ?3, ?4, ?5, NULL, NULL, NULL, ?6, NULL, NULL, NULL, ?7, ?8, ?9, NULL, NULL, NULL)",
rusqlite::params![
note_id,
vault_id,
"reference", "indexed", 1i64, 1_700_000_000_000i64, hash,
1i64, body_text,
],
)
.expect("INSERT note test");
}
fn insert_embedding(conn: &rusqlite::Connection, note_id: &str, embedder_id: &str) {
let vector = 0f32.to_le_bytes().to_vec();
conn.execute(
"INSERT INTO note_embeddings (note_id, embedder_id, vector, dim, model_version, computed_at)
VALUES (?1, ?2, ?3, ?4, NULL, ?5)",
rusqlite::params![
note_id,
embedder_id,
vector,
1i64,
1_700_000_000_000i64,
],
)
.expect("INSERT note_embeddings test");
}
#[tokio::test]
async fn backfill_zero_notes_returns_zero() {
let (root, _tmp) = setup_root().await;
let args = BackfillArgs {
root,
tenant: Some("main".to_string()),
limit: None,
};
let count = gradatum_admin::backfill(args).await.expect("backfill");
assert_eq!(count, 0, "aucune note → 0 jobs enqueued");
}
#[tokio::test]
async fn backfill_idempotent_re_run_skips_embedded() {
let (root, _tmp) = setup_root().await;
let index_path = root.join("vault/.gradatum/index.db");
{
let conn = rusqlite::Connection::open(&index_path).expect("open index");
insert_note(&conn, "01AAAA", "main", "Corps de la note 1");
insert_note(&conn, "01BBBB", "main", "Corps de la note 2");
insert_note(&conn, "01CCCC", "main", "Corps de la note 3");
insert_embedding(&conn, "01AAAA", "bge-small");
insert_embedding(&conn, "01BBBB", "bge-small");
}
let args1 = BackfillArgs {
root: root.clone(),
tenant: Some("main".to_string()),
limit: None,
};
let count1 = gradatum_admin::backfill(args1)
.await
.expect("backfill run 1");
assert_eq!(count1, 1, "run 1 : 1 note sans embedding → 1 job enqueued");
{
let conn = rusqlite::Connection::open(&index_path).expect("open index");
insert_embedding(&conn, "01CCCC", "bge-small");
}
let args2 = BackfillArgs {
root: root.clone(),
tenant: Some("main".to_string()),
limit: None,
};
let count2 = gradatum_admin::backfill(args2)
.await
.expect("backfill run 2");
assert_eq!(
count2, 0,
"run 2 idempotent : 0 notes sans embedding → 0 jobs"
);
}