- PGlite (in-memory WASM Postgres) hosted in a child process, exposed via TCP socket; real migrations applied with prisma migrate deploy - Vitest harness (vitest.integration.config.ts, global-setup spawns PGlite + built Nuxt server, per-test TRUNCATE, factories, cookie-jar API client); no mocks, zero production code changes - 50 tests across auth, hunt, team, submit, review, showcase, diashow incl. SuperJSON envelope check; *.itest.ts keeps bun test untouched - CI: parallel unit + integration jobs in .github/workflows/test.yml - tests/ covered by nuxt typecheck via tests/tsconfig.json reference
70 lines
1.9 KiB
TypeScript
70 lines
1.9 KiB
TypeScript
import { baseUrl } from '../integration/setup';
|
|
|
|
/**
|
|
* Minimal fetch wrapper bound to the test server with a per-instance
|
|
* cookie jar, so each client represents one browser session.
|
|
*/
|
|
export class ApiClient {
|
|
private readonly cookies = new Map<string, string>();
|
|
|
|
constructor(private readonly base: string) {}
|
|
|
|
async request(path: string, init: RequestInit = {}): Promise<Response> {
|
|
const headers = new Headers(init.headers);
|
|
if (this.cookies.size > 0) {
|
|
headers.set(
|
|
'cookie',
|
|
[...this.cookies].map(([name, value]) => `${name}=${value}`).join('; ')
|
|
);
|
|
}
|
|
|
|
const res = await fetch(`${this.base}${path}`, { ...init, headers });
|
|
|
|
for (const cookie of res.headers.getSetCookie()) {
|
|
const [pair, ...attributes] = cookie.split(';');
|
|
const separator = pair.indexOf('=');
|
|
const name = pair.slice(0, separator).trim();
|
|
const value = pair.slice(separator + 1).trim();
|
|
const expired = attributes.some((attr) =>
|
|
/^expires=Thu, 01 Jan 1970/i.test(attr.trim())
|
|
);
|
|
if (value === '' || expired) {
|
|
this.cookies.delete(name);
|
|
} else {
|
|
this.cookies.set(name, value);
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
get(path: string) {
|
|
return this.request(path);
|
|
}
|
|
|
|
post(path: string, body?: unknown) {
|
|
const init: RequestInit =
|
|
body instanceof FormData
|
|
? { method: 'POST', body }
|
|
: {
|
|
method: 'POST',
|
|
headers: { 'content-type': 'application/json' },
|
|
body: JSON.stringify(body ?? {})
|
|
};
|
|
return this.request(path, init);
|
|
}
|
|
|
|
async register(email: string, name: string, password: string) {
|
|
return this.post('/api/auth/register', { email, name, password });
|
|
}
|
|
|
|
async login(email: string, password: string) {
|
|
return this.post('/api/auth/login', { email, password });
|
|
}
|
|
}
|
|
|
|
export const api = new ApiClient(baseUrl);
|
|
|
|
export function newClient() {
|
|
return new ApiClient(baseUrl);
|
|
}
|