Files
auth-portal/packages/db/test/portal-revocations.test.ts

48 lines
2.8 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import {
createMemoryDb, migrateSchema, closeDb, createUser, getUserById,
getUserPermissions, setUserAccess, updateUser, deleteUser,
createRefreshSession, revokeRefreshSession, revokeSessionById, revokeAllSessionsForUser,
enablePortalRevocations,
} from '../src/index.js'
const target = 'https://mm.example.invalid/_internal/mcp/portal/revocations'
describe('integrated portal revocation outbox', () => {
it('opts in explicitly and retains deletion event independently of user FK', () => {
const { db, sqlite } = createMemoryDb(); migrateSchema(sqlite)
try {
const user = createUser(db, {email:'owned@example.invalid',name:'owned',passwordHash:'synthetic',isAdmin:false})
setUserAccess(db,user.id,['mm'],['old'])
expect(sqlite.prepare('SELECT count(*) AS n FROM portal_revocation_outbox').get()).toEqual({n:0})
enablePortalRevocations(db,target)
setUserAccess(db,user.id,['mm'],['new']); deleteUser(db,user.id)
const rows = sqlite.prepare('SELECT body FROM portal_revocation_outbox ORDER BY sequence').all() as {body:string}[]
expect(rows.map(row=>JSON.parse(row.body).type)).toEqual(['permissions.changed','user.disabled'])
for (const row of rows) expect(JSON.parse(row.body)).toMatchObject({version:2,userId:user.id})
expect(getUserById(db,user.id)).toBeUndefined()
} finally { closeDb(sqlite) }
})
it('enqueue failure prevents success of every genuine access/disable/delete/session mutation', () => {
const { db, sqlite } = createMemoryDb(); migrateSchema(sqlite)
try {
const user = createUser(db,{email:'atomic@example.invalid',name:'owned',passwordHash:'synthetic',isAdmin:false})
setUserAccess(db,user.id,['mm'],['old'])
const session = createRefreshSession(db,user.id,'owned-refresh',new Date(Date.now()+60000))
enablePortalRevocations(db,target)
sqlite.exec("CREATE TRIGGER deny_revocation BEFORE INSERT ON portal_revocation_outbox BEGIN SELECT RAISE(ABORT,'OWNED_ENQUEUE_FAILURE'); END")
for (const mutation of [
()=>setUserAccess(db,user.id,[],[]), ()=>updateUser(db,user.id,{isAdmin:true}),
()=>updateUser(db,user.id,{disabled:true}), ()=>deleteUser(db,user.id),
()=>revokeSessionById(db,session), ()=>revokeRefreshSession(db,'owned-refresh'),
()=>revokeAllSessionsForUser(db,user.id),
]) {
expect(mutation).toThrow('OWNED_ENQUEUE_FAILURE')
expect(getUserById(db,user.id)).toMatchObject({isAdmin:false,disabled:false})
expect(getUserPermissions(db,user.id)).toEqual(['old'])
expect(sqlite.prepare('SELECT revoked_at FROM refresh_sessions WHERE id=?').get(session)).toEqual({revoked_at:null})
expect(sqlite.prepare('SELECT count(*) AS n FROM portal_revocation_outbox').get()).toEqual({n:0})
}
} finally { closeDb(sqlite) }
})
})