Files
agent/packages/harness/tests/cms-connection.test.ts
T

103 lines
6.6 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { CmsConnection } from "../src/cms/cms-connection.js";
const dramaId = "11111111-1111-4111-8111-111111111111";
const episodeId = "22222222-2222-4222-8222-222222222222";
const userId = "33333333-3333-4333-8333-333333333333";
const token = "test-access-secret";
function server(role = "admin") {
const requests: { url: URL; init: RequestInit | undefined }[] = [];
const fetcher: typeof fetch = (input, init) => {
const url = new URL(typeof input === "string" ? input : input instanceof URL ? input.href : input.url);
requests.push({ url, init });
const user = { id: userId, aud: "authenticated", role: "authenticated", email: "fixture@example.com", app_metadata: { role }, user_metadata: { role: "admin" }, created_at: "2026-01-01T00:00:00Z" };
let data: unknown;
if (url.pathname === "/auth/v1/token") data = { access_token: token, refresh_token: "refresh-secret", token_type: "bearer", expires_in: 3600, user };
else if (url.pathname === "/auth/v1/user") data = user;
else if (url.pathname === "/rest/v1/drama") data = [{ id: dramaId, title: "Test drama", synopsis: "Fixture only", language: "en", total_episodes: 2, status: "published" }];
else if (url.pathname === "/rest/v1/episode") data = [{ id: episodeId, drama_id: dramaId, episode_no: 1, title: "One", duration_seconds: 90, status: "published" }];
else if (url.pathname === "/rest/v1/drama_category") data = [{drama_id:dramaId,category:{name:"Mystery"}}];
else if (url.pathname === "/rest/v1/episode_source") data = [{ episode_id: episodeId, provider: "fixture", playback_path: "private/fixture.m3u8" }];
else if (url.pathname === "/rest/v1/subtitle_track") data = [{ episode_id: episodeId, locale: "en", path: "private/fixture.srt" }];
else if (url.pathname.endsWith("/subtitles/en.vtt")) return Promise.resolve(new Response("WEBVTT\n\n00:00:00.000 --> 00:00:01.000\nFixture", { headers: { "content-type": "text/vtt" } }));
else return Promise.resolve(new Response("unexpected destination", { status: 500 }));
return Promise.resolve(new Response(JSON.stringify(data), { headers: { "content-type": "application/json" } }));
};
return { requests, fetcher };
}
describe("CMS connection", () => {
it("binds signing to the selected drama and keeps it on the fixed CMS admin route", async () => {
const fixture = server();
const cms = new CmsConnection({publishableKey:"sb_publishable_test",fetch:async(input,init)=>{
const url=new URL(typeof input==="string"?input:input instanceof URL?input.href:input.url);
if(url.pathname.endsWith(`/cms/playback/${episodeId}`)) {
expect(url.origin).toBe("https://swave.pencil-stub.com");
expect(new Headers(init?.headers).get("authorization")).toBe(`Bearer ${token}`);
return new Response(JSON.stringify({url:"https://video.pencil-stub.com/fixture/master.m3u8?auth_key=private",expires_at:new Date(Date.now()+600000).toISOString()}));
}
return fixture.fetcher(input,init);
}});
await cms.login("fixture@example.com","secret");
await expect(cms.playback(dramaId,userId)).rejects.toMatchObject({code:"CMS_EPISODE_NOT_FOUND"});
expect((await cms.playback(dramaId,episodeId)).url).toContain("https://video.pencil-stub.com/");
expect(JSON.stringify(cms.status())).not.toContain("auth_key");
});
it("blocks content before login and never sends credentials to a model-facing status", async () => {
const { fetcher } = server();
const cms = new CmsConnection({ publishableKey: "sb_publishable_test", fetch: fetcher });
await expect(cms.listDramas("", 0)).rejects.toMatchObject({ code: "CMS_LOGIN_REQUIRED" });
await cms.login("fixture@example.com", "password-secret");
expect(cms.status()).toMatchObject({ configured: true, connected: true });
expect(JSON.stringify(cms.status())).not.toMatch(/password-secret|test-access-secret|refresh-secret|fixture@example/);
expect((await cms.listDramas("", 0)).dramas[0]?.title).toBe("Test drama");
cms.clear();
expect(cms.status().connected).toBe(false);
await expect(cms.listDramas("", 0)).rejects.toMatchObject({ code: "CMS_LOGIN_REQUIRED" });
});
it("rejects user-editable administrator claims and discards the session", async () => {
const { fetcher } = server("viewer");
const cms = new CmsConnection({ publishableKey: "sb_publishable_test", fetch: fetcher });
await expect(cms.login("fixture@example.com", "secret")).rejects.toMatchObject({ code: "CMS_ADMIN_REQUIRED" });
expect(cms.status().connected).toBe(false);
});
it("reports incomplete inventory without leaking playback or subtitle paths", async () => {
const { fetcher } = server();
const cms = new CmsConnection({ publishableKey: "sb_publishable_test", fetch: fetcher });
await cms.login("fixture@example.com", "secret");
const inventory = await cms.inventory(dramaId);
expect(inventory.complete).toBe(false);
expect(inventory.drama.tags).toEqual(["Mystery"]);
expect(inventory.missingEpisodeNumbers).toEqual([2]);
expect(inventory.episodes[0]).toMatchObject({ episodeNo: 1, hasSource: true, subtitleLocales: ["en"] });
expect(JSON.stringify(inventory)).not.toContain("private/");
expect(inventory.fetchedAt).toMatch(/^\d{4}-/);
});
it("binds subtitle access to an actual episode in the chosen drama", async () => {
const { fetcher, requests } = server();
const cms = new CmsConnection({ publishableKey: "sb_publishable_test", fetch: fetcher });
await cms.login("fixture@example.com", "secret");
await expect(cms.subtitle(dramaId, userId, "en")).rejects.toMatchObject({ code: "CMS_EPISODE_NOT_FOUND" });
expect((await cms.subtitle(dramaId, episodeId, "en")).text).toContain("WEBVTT");
const subtitleRequest = requests.find((request) => request.url.pathname.endsWith(".vtt"));
expect(subtitleRequest?.url.origin).toBe("https://swave.pencil-stub.com");
expect(subtitleRequest?.init?.redirect).toBe("error");
expect(new Headers(subtitleRequest?.init?.headers).get("authorization")).toBe(`Bearer ${token}`);
});
it("rejects a late login result after the connection was cleared", async () => {
const { fetcher } = server();
let release: (() => void) | undefined;
const gate = new Promise<void>((resolve) => { release = resolve; });
const cms = new CmsConnection({ publishableKey: "sb_publishable_test", fetch: async (input, init) => { await gate; return fetcher(input, init); } });
const pending = cms.login("fixture@example.com", "secret");
cms.clear();
release?.();
await expect(pending).rejects.toMatchObject({ code: "CMS_LOGIN_CANCELLED" });
expect(cms.status().connected).toBe(false);
});
});