Initial import of verae-middleware from zapier monorepo
This commit is contained in:
commit
fb6db30e0e
66 changed files with 6590 additions and 0 deletions
164
test/integration/http-api.test.js
Normal file
164
test/integration/http-api.test.js
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
/**
|
||||
* 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);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue