|
|
@@ -0,0 +1,68 @@
|
|
|
+import { test } from "node:test";
|
|
|
+import assert from "node:assert/strict";
|
|
|
+import {
|
|
|
+ applyLengthLimits,
|
|
|
+ isMissingGithubScenes,
|
|
|
+} from "../src/postprocess.ts";
|
|
|
+
|
|
|
+// --- applyLengthLimits: scene id repair ---
|
|
|
+
|
|
|
+test("applyLengthLimits fills missing scene id deterministically", () => {
|
|
|
+ const input = { scenes: [{ narration: "a" }, { id: "", narration: "b" }] };
|
|
|
+ const out = applyLengthLimits(input, "news") as any;
|
|
|
+ assert.equal(out.scenes[0].id, "scene-1");
|
|
|
+ assert.equal(out.scenes[1].id, "scene-2");
|
|
|
+});
|
|
|
+
|
|
|
+test("applyLengthLimits keeps existing scene ids untouched", () => {
|
|
|
+ const input = { scenes: [{ id: "my-scene", narration: "a" }] };
|
|
|
+ const out = applyLengthLimits(input, "news") as any;
|
|
|
+ assert.equal(out.scenes[0].id, "my-scene");
|
|
|
+});
|
|
|
+
|
|
|
+// --- isMissingGithubScenes: github board structure detection ---
|
|
|
+// Regression guard: the detection used to be an object flag that
|
|
|
+// applyLengthLimits deleted before the caller could read it (dead retry path).
|
|
|
+
|
|
|
+test("isMissingGithubScenes: true when no scene carries a github object", () => {
|
|
|
+ const input = {
|
|
|
+ scenes: [
|
|
|
+ { id: "s1", title: "overview", narration: "..." },
|
|
|
+ { id: "s2", title: "another overview", narration: "..." },
|
|
|
+ ],
|
|
|
+ };
|
|
|
+ assert.equal(isMissingGithubScenes(applyLengthLimits(input, "github-trending")), true);
|
|
|
+});
|
|
|
+
|
|
|
+test("isMissingGithubScenes: false when at least one scene has github block", () => {
|
|
|
+ const input = {
|
|
|
+ scenes: [
|
|
|
+ { id: "s1", narration: "...", github: { repo: {}, highlights: "x", intro: "y", review: "z" } },
|
|
|
+ { id: "s2", narration: "..." },
|
|
|
+ ],
|
|
|
+ };
|
|
|
+ assert.equal(isMissingGithubScenes(applyLengthLimits(input, "github-trending")), false);
|
|
|
+});
|
|
|
+
|
|
|
+test("isMissingGithubScenes: true for non-object / missing scenes input", () => {
|
|
|
+ assert.equal(isMissingGithubScenes(undefined), true);
|
|
|
+ assert.equal(isMissingGithubScenes({}), true);
|
|
|
+ assert.equal(isMissingGithubScenes({ scenes: [] }), true);
|
|
|
+});
|
|
|
+
|
|
|
+test("isMissingGithubScenes ignores non-github templates' shape via caller", () => {
|
|
|
+ // Detection itself is template-agnostic; the generate layer gates it on
|
|
|
+ // github-trending/github-weekly only. Here we just pin the pure behavior.
|
|
|
+ const input = { scenes: [{ narration: "plain news scene" }] };
|
|
|
+ assert.equal(isMissingGithubScenes(input), true);
|
|
|
+});
|
|
|
+
|
|
|
+// --- applyLengthLimits must not leak internal markers into output ---
|
|
|
+
|
|
|
+test("applyLengthLimits output contains no internal marker keys", () => {
|
|
|
+ const input = {
|
|
|
+ scenes: [{ narration: "overview only, no github blocks here" }],
|
|
|
+ };
|
|
|
+ const out = applyLengthLimits(input, "github-weekly") as any;
|
|
|
+ assert.equal(Object.hasOwn(out, "__missingGithubScenes"), false);
|
|
|
+});
|