|
| 1 | +import { describe, test, expect } from 'bun:test' |
| 2 | +import { handleClaimAuto, RESERVED_NAMES } from '../../worker/src/handlers/claim' |
| 3 | + |
| 4 | +function mockDB(existingMailboxes: string[] = []) { |
| 5 | + return { |
| 6 | + prepare: (sql: string) => ({ |
| 7 | + bind: (...args: unknown[]) => ({ |
| 8 | + first: async () => { |
| 9 | + if (sql.includes('SELECT mailbox FROM auth_tokens WHERE mailbox')) { |
| 10 | + const mb = args[0] as string |
| 11 | + return existingMailboxes.includes(mb) ? { mailbox: mb } : null |
| 12 | + } |
| 13 | + return null |
| 14 | + }, |
| 15 | + run: async () => ({ meta: { changes: 1 } }), |
| 16 | + }), |
| 17 | + }), |
| 18 | + } as unknown as D1Database |
| 19 | +} |
| 20 | + |
| 21 | +function mockEnv(db = mockDB()) { |
| 22 | + return { DB: db } as any |
| 23 | +} |
| 24 | + |
| 25 | +function makeRequest(body: Record<string, unknown>) { |
| 26 | + return new Request('http://localhost/v1/claim/auto', { |
| 27 | + method: 'POST', |
| 28 | + headers: { 'Content-Type': 'application/json' }, |
| 29 | + body: JSON.stringify(body), |
| 30 | + }) |
| 31 | +} |
| 32 | + |
| 33 | +describe('Headless Claim (POST /v1/claim/auto)', () => { |
| 34 | + test('creates mailbox successfully', async () => { |
| 35 | + const auth = { mailbox: 'existing@mails0.com', scope: 'full' as const } |
| 36 | + const res = await handleClaimAuto(makeRequest({ name: 'newagent' }), mockEnv(), auth) |
| 37 | + |
| 38 | + expect(res.status).toBe(201) |
| 39 | + const data = await res.json() as { mailbox: string; api_key: string } |
| 40 | + expect(data.mailbox).toBe('newagent@mails0.com') |
| 41 | + expect(data.api_key).toMatch(/^mk_/) |
| 42 | + }) |
| 43 | + |
| 44 | + test('rejects missing name', async () => { |
| 45 | + const auth = { mailbox: 'a@mails0.com', scope: 'full' as const } |
| 46 | + const res = await handleClaimAuto(makeRequest({}), mockEnv(), auth) |
| 47 | + expect(res.status).toBe(400) |
| 48 | + }) |
| 49 | + |
| 50 | + test('rejects reserved names', async () => { |
| 51 | + const auth = { mailbox: 'a@mails0.com', scope: 'full' as const } |
| 52 | + const res = await handleClaimAuto(makeRequest({ name: 'admin' }), mockEnv(), auth) |
| 53 | + expect(res.status).toBe(400) |
| 54 | + const data = await res.json() as { error: string } |
| 55 | + expect(data.error).toContain('reserved') |
| 56 | + }) |
| 57 | + |
| 58 | + test('rejects invalid name format', async () => { |
| 59 | + const auth = { mailbox: 'a@mails0.com', scope: 'full' as const } |
| 60 | + |
| 61 | + const res2 = await handleClaimAuto(makeRequest({ name: 'has spaces' }), mockEnv(), auth) |
| 62 | + expect(res2.status).toBe(400) |
| 63 | + |
| 64 | + const res3 = await handleClaimAuto(makeRequest({ name: '-start-dash' }), mockEnv(), auth) |
| 65 | + expect(res3.status).toBe(400) |
| 66 | + |
| 67 | + const res4 = await handleClaimAuto(makeRequest({ name: 'a'.repeat(50) }), mockEnv(), auth) |
| 68 | + expect(res4.status).toBe(400) |
| 69 | + }) |
| 70 | + |
| 71 | + test('rejects duplicate mailbox', async () => { |
| 72 | + const auth = { mailbox: 'a@mails0.com', scope: 'full' as const } |
| 73 | + const db = mockDB(['taken@mails0.com']) |
| 74 | + const res = await handleClaimAuto(makeRequest({ name: 'taken' }), mockEnv(db), auth) |
| 75 | + expect(res.status).toBe(409) |
| 76 | + }) |
| 77 | + |
| 78 | + test('rejects non-POST method', async () => { |
| 79 | + const auth = { mailbox: 'a@mails0.com', scope: 'full' as const } |
| 80 | + const req = new Request('http://localhost/v1/claim/auto', { method: 'GET' }) |
| 81 | + const res = await handleClaimAuto(req, mockEnv(), auth) |
| 82 | + expect(res.status).toBe(405) |
| 83 | + }) |
| 84 | + |
| 85 | + test('RESERVED_NAMES includes common system names', () => { |
| 86 | + expect(RESERVED_NAMES.has('admin')).toBe(true) |
| 87 | + expect(RESERVED_NAMES.has('postmaster')).toBe(true) |
| 88 | + expect(RESERVED_NAMES.has('abuse')).toBe(true) |
| 89 | + expect(RESERVED_NAMES.has('support')).toBe(true) |
| 90 | + expect(RESERVED_NAMES.has('noreply')).toBe(true) |
| 91 | + expect(RESERVED_NAMES.has('test')).toBe(true) |
| 92 | + expect(RESERVED_NAMES.has('api')).toBe(true) |
| 93 | + }) |
| 94 | +}) |
0 commit comments