164 lines
5 KiB
JavaScript
164 lines
5 KiB
JavaScript
/**
|
|
* GATE 6 — Full HTTP middleware (NATS off, MOCK_VERAE on)
|
|
*/
|
|
|
|
import { describe, it, before, after } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import http from 'node:http';
|
|
import { createApp } from '../../src/app.js';
|
|
import { useTempStore, seedProTenant } from '../helpers.js';
|
|
import { config } from '../../src/config.js';
|
|
import { startInProcessJobPoller, stopInProcessJobPoller } from '../../src/workers/inProcessJobPoller.js';
|
|
import { getActiveWebhooks } from '../../src/store/webhooks.js';
|
|
|
|
describe('HTTP API (mock verae, nats off)', () => {
|
|
/** @type {ReturnType<typeof useTempStore>} */
|
|
let ctx;
|
|
/** @type {import('http').Server} */
|
|
let server;
|
|
/** @type {number} */
|
|
let port;
|
|
/** @type {string} */
|
|
let apiKey;
|
|
/** @type {object[]} */
|
|
let webhookDeliveries;
|
|
/** @type {import('http').Server} */
|
|
let hookServer;
|
|
/** @type {number} */
|
|
let hookPort;
|
|
|
|
before(async () => {
|
|
assert.equal(config.mockVerae, true);
|
|
assert.equal(config.natsEnabled, false);
|
|
|
|
// Fast poll for wait path
|
|
config.jobPollIntervalMs = 20;
|
|
config.jobPollMaxAttempts = 50;
|
|
|
|
ctx = useTempStore();
|
|
const seeded = seedProTenant();
|
|
apiKey = seeded.apiKey;
|
|
|
|
webhookDeliveries = [];
|
|
await new Promise((resolve) => {
|
|
hookServer = http.createServer((req, res) => {
|
|
let body = '';
|
|
req.on('data', (c) => {
|
|
body += c;
|
|
});
|
|
req.on('end', () => {
|
|
webhookDeliveries.push(JSON.parse(body || '{}'));
|
|
res.writeHead(200);
|
|
res.end('ok');
|
|
});
|
|
});
|
|
hookServer.listen(0, '127.0.0.1', () => {
|
|
hookPort = hookServer.address().port;
|
|
resolve();
|
|
});
|
|
});
|
|
|
|
const app = createApp({ load: false });
|
|
await new Promise((resolve) => {
|
|
server = app.listen(0, '127.0.0.1', () => {
|
|
port = server.address().port;
|
|
resolve();
|
|
});
|
|
});
|
|
|
|
startInProcessJobPoller();
|
|
});
|
|
|
|
after(async () => {
|
|
stopInProcessJobPoller();
|
|
await new Promise((resolve) => server.close(resolve));
|
|
await new Promise((resolve) => hookServer.close(resolve));
|
|
ctx.cleanup();
|
|
});
|
|
|
|
function authHeaders() {
|
|
return {
|
|
Authorization: `Bearer ${apiKey}`,
|
|
'Content-Type': 'application/json',
|
|
};
|
|
}
|
|
|
|
it('POST /zapier/v1/timestamp → 202 + jobId', async () => {
|
|
const res = await fetch(`http://127.0.0.1:${port}/zapier/v1/timestamp`, {
|
|
method: 'POST',
|
|
headers: authHeaders(),
|
|
body: JSON.stringify({ data: 'payload-async' }),
|
|
});
|
|
assert.equal(res.status, 202);
|
|
const body = await res.json();
|
|
assert.ok(body.jobId);
|
|
});
|
|
|
|
it('POST /zapier/v1/timestamp/wait → completed status', async () => {
|
|
const res = await fetch(`http://127.0.0.1:${port}/zapier/v1/timestamp/wait`, {
|
|
method: 'POST',
|
|
headers: authHeaders(),
|
|
body: JSON.stringify({ data: 'payload-wait', hashAlg: 'SHA256' }),
|
|
});
|
|
assert.equal(res.status, 200);
|
|
const body = await res.json();
|
|
assert.equal(body.status, 'completed');
|
|
assert.ok(body.result);
|
|
assert.ok(body.metadata?.certificate || body.result);
|
|
});
|
|
|
|
it('POST /zapier/v1/verify → valid true', async () => {
|
|
const waitRes = await fetch(`http://127.0.0.1:${port}/zapier/v1/timestamp/wait`, {
|
|
method: 'POST',
|
|
headers: authHeaders(),
|
|
body: JSON.stringify({ data: 'to-verify' }),
|
|
});
|
|
const done = await waitRes.json();
|
|
|
|
const res = await fetch(`http://127.0.0.1:${port}/zapier/v1/verify`, {
|
|
method: 'POST',
|
|
headers: authHeaders(),
|
|
body: JSON.stringify({ certificate: done.result }),
|
|
});
|
|
assert.equal(res.status, 200);
|
|
const body = await res.json();
|
|
assert.equal(body.valid, true);
|
|
});
|
|
|
|
it('webhook subscribe stores targetUrl; complete delivers', async () => {
|
|
const sub = await fetch(`http://127.0.0.1:${port}/zapier/v1/webhooks/subscribe`, {
|
|
method: 'POST',
|
|
headers: authHeaders(),
|
|
body: JSON.stringify({
|
|
targetUrl: `http://127.0.0.1:${hookPort}/hook`,
|
|
event: 'timestamp.completed',
|
|
}),
|
|
});
|
|
assert.equal(sub.status, 201);
|
|
const hook = await sub.json();
|
|
assert.ok(hook.id);
|
|
|
|
const hooks = getActiveWebhooks('tenant-test-pro', 'timestamp.completed');
|
|
assert.ok(hooks.some((h) => h.id === hook.id));
|
|
|
|
webhookDeliveries.length = 0;
|
|
|
|
const create = await fetch(`http://127.0.0.1:${port}/zapier/v1/timestamp`, {
|
|
method: 'POST',
|
|
headers: authHeaders(),
|
|
body: JSON.stringify({ data: 'webhook-trigger-me' }),
|
|
});
|
|
const { jobId } = await create.json();
|
|
assert.ok(jobId);
|
|
|
|
// Wait for in-process poller to deliver
|
|
const deadline = Date.now() + 5000;
|
|
while (webhookDeliveries.length === 0 && Date.now() < deadline) {
|
|
await new Promise((r) => setTimeout(r, 50));
|
|
}
|
|
|
|
assert.ok(webhookDeliveries.length >= 1, 'expected webhook delivery');
|
|
assert.equal(webhookDeliveries[0].event, 'timestamp.completed');
|
|
assert.equal(webhookDeliveries[0].jobId, jobId);
|
|
});
|
|
});
|