diff --git a/packages/server/src/utils/userDeletion.homeOrphaned.test.ts b/packages/server/src/utils/userDeletion.homeOrphaned.test.ts new file mode 100644 index 00000000..e62d8ed3 --- /dev/null +++ b/packages/server/src/utils/userDeletion.homeOrphaned.test.ts @@ -0,0 +1,54 @@ +import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; +import Database from 'better-sqlite3'; +import { drizzle } from 'drizzle-orm/better-sqlite3'; +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import * as schema from '../db/schema.js'; +import { eq } from 'drizzle-orm'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +type TestDb = ReturnType>; +let sqlite: Database.Database; +let testDb: TestDb; + +vi.mock('../db/index.js', () => ({ getDb: () => testDb, getRawDb: () => sqlite, schema })); + +function applyMigrations(db: Database.Database): void { + const dir = path.resolve(__dirname, '../../drizzle'); + for (const f of fs.readdirSync(dir).filter(x => x.endsWith('.sql')).sort()) { + const sqlText = fs.readFileSync(path.join(dir, f), 'utf8'); + for (const stmt of sqlText.split(/-->\s*statement-breakpoint/)) { + const clean = stmt.trim(); + if (clean) db.exec(clean); + } + } +} + +describe('tombstoneUser — federation_home_orphaned cleanup', () => { + beforeEach(() => { + sqlite = new Database(':memory:'); + testDb = drizzle(sqlite, { schema }); + applyMigrations(sqlite); + vi.clearAllMocks(); + }); + + afterEach(() => { + sqlite.close(); + }); + + it('clears federation_home_orphaned when tombstoning', async () => { + const { getDb } = await import('../db/index.js'); + const { tombstoneUser } = await import('./userDeletion.js'); + const db = getDb(); + const uid = 'user_test_orphan'; + db.insert(schema.users).values({ + id: uid, username: 'carol@remote.example', passwordHash: 'x', + createdAt: Date.now(), federationHomeOrphaned: 1, + }).run(); + tombstoneUser(uid, { purgeContent: false }); + const row = db.select().from(schema.users).where(eq(schema.users.id, uid)).get(); + expect(row?.federationHomeOrphaned ?? 0).toBe(0); + expect(row?.isDeleted).toBe(1); + }); +}); diff --git a/packages/server/src/utils/userDeletion.ts b/packages/server/src/utils/userDeletion.ts index 83fb1728..a391b5f3 100644 --- a/packages/server/src/utils/userDeletion.ts +++ b/packages/server/src/utils/userDeletion.ts @@ -239,6 +239,7 @@ export function tombstoneUser(uid: string, options?: TombstoneOptions): string[] isDeleted: 1, status: 'offline', isAdmin: 0, + federationHomeOrphaned: 0, }).where(eq(schema.users.id, uid)).run(); });