templates2.Base.Next/tests/e2e/smoke.mjs
w4c-templates 219be916cb
Some checks are pending
CI / quality (push) Waiting to run
chore(template): publish Base.Next
2026-10-11 23:23:30 +03:00

76 lines
3.9 KiB
JavaScript

#!/usr/bin/env node
/**
* End-to-end smoke test — drives the running server over HTTP (no browser needed).
* Start the app first (`npm start`), then: QA_BASE_URL=http://127.0.0.1:3000 node tests/e2e/smoke.mjs
* The QA harness runs this automatically while the server is up.
*/
const BASE = (process.env.QA_BASE_URL || 'http://127.0.0.1:3000').replace(/\/$/, '');
const jar = new Map();
const failures = [];
function store(res) {
const list = typeof res.headers.getSetCookie === 'function' ? res.headers.getSetCookie() : [];
for (const raw of list) {
const pair = raw.split(';')[0];
const eq = pair.indexOf('=');
if (eq > 0) jar.set(pair.slice(0, eq), pair.slice(eq + 1));
}
}
async function req(path, { method = 'GET', body } = {}) {
const headers = {};
if (body !== undefined) headers['Content-Type'] = 'application/json';
if (jar.size) headers.cookie = [...jar.entries()].map(([k, v]) => `${k}=${v}`).join('; ');
const res = await fetch(BASE + path, { method, headers, body: body === undefined ? undefined : JSON.stringify(body), redirect: 'manual' });
store(res);
const json = await res.clone().json().catch(() => null);
return { status: res.status, json };
}
function check(name, cond, detail = '') {
if (cond) console.log(` ok ${name}`);
else { console.log(` FAIL ${name}${detail ? ` — ${detail}` : ''}`); failures.push(name); }
}
async function run() {
const email = `smoke_${Date.now().toString(36)}@example.com`;
const health = await req('/api/health');
check('health 200', health.status === 200 && health.json?.status === 'ok', `status=${health.status}`);
const reg = await req('/api/auth/register', { method: 'POST', body: { email, password: 'password123', name: 'Smoke' } });
check('register 201', reg.status === 201, `status=${reg.status}`);
const regMe = await req('/api/auth/me');
check('session after register', regMe.status === 200 && regMe.json?.user?.email === email, `status=${regMe.status}`);
const newUserAdmin = await req('/api/admin/users');
check('non-admin blocked from admin API', newUserAdmin.status === 403, `status=${newUserAdmin.status}`);
const mailProvider = (await req('/api/billing/checkout', { method: 'POST', body: { plan: 'pro' } }));
check('mock checkout returns a url', mailProvider.status === 200 && String(mailProvider.json?.url).includes('billing=success'), `status=${mailProvider.status}`);
await req('/api/auth/logout', { method: 'POST' });
const afterLogout = await req('/api/auth/me');
check('logout clears session', afterLogout.status === 401, `status=${afterLogout.status}`);
const adminLogin = await req('/api/auth/login', { method: 'POST', body: { email: 'admin@example.com', password: 'password' } });
check('seeded admin can sign in', adminLogin.status === 200 && adminLogin.json?.user?.role === 'admin', `status=${adminLogin.status}`);
const users = await req('/api/admin/users');
check('admin sees users', users.status === 200 && Array.isArray(users.json?.users) && users.json.users.length >= 2, `status=${users.status}`);
const webhook = await req('/api/billing/webhook', { method: 'POST', body: { type: 'checkout.completed', userId: adminLogin.json?.user?.id, plan: 'pro' } });
check('mock webhook accepted', webhook.status === 200, `status=${webhook.status}`);
await req('/api/auth/logout', { method: 'POST' });
const userLogin = await req('/api/auth/login', { method: 'POST', body: { email: 'user@example.com', password: 'password' } });
check('seeded user can sign in', userLogin.status === 200 && userLogin.json?.user?.role === 'user', `status=${userLogin.status}`);
const forbidden = await req('/api/admin/users');
check('member forbidden on admin API', forbidden.status === 403, `status=${forbidden.status}`);
console.log(failures.length ? `\n${failures.length} check(s) failed` : '\nAll e2e checks passed');
process.exit(failures.length ? 1 : 0);
}
run().catch((e) => { console.error(e); process.exit(1); });