// Run: // node backend/tests/auth_signup_signin.mjs // BASE=https://staging.aptivaai.com ALLOW_NON_DEV=1 node backend/tests/auth_signup_signin.mjs // // Behavior: // - Creates a brand-new user each run (unique email/username) // - Cookie-based auth only (captures Set-Cookie from register/signin) // - Verifies /api/signin returns { message }, /api/user-profile returns 200 JSON w/ NO id leakage // - Verifies /api/user-profile?fields=… respects allowlist // - Verifies /api/logout clears cookie and subsequent /api/user-profile is unauthorized // - Defaults to dev; requires ALLOW_NON_DEV=1 to run on non-dev BASE import assert from 'node:assert/strict'; const BASE = process.env.BASE || 'https://dev1.aptivaai.com'; if (BASE !== 'https://dev1.aptivaai.com' && process.env.ALLOW_NON_DEV !== '1') { console.error(`Refusing to run against non-dev BASE='${BASE}'. Set ALLOW_NON_DEV=1 to override.`); process.exit(2); } const j = (o) => JSON.stringify(o); const rand = () => Math.random().toString(36).slice(2, 10); const email = `jcoakley@aptivaai.com`; const username = `qa_${rand()}`; const password = `Aa1!${rand()}Z`; let cookie = ''; // session cookie (auth) function captureSetCookie(headers) { // In ESM fetch, headers.get('set-cookie') returns the first Set-Cookie (enough for session) const sc = headers.get('set-cookie'); if (sc) cookie = sc.split(';')[0]; } async function req(path, { method = 'GET', headers = {}, body } = {}) { const h = { 'Content-Type': 'application/json', ...(cookie ? { Cookie: cookie } : {}), ...headers, }; const res = await fetch(`${BASE}${path}`, { method, headers: h, body: body ? j(body) : undefined, }); const text = await res.text(); let json = null; try { json = JSON.parse(text); } catch {} return { res, text, json }; } (async () => { // 1) Register (201) { const { res, json } = await req('/api/register', { method: 'POST', body: { username, password, firstname: 'QA', lastname: 'Bot', email, zipcode: '30024', state: 'GA', area: 'Atlanta', career_situation: 'planning', }, }); assert.equal(res.status, 201, `register should 201, got ${res.status}`); captureSetCookie(res.headers); assert.ok(cookie, 'session cookie must be set after register'); } // 2) Sign in (200) — cookie refreshed, { message } in body { const { res, json } = await req('/api/signin', { method: 'POST', body: { username, password }, }); assert.equal(res.status, 200, `signin should 200, got ${res.status}`); assert.ok(json && typeof json.message === 'string', 'signin returns { message }'); captureSetCookie(res.headers); assert.ok(cookie, 'session cookie must be present after signin'); } // 3) Profile (200, JSON, no id leakage) { const { res, json, text } = await req('/api/user-profile'); assert.equal(res.status, 200, `profile fetch should 200, got ${res.status}, body=${text.slice(0,120)}`); assert.ok(json && typeof json === 'object', 'profile returns JSON object'); if ('id' in json || 'user_id' in json) throw new Error('profile must NOT include id/user_id'); } // 4) Field-filtered profile (allowlist) { const fields = 'firstname,lastname,career_situation'; const { res, json, text } = await req(`/api/user-profile?fields=${encodeURIComponent(fields)}`); assert.equal(res.status, 200, `filtered profile should 200, got ${res.status}, body=${text.slice(0,120)}`); const keys = Object.keys(json || {}); for (const k of keys) { if (!['firstname','lastname','career_situation','sms_opt_in','phone_e164','email'].includes(k)) { throw new Error(`unexpected field '${k}' in filtered profile`); } } } // 5) Username existence { const { res, json } = await req(`/api/check-username/${encodeURIComponent(username)}`); assert.equal(res.status, 200, 'check-username should 200'); assert.equal(json?.exists, true, 'new username should exist'); } // 6) Logout then profile blocked { const out = await req('/api/logout', { method: 'POST' }); assert.equal(out.res.status, 200, `logout should 200, got ${out.res.status}`); cookie = ''; // simulate cleared cookie const { res } = await req('/api/user-profile'); if (res.status === 200) throw new Error('profile should NOT be accessible after logout'); } console.log('✓ AUTH regression suite passed'); })().catch((e) => { console.error('✖ AUTH regression failed:', e?.message || e); process.exit(1); });