import { identityApp, type IdentityApp } from './helpers/identity-app'; import { legacyOrganizationId } from './helpers/test-database'; import { RecoveryStore } from '../src/identity/recovery.store'; import { issueToken } from '../src/identity/tokens'; describe('migration integrity', () => { let ctx: IdentityApp; beforeAll(async () => { ctx = await identityApp(); }, 60000); afterAll(async () => { await ctx?.close(); }); it('preserves organization rows from the foundation migration', async () => { expect( await ctx.db.organization.findUnique({ where: { id: legacyOrganizationId }, }), ).toMatchObject({ name: 'Existing organization' }); }); it('enforces normalized emails in the database', async () => { await expect( ctx.db.user.create({ data: { organizationId: ctx.owner.organizationId, email: 'UPPER@example.com', name: 'Bad', passwordHash: 'hash', }, }), ).rejects.toThrow(); }); it('prevents a second owner even when the API is bypassed', async () => { await expect( ctx.db.user.create({ data: { organizationId: ctx.owner.organizationId, email: 'second@example.com', name: 'Second', passwordHash: 'hash', isOwner: true, }, }), ).rejects.toThrow(); }); it('rejects audit modification and deletion', async () => { const audit = await ctx.db.auditEvent.findFirstOrThrow(); await expect( ctx.db.auditEvent.update({ where: { id: audit.id }, data: { action: 'tampered' }, }), ).rejects.toThrow(); await expect( ctx.db.auditEvent.delete({ where: { id: audit.id } }), ).rejects.toThrow(); expect( await ctx.db.auditEvent.findUnique({ where: { id: audit.id } }), ).not.toBeNull(); }); it('discards failed-delivery recovery tokens', async () => { const token = issueToken(); const store = ctx.app.get(RecoveryStore); await store.create( ctx.owner.userId, token.tokenHash, new Date(Date.now() + 60000), ); await store.discard(token.tokenHash); expect( await ctx.db.recoveryToken.findUnique({ where: { tokenHash: token.tokenHash }, }), ).toBeNull(); }); const nativeOnly = process.env.TEST_DATABASE_URL ? it : it.skip; nativeOnly( 'allows exactly one winner for concurrent recovery token consumption on native PostgreSQL', async () => { const token = issueToken(); const store = ctx.app.get(RecoveryStore); await store.create( ctx.owner.userId, token.tokenHash, new Date(Date.now() + 60000), ); const results = await Promise.allSettled([ store.reset(token.tokenHash, 'hash-one'), store.reset(token.tokenHash, 'hash-two'), ]); expect( results.filter((result) => result.status === 'fulfilled'), ).toHaveLength(1); expect( results.filter((result) => result.status === 'rejected'), ).toHaveLength(1); }, ); });