import { test } from 'node:test'; import assert from 'node:assert/strict'; import { eq } from 'drizzle-orm'; import { fixture } from './helpers.js'; import { newInviteCode } from '../src/inviteCode.js'; import { loadConfig } from '../src/config.js'; import { hmac } from '../src/security.js'; import * as s from '../src/db/schema.js'; test('짧은 초대 코드는 충돌 시 다시 생성하고 반복 충돌은 제한한다', () => { let calls = 0; const code = newInviteCode( 4, (value) => value === 'AAAA', () => (calls++ < 4 ? 0 : 1), ); assert.equal(code, 'BBBB'); let attempts = 0; assert.equal( newInviteCode( 4, () => { attempts++; return true; }, () => 0, ), null, ); assert.equal(attempts, 32); assert.equal(loadConfig({ SPACE_INVITE_CODE_LENGTH: '6' }).spaceInviteCodeLength, 6); assert.throws(() => loadConfig({ SPACE_INVITE_CODE_LENGTH: '5' }), /SPACE_INVITE_CODE_LENGTH/); }); test('새 초대 4자리와 6자리 설정, 기존 8자리 초대 모두 참여 가능하다', async () => { const f = await fixture(); try { const a = await f.create(), b = await f.create(); const room = (await f.call(a, '/spaces', 'POST', {})).json(); assert.match(room.code, /^[A-HJ-NP-Z2-9]{4}$/); assert.equal( (await f.call(b, '/spaces/join', 'POST', { code: room.code.toLowerCase() })).statusCode, 200, ); await f.call(a, `/spaces/${room.space.id}`, 'DELETE'); f.service.cfg.spaceInviteCodeLength = 6; const six = (await f.call(a, '/spaces', 'POST', {})).json(); assert.match(six.code, /^[A-HJ-NP-Z2-9]{6}$/); assert.equal((await f.call(a, '/device/snapshot')).json().spaceInviteCodeLength, 6); assert.equal((await f.call(b, '/spaces/join', 'POST', { code: six.code })).statusCode, 200); await f.call(a, `/spaces/${six.space.id}`, 'DELETE'); const legacy = (await f.call(a, '/spaces', 'POST', {})).json(); f.service.db .update(s.spaces) .set({ codeHash: hmac(f.service.cfg.discoverySecret, 'ABCDEFGH') }) .where(eq(s.spaces.id, legacy.space.id)) .run(); assert.equal((await f.call(b, '/spaces/join', 'POST', { code: 'ABCDEFGH' })).statusCode, 200); assert.equal((await f.call(b, '/spaces/join', 'POST', { code: 'ABCDE' })).statusCode, 400); } finally { await f.close(); } });