convergio_backup/
schema.rs1use convergio_types::extension::Migration;
6
7pub fn migrations() -> Vec<Migration> {
8 vec![Migration {
9 version: 1,
10 description: "backup tables",
11 up: "\
12CREATE TABLE IF NOT EXISTS backup_snapshots (
13 id TEXT PRIMARY KEY,
14 path TEXT NOT NULL,
15 size_bytes INTEGER NOT NULL DEFAULT 0,
16 checksum TEXT NOT NULL,
17 node TEXT NOT NULL,
18 created_at TEXT NOT NULL DEFAULT (datetime('now'))
19);
20CREATE INDEX IF NOT EXISTS idx_backup_snap_date
21 ON backup_snapshots(created_at);
22
23CREATE TABLE IF NOT EXISTS backup_retention_rules (
24 id INTEGER PRIMARY KEY AUTOINCREMENT,
25 table_name TEXT NOT NULL,
26 timestamp_column TEXT NOT NULL DEFAULT 'created_at',
27 max_age_days INTEGER NOT NULL,
28 org_id TEXT NOT NULL DEFAULT '__global__',
29 created_at TEXT NOT NULL DEFAULT (datetime('now')),
30 UNIQUE(table_name, org_id)
31);
32CREATE TABLE IF NOT EXISTS backup_purge_log (
33 id INTEGER PRIMARY KEY AUTOINCREMENT,
34 table_name TEXT NOT NULL,
35 rows_deleted INTEGER NOT NULL,
36 cutoff_date TEXT NOT NULL,
37 executed_at TEXT NOT NULL DEFAULT (datetime('now'))
38);
39CREATE INDEX IF NOT EXISTS idx_purge_log_date
40 ON backup_purge_log(executed_at);",
41 }]
42}
43
44#[cfg(test)]
45mod tests {
46 use super::*;
47
48 #[test]
49 fn migrations_have_sequential_versions() {
50 let migs = migrations();
51 assert_eq!(migs.len(), 1);
52 assert_eq!(migs[0].version, 1);
53 }
54
55 #[test]
56 fn migrations_apply_to_sqlite() {
57 let conn = rusqlite::Connection::open_in_memory().unwrap();
58 for m in migrations() {
59 conn.execute_batch(m.up).unwrap();
60 }
61 let count: i64 = conn
62 .query_row(
63 "SELECT COUNT(*) FROM sqlite_master WHERE type='table' \
64 AND name LIKE 'backup_%'",
65 [],
66 |r| r.get(0),
67 )
68 .unwrap();
69 assert_eq!(count, 3);
70 }
71
72 #[test]
73 fn retention_rules_table_has_unique_constraint() {
74 let conn = rusqlite::Connection::open_in_memory().unwrap();
75 for m in migrations() {
76 conn.execute_batch(m.up).unwrap();
77 }
78 conn.execute(
79 "INSERT INTO backup_retention_rules \
80 (table_name, max_age_days, org_id) \
81 VALUES ('audit_log', 365, '__global__')",
82 [],
83 )
84 .unwrap();
85 let result = conn.execute(
87 "INSERT INTO backup_retention_rules \
88 (table_name, max_age_days, org_id) \
89 VALUES ('audit_log', 30, '__global__')",
90 [],
91 );
92 assert!(result.is_err());
93 }
94}