SPB Git

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%
3.2 KB · 87 lines typescript
Raw Blame History
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 40 durable and 6 ephemeral event types (EVENT_MODEL.md §3 + v2 additions)", () => {23    expect(DURABLE_EVENT_TYPES.size).toBe(40);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(40);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