import { describe, expect, it } from "vitest"; import { curatePublicModels, formatContext, formatPricePair, type CurationInput } from "@/lib/marketing/public-models"; const NOW = new Date("2026-09-11T12:00:00Z"); const days = (n: number) => new Date(NOW.getTime() - n * 86_400_000); function row(partial: Partial & Pick): CurationInput { return { displayName: partial.key, status: "active", sortWeight: 0, firstSeenAt: days(120), contextTokens: 128_000, inputPerMillion: 1, outputPerMillion: 4, ...partial, }; } describe("curatePublicModels", () => { it("drops hidden and deprecated/unknown models", () => { const out = curatePublicModels( [row({ key: "openai/a", provider: "openai" }), row({ key: "openai/b", provider: "openai", hidden: true }), row({ key: "openai/c", provider: "openai", status: "deprecated" }), row({ key: "openai/d", provider: "openai", status: "unknown" })], NOW, ); expect(out.map((m) => m.key)).toEqual(["openai/a"]); }); it("puts new releases first, then flagships by sortWeight, interleaved per provider", () => { const out = curatePublicModels( [ row({ key: "openai/old-flagship", provider: "openai", sortWeight: 100 }), row({ key: "openai/new", provider: "openai", sortWeight: 10, firstSeenAt: days(3) }), row({ key: "anthropic/flagship", provider: "anthropic", sortWeight: 90 }), row({ key: "anthropic/small", provider: "anthropic", sortWeight: 5 }), row({ key: "gemini/only", provider: "gemini", sortWeight: 1 }), ], NOW, ); expect(out.map((m) => m.key)).toEqual(["openai/new", "anthropic/flagship", "gemini/only", "openai/old-flagship", "anthropic/small"]); }); it("caps the list and never leaks extra fields", () => { const rows = Array.from({ length: 60 }, (_, i) => row({ key: `openai/m${i}`, provider: "openai", sortWeight: i })); const out = curatePublicModels(rows, NOW, 40); expect(out).toHaveLength(40); expect(out[0].key).toBe("openai/m59"); expect(Object.keys(out[0]).sort()).toEqual(["contextTokens", "displayName", "firstSeenAt", "inputPerMillion", "outputPerMillion", "provider", "status"].concat(["key"]).sort()); }); }); describe("formatters", () => { it("formats context windows", () => { expect(formatContext(1_000_000)).toBe("1M"); expect(formatContext(2_000_000)).toBe("2M"); expect(formatContext(400_000)).toBe("400K"); expect(formatContext(null)).toBe("—"); }); it("formats price pairs", () => { expect(formatPricePair(1.25, 10)).toBe("$1.25 / $10"); expect(formatPricePair(0.3, 2.5)).toBe("$0.30 / $2.50"); expect(formatPricePair(0, 0)).toBe("free / free"); expect(formatPricePair(null, null)).toBeNull(); }); });