T-HUB/packages/connector-peertube/src/resolve.test.ts
glass beadian aa5e262f3a Tactical Media Broadcast Hub — initial release
Self-hosted multi-platform broadcast newsroom for activist collectives:
publishing pipeline with group approval and DST-proof scheduling, TweetDeck-style
column wall with hostile-content-safe rendering, live control room with Auto
Director, bridged chat governance with polls and quorum, distributed sandboxed
transcode workers, encrypted content-addressed archive, monthly analytics.

MIT licensed. See README.md and docs/.
2026-10-10 11:56:42 +00:00

116 lines
5.6 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import {
canonicalPeerTubeHost,
channelRemoteRef,
PeerTubeResolveError,
resolvePeerTubeSource,
videoRemoteRef,
} from "./resolve.js";
const INSTANCE = "https://video.example";
const UUID = "0d1c2b3a-4e5f-4a6b-8c7d-9e0f1a2b3c4d";
function json(body: unknown, status = 200): Response {
return new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
}
describe("remote_ref canonicalization", () => {
it("IDNA-lowercases hosts in channel refs", () => {
expect(channelRemoteRef("alice", "VIDEO.Example.")).toBe("channel:alice@video.example");
expect(channelRemoteRef("Alice", "vïdeo.example")).toBe(channelRemoteRef("alice", "xn--vdeo-5pa.example"));
});
it("lowercases uuids in video refs", () => {
expect(videoRemoteRef(UUID.toUpperCase(), "video.example")).toBe(`video:${UUID}@video.example`);
});
it("canonicalPeerTubeHost strips brackets and trailing dots", () => {
expect(canonicalPeerTubeHost("[video.example].")).toBe("video.example");
expect(canonicalPeerTubeHost("")).toBe("");
});
});
describe("resolvePeerTubeSource", () => {
it("resolves a channel through the public video-channels endpoint", async () => {
const fetch = vi.fn(async (url: string) => {
expect(url).toBe(`${INSTANCE}/api/v1/video-channels/alice%40video.example`);
return json({ id: 7, name: "alice", host: "video.example", displayName: "Alice's channel" });
});
const result = await resolvePeerTubeSource("channel", "alice", "video.example", { fetch });
expect(result).toMatchObject({
remoteRef: "channel:alice@video.example",
instanceUrl: INSTANCE,
live: true,
label: "Alice's channel",
channelId: 7,
});
expect(fetch).toHaveBeenCalledOnce();
});
it("resolves a running live: isLive + PUBLISHED, deterministic master.m3u8", async () => {
const fetch = vi.fn(async () => json({ uuid: UUID, name: "My live", isLive: true, state: { id: 1, label: "Published" } }));
const result = await resolvePeerTubeSource("live", UUID, "video.example", { fetch });
expect(result.live).toBe(true);
expect(result.playbackHlsUrl).toBe(`${INSTANCE}/static/streaming-playlists/hls/${UUID}/master.m3u8`);
expect(result.remoteRef).toBe(`video:${UUID}@video.example`);
});
it("a WAITING_FOR_LIVE video is not live and carries no playback URL", async () => {
const fetch = vi.fn(async () => json({ uuid: UUID, name: "Waiting", isLive: true, state: { id: 4 } }));
const result = await resolvePeerTubeSource("live", UUID, "video.example", { fetch });
expect(result.live).toBe(false);
expect(result.playbackHlsUrl).toBeUndefined();
});
it("a VOD uses the API streamingPlaylists[].playlistUrl (type 1 only)", async () => {
const fetch = vi.fn(async () => json({
uuid: UUID, name: "VOD", isLive: false, state: { id: 1 },
streamingPlaylists: [
{ id: 1, type: 2, playlistUrl: "/dash/manifest.mpd" },
{ id: 2, type: 1, playlistUrl: "/static/streaming-playlists/hls/x/master.m3u8" },
],
}));
const result = await resolvePeerTubeSource("live", UUID, "video.example", { fetch });
expect(result.live).toBe(false);
expect(result.playbackHlsUrl).toBe(`${INSTANCE}/static/streaming-playlists/hls/x/master.m3u8`);
});
it("404 maps to not_found (invalid row), 500 to upstream (failed row), 403 to denied", async () => {
for (const [status, reason] of [[404, "not_found"], [410, "not_found"], [500, "upstream"], [403, "denied"]] as const) {
const fetch = vi.fn(async () => json({ detail: "nope" }, status));
await expect(resolvePeerTubeSource("channel", "alice", "video.example", { fetch }))
.rejects.toMatchObject({ reason });
await expect(resolvePeerTubeSource("live", UUID, "video.example", { fetch }))
.rejects.toMatchObject({ reason });
}
});
it("transport failures are upstream, malformed payloads are upstream", async () => {
const dead = vi.fn(async () => { throw new TypeError("socket hang up"); });
await expect(resolvePeerTubeSource("channel", "alice", "video.example", { fetch: dead })).rejects.toMatchObject({ reason: "upstream" });
const garbage = vi.fn(async () => new Response("<html>not peertube</html>", { status: 200 }));
await expect(resolvePeerTubeSource("channel", "alice", "video.example", { fetch: garbage })).rejects.toBeInstanceOf(PeerTubeResolveError);
const notVideo = vi.fn(async () => json({ name: "not a video" }));
await expect(resolvePeerTubeSource("live", UUID, "video.example", { fetch: notVideo })).rejects.toMatchObject({ reason: "upstream" });
});
it("forwards the abort signal to the transport", async () => {
const fetch = vi.fn(async (_url: string, init?: RequestInit) => {
expect((init as { signal?: AbortSignal } | undefined)?.signal?.aborted).toBe(true);
return json({ uuid: UUID, isLive: false, name: "x", state: { id: 1 } });
});
const controller = new AbortController();
controller.abort();
await resolvePeerTubeSource("live", UUID, "video.example", { fetch, signal: controller.signal });
expect(fetch).toHaveBeenCalledOnce();
});
it("a shortUUID is passed through to the videos endpoint", async () => {
const fetch = vi.fn(async (url: string) => {
expect(url.endsWith("/api/v1/videos/abc123_XY")).toBe(true);
return json({ uuid: UUID, isLive: false, name: "x", state: { id: 1 } });
});
const result = await resolvePeerTubeSource("live", "abc123_XY", "video.example", { fetch });
expect(result.remoteRef).toBe(`video:${UUID}@video.example`);
});
});