/** * Seat recovery codes — Gitea#33. * * The properties worth pinning are the ones that make a code safe to put in a link: it is spendable * exactly once, it stops working on its own, and a bad code is indistinguishable from a spent one. * `now` is a parameter rather than a clock, so expiry is tested without faking timers. */ import { describe, it } from 'node:test'; import assert from 'node:assert/strict'; import { CLAIM_TTL_MS, createClaimStore } from '../../src/server/claims.ts'; describe('seat recovery codes', () => { it('mints a code that names the seat it was minted for', () => { const claims = createClaimStore(); const { code, expiresAt } = claims.mint('tok-abc', 'game-1', 1000); assert.equal(expiresAt, 1000 + CLAIM_TTL_MS); assert.deepEqual(claims.redeem(code, 1000), { token: 'tok-abc', gameId: 'game-1' }); }); it('spends a code exactly once — a link in a chat log is worth nothing afterwards', () => { const claims = createClaimStore(); const { code } = claims.mint('tok-abc', 'game-1', 0); assert.ok(claims.redeem(code, 1)); assert.equal(claims.redeem(code, 2), null, 'the same code was accepted twice'); }); it('stops working once its time is up, without anything having to sweep it', () => { const claims = createClaimStore(); const { code } = claims.mint('tok-abc', 'game-1', 0); assert.equal(claims.redeem(code, CLAIM_TTL_MS - 1)?.token, 'tok-abc', 'expired early'); const again = claims.mint('tok-abc', 'game-1', 0).code; assert.equal(claims.redeem(again, CLAIM_TTL_MS), null, 'a code outlived its expiry'); }); it('answers the same way for unknown, spent and expired codes', () => { const claims = createClaimStore(); const { code } = claims.mint('tok-abc', 'game-1', 0); claims.redeem(code, 1); const expired = claims.mint('tok-abc', 'game-1', 0).code; assert.equal(claims.redeem('never-existed', 1), null); assert.equal(claims.redeem(code, 1), null); assert.equal(claims.redeem(expired, CLAIM_TTL_MS + 1), null); }); it('gives every mint its own code', () => { const claims = createClaimStore(); const codes = new Set([0, 1, 2, 3, 4].map(() => claims.mint('tok-abc', 'game-1', 0).code)); assert.equal(codes.size, 5, 'two mints produced the same code'); }); it('forgets expired codes rather than accumulating them', () => { const claims = createClaimStore(); claims.mint('tok-a', 'game-1', 0); claims.mint('tok-b', 'game-1', 0); assert.equal(claims.outstanding(0), 2); assert.equal(claims.outstanding(CLAIM_TTL_MS), 0, 'expired codes were still being held'); }); it('keeps a short-lived code short-lived when asked for one', () => { const claims = createClaimStore(); const { code, expiresAt } = claims.mint('tok-abc', 'game-1', 500, 60_000); assert.equal(expiresAt, 60_500); assert.equal(claims.redeem(code, 60_500), null); }); });