From e95d3ef0650682ca9d2407a3ecdb998ff4abcbc6 Mon Sep 17 00:00:00 2001 From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com> Date: Sun, 3 May 2026 23:20:59 +0200 Subject: [PATCH] test(harness): read-only DB inspector helpers --- packages/server/test/helpers/dbInspect.ts | 66 +++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 packages/server/test/helpers/dbInspect.ts diff --git a/packages/server/test/helpers/dbInspect.ts b/packages/server/test/helpers/dbInspect.ts new file mode 100644 index 00000000..465ad583 --- /dev/null +++ b/packages/server/test/helpers/dbInspect.ts @@ -0,0 +1,66 @@ +import Database from 'better-sqlite3'; +import type { SpawnedInstance } from './twoInstanceHarness.js'; + +export interface UserRow { + id: string; + username: string; + passwordHash: string; + displayName: string | null; + avatar: string | null; + banner: string | null; + bio: string | null; + customStatus: string | null; + accentColor: string | null; + avatarColor: string | null; + replicatedInstances: string; + isDeleted: number; + status: string; + isAdmin: number; + homeInstance: string | null; + homeUserId: string | null; +} + +export interface DbInspector { + user(uid: string): UserRow | null; + userByUsername(username: string): UserRow | null; + spaceMembersForUser(uid: string): { spaceId: string; userId: string }[]; + reactionsForUser(uid: string): { messageId: string; userId: string; emoji: string }[]; + dmReactionsForUser(uid: string): { dmMessageId: string; userId: string; emoji: string }[]; + messagesAuthored(uid: string): { id: string; userId: string; content: string }[]; + dmMembership(uid: string): { dmChannelId: string; userId: string }[]; + dmChannelExists(dmChannelId: string): boolean; + registryRow(userId: string, origin: string): unknown; + replicatedInstancesArray(userId: string): { origin: string }[]; + close(): void; +} + +export function openInspector(instance: SpawnedInstance): DbInspector { + const db = new Database(instance.dbPath, { readonly: true, fileMustExist: true }); + db.pragma('journal_mode = WAL'); + return { + user: (uid) => db.prepare('SELECT * FROM users WHERE id = ?').get(uid) as UserRow | null, + userByUsername: (u) => db.prepare('SELECT * FROM users WHERE username = ?').get(u) as UserRow | null, + spaceMembersForUser: (uid) => + db.prepare('SELECT space_id AS spaceId, user_id AS userId FROM space_members WHERE user_id = ?').all(uid) as { spaceId: string; userId: string }[], + reactionsForUser: (uid) => + db.prepare('SELECT message_id AS messageId, user_id AS userId, emoji FROM reactions WHERE user_id = ?').all(uid) as { messageId: string; userId: string; emoji: string }[], + dmReactionsForUser: (uid) => + db.prepare('SELECT dm_message_id AS dmMessageId, user_id AS userId, emoji FROM dm_reactions WHERE user_id = ?').all(uid) as { dmMessageId: string; userId: string; emoji: string }[], + messagesAuthored: (uid) => + db.prepare('SELECT id, user_id AS userId, content FROM messages WHERE user_id = ?').all(uid) as { id: string; userId: string; content: string }[], + dmMembership: (uid) => + db.prepare('SELECT dm_channel_id AS dmChannelId, user_id AS userId FROM dm_members WHERE user_id = ?').all(uid) as { dmChannelId: string; userId: string }[], + dmChannelExists: (id) => { + const row = db.prepare('SELECT 1 AS one FROM dm_channels WHERE id = ?').get(id); + return !!row; + }, + registryRow: (userId, origin) => + db.prepare('SELECT * FROM user_federation_registry WHERE user_id = ? AND origin = ?').get(userId, origin), + replicatedInstancesArray: (userId) => { + const row = db.prepare('SELECT replicated_instances AS replicatedInstances FROM users WHERE id = ?').get(userId) as { replicatedInstances: string } | undefined; + if (!row?.replicatedInstances) return []; + try { return JSON.parse(row.replicatedInstances); } catch { return []; } + }, + close: () => db.close(), + }; +}