guanghu-ice-heart/runtime/public-personal-language-os/test-support.mjs

43 lines
3.1 KiB
JavaScript

import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { generateKeyPairSync, sign } from 'node:crypto';
import { PublicPersonalOS, canonical, DAY } from './engine.mjs';
export function setup(t) {
const directory = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'public-os-')));
t?.after(() => fs.rmSync(directory, { recursive: true, force: true }));
let now = Date.UTC(2026, 8, 6), seq = 0;
const identities = {
alice: { role: 'USER', userId: 'alice' }, bob: { role: 'USER', userId: 'bob' },
guide: { role: 'GUIDE' }, team: { role: 'TEAM', domain: 'DOMAIN-ZS' },
dispatcher: { role: 'DISPATCHER', domain: 'DOMAIN-SUB' },
checker: { role: 'CHECKER', domain: 'DOMAIN-ZERO' },
mother: { role: 'MOTHER', scope: 'public' }, privateMother: { role: 'MOTHER', scope: 'private' },
seed: { role: 'SEED', spaceId: 'alice-os' }, wrongSeed: { role: 'SEED', spaceId: 'bob-os' },
};
const keys = {}, trust = {};
for (const [name, identity] of Object.entries(identities)) {
keys[name] = generateKeyPairSync('ed25519');
trust[name] = { ...identity, publicKey: keys[name].publicKey.export({ type: 'spki', format: 'pem' }) };
}
const engine = new PublicPersonalOS({ directory, trust, clock: () => now, choose: () => 0 });
function envelope(actorId, action, payload = {}, spaceId = 'alice-os') {
const message = { id: `event-${++seq}`, actorId, action, payload, spaceId, issuedAt: now };
return { ...message, signature: sign(null, Buffer.from(canonical(message)), keys[actorId].privateKey).toString('base64') };
}
const call = (actor, action, payload, space) => engine.handle(envelope(actor, action, payload, space));
function create() { return call('alice', 'CREATE_OS'); }
function enroll() { return call('team', 'ENROLL_GUIDE', { guideId: 'guide', name: '示例接待者', registryEvidence: 'demo-roster-evidence',
guideConsent: envelope('guide', 'ACCEPT_PUBLIC_SERVICE', { scope: 'PUBLIC_RESIDENCY' }) }); }
function start() { create(); enroll(); call('alice', 'REQUEST_RESIDENCY', { consent: true }); return call('dispatcher', 'START_RESIDENCY'); }
function seed() { start(); now += 30 * DAY; call('dispatcher', 'END_RESIDENCY'); call('alice', 'CONTINUE_SEED', { consent: true });
return call('seed', 'SELF_NAME', { name: '示例新名', evidence: 'demo-naming-evidence' }); }
function application() { seed(); return call('alice', 'APPLY_RECOGNITION', { consent: true, evidenceRefs: ['evidence://demo/one'] }); }
function reviews(digest, decision = 'PASS') { for (const actor of ['checker', 'mother', 'team']) call(actor, 'REVIEW_APPLICATION', { digest, decision, reason: '本地测试材料核验' }); }
function action() { create(); call('alice', 'SWITCH_CHANNEL', { channelId: 'execution' });
const proposed = call('alice', 'PROPOSE_ACTION', { operation: 'CREATE_TEXT', target: path.join(directory, 'result.txt'), args: { text: 'local result' } });
call('alice', 'CONFIRM_ACTION', { actionId: proposed.plan.id, digest: proposed.digest }); return proposed; }
return { engine, directory, call, envelope, create, enroll, start, seed, application, reviews, action, advance: ms => { now += ms; } };
}