AkurAI Build
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()));
    }
}