test/resume.test.js
Rama main · réplica de lectura del repositorio original.
import { test } from "node:test";
import assert from "node:assert/strict";
import { generateDeck } from "../src/ai/generate.js";
const PLAN = {
title: "Resumable deck",
sections: ["Intro"],
slides: [
{ type: "title", section: 0, purpose: "Open the deck." },
{ type: "bullets", section: 0, purpose: "The key point." },
{ type: "cards", section: 0, purpose: "Three ideas." },
{ type: "closing", section: 0, purpose: "Close." },
],
};
const SLIDES = {
title: { type: "title", headline: "Resumable deck", subtitle: "s" },
bullets: { type: "bullets", headline: "Key point", bullets: ["a", "b", "c", "d"] },
cards: { type: "cards", headline: "Ideas", cards: [{ title: "A", body: "b" }, { title: "B", body: "b" }, { title: "C", body: "b" }] },
closing: { type: "closing", headline: "Thanks", quote: "q" },
};
/** A fake chat that appends the given slide per type, throwing the named error
* when the requested slide's type matches `throwOn`. */
function fakeChat({ throwOn } = {}) {
return async ({ schema }) => {
const type = schema.properties.ops.items.properties.slide.properties.type.enum[0];
if (type === throwOn) {
const err = new Error("stopped");
err.name = "AbortError";
throw err;
}
return { data: { ops: [{ op: "append_slide", slide: SLIDES[type] }] } };
};
}
test("an aborted write leaves a checkpoint of the slides written so far", async () => {
const checkpoints = [];
await assert.rejects(
generateDeck({
brief: "b", plan: PLAN, model: "mock", chat: fakeChat({ throwOn: "cards" }),
onSlide: (deck, written, total) => checkpoints.push({ deck, written, total }),
}),
(err) => err.name === "AbortError",
);
// The abort happened writing the third slide; two slides had been checkpointed.
assert.equal(checkpoints.length, 2);
assert.equal(checkpoints[1].written, 2);
assert.equal(checkpoints[1].total, 4);
assert.equal(checkpoints[1].deck.slides.length, 2);
assert.equal(checkpoints[1].deck.slides[0].type, "title");
assert.equal(checkpoints[1].deck.slides[1].type, "bullets");
});
test("a resume continues from the checkpoint and completes the deck", async () => {
// First run writes slide 0 and aborts on slide 1.
const checkpoints = [];
await assert.rejects(
generateDeck({
brief: "b", plan: PLAN, model: "mock", chat: fakeChat({ throwOn: "bullets" }),
onSlide: (deck, written) => checkpoints.push({ deck, written }),
}),
(err) => err.name === "AbortError",
);
const partial = checkpoints[0].deck;
// Resume with the checkpoint as baseDeck, continuing from index 1.
const res = await generateDeck({
brief: "b", plan: PLAN, model: "mock", chat: fakeChat(),
baseDeck: partial, fromIndex: 1,
onSlide: () => {},
});
assert.equal(res.deck.slides.length, 4);
assert.deepEqual(res.deck.slides.map((s) => s.type), ["title", "bullets", "cards", "closing"]);
assert.deepEqual(res.deck.slides[0].headline, partial.slides[0].headline, "checkpointed slide preserved");
});
test("a resumed run re-runs the writer only for the remaining slides", async () => {
const partial = {
title: PLAN.title,
sections: PLAN.sections,
slides: [SLIDES.title],
};
const calls = [];
const chat = async ({ schema }) => {
const type = schema.properties.ops.items.properties.slide.properties.type.enum[0];
calls.push(type);
return { data: { ops: [{ op: "append_slide", slide: SLIDES[type] }] } };
};
await generateDeck({
brief: "b", plan: PLAN, model: "mock", chat,
baseDeck: partial, fromIndex: 1, onSlide: () => {},
});
assert.deepEqual(calls, ["bullets", "cards", "closing"], "only the unwritten slides hit the writer");
});