| 1 | import test from 'node:test'; |
| 2 | import assert from 'node:assert/strict'; |
| 3 | import { readFileSync } from 'node:fs'; |
| 4 | import { PetNative } from '../dist/core/pet-native.js'; |
| 5 | import { PetLiveTape, compilePetTelemetry } from '../dist/core/pet-telemetry.js'; |
| 6 | import { petDemoEvents } from '../dist/core/pet-demo.js'; |
| 7 | |
| 8 | 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))); |
| 9 | const human = compilePetTelemetry(petDemoEvents()).find(b => b.channel === 'human' && b.waiting); |
| 10 | const packet = sequence => ({ ...human, sequence, simTimeMs: sequence * 400 }); |
| 11 | const line = sequence => JSON.stringify(packet(sequence)) + '\n'; |
| 12 | |
| 13 | test('live tails require advancement after attachment, replacement, invalid input and resume', () => { |
| 14 | const live = new PetLiveTape(); |
| 15 | assert.equal(live.readTail(line(50)), undefined, 'Existing bytes establish a baseline'); |
| 16 | assert.equal(live.readTail(line(50)), undefined); |
| 17 | assert.deepEqual(live.readTail(line(51)), packet(51)); |
| 18 | assert.equal(live.readTail(line(0)), undefined, 'An in-place producer restart is a new baseline'); |
| 19 | assert.deepEqual(live.readTail(line(1)), packet(1)); |
| 20 | assert.equal(live.readTail(line(2).trimEnd()), undefined, 'Wait for a complete append'); |
| 21 | assert.deepEqual(live.readTail(line(2)), packet(2)); |
| 22 | assert.throws(() => live.readTail('{bad}\n')); |
| 23 | assert.equal(live.readTail(line(3)), undefined); |
| 24 | assert.deepEqual(live.readTail(line(4)), packet(4)); |
| 25 | live.readTail(''); assert.equal(live.readTail(line(5)), undefined); |
| 26 | assert.deepEqual(live.readTail('partial first line\n' + line(6)), packet(6)); |
| 27 | live.reset(); assert.equal(live.readTail(line(7)), undefined); |
| 28 | assert.deepEqual(live.readTail(line(8)), packet(8)); |
| 29 | assert.throws(() => live.readTail('x'.repeat(262_145))); |
| 30 | assert.equal(live.readTail(line(9)), undefined); |
| 31 | }); |
| 32 | |
| 33 | test('native normal-tick live resume uses the world clock and discards old voices', () => { |
| 34 | const pet = new PetNative(points, '', '[]', true); |
| 35 | for (let i = 0; i < 90; i++) pet.step(1 / 30, true); |
| 36 | pet.accept(JSON.stringify(packet(40))); |
| 37 | for (let i = 0; i < 12; i++) pet.step(1 / 30, true); |
| 38 | const before = JSON.parse(pet.snapshot()); |
| 39 | assert.equal(before.state.channel, 'human'); |
| 40 | assert.ok(before.state.observed >= .92); |
| 41 | const resumedAt = pet.resumeEngine(), after = JSON.parse(pet.snapshot()); |
| 42 | assert.ok(resumedAt >= before.timeMs && resumedAt - before.timeMs <= 800, 'Skip only the unexpired live interval'); |
| 43 | assert.equal(after.state.observed, 0); |
| 44 | assert.equal(after.needs, 'none'); assert.deepEqual(after.voices, []); |
| 45 | const copy = new PetNative(points); copy.restoreRecording(pet.recording(true)); |
| 46 | assert.deepEqual(JSON.parse(copy.snapshot()), after); |
| 47 | for (let i = 0; i < 60; i++) assert.equal(pet.step(1 / 30, true), copy.step(1 / 30, true)); |
| 48 | }); |
| 49 | |
| 50 | test('native live delivery journals only newly observed packets across resume', () => { |
| 51 | const pet = new PetNative(points, '', '[]', true); |
| 52 | assert.equal(pet.acceptLiveTail(line(20)), false); |
| 53 | assert.equal(JSON.parse(pet.recording()).tape.length, 1); |
| 54 | assert.equal(pet.acceptLiveTail(line(21)), true); |
| 55 | for (let i = 0; i < 12; i++) pet.step(1 / 30, true); |
| 56 | assert.equal(JSON.parse(pet.snapshot()).state.channel, 'human'); |
| 57 | pet.resetLiveInput(); |
| 58 | assert.equal(pet.acceptLiveTail(line(22)), false, 'A background append establishes the resumed baseline'); |
| 59 | assert.equal(JSON.parse(pet.snapshot()).needs, 'none'); |
| 60 | assert.equal(pet.acceptLiveTail(line(23)), true); |
| 61 | for (let i = 0; i < 12; i++) pet.step(1 / 30, true); |
| 62 | assert.equal(JSON.parse(pet.snapshot()).state.channel, 'human'); |
| 63 | }); |
| 64 |