import { test } from 'node:test'; import assert from 'node:assert/strict'; // Env vor dem Import setzen: supabase.js bricht ohne URL/Key ab, ADMIN_EMAILS // und JWT_SECRET werden beim Modul-Load gelesen. process.env.SUPABASE_URL ||= 'http://localhost'; process.env.SUPABASE_SERVICE_KEY ||= 'dummy'; process.env.JWT_SECRET = 'test-secret'; process.env.ADMIN_EMAILS = 'boss@x.ch'; const { roleOf, requireAuth } = await import('../src/auth.js'); const { sign } = await import('hono/jwt'); test('roleOf: Admin aus ADMIN_EMAILS', () => { assert.equal(roleOf({ email: 'boss@x.ch' }), 'admin'); assert.equal(roleOf({ email: 'BOSS@X.CH' }), 'admin'); }); test('roleOf: Rolle aus app_metadata', () => { assert.equal(roleOf({ email: 'a@x.ch', app_metadata: { role: 'admin' } }), 'admin'); assert.equal(roleOf({ email: 'a@x.ch', app_metadata: { role: 'editor' } }), 'editor'); assert.equal(roleOf({ email: 'a@x.ch' }), 'user'); }); // Minimaler Hono-Kontext-Stub. function fakeCtx(authHeader) { const store = {}; return { req: { header: (h) => (h === 'Authorization' ? authHeader : undefined) }, set: (k, v) => { store[k] = v; }, get: (k) => store[k], json: (body, status = 200) => ({ __status: status, body }), }; } test('requireAuth: gültiges Token wird lokal verifiziert', async () => { const token = await sign( { sub: 'u1', email: 'A@x.ch', app_metadata: { role: 'editor' }, exp: Math.floor(Date.now() / 1000) + 60 }, 'test-secret', 'HS256'); let passed = false; const c = fakeCtx('Bearer ' + token); await requireAuth(c, async () => { passed = true; }); assert.equal(passed, true); assert.equal(c.get('email'), 'a@x.ch'); // kleingeschrieben assert.equal(c.get('role'), 'editor'); assert.equal(c.get('canModerate'), true); assert.equal(c.get('isAdmin'), false); }); test('requireAuth: fehlendes Token → 401', async () => { const c = fakeCtx(''); const r = await requireAuth(c, async () => { throw new Error('darf nicht laufen'); }); assert.equal(r.__status, 401); }); test('requireAuth: kaputtes/falsch signiertes Token → 401', async () => { const bad = await sign({ sub: 'u1', exp: Math.floor(Date.now() / 1000) + 60 }, 'falsches-secret', 'HS256'); for (const t of ['Bearer garbage', 'Bearer ' + bad]) { const r = await requireAuth(fakeCtx(t), async () => { throw new Error('darf nicht laufen'); }); assert.equal(r.__status, 401); } }); test('requireAuth: abgelaufenes Token → 401', async () => { const expired = await sign({ sub: 'u1', exp: Math.floor(Date.now() / 1000) - 10 }, 'test-secret', 'HS256'); const r = await requireAuth(fakeCtx('Bearer ' + expired), async () => { throw new Error('darf nicht laufen'); }); assert.equal(r.__status, 401); });