T-HUB/packages/core/src/stream-columns.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

225 lines
10 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
ACCOUNTS_MAX,
clampIntervalMs,
clampPosition,
clampTitle,
DEFAULT_INTERVAL_MS,
defaultIntervalMs,
isKeywordLike,
parseRefreshPolicyClamped,
parseStreamColumnSpec,
QUERY_MAX,
STREAM_COLUMN_TITLE_MAX,
StreamColumnsValidationError,
} from "./stream-columns.js";
const rejects = (fn: () => unknown): void => {
expect(fn).toThrow(StreamColumnsValidationError);
};
describe("column type dispatch", () => {
it("covers exactly the four persisted types", () => {
expect(
parseStreamColumnSpec("platform_feed", { connectionId: "c1", kinds: ["home"] }),
).toEqual({ connectionId: "c1", kinds: ["home"] });
expect(
parseStreamColumnSpec("saved_search", { query: "product", platforms: ["bluesky"] }),
).toEqual({ query: "product", platforms: ["bluesky"], matchMode: "any" });
expect(
parseStreamColumnSpec("keyword", { query: "acampada26", platforms: ["bluesky", "mastodon"] }),
).toEqual({ query: "acampada26", platforms: ["bluesky", "mastodon"], matchMode: "any" });
expect(
parseStreamColumnSpec("account_list", {
accounts: [{ provider: "mastodon", handle: "@user@mastodon.social" }],
}),
).toEqual({ accounts: [{ provider: "mastodon", handle: "@user@mastodon.social" }] });
});
it("treats saved_search as keyword-shaped (shared engine, §1.1)", () => {
const good = { query: "t", platforms: ["bluesky"] };
expect(parseStreamColumnSpec("saved_search", good)).toEqual(
parseStreamColumnSpec("keyword", good),
);
rejects(() => parseStreamColumnSpec("saved_search", { platforms: ["bluesky"] }));
});
it("isKeywordLike groups the shared keyword engine", () => {
expect(isKeywordLike("keyword")).toBe(true);
expect(isKeywordLike("saved_search")).toBe(true);
expect(isKeywordLike("platform_feed")).toBe(false);
expect(isKeywordLike("account_list")).toBe(false);
});
});
describe("platform_feed spec", () => {
it("defaults kinds to [\"home\"] when omitted or empty is rejected", () => {
expect(parseStreamColumnSpec("platform_feed", { connectionId: "c1" }))
.toEqual({ connectionId: "c1", kinds: ["home"] });
rejects(() => parseStreamColumnSpec("platform_feed", { connectionId: "c1", kinds: [] }));
});
it("accepts home+mentions in any order, deduped", () => {
expect(parseStreamColumnSpec("platform_feed", { connectionId: "c1", kinds: ["mentions", "home", "mentions"] }))
.toEqual({ connectionId: "c1", kinds: ["mentions", "home"] });
});
it("rejects bad kinds and connection ids", () => {
rejects(() => parseStreamColumnSpec("platform_feed", { connectionId: "c1", kinds: ["home", "list"] }));
rejects(() => parseStreamColumnSpec("platform_feed", { connectionId: "c1", kinds: "home" }));
rejects(() => parseStreamColumnSpec("platform_feed", { connectionId: "", kinds: ["home"] }));
rejects(() => parseStreamColumnSpec("platform_feed", { connectionId: 42, kinds: ["home"] }));
rejects(() => parseStreamColumnSpec("platform_feed", {}));
rejects(() => parseStreamColumnSpec("platform_feed", null));
rejects(() => parseStreamColumnSpec("platform_feed", "c1"));
rejects(() => parseStreamColumnSpec("platform_feed", []));
});
});
describe("keyword / saved_search spec", () => {
it("trims the query and rejects empty-after-trim", () => {
expect(parseStreamColumnSpec("keyword", { query: " acampada26 ", platforms: ["bluesky"] }).query)
.toBe("acampada26");
rejects(() => parseStreamColumnSpec("keyword", { query: " ", platforms: ["bluesky"] }));
rejects(() => parseStreamColumnSpec("keyword", { query: "", platforms: ["bluesky"] }));
rejects(() => parseStreamColumnSpec("keyword", { platforms: ["bluesky"] }));
rejects(() => parseStreamColumnSpec("keyword", { query: 7, platforms: ["bluesky"] }));
});
it("enforces the 1..200 query window and control-char ban", () => {
const edge = "x".repeat(QUERY_MAX);
expect(parseStreamColumnSpec("keyword", { query: ` ${edge} `, platforms: ["bluesky"] }).query)
.toBe(edge); // exactly 200 after trim passes
rejects(() => parseStreamColumnSpec("keyword", { query: `${edge}x`, platforms: ["bluesky"] }));
rejects(() => parseStreamColumnSpec("keyword", { query: "a\u0000b", platforms: ["bluesky"] }));
rejects(() => parseStreamColumnSpec("keyword", { query: "a\nb", platforms: ["bluesky"] }));
rejects(() => parseStreamColumnSpec("keyword", { query: "a\u007fb", platforms: ["bluesky"] }));
});
it("dedupes platforms and rejects empty/invalid lists", () => {
expect(parseStreamColumnSpec("keyword", { query: "q", platforms: ["bluesky", "bluesky", "youtube"] }).platforms)
.toEqual(["bluesky", "youtube"]);
rejects(() => parseStreamColumnSpec("keyword", { query: "q", platforms: [] }));
rejects(() => parseStreamColumnSpec("keyword", { query: "q" }));
rejects(() => parseStreamColumnSpec("keyword", { query: "q", platforms: ["bluesky", ""] }));
rejects(() => parseStreamColumnSpec("keyword", { query: "q", platforms: [42] }));
});
it("allows only the reserved matchMode \"any\"", () => {
expect(parseStreamColumnSpec("keyword", { query: "q", platforms: ["bluesky"], matchMode: "any" }).matchMode)
.toBe("any");
rejects(() => parseStreamColumnSpec("keyword", { query: "q", platforms: ["bluesky"], matchMode: "all" }));
rejects(() => parseStreamColumnSpec("keyword", { query: "q", platforms: ["bluesky"], matchMode: null }));
});
});
describe("account_list spec", () => {
it("trims handles and accepts the documented shapes", () => {
expect(
parseStreamColumnSpec("account_list", {
accounts: [
{ provider: "mastodon", handle: " @user@mastodon.social " },
{ provider: "bluesky", handle: "@user.bsky.social" },
{ provider: "youtube", handle: "UCxxxxxxxxxxxxxxxxxxxxxx" },
],
}).accounts,
).toEqual([
{ provider: "mastodon", handle: "@user@mastodon.social" },
{ provider: "bluesky", handle: "@user.bsky.social" },
{ provider: "youtube", handle: "UCxxxxxxxxxxxxxxxxxxxxxx" },
]);
});
it("enforces the 1..50 window", () => {
const account = { provider: "mastodon", handle: "@user" };
expect(parseStreamColumnSpec("account_list", { accounts: [account] }).accounts).toHaveLength(1);
expect(
parseStreamColumnSpec("account_list", { accounts: Array.from({ length: ACCOUNTS_MAX }, () => account) }).accounts,
).toHaveLength(ACCOUNTS_MAX);
rejects(() => parseStreamColumnSpec("account_list", { accounts: [] }));
rejects(() =>
parseStreamColumnSpec("account_list", {
accounts: Array.from({ length: ACCOUNTS_MAX + 1 }, () => account),
}),
);
});
it("rejects malformed entries", () => {
rejects(() => parseStreamColumnSpec("account_list", { accounts: [{ provider: "mastodon" }] }));
rejects(() => parseStreamColumnSpec("account_list", { accounts: [{ handle: "@user" }] }));
rejects(() => parseStreamColumnSpec("account_list", { accounts: [{ provider: "", handle: "@u" }] }));
rejects(() => parseStreamColumnSpec("account_list", { accounts: [{ provider: "mastodon", handle: " " }] }));
rejects(() =>
parseStreamColumnSpec("account_list", { accounts: [{ provider: "mastodon", handle: "a\u0000b" }] }),
);
rejects(() => parseStreamColumnSpec("account_list", { accounts: ["@user"] }));
rejects(() => parseStreamColumnSpec("account_list", { accounts: {} }));
});
});
describe("refresh_policy", () => {
it("defaults to {}", () => {
expect(parseRefreshPolicyClamped()).toEqual({});
expect(parseRefreshPolicyClamped({})).toEqual({});
expect(parseRefreshPolicyClamped(undefined)).toEqual({});
expect(parseRefreshPolicyClamped(null)).toEqual({});
});
it("keeps valid interval/paused values", () => {
expect(parseRefreshPolicyClamped({ intervalMs: 300_000, paused: true }))
.toEqual({ intervalMs: 300_000, paused: true });
expect(parseRefreshPolicyClamped({ paused: false })).toEqual({ paused: false });
});
it("clamps intervalMs into 60_000..3_600_000", () => {
expect(parseRefreshPolicyClamped({ intervalMs: 0 }).intervalMs).toBe(60_000);
expect(parseRefreshPolicyClamped({ intervalMs: -5_000 }).intervalMs).toBe(60_000);
expect(parseRefreshPolicyClamped({ intervalMs: 59_999 }).intervalMs).toBe(60_000);
expect(parseRefreshPolicyClamped({ intervalMs: 10 }).intervalMs).toBe(60_000);
expect(parseRefreshPolicyClamped({ intervalMs: 3_600_000 }).intervalMs).toBe(3_600_000);
expect(parseRefreshPolicyClamped({ intervalMs: 10_000_000 }).intervalMs).toBe(3_600_000);
expect(parseRefreshPolicyClamped({ intervalMs: 90_500 }).intervalMs).toBe(90_500);
expect(parseRefreshPolicyClamped({ intervalMs: 60_000.4 }).intervalMs).toBe(60_000);
expect(parseRefreshPolicyClamped({ intervalMs: Number.NaN }).intervalMs).toBe(60_000);
expect(parseRefreshPolicyClamped({ intervalMs: Number.POSITIVE_INFINITY }).intervalMs).toBe(3_600_000);
expect(parseRefreshPolicyClamped({ intervalMs: Number.NEGATIVE_INFINITY }).intervalMs).toBe(60_000);
});
it("rejects malformed policies", () => {
rejects(() => parseRefreshPolicyClamped({ intervalMs: "60000" }));
rejects(() => parseRefreshPolicyClamped({ paused: "yes" }));
rejects(() => parseRefreshPolicyClamped([1, 2]));
rejects(() => parseRefreshPolicyClamped("paused"));
});
it("exposes the per-type default cadences", () => {
expect(DEFAULT_INTERVAL_MS).toEqual({
platform_feed: 60_000,
keyword: 120_000,
account_list: 180_000,
saved_search: 300_000,
});
for (const type of ["platform_feed", "keyword", "account_list", "saved_search"] as const) {
expect(defaultIntervalMs(type)).toBe(DEFAULT_INTERVAL_MS[type]);
expect(defaultIntervalMs(type)).toBeGreaterThanOrEqual(clampIntervalMs(60_000));
}
});
});
describe("shared clamps", () => {
it("clampTitle trims and caps at 80 chars (mirrors the DB CHECK)", () => {
expect(clampTitle(" Movement voices ")).toBe("Movement voices");
expect(clampTitle("x".repeat(STREAM_COLUMN_TITLE_MAX))).toHaveLength(STREAM_COLUMN_TITLE_MAX);
expect(clampTitle(` ${"y".repeat(STREAM_COLUMN_TITLE_MAX + 20)} `)).toHaveLength(STREAM_COLUMN_TITLE_MAX);
expect(clampTitle("").length).toBe(0);
});
it("clampPosition yields finite non-negative integers", () => {
expect(clampPosition(0)).toBe(0);
expect(clampPosition(7.9)).toBe(8);
expect(clampPosition(-3)).toBe(0);
expect(clampPosition(Number.NaN)).toBe(0);
expect(clampPosition(Number.POSITIVE_INFINITY)).toBe(2_147_483_647);
expect(clampPosition(2_147_483_648)).toBe(2_147_483_647);
});
});