import { test, after } from 'node:test'; import assert from 'node:assert/strict'; import { mkdtempSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; const tmpDir = mkdtempSync(join(tmpdir(), 'oikos-setup-test-')); process.env.SESSION_SECRET = 'test-setup-secret-minimum-32-chars-x'; process.env.DB_PATH = join(tmpDir, 'test.db'); process.env.SESSION_SECURE = 'false'; process.env.PORT = '13099'; // Dynamic import so env vars are set before module initialization const { default: app } = await import('./server/index.js'); await new Promise(r => setTimeout(r, 400)); const BASE = 'http://localhost:13099'; after(() => { rmSync(tmpDir, { recursive: true, force: true }); process.exit(0); }); // Validation tests run first (DB is empty at this point) test('POST /api/v1/auth/setup: 400 when required fields missing', async () => { const res = await fetch(`${BASE}/api/v1/auth/setup`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: 'admin' }), }); assert.equal(res.status, 400); }); test('POST /api/v1/auth/setup: 400 when username invalid', async () => { const res = await fetch(`${BASE}/api/v1/auth/setup`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: 'a!b', display_name: 'Test', password: 'password123' }), }); assert.equal(res.status, 400); }); test('POST /api/v1/auth/setup: 400 when password too short', async () => { const res = await fetch(`${BASE}/api/v1/auth/setup`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: 'admin', display_name: 'Test', password: 'short' }), }); assert.equal(res.status, 400); }); test('POST /api/v1/auth/setup: 400 when display_name too long', async () => { const res = await fetch(`${BASE}/api/v1/auth/setup`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: 'admin', display_name: 'x'.repeat(129), password: 'password123' }), }); assert.equal(res.status, 400); }); test('POST /api/v1/auth/setup: 201 creates first admin', async () => { const res = await fetch(`${BASE}/api/v1/auth/setup`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: 'admin', display_name: 'Test Admin', password: 'password123' }), }); assert.equal(res.status, 201); const data = await res.json(); assert.equal(data.user.username, 'admin'); assert.equal(data.user.display_name, 'Test Admin'); assert.equal(data.user.role, 'admin'); assert.ok(typeof data.user.id === 'number'); assert.ok(typeof data.user.avatar_color === 'string' && data.user.avatar_color.startsWith('#')); }); test('POST /api/v1/auth/setup: 403 when users already exist', async () => { const res = await fetch(`${BASE}/api/v1/auth/setup`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: 'admin2', display_name: 'Another', password: 'password123' }), }); assert.equal(res.status, 403); const data = await res.json(); assert.equal(data.code, 403); });