import { test } from "node:test"; import assert from "node:assert/strict"; import { buildHomepageSummary, HOMEPAGE_SUMMARY_VERSION } from "./homepageSummary"; import type { VideoStat } from "./stats"; import type { Site } from "./site"; // The v5 additions: `monthly` (transcripts by UPLOAD month, stacked by primary // public site, full history, zero-filled), per-site numbers, and `official` // (the sum over the public sites, not the instance-wide totals). // // Limits, pinned here rather than discovered: `monthly` is NOT capped — it runs // from the earliest upload month to the last COMPLETE month, however old. The // build month is never emitted (it is partial). A transcript whose upload date // is missing, malformed, in the build month or later is not placed in any // month; `monthlyUnplaced` counts it. Channels exposed by no // public site (pool-only, or a site without a siteUrl) are outside `monthly`, // the site numbers and `official`, but still inside `totals`. const NOW = new Date("2026-03-15T12:00:00Z"); function stat(p: Partial & { channelSlug: string; id: string }): VideoStat { return { slug: `${p.channelSlug}/${p.id}`, channel: p.channelSlug.toUpperCase(), title: p.id, platform: "youtube", uploadDate: "20260101", downloadedDate: "20260301", transcribedDate: "20260302", timestamp: null, duration: 1800, viewCount: null, likeCount: null, commentCount: null, channelFollowerCount: null, categories: [], tags: [], language: null, isLivestream: false, mediaType: "video", status: "available", hasTranscript: true, cueCount: null, coverage: null, ...p, } as VideoStat; } function site(siteId: string, slugs: string[], siteUrl?: string): Site { return { siteId, siteTitle: siteId.toUpperCase(), siteDescription: `${siteId} desc`, channels: slugs.map((slug) => ({ slug })), ...(siteUrl ? { siteUrl } : {}), } as unknown as Site; } const SITES = [ site("alpha", ["a1", "a2"], "https://alpha.example"), site("beta", ["b1"], "https://beta.example"), site("private", ["p1"]), // no siteUrl: not an official instance ]; const CHANNEL_SITES = { a1: ["alpha"], a2: ["alpha"], b1: ["beta"], p1: ["private"] }; const STATS: VideoStat[] = [ stat({ channelSlug: "a1", id: "x1", uploadDate: "20251101", duration: 3600 }), stat({ channelSlug: "a1", id: "x2", uploadDate: "20251101", duration: 3600, status: "deleted" }), stat({ channelSlug: "a2", id: "x3", uploadDate: "20260203", duration: 1800 }), // downloaded, not transcribed: a recording, not a transcript, not placed stat({ channelSlug: "a2", id: "x4", hasTranscript: false, transcribedDate: null }), stat({ channelSlug: "b1", id: "y1", uploadDate: "20260115", duration: 7200 }), // unplaceable upload dates: missing, malformed, future stat({ channelSlug: "b1", id: "y2", uploadDate: "" }), stat({ channelSlug: "b1", id: "y3", uploadDate: "2026-01" }), stat({ channelSlug: "b1", id: "y4", uploadDate: "20260501" }), // uploaded in the build month (2026-03): partial, so not placed stat({ channelSlug: "b1", id: "y5", uploadDate: "20260310" }), // pool-only (no public site): counted in totals, nowhere else stat({ channelSlug: "p1", id: "z1", uploadDate: "20200101", status: "deleted" }), ]; test("monthly: upload months, zero-filled from first upload to the last complete month, every site keyed", () => { const s = buildHomepageSummary(STATS, CHANNEL_SITES, SITES, NOW); assert.equal(s.version, HOMEPAGE_SUMMARY_VERSION); assert.deepEqual( s.monthly!.map((m) => m.month), ["2025-11", "2025-12", "2026-01", "2026-02"], ); assert.deepEqual(s.monthly![0].bySite, { alpha: 2, beta: 0 }); assert.deepEqual(s.monthly![1].bySite, { alpha: 0, beta: 0 }); assert.deepEqual(s.monthly![2].bySite, { alpha: 0, beta: 1 }); assert.deepEqual(s.monthly![3].bySite, { alpha: 1, beta: 0 }); // A month equal to the generation month is not emitted. assert.ok(!s.monthly!.some((m) => m.month === "2026-03")); // Placed + unplaced = every transcribed public-universe record. const placed = s.monthly!.reduce((a, m) => a + m.bySite.alpha + m.bySite.beta, 0); assert.equal(s.monthlyUnplaced, 4); assert.equal(placed + s.monthlyUnplaced!, s.official!.transcripts); }); test("per-site numbers and official totals sum over the public sites only", () => { const s = buildHomepageSummary(STATS, CHANNEL_SITES, SITES, NOW); const alpha = s.sites.find((x) => x.siteId === "alpha")!; const beta = s.sites.find((x) => x.siteId === "beta")!; assert.equal(s.sites.length, 2); assert.deepEqual( [alpha.channels, alpha.recordings, alpha.transcribed.total, alpha.hoursArchived, alpha.gone], [2, 4, 3, 3, 1], // 3600 + 3600 + 1800 s = 2.5 h -> 3 ); assert.deepEqual( [beta.channels, beta.recordings, beta.transcribed.total, beta.hoursArchived, beta.gone], [1, 5, 5, 4, 0], // 7200 + 4 x 1800 s = 4 h ); // Hours are rounded once over the summed seconds, not per site. assert.deepEqual(s.official, { sites: 2, channels: 3, recordings: 9, transcripts: 8, hoursArchived: 7, // 23400 s = 6.5 h -> 7 gone: 1, }); // The instance-wide totals still count the pool-only channel. assert.equal(s.totals.transcripts, 9); assert.equal(s.availability!.byState.deleted, 2); }); test("no transcribed public content: monthly is empty, not a run of zeroes", () => { const s = buildHomepageSummary(STATS.filter((x) => x.id === "z1"), CHANNEL_SITES, SITES, NOW); assert.deepEqual(s.monthly, []); assert.equal(s.monthlyUnplaced, 0); assert.equal(s.official!.sites, 0); }); test("the build month is never emitted, even in January (the year rolls back)", () => { const s = buildHomepageSummary( [stat({ channelSlug: "a1", id: "j1", uploadDate: "20251215" }), stat({ channelSlug: "a1", id: "j2", uploadDate: "20260105" })], CHANNEL_SITES, SITES, new Date("2026-01-20T00:00:00Z"), ); assert.deepEqual(s.monthly!.map((m) => m.month), ["2025-12"]); assert.equal(s.monthlyUnplaced, 1); }); // Release 8 review of slice H, the two LOWs (release 9). test("a transcript bucketize drops (a future transcribedDate) still keeps placed + unplaced = official", () => { const stats = [ ...STATS, // Transcribed "next year" (clock skew or a bad sidecar): the site card's // bucketized total drops it, but it is placed by its upload month. stat({ channelSlug: "a1", id: "x9", uploadDate: "20251201", transcribedDate: "20270101" }), ]; const s = buildHomepageSummary(stats, CHANNEL_SITES, SITES, NOW); const placed = s.monthly!.reduce((a, m) => a + m.bySite.alpha + m.bySite.beta, 0); assert.equal(s.official!.transcripts, 9); assert.equal(placed + s.monthlyUnplaced!, s.official!.transcripts); assert.deepEqual(s.monthly![1].bySite, { alpha: 1, beta: 0 }); }); test("a site the activity filter drops gains no monthly key and no official count", () => { const sites = [...SITES, site("gamma", ["g1"], "https://gamma.example")]; const stats = [ ...STATS, // gamma's only transcript has a transcribedDate bucketize cannot place, // so gamma has no activity and is filtered out of `sites`. stat({ channelSlug: "g1", id: "w1", uploadDate: "20251101", transcribedDate: "20270101", downloadedDate: null }), ]; const s = buildHomepageSummary(stats, { ...CHANNEL_SITES, g1: ["gamma"] }, sites, NOW); assert.ok(!s.sites.some((x) => x.siteId === "gamma")); assert.ok(s.monthly!.every((m) => !("gamma" in m.bySite))); const placed = s.monthly!.reduce((a, m) => a + m.bySite.alpha + m.bySite.beta, 0); assert.equal(placed + s.monthlyUnplaced!, s.official!.transcripts); }); test("gone counts only a deleted record we hold (it has a downloadedDate)", () => { const stats = [ ...STATS, stat({ channelSlug: "b1", id: "y9", status: "deleted", downloadedDate: null, hasTranscript: false, transcribedDate: null }), ]; const s = buildHomepageSummary(stats, CHANNEL_SITES, SITES, NOW); assert.equal(s.sites.find((x) => x.siteId === "beta")!.gone, 0); assert.equal(s.official!.gone, 1); }); test("a site's accent is published as a hex: an id becomes its on-dark value", () => { const sites = [ { ...site("alpha", ["a1", "a2"], "https://alpha.example"), accent: "brass" }, { ...site("beta", ["b1"], "https://beta.example"), accent: "#CC3366" }, ] as Site[]; const s = buildHomepageSummary(STATS, CHANNEL_SITES, sites, NOW); assert.equal(s.sites.find((x) => x.siteId === "alpha")!.accent, "#e3b15c"); assert.equal(s.sites.find((x) => x.siteId === "beta")!.accent, "#cc3366"); // No accent: no key, as before. const plain = buildHomepageSummary(STATS, CHANNEL_SITES, SITES, NOW); assert.ok(plain.sites.every((x) => !("accent" in x))); }); // Stats schema 6 guarantees a transcript a date, but a stats page written // before it could carry a whole channel of transcripts with none — and the fold // used to require the date for everything, so a site serving 1,889 videos // showed 0 transcripts, 0 channels, 0 hours. The date is only for the time axis. test("a transcript with no transcribedDate (a pre-schema-6 page) is counted, just not charted", () => { const sites = [...SITES, site("gamma", ["g1"], "https://gamma.example")]; const undated = [ stat({ channelSlug: "g1", id: "u1", uploadDate: "20251101", duration: 3600, transcribedDate: null }), stat({ channelSlug: "g1", id: "u2", uploadDate: "20260210", duration: 3600, transcribedDate: null }), ]; const base = buildHomepageSummary(STATS, CHANNEL_SITES, sites, NOW); const s = buildHomepageSummary([...STATS, ...undated], { ...CHANNEL_SITES, g1: ["gamma"] }, sites, NOW); const gamma = s.sites.find((x) => x.siteId === "gamma"); assert.ok(gamma, "a site whose transcripts are all undated is still on the family page"); assert.deepEqual( [gamma.channels, gamma.transcribed.total, gamma.hoursArchived, gamma.recordings], [1, 2, 2, 2], ); // No month to put them in: not this month, not the sparkline, not a series. assert.equal(gamma.transcribed.thisMonth, 0); assert.deepEqual(gamma.transcribed.last12, new Array(12).fill(0)); assert.equal(s.series.transcribed.month.bySite.gamma, undefined); assert.deepEqual(s.series.transcribed.month.total, base.series.transcribed.month.total); assert.equal(s.totals.transcribedThisMonth, base.totals.transcribedThisMonth); assert.ok(!s.recent.some((r) => r.siteId === "gamma"), "the recent rail needs a date"); // Counted everywhere a count lives, and placed by UPLOAD month like any other. assert.equal(s.totals.transcripts, base.totals.transcripts + 2); assert.equal(s.totals.channels, base.totals.channels + 1); assert.equal(s.official!.transcripts, base.official!.transcripts + 2); assert.equal(s.official!.channels, base.official!.channels + 1); assert.equal(s.monthly!.find((m) => m.month === "2025-11")!.bySite.gamma, 1); assert.equal(s.monthly!.find((m) => m.month === "2026-02")!.bySite.gamma, 1); const placed = s.monthly!.reduce( (a, m) => a + Object.values(m.bySite).reduce((x, y) => x + y, 0), 0, ); assert.equal(placed + s.monthlyUnplaced!, s.official!.transcripts); }); test("a named accent also travels as its id; a custom hex and no accent carry none", () => { const sites = [ { ...site("alpha", ["a1", "a2"], "https://alpha.example"), accent: " Brass " }, { ...site("beta", ["b1"], "https://beta.example"), accent: "#e3b15c" }, ] as Site[]; const s = buildHomepageSummary(STATS, CHANNEL_SITES, sites, NOW); const alpha = s.sites.find((x) => x.siteId === "alpha")!; assert.equal(alpha.accentId, "brass"); assert.equal(alpha.accent, "#e3b15c"); // the hex is unchanged // A custom hex is never mapped back to an id, even Brass's own on-dark value. const beta = s.sites.find((x) => x.siteId === "beta")!; assert.equal(beta.accent, "#e3b15c"); assert.ok(!("accentId" in beta)); const plain = buildHomepageSummary(STATS, CHANNEL_SITES, SITES, NOW); assert.ok(plain.sites.every((x) => !("accentId" in x))); }); test("a site's wordmark lead travels when it is a proper prefix of the title; otherwise no key", () => { const sites = [ { ...site("alpha", ["a1", "a2"], "https://alpha.example"), siteTitle: "Jeralyzer", wordmarkLead: " Jer " }, { ...site("beta", ["b1"], "https://beta.example"), siteTitle: "Anilyzer", wordmarkLead: "Anilyzer" }, ] as Site[]; const s = buildHomepageSummary(STATS, CHANNEL_SITES, sites, NOW); assert.equal(s.sites.find((x) => x.siteId === "alpha")!.wordmarkLead, "Jer"); // The whole title is no split (lib/brand.ts wordmarkLeadFor): no key. assert.ok(!("wordmarkLead" in s.sites.find((x) => x.siteId === "beta")!)); // Resolved against the title the card shows, case-sensitive. const other = buildHomepageSummary( STATS, CHANNEL_SITES, [{ ...sites[0], wordmarkLead: "jer" }] as Site[], NOW, ); assert.ok(!("wordmarkLead" in other.sites[0])); // No lead configured: no key, as before. const plain = buildHomepageSummary(STATS, CHANNEL_SITES, SITES, NOW); assert.ok(plain.sites.every((x) => !("wordmarkLead" in x))); assert.equal(s.version, HOMEPAGE_SUMMARY_VERSION); }); // Release 14 slice HS: site.json `listed: false`. The site still builds and // deploys; the family's public pages do not list it, and no public total counts // the channels only it exposes. test("an unlisted site is in no array and no total; a channel it shares is the listed site's", () => { const unlisted = { ...site("zeta", ["q1", "a1"], "https://zeta.example"), listed: false } as Site; const sites = [...SITES, unlisted]; const channelSites = { ...CHANNEL_SITES, a1: ["alpha", "zeta"], q1: ["zeta"] }; const own = [ stat({ channelSlug: "q1", id: "q-1", uploadDate: "20251101", duration: 7200 }), stat({ channelSlug: "q1", id: "q-2", uploadDate: "20260201", status: "deleted" }), stat({ channelSlug: "q1", id: "q-3", hasTranscript: false, transcribedDate: null }), ]; const s = buildHomepageSummary([...STATS, ...own], channelSites, sites, NOW); const base = buildHomepageSummary(STATS, CHANNEL_SITES, SITES, NOW); // Byte for byte the summary without the unlisted site: every array (sites, // channels, series, recent, monthly) and every total (totals, official, // availability, monthlyUnplaced). assert.deepEqual(s, base); const text = JSON.stringify(s); for (const needle of ["zeta", "ZETA", "q1", "Q1", "q-1"]) { assert.ok(!text.includes(needle), needle); } // The shared channel stays credited to alpha. assert.equal(s.sites.find((x) => x.siteId === "alpha")!.channels, 2); assert.equal(s.version, 6); }); // Report sites: a report-only site (`search: false`) is not a searchable archive, // so the homepage lists it no more than an unlisted one. test("a cited site is in no array and no total, exactly as an unlisted one", () => { const cited = { ...site("zeta", ["q1", "a1"], "https://zeta.example"), search: false } as Site; const channelSites = { ...CHANNEL_SITES, a1: ["alpha", "zeta"], q1: ["zeta"] }; const own = [stat({ channelSlug: "q1", id: "q-1", uploadDate: "20251101", duration: 7200 })]; const s = buildHomepageSummary([...STATS, ...own], channelSites, [...SITES, cited], NOW); assert.deepEqual(s, buildHomepageSummary(STATS, CHANNEL_SITES, SITES, NOW)); assert.ok(!JSON.stringify(s).includes("zeta")); }); test("an unlisted site with no siteUrl, or with every channel shared, changes nothing either", () => { const base = buildHomepageSummary(STATS, CHANNEL_SITES, SITES, NOW); // No siteUrl: never public, and its own channel is still in no total (unlike // a pool-only channel, which `totals` counts). const urlless = { ...site("zeta", ["r1"]), listed: false } as Site; assert.deepEqual( buildHomepageSummary( [...STATS, stat({ channelSlug: "r1", id: "r-1" })], { ...CHANNEL_SITES, r1: ["zeta"] }, [...SITES, urlless], NOW, ), base, ); const sharedOnly = { ...site("zeta", ["a1", "b1"], "https://zeta.example"), listed: false } as Site; assert.deepEqual( buildHomepageSummary(STATS, { ...CHANNEL_SITES, a1: ["alpha", "zeta"], b1: ["beta", "zeta"] }, [...SITES, sharedOnly], NOW), base, ); // Listed explicitly is the default: the same summary as no key at all. const listedTrue = SITES.map((x) => ({ ...x, listed: true })) as Site[]; assert.deepEqual(buildHomepageSummary(STATS, CHANNEL_SITES, listedTrue, NOW), base); }); test("a shared channel is the listed site's even when the unlisted site's id sorts first", () => { const base = buildHomepageSummary(STATS, CHANNEL_SITES, SITES, NOW); // "aaa-hidden" < "beta": the primary pick sorts ids, so only the listed // filter keeps b1 with beta. const unlisted = { ...site("aaa-hidden", ["b1", "q1"], "https://aaa-hidden.example"), listed: false } as Site; const s = buildHomepageSummary( [...STATS, stat({ channelSlug: "q1", id: "q-1", uploadDate: "20251101" })], { ...CHANNEL_SITES, b1: ["aaa-hidden", "beta"], q1: ["aaa-hidden"] }, [unlisted, ...SITES], NOW, ); assert.deepEqual(s, base); assert.ok(s.recent.filter((r) => r.slug.startsWith("b1/")).every((r) => r.siteId === "beta")); assert.ok(!JSON.stringify(s).includes("aaa-hidden")); });