Record standby subscription events with signed durable receipts
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
import sqlite3
|
||||
import tempfile
|
||||
import unittest
|
||||
import hashlib
|
||||
|
||||
spec = importlib.util.spec_from_file_location("migration", Path(__file__).with_name("migrate_subscription_events.py"))
|
||||
migration = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(migration)
|
||||
|
||||
|
||||
class SubscriptionEventsMigrationTest(unittest.TestCase):
|
||||
def test_explicit_backup_atomic_and_idempotent(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
path = Path(directory) / "main.db"
|
||||
with sqlite3.connect(path) as db:
|
||||
db.executescript("""
|
||||
CREATE TABLE User(id INTEGER PRIMARY KEY,username TEXT);
|
||||
CREATE TABLE sub_bind(key TEXT,user TEXT,user_id INTEGER);
|
||||
CREATE TABLE sub_update_record(id INTEGER PRIMARY KEY,user_id INTEGER,time INTEGER);
|
||||
INSERT INTO User VALUES(3,'alice');
|
||||
INSERT INTO sub_bind VALUES('public-test-key','alice',3);
|
||||
""")
|
||||
dry = migration.migrate(path)
|
||||
self.assertEqual(2, len(dry["tables_to_create"]))
|
||||
with self.assertRaises(ValueError):
|
||||
migration.migrate(path, True)
|
||||
first = migration.migrate(path, True, True)
|
||||
self.assertEqual("ok", first["integrity"])
|
||||
self.assertEqual(0o600, Path(first["backup"]).stat().st_mode & 0o777)
|
||||
with sqlite3.connect(first["backup"]) as db:
|
||||
self.assertEqual(0, db.execute("SELECT count(*) FROM sqlite_master WHERE name='subscription_event_binding'").fetchone()[0])
|
||||
with sqlite3.connect(path) as db:
|
||||
row = db.execute("SELECT key_hash,user_id,user_name FROM subscription_event_binding").fetchone()
|
||||
self.assertEqual((hashlib.sha256(b"public-test-key").hexdigest(), 3, "alice"), row)
|
||||
db.execute("INSERT INTO subscription_event_receipt VALUES('id','hash','v2',0,'primary',1,'fingerprint','recorded')")
|
||||
second = migration.migrate(path, True, True)
|
||||
self.assertEqual([], second["tables_to_create"])
|
||||
with sqlite3.connect(path) as db:
|
||||
self.assertEqual(1, db.execute("SELECT count(*) FROM subscription_event_receipt").fetchone()[0])
|
||||
self.assertEqual(1, db.execute("SELECT count(*) FROM subscription_event_binding").fetchone()[0])
|
||||
|
||||
def test_legacy_labels_without_user_ids_remain_null(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
path = Path(directory) / "main.db"
|
||||
with sqlite3.connect(path) as db:
|
||||
db.executescript("""
|
||||
CREATE TABLE User(id INTEGER PRIMARY KEY,username TEXT);
|
||||
CREATE TABLE sub_bind(key TEXT,user TEXT,user_id INTEGER);
|
||||
CREATE TABLE sub_update_record(id INTEGER PRIMARY KEY,user_id INTEGER,time INTEGER);
|
||||
INSERT INTO sub_bind VALUES('legacy-key','legacy-label',NULL);
|
||||
""")
|
||||
result = migration.migrate(path, True, True)
|
||||
self.assertEqual(1, result["history_bindings"])
|
||||
with sqlite3.connect(path) as db:
|
||||
row = db.execute("SELECT key_hash,user_id,user_name FROM subscription_event_binding").fetchone()
|
||||
self.assertEqual((hashlib.sha256(b"legacy-key").hexdigest(), None, "legacy-label"), row)
|
||||
self.assertIsNone(db.execute("SELECT user_id FROM sub_bind").fetchone()[0])
|
||||
self.assertEqual(0, db.execute("SELECT count(*) FROM User").fetchone()[0])
|
||||
migration.migrate(path, True, True)
|
||||
with sqlite3.connect(path) as db:
|
||||
self.assertEqual(1, db.execute("SELECT count(*) FROM subscription_event_binding").fetchone()[0])
|
||||
|
||||
def test_orphan_binding_aborts_without_ddl(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
path = Path(directory) / "main.db"
|
||||
with sqlite3.connect(path) as db:
|
||||
db.executescript("""
|
||||
CREATE TABLE User(id INTEGER PRIMARY KEY,username TEXT);
|
||||
CREATE TABLE sub_bind(key TEXT,user TEXT,user_id INTEGER);
|
||||
CREATE TABLE sub_update_record(id INTEGER PRIMARY KEY,user_id INTEGER,time INTEGER);
|
||||
INSERT INTO sub_bind VALUES('test','deleted',9);
|
||||
""")
|
||||
with self.assertRaises(ValueError):
|
||||
migration.migrate(path, True, True)
|
||||
with sqlite3.connect(path) as db:
|
||||
self.assertEqual(0, db.execute("SELECT count(*) FROM sqlite_master WHERE name LIKE 'subscription_event_%'").fetchone()[0])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user