- tools/: express host (hardcoded config, YOUreka Auth cookie session, OpenFGA domain filtering with 5-min cache, a2loft proxies) - /fingerprint wizard: domain picker, two-finger capture via fpagent Socket.IO, OTP-validated lookup/save, domain refresh control - node --test suite (27 tests, fake portal/PocketBase/a2loft upstreams) - Dockerfile (node:22-alpine, non-root, healthcheck) - README rewritten; node_modules gitignored Field-verified end to end: portal login, permitted-domain filtering, real U.are.U 4500 captures, cancel-preserves-capture, a2loft save.
73 lines
2.3 KiB
JavaScript
73 lines
2.3 KiB
JavaScript
// Shared test helpers: unsigned JWTs, real PocketBase cookie envelopes, and
|
|
// throwaway http servers for fake upstreams.
|
|
import http from 'node:http';
|
|
import PocketBase from 'pocketbase';
|
|
|
|
export function b64url(obj) {
|
|
return Buffer.from(JSON.stringify(obj)).toString('base64url');
|
|
}
|
|
|
|
/** Structurally valid (unsigned) JWT with a controllable exp. */
|
|
export function makeJwt({ expiresInSec = 3600 } = {}) {
|
|
const payload = {
|
|
exp: Math.floor(Date.now() / 1000) + expiresInSec,
|
|
id: 'u1',
|
|
collectionId: 'c1',
|
|
collectionName: 'users',
|
|
};
|
|
return `${b64url({ alg: 'HS256', typ: 'JWT' })}.${b64url(payload)}.sig`;
|
|
}
|
|
|
|
export const TEST_RECORD = {
|
|
id: 'u1',
|
|
collectionId: 'c1',
|
|
collectionName: 'users',
|
|
phone: '+2348068287222',
|
|
};
|
|
|
|
/** Build the real PocketBase cookie envelope (value only) via the actual SDK. */
|
|
export function makePbCookie(token, record = TEST_RECORD) {
|
|
const pb = new PocketBase('http://127.0.0.1:9'); // never contacted for this
|
|
pb.authStore.save(token, record);
|
|
const setCookie = pb.authStore.exportToCookie({}, 'pb_auth');
|
|
const m = /pb_auth=([^;]+)/.exec(setCookie);
|
|
if (!m) throw new Error('could not build pb cookie envelope');
|
|
return m[1];
|
|
}
|
|
|
|
export function startServer(handler) {
|
|
return new Promise((resolve) => {
|
|
const server = http.createServer(handler);
|
|
server.listen(0, '127.0.0.1', () => resolve(server));
|
|
});
|
|
}
|
|
|
|
export const urlOf = (server) => `http://127.0.0.1:${server.address().port}`;
|
|
|
|
export function stopServer(server) {
|
|
return new Promise((resolve) => server.close(resolve));
|
|
}
|
|
|
|
/** Read the `name=value` pair out of a Set-Cookie response header list. */
|
|
export function cookieValue(res, name) {
|
|
for (const c of res.headers.getSetCookie()) {
|
|
const m = new RegExp(`(?:^|;\\s*)${name}=([^;]*)`).exec(c);
|
|
if (m) return m[1];
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
/** Scriptable fetch double that records every call. */
|
|
export function fakeFetch(script) {
|
|
const calls = [];
|
|
const fn = async (url, init) => {
|
|
const body = init?.body ? JSON.parse(init.body) : undefined;
|
|
const out = await script({ url, body, init });
|
|
calls.push({ url, body });
|
|
if (out instanceof Error) throw out;
|
|
return { status: out.status ?? 200, json: async () => out.body, text: async () => JSON.stringify(out.body ?? {}) };
|
|
};
|
|
fn.calls = calls;
|
|
return fn;
|
|
}
|