Menu
Public repository / 3503b684768a57d3fa15dd6fd3c2af86320c5b56
AkurAI-DNS / src/schema.rs
Latest change 3503b684768a57d3fa15dd6fd3c2af86320c5b56 - feat: add domain_ownerships table and CRUD API with tenant isolation (AKURAI-DNS-5) by agent
use rusqlite::{Connection, Result};
/// Run all schema migrations idempotently.
pub fn migrate(conn: &Connection) -> Result<()> {
conn.execute_batch(
"CREATE TABLE IF NOT EXISTS domain_ownerships (
id INTEGER PRIMARY KEY AUTOINCREMENT,
org_id TEXT NOT NULL,
workspace_id TEXT NOT NULL,
domain TEXT NOT NULL UNIQUE,
state TEXT NOT NULL DEFAULT 'requested'
CHECK(state IN (
'requested','verifying','provisioned',
'renewing','failed','retired'
)),
verification_token TEXT,
verified_at TEXT,
provisioned_at TEXT,
expires_at TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_domain_ownerships_workspace
ON domain_ownerships(org_id, workspace_id);",
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn migration_is_idempotent() {
let conn = Connection::open_in_memory().unwrap();
migrate(&conn).unwrap();
migrate(&conn).unwrap(); // second call must not fail
}
#[test]
fn schema_has_expected_columns() {
let conn = Connection::open_in_memory().unwrap();
migrate(&conn).unwrap();
let mut stmt = conn
.prepare("SELECT id, org_id, workspace_id, domain, state, verification_token, verified_at, provisioned_at, expires_at, created_at, updated_at FROM domain_ownerships")
.unwrap();
let cols: Vec<String> = stmt.column_names().iter().map(|c| c.to_string()).collect();
assert!(cols.contains(&"org_id".into()));
assert!(cols.contains(&"workspace_id".into()));
assert!(cols.contains(&"domain".into()));
assert!(cols.contains(&"state".into()));
}
}