cerberus-e2e / tree / test/helpers.ts ยท raw (this view has old GitHub links rewritten for the mirror; raw is byte-exact)
import { readFileSync } from 'node:fs';
import { privateKeyToAccount } from 'viem/accounts';
import { loadChambers } from '../src/load.ts';
import type { AirlockDecision, Chambers, SessionToken, Tier } from '../src/types.ts';

export const fx = JSON.parse(readFileSync(new URL('../fixtures/airlock-cases.json', import.meta.url), 'utf8'));
export const NOW = Date.parse(fx.now);
/** The well known public test accounts of local Ethereum dev chains (anvil accounts 0 to 2): never funded. */
const ANVIL_TEST_ACCOUNTS = [
  '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80',
  '0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d',
  '0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a',
] as const;
export const owner = privateKeyToAccount(ANVIL_TEST_ACCOUNTS[fx.owner]);
export const other = privateKeyToAccount(ANVIL_TEST_ACCOUNTS[fx.other]);

export interface AirlockCase {
  id: string; name: string; record?: string; outside?: number; intent?: string; laws?: string; min_tier?: Tier;
  sign?: 'owner' | 'other' | 'garbage'; owner_address?: 'other'; at?: number; replay?: boolean; tamper?: Record<string, string>;
  expect: { passed: boolean; refused_at?: number; tier?: Tier; steps?: number; flags?: boolean };
}
export interface SessionCase {
  id: string; name: string; tier: Tier; check: number; check_expired?: number; mutate?: Record<string, string>;
  job_id?: string; secret?: string; expect: { ok: boolean };
}

const intentText = (k: string): string => {
  const v = fx.intents[k];
  return typeof v === 'string' ? v : v.repeat.repeat(v.times);
};

let chambers: Chambers | undefined;
export const ch = async () => (chambers ??= await loadChambers());

export async function runCase(c: AirlockCase): Promise<{ d: AirlockDecision; second?: AirlockDecision }> {
  const k = await ch();
  // a fresh challenge per case, signed over the exact message chamber 1 gives
  const challenge = k.createChallenge({ did: fx.did, listingId: fx.listing_id, now: NOW });
  const msg = k.challengeMessage(challenge);
  const signature = c.sign === 'garbage' ? '0xdeadbeef' : await (c.sign === 'other' ? other : owner).signMessage({ message: msg });
  const sent = c.tamper ? { ...challenge, ...c.tamper } : challenge; // message left as signed: a tampered field
  const input = {
    did: fx.did, listingId: fx.listing_id,
    ownerAddress: c.owner_address === 'other' ? other.address : owner.address,
    challenge: sent, signature,
    record: fx.records[c.record ?? 'proven'], outside: c.outside,
    intent: intentText(c.intent ?? 'clean'), laws: fx.laws[c.laws ?? 'all'],
    jobId: fx.job_id, minTier: c.min_tier,
  };
  const opts = { now: NOW + (c.at ?? 1000), secret: fx.secret };
  const d = await k.runAirlock(input, opts);
  if (!c.replay) return { d };
  const second = await k.runAirlock(input, opts);
  return { d, second };
}

export async function sessionToken(tier: Tier): Promise<SessionToken> {
  return (await ch()).issueSessionToken({ did: fx.did, jobId: fx.job_id, tier, now: NOW, secret: fx.secret });
}
export function mutate(t: SessionToken, m?: Record<string, string>): SessionToken {
  if (!m) return t;
  const out: any = { ...t };
  for (const [key, v] of Object.entries(m)) out[key] = key === 'mac' && v === 'flip' ? (t.mac[0] === 'a' ? 'b' : 'a') + t.mac.slice(1) : v;
  return out;
}

agentgate-source: code mirror on Cloudflare Pages. All repos