import { describe, expect, it, vi } from "vitest"; import { createClientRecord } from "../plugin/core/persistence.js"; import { createServerRuleRegistry, ServerRuleRegistryError } from "../plugin/core/rules.js"; import { createPairingService } from "../plugin/services/pairing.js"; describe("Yonexus.Server PairingService", () => { it("creates a pending pairing request with ttl metadata", () => { const record = createClientRecord("client-a"); const pairing = createPairingService({ now: () => 1_710_000_000 }); const request = pairing.createPairingRequest(record, { ttlSeconds: 180 }); expect(request.identifier).toBe("client-a"); expect(request.ttlSeconds).toBe(180); expect(request.expiresAt).toBe(1_710_000_180); expect(record.pairingStatus).toBe("pending"); expect(record.pairingCode).toBe(request.pairingCode); expect(record.pairingNotifyStatus).toBe("pending"); }); it("confirms a valid pairing and clears pairing-only fields", () => { const record = createClientRecord("client-a"); const pairing = createPairingService({ now: () => 1_710_000_100 }); const request = pairing.createPairingRequest(record, { ttlSeconds: 300 }); const result = pairing.confirmPairing(record, request.pairingCode); expect(result).toMatchObject({ success: true, pairedAt: 1_710_000_100 }); expect(typeof result.secret).toBe("string"); expect(record.pairingStatus).toBe("paired"); expect(record.secret).toBe(result.secret); expect(record.pairingCode).toBeUndefined(); expect(record.pairingExpiresAt).toBeUndefined(); expect(record.pairingNotifyStatus).toBeUndefined(); }); it("rejects expired and invalid pairing confirmations without dirtying state", () => { const record = createClientRecord("client-a"); let now = 1_710_000_000; const pairing = createPairingService({ now: () => now }); const request = pairing.createPairingRequest(record, { ttlSeconds: 60 }); const invalid = pairing.confirmPairing(record, "WRONG-CODE-000"); expect(invalid).toEqual({ success: false, reason: "invalid_code" }); expect(record.pairingStatus).toBe("pending"); expect(record.pairingCode).toBe(request.pairingCode); now = 1_710_000_100; const expired = pairing.confirmPairing(record, request.pairingCode); expect(expired).toEqual({ success: false, reason: "expired" }); expect(record.pairingStatus).toBe("unpaired"); expect(record.pairingCode).toBeUndefined(); }); it("marks notification delivery state transitions", () => { const record = createClientRecord("client-a"); const pairing = createPairingService({ now: () => 1_710_000_000 }); pairing.createPairingRequest(record); pairing.markNotificationSent(record); expect(record.pairingNotifyStatus).toBe("sent"); expect(record.pairingNotifiedAt).toBe(1_710_000_000); pairing.markNotificationFailed(record); expect(record.pairingNotifyStatus).toBe("failed"); }); }); describe("Yonexus.Server RuleRegistry", () => { it("dispatches exact-match rewritten messages to the registered processor", () => { const registry = createServerRuleRegistry(); const processor = vi.fn(); registry.registerRule("chat_sync", processor); const handled = registry.dispatch("chat_sync::client-a::{\"body\":\"hello\"}"); expect(handled).toBe(true); expect(processor).toHaveBeenCalledWith("chat_sync::client-a::{\"body\":\"hello\"}"); expect(registry.hasRule("chat_sync")).toBe(true); expect(registry.getRules()).toEqual(["chat_sync"]); }); it("rejects reserved and duplicate rule registrations", () => { const registry = createServerRuleRegistry(); registry.registerRule("chat_sync", () => undefined); expect(() => registry.registerRule("builtin", () => undefined)).toThrow(ServerRuleRegistryError); expect(() => registry.registerRule("chat_sync", () => undefined)).toThrow( "Rule 'chat_sync' is already registered" ); }); it("returns false when no processor matches a rewritten message", () => { const registry = createServerRuleRegistry(); expect(registry.dispatch("chat_sync::client-a::{\"body\":\"hello\"}")).toBe(false); }); });