dev1/backend/tests/auth_signup_signin.mjs

127 lines
4.5 KiB
JavaScript

// 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);
});