From bc84eb2917077b6c04759da93c044920ab99d78d Mon Sep 17 00:00:00 2001 From: xRangerDE Date: Mon, 11 May 2026 22:11:32 +0200 Subject: [PATCH] feat(db): add oauth_accounts table to schema v5 (2 new tests) UNIQUE(provider, twitch_user_id), indices on provider + is_default. 108 unit tests gruen. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/main/infra/db.test.ts | 32 ++++++++++++++++++++++++++++++++ src/main/infra/schema-v5.ts | 19 +++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/src/main/infra/db.test.ts b/src/main/infra/db.test.ts index a609eb2..6577918 100644 --- a/src/main/infra/db.test.ts +++ b/src/main/infra/db.test.ts @@ -72,6 +72,38 @@ describe('openDatabase', () => { expect(c?.c).toBe(2); }); + test('oauth_accounts table exists and accepts insert', () => { + db = openDatabase(path.join(tmpDir, 'oauth.db')); + db.run( + `INSERT INTO oauth_accounts(provider, twitch_user_id, login, encrypted_access_token) + VALUES (?, ?, ?, ?)`, + ['twitch', 'user-123', 'alice', 'ciphertext-blob'] + ); + const row = db.get<{ login: string; provider: string }>( + 'SELECT login, provider FROM oauth_accounts WHERE twitch_user_id = ?', + ['user-123'] + ); + expect(row?.login).toBe('alice'); + expect(row?.provider).toBe('twitch'); + }); + + test('oauth_accounts UNIQUE(provider, twitch_user_id) enforced', () => { + db = openDatabase(path.join(tmpDir, 'oauth-unique.db')); + db.run( + `INSERT INTO oauth_accounts(provider, twitch_user_id, login, encrypted_access_token) + VALUES (?, ?, ?, ?)`, + ['twitch', 'u1', 'a', 'x'] + ); + const handle = db; + expect(() => { + handle.run( + `INSERT INTO oauth_accounts(provider, twitch_user_id, login, encrypted_access_token) + VALUES (?, ?, ?, ?)`, + ['twitch', 'u1', 'b', 'y'] + ); + }).toThrow(); + }); + test('transaction rolls back on throw', () => { db = openDatabase(path.join(tmpDir, 'g.db')); const handle = db; diff --git a/src/main/infra/schema-v5.ts b/src/main/infra/schema-v5.ts index e30738a..0df1612 100644 --- a/src/main/infra/schema-v5.ts +++ b/src/main/infra/schema-v5.ts @@ -64,6 +64,25 @@ CREATE TABLE IF NOT EXISTS archive_files ( CREATE INDEX IF NOT EXISTS idx_archive_streamer ON archive_files(streamer_login); +CREATE TABLE IF NOT EXISTS oauth_accounts ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + provider TEXT NOT NULL, + twitch_user_id TEXT, + login TEXT, + display_name TEXT, + encrypted_access_token TEXT NOT NULL, + encrypted_refresh_token TEXT, + expires_at INTEGER, + scopes_json TEXT, + is_default INTEGER NOT NULL DEFAULT 0, + created_at INTEGER NOT NULL DEFAULT (strftime('%s','now')), + updated_at INTEGER NOT NULL DEFAULT (strftime('%s','now')), + UNIQUE(provider, twitch_user_id) +); + +CREATE INDEX IF NOT EXISTS idx_oauth_provider ON oauth_accounts(provider); +CREATE INDEX IF NOT EXISTS idx_oauth_default ON oauth_accounts(is_default); + CREATE TABLE IF NOT EXISTS migrations_applied ( name TEXT PRIMARY KEY, applied_at INTEGER NOT NULL DEFAULT (strftime('%s','now')),