import { describe, expect, test } from "vitest"; import { COURSE_CHAT_NONCE_HEADER, COURSE_CHAT_SIGNATURE_HEADER, COURSE_CHAT_TIMESTAMP_HEADER, CourseChatCapabilityVerifier, decodeCourseChatCapability, signCourseChatRequest, } from "../src/courses/web-capability.js"; describe("course chat loopback capability", () => { test("binds one fresh nonce to the exact path and body", () => { const key = Buffer.alloc(32, 17); const body = Buffer.from('{"question":"What is SFT?"}'); const path = "/api/courses/post-training/questions"; const signature = signCourseChatRequest(key, { method: "POST", path, body, timestamp: 1_900_000_000_000, nonce: "A".repeat(32), }); const headers = { [COURSE_CHAT_TIMESTAMP_HEADER]: signature.timestamp, [COURSE_CHAT_NONCE_HEADER]: signature.nonce, [COURSE_CHAT_SIGNATURE_HEADER]: signature.signature, }; const verifier = new CourseChatCapabilityVerifier(key, { now: () => 1_900_000_000_000 }); expect(verifier.verify(headers, "POST", path, body)).toBe(true); expect(verifier.verify(headers, "POST", path, body)).toBe(false); const changed = new CourseChatCapabilityVerifier(key, { now: () => 1_900_000_000_000 }); expect(changed.verify(headers, "POST", `${path}/other`, body)).toBe(false); expect(changed.verify(headers, "POST", path, Buffer.from("{}"))).toBe(false); }); test("decodes only canonical bounded base64", () => { const key = Buffer.alloc(32, 9); expect(decodeCourseChatCapability(key.toString("base64"))).toEqual(key); expect(() => decodeCourseChatCapability("not base64")).toThrow("canonical base64"); expect(() => decodeCourseChatCapability(Buffer.alloc(16).toString("base64"))).toThrow("32 to 128 bytes"); }); });