Repository Public
1import { describe, expect, it } from "vitest";2import { canonical, hashOf, slugify } from "../src/lib/hash";3import { cosine, embed, sharedTerms } from "../src/lib/embedding";4import { redact } from "../src/lib/log";5import { videoCaps } from "../src/providers/capabilities";6import { isAllowedMediaHost } from "../src/providers/http";78// Pure helpers are re-declared here so the test does not need a database connection.9const LEAD = 0.5, TAIL = 0.9, MIN_SHOT = 5;10const shotDuration = (n: number | null) => Math.max(MIN_SHOT, Math.round(((n ?? 0) + LEAD + TAIL) * 100) / 100);1112describe("idempotency keys", () => {13 it("is independent of key order and sensitive to every input", () => {14 expect(hashOf({ a: 1, b: { c: 2, d: [1, 2] } })).toBe(hashOf({ b: { d: [1, 2], c: 2 }, a: 1 }));15 expect(hashOf({ prompt: "x", rev: 0 })).not.toBe(hashOf({ prompt: "x", rev: 1 }));16 expect(canonical({ b: undefined, a: 1 })).toBe('{"a":1}');17 });18 it("gives the same key to a repeated request (no duplicate paid generation)", () => {19 const key = (rev: number) => hashOf({ kind: "video", model: "m", input: { image: "sha", prompt: "p", duration: 6, resolution: "720p", rev } });20 expect(key(0)).toBe(key(0));21 expect(key(0)).not.toBe(key(1)); // an explicit regeneration bumps the revision22 });23});2425describe("timing grammar", () => {26 it("sizes a shot from its narration with a lead and a tail, never under four seconds", () => {27 expect(shotDuration(4.85)).toBe(6.25);28 expect(shotDuration(1)).toBe(5);29 expect(shotDuration(null)).toBe(5);30 });31});3233describe("slugs", () => {34 it("drops accents and apostrophes", () => {35 expect(slugify("The Keeper's Debt")).toBe("the-keepers-debt");36 expect(slugify("L’Été à Québec")).toBe("lete-a-quebec");37 expect(slugify("???")).toBe("film");38 });39});4041describe("lexical embedding", () => {42 it("is normalised, deterministic and ranks related text higher", () => {43 const a = embed("a lighthouse keeper and his pocket watch"), b = embed("the keeper's watch stops when the lighthouse goes dark"), c = embed("urban transit design for a future city");44 expect(cosine(a, a)).toBeCloseTo(1, 3);45 expect(cosine(a, b)).toBeGreaterThan(cosine(a, c));46 expect(embed("x y z")).toEqual(embed("x y z"));47 expect(sharedTerms("pocket watch on a table", "a watch in a pocket")).toEqual(expect.arrayContaining(["pocket", "watch"]));48 });49});5051describe("safety helpers", () => {52 it("redacts API keys from log lines", () => {53 expect(redact("Authorization failed for xai-ABCDEFGHIJKLMNOPQRSTUVWX123456")).toBe("Authorization failed for xai-[redacted]");54 });55 it("only downloads generated media from xAI hosts over https", () => {56 expect(isAllowedMediaHost("https://vidgen.x.ai/bucket/video.mp4")).toBe(true);57 expect(isAllowedMediaHost("http://vidgen.x.ai/video.mp4")).toBe(false);58 expect(isAllowedMediaHost("https://169.254.169.254/latest/meta-data")).toBe(false);59 expect(isAllowedMediaHost("https://x.ai.evil.example/video.mp4")).toBe(false);60 expect(isAllowedMediaHost("not a url")).toBe(false);61 });62 it("knows what each video model can do and refuses unknown models", () => {63 expect(videoCaps("grok-imagine-video-1.5").referenceToVideo).toBe(true);64 expect(videoCaps("grok-imagine-video-1.5-lite").referenceToVideo).toBe(false);65 expect(videoCaps("grok-imagine-video").editVideo).toBe(true);66 expect(() => videoCaps("made-up")).toThrow();67 });68});69