: import { describe, it, expect, beforeEach } from "vitest"; import { encodeBuiltin, decodeBuiltin, parseRuleMessage, encodeRuleMessage, parseRewrittenRuleMessage, encodeRewrittenRuleMessage, isBuiltinMessage, CodecError } from "../src/codec.js"; import type { BuiltinEnvelope, HelloPayload, HelloAckPayload } from "../src/types.js"; describe("Protocol Codec", () => { describe("encodeBuiltin / decodeBuiltin", () => { it("should encode and decode a hello message", () => { const envelope: BuiltinEnvelope<"hello", HelloPayload> = { type: "hello", requestId: "req_001", timestamp: 1711886400, payload: { identifier: "client-a", hasSecret: true, hasKeyPair: true, publicKey: "pk_test", protocolVersion: "1" } }; const encoded = encodeBuiltin(envelope); expect(encoded).toBe( 'builtin::{"type":"hello","requestId":"req_001","timestamp":1711886400,"payload":{"identifier":"client-a","hasSecret":true,"hasKeyPair":true,"publicKey":"pk_test","protocolVersion":"1"}}' ); const decoded = decodeBuiltin(encoded); expect(decoded).toEqual(envelope); }); it("should encode and decode a hello_ack message", () => { const envelope: BuiltinEnvelope<"hello_ack", HelloAckPayload> = { type: "hello_ack", requestId: "req_001", timestamp: 1711886401, payload: { identifier: "client-a", nextAction: "pair_required" } }; const encoded = encodeBuiltin(envelope); const decoded = decodeBuiltin(encoded); expect(decoded).toEqual(envelope); }); it("should handle envelope without optional fields", () => { const envelope: BuiltinEnvelope = { type: "heartbeat" }; const encoded = encodeBuiltin(envelope); expect(encoded).toBe('builtin::{"type":"heartbeat"}'); const decoded = decodeBuiltin(encoded); expect(decoded).toEqual(envelope); }); it("should throw CodecError for malformed builtin message", () => { expect(() => decodeBuiltin("invalid")).toThrow(CodecError); expect(() => decodeBuiltin("builtin::")).toThrow(CodecError); expect(() => decodeBuiltin("builtin::invalid json")).toThrow(CodecError); }); it("should throw CodecError for non-builtin message prefix", () => { expect(() => decodeBuiltin("hello::{\"type\":\"test\"}")).toThrow(CodecError); }); }); describe("isBuiltinMessage", () => { it("should return true for builtin messages", () => { expect(isBuiltinMessage('builtin::{"type":"hello"}')).toBe(true); expect(isBuiltinMessage("builtin::{}")).toBe(true); }); it("should return false for non-builtin messages", () => { expect(isBuiltinMessage("hello::world")).toBe(false); expect(isBuiltinMessage("rule::content")).toBe(false); expect(isBuiltinMessage("")).toBe(false); }); }); describe("parseRuleMessage", () => { it("should parse simple rule message", () => { const result = parseRuleMessage("chat_sync::{\"body\":\"hello\"}"); expect(result).toEqual({ rule: "chat_sync", content: '{"body":"hello"}' }); }); it("should parse rule message with content containing ::", () => { const result = parseRuleMessage("chat_sync::sender::extra::{\"body\":\"hello::world\"}"); expect(result).toEqual({ rule: "chat_sync", content: 'sender::extra::{"body":"hello::world"}' }); }); it("should throw CodecError for malformed rule message", () => { expect(() => parseRuleMessage("invalid")).toThrow(CodecError); expect(() => parseRuleMessage("")).toThrow(CodecError); }); it("should reject builtin as rule identifier", () => { expect(() => parseRuleMessage('builtin::{"type":"hello"}')).toThrow(CodecError); }); }); describe("encodeRuleMessage", () => { it("should encode rule message", () => { const encoded = encodeRuleMessage("chat_sync", '{"body":"hello"}'); expect(encoded).toBe('chat_sync::{"body":"hello"}'); }); it("should reject builtin as rule", () => { expect(() => encodeRuleMessage("builtin", "content")).toThrow(CodecError); }); }); describe("parseRewrittenRuleMessage", () => { it("should parse rewritten server-side message", () => { const result = parseRewrittenRuleMessage("chat_sync::client-a::{\"body\":\"hello\"}"); expect(result).toEqual({ rule: "chat_sync", sender: "client-a", content: '{"body":"hello"}' }); }); it("should handle content with :: delimiters", () => { const result = parseRewrittenRuleMessage("chat_sync::client-a::extra::{\"body\":\"hello::world\"}"); expect(result).toEqual({ rule: "chat_sync", sender: "client-a", content: 'extra::{"body":"hello::world"}' }); }); it("should throw CodecError for malformed rewritten message", () => { expect(() => parseRewrittenRuleMessage("rule::content")).toThrow(CodecError); expect(() => parseRewrittenRuleMessage("invalid")).toThrow(CodecError); }); }); describe("encodeRewrittenRuleMessage", () => { it("should encode rewritten message", () => { const encoded = encodeRewrittenRuleMessage("chat_sync", "client-a", '{"body":"hello"}'); expect(encoded).toBe('chat_sync::client-a::{"body":"hello"}'); }); it("should reject builtin as rule", () => { expect(() => encodeRewrittenRuleMessage("builtin", "sender", "content")).toThrow(CodecError); }); }); }); describe("Protocol Message Examples", () => { describe("Hello Flow", () => { it("should demonstrate complete hello flow", () => { // Client sends hello const hello: BuiltinEnvelope<"hello", HelloPayload> = { type: "hello", requestId: "req_001", timestamp: Math.floor(Date.now() / 1000), payload: { identifier: "client-a", hasSecret: false, hasKeyPair: true, publicKey: "ed25519_pk_test123", protocolVersion: "1" } }; const helloWire = encodeBuiltin(hello); expect(isBuiltinMessage(helloWire)).toBe(true); // Server parses hello const parsedHello = decodeBuiltin(helloWire); expect(parsedHello.type).toBe("hello"); expect((parsedHello.payload as HelloPayload).identifier).toBe("client-a"); // Server responds with hello_ack const helloAck: BuiltinEnvelope<"hello_ack", HelloAckPayload> = { type: "hello_ack", requestId: hello.requestId, timestamp: Math.floor(Date.now() / 1000), payload: { identifier: "client-a", nextAction: "pair_required" } }; const ackWire = encodeBuiltin(helloAck); const parsedAck = decodeBuiltin(ackWire); expect(parsedAck.type).toBe("hello_ack"); expect((parsedAck.payload as HelloAckPayload).nextAction).toBe("pair_required"); }); }); describe("Rule Message Flow", () => { it("should demonstrate client to server message", () => { const message = encodeRuleMessage("chat_sync", '{"conversationId":"abc","body":"hello"}'); expect(message).toBe('chat_sync::{"conversationId":"abc","body":"hello"}'); // Server parses const parsed = parseRuleMessage(message); expect(parsed.rule).toBe("chat_sync"); expect(parsed.content).toBe('{"conversationId":"abc","body":"hello"}'); }); it("should demonstrate server-side rewriting", () => { // Server rewrites incoming message const rewritten = encodeRewrittenRuleMessage( "chat_sync", "client-a", '{"conversationId":"abc","body":"hello"}' ); expect(rewritten).toBe('chat_sync::client-a::{"conversationId":"abc","body":"hello"}'); // Server-side handler parses const parsed = parseRewrittenRuleMessage(rewritten); expect(parsed.rule).toBe("chat_sync"); expect(parsed.sender).toBe("client-a"); expect(parsed.content).toBe('{"conversationId":"abc","body":"hello"}'); }); }); });