mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-17 15:17:40 +02:00
186 lines
10 KiB
TypeScript
186 lines
10 KiB
TypeScript
// Run: node --test (via frontend/scripts/run-tests.mjs)
|
|
//
|
|
// The 1.7.8-exp.1 invariant suite. Every fix in that release that is expressible as a pure function
|
|
// gets its ENTIRE input space enumerated here, not sampled, because a sampled test cannot tell you
|
|
// which case it missed and a passing sample is the most confident kind of wrong.
|
|
//
|
|
// Three habits this file enforces on itself:
|
|
// 1. Expectations are computed INDEPENDENTLY of the implementation. Re-deriving the answer with
|
|
// the same expression the code uses proves only that the expression equals itself.
|
|
// 2. Hostile inputs are in the enumeration, not a separate afterthought section.
|
|
// 3. The suite asserts its own size at the end. A test that silently stops running reads exactly
|
|
// like a test that passed, and this repo has been burned by that three times (unmarked async
|
|
// tests, the shutdown fuse killing the runner, four linter gates switched off while printing
|
|
// "0 errors"). Counting is the only defence against a false pass on a false pass.
|
|
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { notifyWanted, notifyAllowedNow, notifyOn, type NotifyPrefs } from './notifyWanted.ts';
|
|
import { googleStartUrl, isUsableInstallId } from './googleStartUrl.ts';
|
|
import { focusGuestForKeys, guestKeyTakeoverCount, resetGuestKeyTakeoverCount } from './focusGuestForKeys.ts';
|
|
|
|
let checks = 0;
|
|
const check = (cond: boolean, msg: string): void => { checks += 1; assert.ok(cond, msg); };
|
|
const eq = (a: unknown, b: unknown, msg: string): void => { checks += 1; assert.equal(a, b, msg); };
|
|
|
|
// ---------------------------------------------------------------- ENG-276, notifications
|
|
|
|
const TRI = [undefined, true, false] as const;
|
|
|
|
test('ENG-276: all 64 toggle combinations, agent and workflow gates are fully independent', () => {
|
|
let cases = 0;
|
|
for (const ac of TRI) for (const ae of TRI) for (const wr of TRI) for (const wf of TRI) {
|
|
const p: NotifyPrefs = {
|
|
notify_agent_completion: ac, notify_agent_errors: ae,
|
|
notify_workflow_runs: wr, notify_workflow_failures: wf,
|
|
};
|
|
// Independently derived: absent means on, so the answer is "this specific field is not false".
|
|
const wantAgentOk = ac !== false;
|
|
const wantAgentBad = ae !== false;
|
|
const wantWfOk = wr !== false;
|
|
const wantWfBad = wf !== false;
|
|
|
|
eq(notifyWanted(p, 'agent', false), wantAgentOk, `agent-ok ${JSON.stringify(p)}`);
|
|
eq(notifyWanted(p, 'agent', true), wantAgentBad, `agent-bad ${JSON.stringify(p)}`);
|
|
eq(notifyWanted(p, 'workflow', false), wantWfOk, `wf-ok ${JSON.stringify(p)}`);
|
|
eq(notifyWanted(p, 'workflow', true), wantWfBad, `wf-bad ${JSON.stringify(p)}`);
|
|
|
|
// The cross-gate bug this shipped to kill: an agent field must never move a workflow answer.
|
|
const agentsSilenced: NotifyPrefs = { ...p, notify_agent_completion: false, notify_agent_errors: false };
|
|
eq(notifyWanted(agentsSilenced, 'workflow', false), wantWfOk, `agents-off changed wf-ok ${JSON.stringify(p)}`);
|
|
eq(notifyWanted(agentsSilenced, 'workflow', true), wantWfBad, `agents-off changed wf-bad ${JSON.stringify(p)}`);
|
|
const wfSilenced: NotifyPrefs = { ...p, notify_workflow_runs: false, notify_workflow_failures: false };
|
|
eq(notifyWanted(wfSilenced, 'agent', false), wantAgentOk, `wf-off changed agent-ok ${JSON.stringify(p)}`);
|
|
eq(notifyWanted(wfSilenced, 'agent', true), wantAgentBad, `wf-off changed agent-bad ${JSON.stringify(p)}`);
|
|
cases += 1;
|
|
}
|
|
eq(cases, 81, 'the enumeration itself changed size, so the coverage claim is stale');
|
|
});
|
|
|
|
test('ENG-276: focus gating is exhaustive and defaults to holding notifications back', () => {
|
|
for (const whenFocused of TRI) for (const hidden of [true, false]) {
|
|
const got = notifyAllowedNow({ notify_when_focused: whenFocused }, hidden);
|
|
// Independently derived: allowed when the window is hidden, or when explicitly opted in.
|
|
eq(got, hidden || whenFocused === true, `whenFocused=${whenFocused} hidden=${hidden}`);
|
|
}
|
|
// Absent must behave as off here, the opposite of every other notify field, because a notification
|
|
// for the window you are already looking at is noise.
|
|
eq(notifyAllowedNow({}, false), false, 'absent notify_when_focused leaked as on');
|
|
eq(notifyOn(undefined), true, 'absent should mean ON for the ordinary fields');
|
|
});
|
|
|
|
// ---------------------------------------------------------------- sign-in guard, incl. attacks
|
|
|
|
test('install id: every length from 0 to 200 agrees with the cloud bound, no off-by-one', () => {
|
|
for (let n = 0; n <= 200; n++) {
|
|
const id = 'a'.repeat(n);
|
|
const usable = n >= 8 && n <= 128;
|
|
eq(isUsableInstallId(id), usable, `length ${n}`);
|
|
eq(googleStartUrl('https://api.openswarm.com', id, 8324) !== null, usable, `url at length ${n}`);
|
|
}
|
|
});
|
|
|
|
test('install id: hostile values cannot inject extra query parameters into the OAuth URL', () => {
|
|
// The real attack shape: if the id were concatenated rather than encoded, an `&` would let a
|
|
// crafted id append its own parameters (a redirect_uri, say) to a URL the user is sent to.
|
|
const hostile = [
|
|
'aaaaaaaa&redirect_uri=https://evil.example',
|
|
'aaaaaaaa#fragment',
|
|
'aaaaaaaa?x=1',
|
|
'aaaaaaaa https://evil.example',
|
|
'aaaaaaaa\nlocal_port=1',
|
|
'aaaaaaaa%26redirect_uri%3Dx',
|
|
'../../aaaaaaaa',
|
|
'aaaaaaaa |