返回 CodeWhale
pet-engine.test.mjs
根目录 / pet / tests / pet-engine.test.mjs
1 import test from 'node:test';
2 import assert from 'node:assert/strict';
3 import { readFileSync } from 'node:fs';
4 import { PetEngineTelemetry } from '../dist/core/pet-engine.js';
5 import { PetNative } from '../dist/core/pet-native.js';
6 import { compilePetTelemetry, encodePetJSONL } from '../dist/core/pet-telemetry.js';
7 import { petDemoEvents } from '../dist/core/pet-demo.js';
8
9 const points = JSON.stringify(readFileSync(new URL('../public/whale-points.tsv', import.meta.url), 'utf8').trim().split('\n').map(row => row.trim().split(/\s+/).map(Number)));
10 test('action receipts distinguish tool names without inventing shell contents and expire', () => {
11 const e = new PetEngineTelemetry();
12 for (const [i, name, kind] of [[0,'read_file','reading'],[1,'search_files','searching'],[2,'apply_patch','editing'],[3,'exec_command','executing'],[4,'run_tests','testing'],[5,'browser_navigate','browsing']]) {
13 e.observe({event:'turn_started'},i*1000);
14 e.observe({event:'tool_call_started',tool_call_id:'private-id',tool_name:name},i*1000);
15 const activity=e.activity(i*1000+100);
16 assert.equal(activity.kind,kind); assert.equal(activity.tool,name);
17 assert.equal(JSON.stringify(activity).includes('private-id'),false);
18 assert.equal(e.activity(i*1000+801).observed,false);
19 }
20 });
21 test('waiting and concurrent action receipts stay bounded and clear after disconnect', () => {
22 const pet=new PetNative(points,'','[]',true);
23 pet.observeEngineBatch(JSON.stringify([{event:'tool_call_started',tool_call_id:'a',tool_name:'read_file'},
24 ...Array.from({length:3},(_,i)=>({event:'agent_spawned',id:'private-'+i}))]),0);
25 pet.advanceEngine(200,true,false);
26 const frame=JSON.parse(pet.presentation());
27 assert.equal(frame.activity.kind,'reading');assert.equal(frame.activity.parallel,3);
28 assert.equal(JSON.stringify(frame.activity).includes('private-'),false);
29 pet.observeEngine(JSON.stringify({event:'approval_required',id:'private'}),200);
30 pet.advanceEngine(600,true,true);assert.equal(JSON.parse(pet.presentation()).activity.kind,'waiting');
31 const before=pet.recording(true);pet.presentation();assert.equal(pet.recording(true),before);
32 pet.disconnectEngine();assert.equal(JSON.parse(pet.presentation()).activity.observed,false);
33 });
34 test('the incremental bucket range uses the same measured projection as full replay', () => {
35 const events = petDemoEvents(), full = compilePetTelemetry(events, 80_000);
36 for (let i = 0; i < full.length; i++) assert.deepEqual(compilePetTelemetry(events, 80_000, i)[0], full[i]);
37 });
38 test('Engine pulses expire; a late failed completion tears at receipt time without rewriting history', () => {
39 const engine = new PetEngineTelemetry();
40 engine.observe({ event: 'tool_call_started', tool_call_id: 'a', tool_name: 'exec_command' }, 0);
41 engine.observe({ event: 'tool_call_heartbeat' }, 300);
42 const first = engine.bucket(0);
43 assert.equal(first.channel, 'code'); assert.equal(first.activeMs[3], 300);
44 assert.equal(engine.bucket(3).observed, 0);
45 engine.observe({ event: 'tool_call_complete', tool_call_id: 'a', tool_name: 'exec_command', failed: true }, 5900);
46 assert.equal(engine.bucket(13).observed, 0);
47 assert.equal(engine.bucket(14).channel, 'error'); assert.equal(engine.bucket(14).errors, 1);
48 assert.deepEqual(engine.bucket(0), first);
49 });
50 test('an authoritative waiting request escalates, accepted tape replays, and silence stays unknown', () => {
51 const pet = new PetNative(points, '', '[]', true);
52 pet.observeEngine(JSON.stringify({ event: 'approval_required', id: 'permission' }), 100);
53 for (let ms = 400; ms <= 30_000; ms += 400) pet.advanceEngine(ms, true, true);
54 assert.equal(JSON.parse(pet.snapshot()).needs, 'call');
55 assert.equal(JSON.parse(pet.snapshot()).state.channel, 'human');
56 const saved = JSON.parse(pet.recording()), replay = new PetNative(points, encodePetJSONL(saved.tape), JSON.stringify(saved.interactions));
57 for (let i = 0; i < 900; i++) replay.step(1 / 30, true);
58 assert.deepEqual(JSON.parse(replay.snapshot()).state, JSON.parse(pet.snapshot()).state);
59 assert.equal(JSON.parse(replay.snapshot()).digest, JSON.parse(pet.snapshot()).digest);
60 pet.observeEngine(JSON.stringify({ event: 'turn_complete' }), 30_100);
61 pet.advanceEngine(32_000, true, false);
62 assert.equal(JSON.parse(pet.snapshot()).state.observed, 0);
63 });
64 test('Engine worker boundary rejects payloads, invalid clocks and oversized active sets', () => {
65 const e = new PetEngineTelemetry();
66 assert.throws(() => e.observe({ event: 'response_delta', index: 0, delta: 'private' }, 0));
67 assert.throws(() => e.observe({ event: 'response_delta', index: 0, channel: 'fabricated' }, 0));
68 e.observe({ event: 'thinking_started', index: 0 }, 100);
69 assert.throws(() => e.observe({ event: 'thinking_complete', index: 0 }, 90));
70 for (let i = 1; i < 256; i++) e.observe({ event: 'thinking_started', index: i }, 100);
71 assert.throws(() => e.observe({ event: 'thinking_started', index: 257 }, 100));
72 });
73
74 test('failed shared metadata batches accept no partial spans or private input', () => {
75 const control = new PetNative(points, '', '[]', true), pet = new PetNative(points, '', '[]', true);
76 assert.throws(() => pet.observeEngineBatch(JSON.stringify([{event:'thinking_started',index:1},{event:'response_delta',index:1,content:'PRIVATE'}]),0));
77 pet.advanceEngine(400,true,false); control.advanceEngine(400,true,false);
78 assert.deepEqual(JSON.parse(pet.recording(true)),JSON.parse(control.recording(true)));
79 pet.observeEngineBatch(JSON.stringify([{event:'thinking_started',index:1},{event:'response_delta',index:1,channel:'reasoning'}]),400);
80 pet.advanceEngine(800,true,false);
81 assert.equal(JSON.parse(pet.snapshot()).state.channel,'reasoning');
82 });
83
84 test('successive shared batches preserve active and waiting coverage exactly', () => {
85 let shared = new PetEngineTelemetry();
86 const direct = new PetEngineTelemetry();
87 for (const [at, event, waiting] of [
88 [0, {event:'tool_call_started', tool_call_id:'build', tool_name:'exec_command'}, false],
89 [300, {event:'tool_call_heartbeat'}, false],
90 [600, {event:'tool_call_heartbeat'}, false],
91 [800, {event:'tool_call_complete', tool_call_id:'build'}, false],
92 [900, {event:'approval_required', id:'permission'}, true],
93 [1200, {event:'agent_spawned', id:'worker'}, true],
94 [1500, {event:'agent_progress', id:'worker', worker_status:'running'}, true],
95 ]) {
96 shared = shared.clone();
97 for (const engine of [shared, direct]) {
98 engine.observe(event, at);
99 engine.confirmWaiting(at, waiting);
100 }
101 for (let bucket=0; bucket<=Math.floor(at/400); bucket++)
102 assert.deepEqual(shared.bucket(bucket), direct.bucket(bucket));
103 }
104 });
105
106 test('shared presentation and still projections cannot change physics, clock, journal or score', () => {
107 const pet = new PetNative(points, '', '[]', true), control = new PetNative(points, '', '[]', true);
108 for(let i=1;i<=120;i++) {
109 pet.advanceEngine(i*1000/30,true,false); control.advanceEngine(i*1000/30,true,false);
110 const before=pet.recording(true), frame=JSON.parse(pet.presentation());
111 assert.equal(frame.points.length,980);assert.equal(frame.still.points.length,980);
112 for(let view=0;view<3;view++) pet.presentation();
113 assert.equal(pet.recording(true),before);
114 }
115 assert.deepEqual(JSON.parse(pet.recording(true)),JSON.parse(control.recording(true)));
116 });
117
117 lines Plain Text