275 lines
22 KiB
JavaScript
275 lines
22 KiB
JavaScript
// node --test relay/test/handler.test.mjs (no dependencies, no database, no network: a fake Neon, fake blobs, a fake clock)
|
|
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { makeHandler, expire, EXPIRE_EVERY_MS } from '../lib/handler.mjs';
|
|
import { authed } from '../lib/relay.mjs';
|
|
import { RateLimit } from '../lib/wake.mjs';
|
|
import { keygen, signRun, runCanon, machineTag, newNonce, newSecret, secretHash, RATE_PER_MIN, AUTH_FAIL_PER_MIN } from '../lib/guard.mjs';
|
|
|
|
const T = 'ABCDEFGHIJKLMNOPQRST';
|
|
const K = 'relaykeyrelaykeyrelaykeyrelaykeyrelaykeyrelayke';
|
|
const I = 'intakekeyintakekeyintakekeyinta';
|
|
const RUN = keygen();
|
|
const ENV = { RELAY_TOKEN: T, RELAY_KEY: K, LOG_INTAKE_KEY: I, RELAY_RUN_PUB: RUN.pub };
|
|
const BLOB = 'https://abc123.public.blob.vercel-storage.com/relay/aa/x.zip';
|
|
|
|
// The smallest Neon stand-in that answers every query lib/handler.mjs sends, matched on the query text.
|
|
function fakeDb({ nowIso = () => '2026-10-05T22:00:00.000Z' } = {}) {
|
|
const items = []; const machines = []; let seq = 0;
|
|
const flagsOf = r => (typeof r.flags === 'string' ? JSON.parse(r.flags) : r.flags || {});
|
|
const sql = async (query, params = []) => {
|
|
const Q = query.replace(/\s+/g, ' ').trim();
|
|
if (/^ALTER TABLE relay_machines/.test(Q)) return [];
|
|
if (/^SELECT name, secret_hash FROM relay_machines WHERE secret_hash IS NOT NULL/.test(Q)) return machines.filter(m => m.secret_hash).map(m => ({ name: m.name, secret_hash: m.secret_hash }));
|
|
if (/^SELECT secret_hash FROM relay_machines WHERE name = \$1/.test(Q)) return machines.filter(m => m.name === params[0]).map(m => ({ secret_hash: m.secret_hash || null }));
|
|
if (/^DELETE FROM relay_items WHERE ts < \$1 RETURNING/.test(Q)) { const gone = items.filter(i => i.ts < params[0]); for (const g of gone) items.splice(items.indexOf(g), 1); return gone.map(g => ({ id: g.id, file_url: g.file_url })); }
|
|
if (/^SELECT .* FROM relay_items WHERE NOT read AND NOT done AND kind = ANY\(\$2\)/.test(Q)) return items.filter(i => !i.read && !i.done && params[1].includes(i.kind) && (i.to_machine === params[0] || (i.to_machine === 'all' && i.kind === 'task'))).sort((a, b) => a.id - b.id).slice(0, 50);
|
|
if (/^SELECT .* FROM relay_items WHERE id = \$1/.test(Q)) return items.filter(i => i.id === params[0]);
|
|
if (/^SELECT file_name, file_url, file_b64 FROM relay_items WHERE id = \$1/.test(Q)) return items.filter(i => i.id === params[0]);
|
|
if (/^SELECT id FROM relay_items WHERE kind IN \('run', 'start-app'\) AND flags->>'nonce' = \$1/.test(Q)) return items.filter(i => (i.kind === 'run' || i.kind === 'start-app') && flagsOf(i).nonce === params[0]).map(i => ({ id: i.id }));
|
|
if (/^SELECT .* FROM relay_items .*ORDER BY id DESC LIMIT (\d+)/.test(Q)) { const lim = Number(Q.match(/LIMIT (\d+)/)[1]); return [...items].sort((a, b) => b.id - a.id).slice(0, lim); }
|
|
if (/^SELECT m\.name, m\.hostname/.test(Q)) return machines.map(m => ({ ...m, unread: items.filter(i => !i.read && ['task', 'run', 'start-app'].includes(i.kind) && (i.to_machine === m.name || (i.to_machine === 'all' && i.kind === 'task'))).length }));
|
|
if (/^SELECT name, hostname, role, named, info, last_seen, secret_hash FROM relay_machines ORDER BY name/.test(Q)) return machines.map(m => ({ ...m }));
|
|
if (/^SELECT name, role, named, secret_hash FROM relay_machines WHERE hostname = \$1/.test(Q)) return machines.filter(m => m.hostname === params[0]).map(m => ({ ...m }));
|
|
if (/^INSERT INTO relay_items/.test(Q)) {
|
|
const [from_machine, to_machine, kind, title, body, file_name, file_url, file_b64, size, flags, task_id] = params;
|
|
const row = { id: ++seq, ts: nowIso(), from_machine, to_machine, kind, title, body, file_name, file_url, file_b64, size, read: false, read_at: null, done: false, done_at: null, flags: JSON.parse(flags), task_id };
|
|
items.push(row); return [{ id: row.id, ts: row.ts }];
|
|
}
|
|
if (/^INSERT INTO relay_machines \(name, role, named, last_seen\)/.test(Q)) { const m = machines.find(x => x.name === params[0]); if (m) m.last_seen = nowIso(); else machines.push({ name: params[0], hostname: null, role: '', named: false, info: {}, last_seen: nowIso(), secret_hash: null }); return []; }
|
|
if (/^INSERT INTO relay_machines \(name, role, named, secret_hash\)/.test(Q)) { const m = machines.find(x => x.name === params[0]); if (m) { m.secret_hash = params[1]; m.named = true; } else machines.push({ name: params[0], hostname: null, role: '', named: true, info: {}, last_seen: null, secret_hash: params[1] }); return []; }
|
|
if (/^INSERT INTO relay_machines \(name, hostname, role, named, info, last_seen\)/.test(Q)) { let m = machines.find(x => x.name === params[0]); if (!m) { m = { name: params[0], hostname: params[0], role: params[1], named: false, info: JSON.parse(params[2]), last_seen: nowIso(), secret_hash: null }; machines.push(m); } else { m.hostname = params[0]; m.info = JSON.parse(params[2]); } return [{ name: m.name, role: m.role, named: m.named }]; }
|
|
if (/^UPDATE relay_machines SET hostname = \$2, last_seen = now\(\), info = \$3::jsonb WHERE name = \$1 RETURNING/.test(Q)) { const m = machines.find(x => x.name === params[0]); m.hostname = params[1]; m.info = JSON.parse(params[2]); m.last_seen = nowIso(); return [{ name: m.name, role: m.role, named: m.named }]; }
|
|
if (/^UPDATE relay_machines SET last_seen = now\(\), info = \$2::jsonb WHERE hostname = \$1/.test(Q)) { for (const m of machines) if (m.hostname === params[0]) { m.info = JSON.parse(params[1]); m.last_seen = nowIso(); } return []; }
|
|
if (/^UPDATE relay_items SET read = true, read_at = now\(\) WHERE id = ANY\(\$1\)/.test(Q)) { for (const i of items) if (params[0].includes(i.id)) { i.read = true; i.read_at = nowIso(); } return []; }
|
|
if (/^UPDATE relay_items SET done = true/.test(Q)) { const i = items.find(x => x.id === params[0]); if (i) { i.done = true; i.read = true; i.flags = { ...i.flags, ...JSON.parse(params[1]) }; } return []; }
|
|
if (/^DELETE FROM relay_items WHERE id = \$1 RETURNING file_url/.test(Q)) { const i = items.find(x => x.id === params[0]); if (!i) return []; items.splice(items.indexOf(i), 1); return [{ file_url: i.file_url }]; }
|
|
throw new Error('fake db: unexpected query ' + Q.slice(0, 120));
|
|
};
|
|
return { sql, items, machines, seed: (row) => { const r = { id: ++seq, ts: nowIso(), read: false, done: false, flags: {}, file_url: null, file_b64: null, ...row }; items.push(r); return r; } };
|
|
}
|
|
|
|
function fakeBlob() {
|
|
const deleted = [];
|
|
return { deleted, storeBuffer: async (name, buf) => ({ url: BLOB, size: buf.length }), clientUploadToken: async (name, size) => ({ token: 't', put_url: 'https://vercel.com/api/blob/?pathname=x', api_version: '11', max: 50 * 1024 * 1024, size }), deleteBlobs: async urls => { const l = urls.filter(Boolean); deleted.push(...l); return l.length; } };
|
|
}
|
|
|
|
function res() {
|
|
const r = { headers: {}, body: null, code: null };
|
|
r.setHeader = (k, v) => { r.headers[k] = v; return r; };
|
|
r.status = s => { r.code = s; return r; };
|
|
r.end = s => { r.body = s; };
|
|
return r;
|
|
}
|
|
const json = (r, status, obj) => { r.status(status); r.end(JSON.stringify(obj)); };
|
|
const reply = r => ({ code: r.code, ...(r.body ? JSON.parse(r.body) : {}) });
|
|
const req = (method, query = {}, { headers = {}, body, ip = '203.0.113.9' } = {}) => ({ method, query, headers: { 'x-forwarded-for': ip, 'content-type': 'application/json', ...headers }, body: body === undefined ? undefined : JSON.stringify(body) });
|
|
const TOKEN = { 'x-relay-token': T };
|
|
const KEY = { 'x-igneum-key': K };
|
|
const INTAKE = { 'x-igneum-key': I };
|
|
|
|
function rig(opts = {}) {
|
|
const db = fakeDb(); const blob = fakeBlob(); let t = Date.UTC(2026, 9, 5, 22, 0, 0);
|
|
const now = () => t;
|
|
const env = { ...ENV, ...(opts.env || {}) };
|
|
const state = {};
|
|
const h = makeHandler({ sql: db.sql, blob, authed: r => authed(r, env), json, env, now, state, limiter: opts.limiter, authLimiter: opts.authLimiter });
|
|
const call = async (...a) => { const r = res(); await h(req(...a), r); return reply(r); };
|
|
return { db, blob, call, state, tick: ms => { t += ms; } };
|
|
}
|
|
|
|
function signedRun(to, body, secret, flags = {}) {
|
|
const nonce = newNonce();
|
|
const f = { ...flags, nonce };
|
|
f.mac = machineTag(secret, runCanon({ to, nonce, body, flags: f }));
|
|
f.sig = signRun(runCanon({ to, nonce, body, flags: f }), RUN.seed);
|
|
return { to, title: 'job', body, kind: 'run', flags: f };
|
|
}
|
|
|
|
test('X24: the x-relay-token header with no token in the path is the token tier; no auth is 401', async () => {
|
|
const { call } = rig();
|
|
assert.equal((await call('GET', { fn: 'feed' }, { headers: TOKEN })).code, 200);
|
|
assert.equal((await call('GET', { fn: 'feed' }, { headers: { 'x-relay-token': 'wrong' } })).code, 401);
|
|
assert.equal((await call('GET', { fn: 'feed' })).code, 401);
|
|
});
|
|
|
|
test('X23: a token-only run task is refused with 401; a signed, tagged, fresh one is stored; a replayed nonce is 409', async () => {
|
|
const { call, db } = rig();
|
|
const secret = newSecret();
|
|
const bare = await call('POST', { fn: 'task' }, { headers: TOKEN, body: { to: 'PC1', title: 'x', body: 'Write-Host hi', kind: 'run', flags: { elevated: true } } });
|
|
assert.equal(bare.code, 401);
|
|
assert.match(bare.error, /nonce/);
|
|
const good = signedRun('PC1', 'Write-Host hi', secret, { elevated: true });
|
|
const r = await call('POST', { fn: 'task' }, { headers: TOKEN, body: good });
|
|
assert.equal(r.code, 200, r.error);
|
|
assert.equal(db.items[0].kind, 'run');
|
|
assert.equal(db.items[0].flags.sig, good.flags.sig);
|
|
const again = await call('POST', { fn: 'task' }, { headers: TOKEN, body: good });
|
|
assert.equal(again.code, 409);
|
|
// the path token (the phone page) is still the token tier, and still needs the signature
|
|
const viaPath = await call('POST', { fn: 'task', token: T }, { body: { to: 'PC1', title: 'x', body: 'y', kind: 'run' } });
|
|
assert.equal(viaPath.code, 401);
|
|
});
|
|
|
|
test('MF-11: a start-app task needs the same signature, tag and fresh nonce as a run; it lands in the agent inbox beside run items', async () => {
|
|
const { call, db } = rig();
|
|
const secret = newSecret();
|
|
const bare = await call('POST', { fn: 'task' }, { headers: TOKEN, body: { to: 'PC2', title: 'start', body: '', kind: 'start-app', flags: {} } });
|
|
assert.equal(bare.code, 401);
|
|
const good = { ...signedRun('PC2', '# start-app body', secret), kind: 'start-app' };
|
|
const r = await call('POST', { fn: 'task' }, { headers: TOKEN, body: good });
|
|
assert.equal(r.code, 200, r.error);
|
|
assert.equal(db.items[0].kind, 'start-app');
|
|
assert.equal((await call('POST', { fn: 'task' }, { headers: TOKEN, body: good })).code, 409, 'a replayed nonce');
|
|
// the relay key tier may not queue it (the console token only), like run
|
|
assert.equal((await call('POST', { fn: 'task' }, { headers: KEY, body: { ...signedRun('PC2', 'x', secret), kind: 'start-app' } })).code, 403);
|
|
// the agent asks for run and gets both kinds, in order; a task item is not in that inbox
|
|
db.seed({ from_machine: 'Mac', to_machine: 'PC2', kind: 'task', title: 't', body: 'for a person' });
|
|
const inbox = await call('POST', { fn: 'inbox' }, { headers: KEY, body: { machine: 'PC2', kind: 'run', ack: true } });
|
|
assert.equal(inbox.code, 200);
|
|
assert.deepEqual(inbox.items.map(i => i.kind), ['start-app']);
|
|
assert.equal(inbox.acked, 1);
|
|
// a kind the relay does not know becomes a plain task, never an agent kind
|
|
const odd = await call('POST', { fn: 'task' }, { headers: TOKEN, body: { to: 'PC2', title: 'x', body: 'y', kind: 'reboot-now' } });
|
|
assert.equal(odd.code, 200);
|
|
assert.equal(db.items.find(i => i.title === 'x' && i.body === 'y').kind, 'task');
|
|
});
|
|
|
|
test('X23: a run signed by another key, or with a changed body, flag or target after signing, is refused', async () => {
|
|
const { call } = rig();
|
|
const secret = newSecret();
|
|
const good = signedRun('PC1', 'Write-Host hi', secret, { elevated: false });
|
|
assert.equal((await call('POST', { fn: 'task' }, { headers: TOKEN, body: { ...good, body: 'Write-Host bye' } })).code, 401);
|
|
assert.equal((await call('POST', { fn: 'task' }, { headers: TOKEN, body: { ...good, to: 'PC2' } })).code, 401);
|
|
assert.equal((await call('POST', { fn: 'task' }, { headers: TOKEN, body: { ...good, flags: { ...good.flags, elevated: true } } })).code, 401);
|
|
const other = keygen();
|
|
const forged = { ...good, flags: { ...good.flags, sig: signRun(runCanon({ to: 'PC1', nonce: good.flags.nonce, body: good.body, flags: good.flags }), other.seed) } };
|
|
assert.equal((await call('POST', { fn: 'task' }, { headers: TOKEN, body: forged })).code, 401);
|
|
});
|
|
|
|
test('X23: without RELAY_RUN_PUB on the project every run is refused', async () => {
|
|
const { call } = rig({ env: { RELAY_RUN_PUB: '' } });
|
|
const r = await call('POST', { fn: 'task' }, { headers: TOKEN, body: signedRun('PC1', 'x', newSecret()) });
|
|
assert.equal(r.code, 401);
|
|
assert.match(r.error, /RELAY_RUN_PUB/);
|
|
});
|
|
|
|
test('X23: the relay key may report and read but never queue, rename, re-role, delete or bind', async () => {
|
|
const { call } = rig();
|
|
assert.equal((await call('POST', { fn: 'task' }, { headers: KEY, body: { to: 'PC1', title: 'x', body: 'y', kind: 'task' } })).code, 403);
|
|
assert.equal((await call('POST', { fn: 'drop' }, { headers: KEY, body: { to: 'PC1', title: 'x', body: 'y', kind: 'run' } })).code, 403);
|
|
assert.equal((await call('POST', { fn: 'name' }, { headers: KEY, body: { hostname: 'h', name: 'PC9' } })).code, 403);
|
|
assert.equal((await call('POST', { fn: 'role' }, { headers: KEY, body: { name: 'PC1', role: 'miner' } })).code, 403);
|
|
assert.equal((await call('POST', { fn: 'delete' }, { headers: KEY, body: { id: 1 } })).code, 403);
|
|
assert.equal((await call('POST', { fn: 'secret' }, { headers: KEY, body: { name: 'PC1', secret_hash: 'a'.repeat(64) } })).code, 403);
|
|
assert.equal((await call('POST', { fn: 'drop' }, { headers: KEY, body: { from: 'PC1', title: 'note', body: 'hi', kind: 'text' } })).code, 200);
|
|
assert.equal((await call('GET', { fn: 'feed' }, { headers: KEY })).code, 200);
|
|
});
|
|
|
|
test('X23: the intake key (inside every package) may only upload and drop files or text; nothing else, no reads', async () => {
|
|
const { call } = rig();
|
|
assert.equal((await call('POST', { fn: 'upload' }, { headers: INTAKE, body: { name: 'a.zst', size: 10 } })).code, 200);
|
|
assert.equal((await call('POST', { fn: 'drop' }, { headers: INTAKE, body: { from: 'DESKTOP-1234', to: 'mac', kind: 'file', title: 'build-job 7 a.zst', file_name: 'a.zst', file_url: BLOB, size: 10 } })).code, 200);
|
|
assert.equal((await call('POST', { fn: 'drop' }, { headers: INTAKE, body: { from: 'PC1', kind: 'result', title: 'r', body: 'x' } })).code, 403);
|
|
assert.equal((await call('POST', { fn: 'drop' }, { headers: INTAKE, body: { to: 'PC1', kind: 'run', title: 'r', body: 'x' } })).code, 403);
|
|
assert.equal((await call('POST', { fn: 'task' }, { headers: INTAKE, body: { to: 'PC1', title: 'x', body: 'y' } })).code, 403);
|
|
assert.equal((await call('POST', { fn: 'register' }, { headers: INTAKE, body: { hostname: 'h' } })).code, 403);
|
|
assert.equal((await call('POST', { fn: 'ack' }, { headers: INTAKE, body: { ids: [1] } })).code, 403);
|
|
assert.equal((await call('GET', { fn: 'feed' }, { headers: INTAKE })).code, 403);
|
|
assert.equal((await call('GET', { fn: 'inbox', machine: 'PC1' }, { headers: INTAKE })).code, 403);
|
|
const closed = rig({ env: { RELAY_INTAKE_COMPAT: '0' } });
|
|
assert.equal((await closed.call('POST', { fn: 'upload' }, { headers: INTAKE, body: { name: 'a', size: 1 } })).code, 401);
|
|
});
|
|
|
|
test('X27: a result whose from does not match the machine secret is refused; a matching one is stored under that machine', async () => {
|
|
const { call, db } = rig();
|
|
const s1 = newSecret(); const s2 = newSecret();
|
|
assert.equal((await call('POST', { fn: 'secret' }, { headers: TOKEN, body: { name: 'PC1', secret_hash: secretHash(s1) } })).code, 200);
|
|
assert.equal((await call('POST', { fn: 'secret' }, { headers: TOKEN, body: { name: 'PC2', secret_hash: secretHash(s2) } })).code, 200);
|
|
const forged = await call('POST', { fn: 'drop' }, { headers: { ...KEY, 'x-machine-secret': s2 }, body: { from: 'PC1', kind: 'result', title: 'r', body: 'x', task_id: 3 } });
|
|
assert.equal(forged.code, 403);
|
|
assert.match(forged.error, /does not match/);
|
|
const unknown = await call('POST', { fn: 'drop' }, { headers: { ...KEY, 'x-machine-secret': newSecret() }, body: { from: 'PC1', kind: 'result', title: 'r', body: 'x' } });
|
|
assert.equal(unknown.code, 403);
|
|
const bare = await call('POST', { fn: 'drop' }, { headers: KEY, body: { from: 'PC1', kind: 'result', title: 'r', body: 'x' } });
|
|
assert.equal(bare.code, 403);
|
|
assert.match(bare.error, /need its machine secret/);
|
|
const good = await call('POST', { fn: 'drop' }, { headers: { ...KEY, 'x-machine-secret': s1 }, body: { from: 'PC1', kind: 'result', title: 'r', body: 'x' } });
|
|
assert.equal(good.code, 200);
|
|
assert.equal(db.items.at(-1).from_machine, 'PC1');
|
|
assert.equal(db.items.at(-1).flags.unbound, undefined);
|
|
// a machine with no secret yet (the compatibility window) is accepted and marked unbound
|
|
const unbound = await call('POST', { fn: 'drop' }, { headers: KEY, body: { from: 'Laptop', kind: 'result', title: 'r', body: 'x' } });
|
|
assert.equal(unbound.code, 200);
|
|
assert.equal(db.items.at(-1).flags.unbound, true);
|
|
});
|
|
|
|
test('X27: register with the secret names the machine whatever the hostname says; a bound hostname without it is refused', async () => {
|
|
const { call, db } = rig();
|
|
const s1 = newSecret();
|
|
await call('POST', { fn: 'secret' }, { headers: TOKEN, body: { name: 'PC1', secret_hash: secretHash(s1) } });
|
|
const r = await call('POST', { fn: 'register' }, { headers: { ...KEY, 'x-machine-secret': s1 }, body: { hostname: 'DESKTOP-KMCV30N', info: { user: 'someone', dir: 'C:\\secret', gpus: ['5090'] } } });
|
|
assert.deepEqual([r.code, r.name, r.bound], [200, 'PC1', true]);
|
|
assert.equal(db.machines.find(m => m.name === 'PC1').hostname, 'DESKTOP-KMCV30N');
|
|
assert.deepEqual(db.machines.find(m => m.name === 'PC1').info, { gpus: ['5090'] }); // X28: no username, no folder
|
|
const bare = await call('POST', { fn: 'register' }, { headers: KEY, body: { hostname: 'DESKTOP-KMCV30N', info: {} } });
|
|
assert.equal(bare.code, 403);
|
|
const fresh = await call('POST', { fn: 'register' }, { headers: KEY, body: { hostname: 'NEWBOX', info: { user: 'u', dir: 'd', os: 'w' } } });
|
|
assert.deepEqual([fresh.code, fresh.name, fresh.bound], [200, 'NEWBOX', false]);
|
|
assert.deepEqual(db.machines.find(m => m.name === 'NEWBOX').info, { os: 'w' });
|
|
const wrong = await call('POST', { fn: 'register' }, { headers: { ...KEY, 'x-machine-secret': newSecret() }, body: { hostname: 'NEWBOX', info: {} } });
|
|
assert.equal(wrong.code, 403);
|
|
});
|
|
|
|
test('X28: a GET inbox never acks, even with ack=1; POST inbox {ack:true} does', async () => {
|
|
const { call, db } = rig();
|
|
db.seed({ from_machine: 'Mac', to_machine: 'PC1', kind: 'task', title: 't', body: 'b' });
|
|
const peek = await call('GET', { fn: 'inbox', machine: 'PC1', ack: '1' }, { headers: KEY });
|
|
assert.deepEqual([peek.code, peek.items.length, peek.acked, db.items[0].read], [200, 1, 0, false]);
|
|
const got = await call('POST', { fn: 'inbox' }, { headers: KEY, body: { machine: 'PC1', kind: 'all', ack: true } });
|
|
assert.deepEqual([got.code, got.items.length, got.acked, db.items[0].read], [200, 1, 1, true]);
|
|
assert.equal((await call('POST', { fn: 'inbox' }, { headers: KEY, body: { machine: 'PC1', ack: true } })).items.length, 0);
|
|
});
|
|
|
|
test('X28: the rate limit answers 429 per IP, and failed authentications have their own, lower limit', async () => {
|
|
const { call } = rig({ limiter: new RateLimit({ perMinute: 3 }), authLimiter: new RateLimit({ perMinute: 2 }) });
|
|
assert.equal(RATE_PER_MIN, 120); assert.equal(AUTH_FAIL_PER_MIN, 10);
|
|
for (let i = 0; i < 3; i++) assert.equal((await call('GET', { fn: 'machines' }, { headers: TOKEN })).code, 200);
|
|
assert.equal((await call('GET', { fn: 'machines' }, { headers: TOKEN })).code, 429);
|
|
assert.equal((await call('GET', { fn: 'machines' }, { headers: TOKEN, ip: '198.51.100.2' })).code, 200);
|
|
assert.equal((await call('GET', { fn: 'machines' }, { ip: '198.51.100.3' })).code, 401);
|
|
assert.equal((await call('GET', { fn: 'machines' }, { ip: '198.51.100.3' })).code, 401);
|
|
assert.equal((await call('GET', { fn: 'machines' }, { ip: '198.51.100.3' })).code, 429);
|
|
});
|
|
|
|
test('X26: feed is capped at 100 a call; rows older than 30 days go with their blobs; delete takes the blob', async () => {
|
|
const { call, db, blob, tick, state } = rig();
|
|
for (let i = 0; i < 120; i++) db.seed({ from_machine: 'Mac', to_machine: 'all', kind: 'text', title: 't' + i, body: 'b' });
|
|
const f = await call('GET', { fn: 'feed', limit: '500' }, { headers: TOKEN });
|
|
assert.deepEqual([f.code, f.items.length, f.limit], [200, 100, 100]);
|
|
const old = db.seed({ ts: '2026-08-01T00:00:00.000Z', from_machine: 'PC1', to_machine: 'all', kind: 'file', title: 'old', body: '', file_url: BLOB + '?old' });
|
|
const recent = db.seed({ ts: '2026-10-01T00:00:00.000Z', from_machine: 'PC1', to_machine: 'all', kind: 'file', title: 'recent', body: '', file_url: BLOB + '?recent' });
|
|
await call('GET', { fn: 'feed' }, { headers: TOKEN }); // inside the 10-minute window: no second sweep yet
|
|
assert.equal(db.items.includes(old), true);
|
|
tick(EXPIRE_EVERY_MS + 1);
|
|
await call('GET', { fn: 'feed' }, { headers: TOKEN });
|
|
assert.equal(db.items.includes(old), false);
|
|
assert.equal(db.items.includes(recent), true);
|
|
assert.deepEqual(blob.deleted, [BLOB + '?old']);
|
|
assert.deepEqual(state.expired, { rows: 1, blobs: 1 });
|
|
const d = await call('POST', { fn: 'delete' }, { headers: TOKEN, body: { id: recent.id } });
|
|
assert.deepEqual([d.code, d.blobs], [200, 1]);
|
|
assert.deepEqual(blob.deleted, [BLOB + '?old', BLOB + '?recent']);
|
|
const direct = await expire(db.sql, blob, Date.UTC(2027, 0, 1));
|
|
assert.equal(direct.rows, 120);
|
|
});
|
|
|
|
test('done carries the machine the secret proves; a wrong secret is refused', async () => {
|
|
const { call, db } = rig();
|
|
const s1 = newSecret();
|
|
await call('POST', { fn: 'secret' }, { headers: TOKEN, body: { name: 'PC1', secret_hash: secretHash(s1) } });
|
|
const it = db.seed({ from_machine: 'Mac', to_machine: 'PC1', kind: 'run', title: 't', body: 'b' });
|
|
assert.equal((await call('POST', { fn: 'done' }, { headers: { ...KEY, 'x-machine-secret': newSecret() }, body: { id: it.id, exit_code: 0 } })).code, 403);
|
|
assert.equal((await call('POST', { fn: 'done' }, { headers: { ...KEY, 'x-machine-secret': s1 }, body: { id: it.id, exit_code: 0 } })).code, 200);
|
|
assert.deepEqual([it.done, it.flags.exit_code, it.flags.done_by], [true, 0, 'PC1']);
|
|
});
|