import { describe, it, expect, beforeEach } from 'vitest'; import type Database from 'better-sqlite3'; import { allocateMemberNumber, createUser, updateUserRole, deactivateUser } from '../src/lib/db.js'; const db = (globalThis as typeof globalThis & { __bifrost_db?: Database.Database }).__bifrost_db!; function makeUser(name: string, role: 'pilot' | 'cab' | 'fenja' = 'pilot'): number { return createUser({ email: `${name}@x.test`, password_hash: 'x', name, organisation: 'TestOrg', role }); } beforeEach(() => { db.exec(` DELETE FROM votes; DELETE FROM pulses; DELETE FROM activity; DELETE FROM dispatches; DELETE FROM users; `); }); describe('allocateMemberNumber', () => { it('allocates 1 for the first cab user and sequential numbers thereafter', () => { const a = makeUser('alice', 'cab'); const b = makeUser('bob', 'cab'); const c = makeUser('cara', 'cab'); expect(db.prepare('SELECT member_number FROM users WHERE id=?').get(a)).toEqual({ member_number: 1 }); expect(db.prepare('SELECT member_number FROM users WHERE id=?').get(b)).toEqual({ member_number: 2 }); expect(db.prepare('SELECT member_number FROM users WHERE id=?').get(c)).toEqual({ member_number: 3 }); }); it('is idempotent — calling allocate on a user who already has a number returns the same one', () => { const a = makeUser('alice', 'cab'); const first = (db.prepare('SELECT member_number AS n FROM users WHERE id=?').get(a) as { n: number }).n; const reAllocated = allocateMemberNumber(a); expect(reAllocated).toBe(first); }); it('never reuses: deactivating a holder does not free their number', () => { const a = makeUser('alice', 'cab'); // #1 const b = makeUser('bob', 'cab'); // #2 deactivateUser(a); const c = makeUser('cara', 'cab'); // expects #3, not #1 expect((db.prepare('SELECT member_number AS n FROM users WHERE id=?').get(c) as { n: number }).n).toBe(3); }); it('does not allocate when a non-cab user is created', () => { const a = makeUser('alice', 'pilot'); expect(db.prepare('SELECT member_number FROM users WHERE id=?').get(a)).toEqual({ member_number: null }); }); it('allocates on role transition pilot → cab and only the first time', () => { const a = makeUser('alice', 'pilot'); const r1 = updateUserRole(a, 'cab'); expect(r1.allocated).toBe(1); // transition back and forth must not change the number updateUserRole(a, 'pilot'); const r2 = updateUserRole(a, 'cab'); expect(r2.allocated).toBeNull(); expect((db.prepare('SELECT member_number AS n FROM users WHERE id=?').get(a) as { n: number }).n).toBe(1); }); });