spb/khaelor Public
KHAELOR — a terminal-native autonomous engineering agent powered by Anthropic.
TypeScript 82.9%
HTML 14.9%
CSS 1.1%
JavaScript 0.7%
1/**2 * KHAELOR3 * File: tests/session/events.test.ts4 * Description: Event catalog tests — serialization round-trip, durable/ephemeral classification, schema version.5 *6 * Author: Simon-Pierre Boucher7 * Contact: contact@spboucher.ai8 */910import { describe, expect, it } from "vitest";11import {12 DURABLE_EVENT_TYPES,13 EPHEMERAL_EVENT_TYPES,14 EVENT_SCHEMA_VERSION,15 isDurableEvent,16 isDurableEventType,17 isEphemeralEventType,18} from "../../src/session/events.js";19import { envelope, ephemeralEnvelope, sampleDurableInputs, sampleEphemeralInputs } from "./fixtures.js";2021describe("event catalog", () => {22 it("has exactly 32 durable and 6 ephemeral event types (EVENT_MODEL.md §3)", () => {23 expect(DURABLE_EVENT_TYPES.size).toBe(32);24 expect(EPHEMERAL_EVENT_TYPES.size).toBe(6);25 });2627 it("durable and ephemeral sets are disjoint", () => {28 for (const t of DURABLE_EVENT_TYPES) {29 expect(EPHEMERAL_EVENT_TYPES.has(t as never)).toBe(false);30 }31 });3233 it("fixtures cover every catalog type exactly once", () => {34 const durableTypes = sampleDurableInputs().map((i) => i.type);35 expect(new Set(durableTypes).size).toBe(32);36 expect(new Set(durableTypes)).toEqual(new Set(DURABLE_EVENT_TYPES));37 const ephemeralTypes = sampleEphemeralInputs().map((i) => i.type);38 expect(new Set(ephemeralTypes)).toEqual(new Set(EPHEMERAL_EVENT_TYPES));39 });4041 it("classifies types correctly", () => {42 expect(isDurableEventType("session.started")).toBe(true);43 expect(isDurableEventType("model.text-delta")).toBe(false);44 expect(isDurableEventType("nonsense.event")).toBe(false);45 expect(isEphemeralEventType("model.text-delta")).toBe(true);46 expect(isEphemeralEventType("tool.completed")).toBe(false);47 });4849 it("schema version constant is 1", () => {50 expect(EVENT_SCHEMA_VERSION).toBe(1);51 });52});5354describe("serialization round-trip", () => {55 it("every durable event type survives JSON round-trip byte-identically", () => {56 const inputs = sampleDurableInputs();57 inputs.forEach((input, index) => {58 const event = envelope(index + 1, input);59 const line = JSON.stringify(event);60 const parsed = JSON.parse(line);61 expect(parsed).toEqual(event);62 // Byte-stability: re-serializing the parsed object yields identical bytes.63 expect(JSON.stringify(parsed)).toBe(line);64 expect(isDurableEvent(parsed)).toBe(true);65 expect(parsed.v).toBe(EVENT_SCHEMA_VERSION);66 expect(parsed.seq).toBe(index + 1);67 });68 });6970 it("every ephemeral event type survives JSON round-trip (no v, no seq)", () => {71 for (const input of sampleEphemeralInputs()) {72 const event = ephemeralEnvelope(input);73 const parsed = JSON.parse(JSON.stringify(event));74 expect(parsed).toEqual(event);75 expect("v" in parsed).toBe(false);76 expect("seq" in parsed).toBe(false);77 expect(isDurableEvent(parsed)).toBe(false);78 }79 });8081 it("durable envelope keys serialize in the fixed order v,id,sessionId,seq,ts,type,payload", () => {82 const event = envelope(1, sampleDurableInputs()[0]!);83 const keys = Object.keys(JSON.parse(JSON.stringify(event)));84 expect(keys).toEqual(["v", "id", "sessionId", "seq", "ts", "type", "payload"]);85 });86});87