web tests: the fake control plane answers the two routes of every Dot, and the inbox says what one read cannot

The shell reads the failed tasks and the channels of every Dot with GET /api/tasks and GET /api/channels: the web
tests' control plane answers them as the real one does, and the inbox tests that faked one Dot's list failing now
fail the one read, which the inbox reports as such.
This commit is contained in:
feder-cr committed 2026-10-11 18:39:45 +02:00
1 parent 9ee1f32094
commit b50f09ed40
2 files changed
+20 -17

No files matched your search

+8 -17
View File
@@ -114,18 +114,15 @@ describe("Needs you", () => {
await screen.findByText("Nothing needs you");
});
it("says that some failed tasks may be missing when a Dot's tasks could not be read", async () => {
it("says that failed tasks may be missing when they could not be read", async () => {
const real = plane.fetch;
vi.stubGlobal(
"fetch",
vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => (String(input) === "/api/dots/d2/tasks" ? Response.json({ error: "down", message: "down" }, { status: 502 }) : real(input, init))),
vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => (String(input).startsWith("/api/tasks?") ? Response.json({ error: "down", message: "down" }, { status: 502 }) : real(input, init))),
);
plane.tasks = [taskRecord("t1", { dot_id: "d1", description: "Send the digest", status: "FAILED", error: "no", finished_at: hoursAgo(2) })];
await renderInbox();
expect(await screen.findByText("Some failed tasks may be missing")).toBeTruthy();
expect(screen.getByText("The tasks of one Dot could not be read.")).toBeTruthy();
// What could be read is still there.
expect(screen.getByRole("article", { name: "Failed: Send the digest" })).toBeTruthy();
expect(await screen.findByText("Failed tasks may be missing")).toBeTruthy();
expect(screen.getByText("The tasks that failed today could not be read.")).toBeTruthy();
});
it("adds the channels that need linking again, with what the host says and the page where it is done", async () => {
@@ -156,17 +153,11 @@ describe("Needs you", () => {
expect(screen.queryByRole("article", { name: /needs linking again/ })).toBeNull();
});
it("says that some channels may be missing when a Dot's channels could not be read", async () => {
const real = plane.fetch;
vi.stubGlobal(
"fetch",
vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => (String(input) === "/api/dots/d2/channels" ? Response.json({ error: "down", message: "down" }, { status: 502 }) : real(input, init))),
);
plane.channels = { d1: [channelRecord("telegram", { status: "needs_relink" })] };
it("says that channels may be missing when they could not be read", async () => {
plane.failChannels = { status: 502, error: "down", message: "down" };
await renderInbox();
expect(await screen.findByText("Some channels may be missing")).toBeTruthy();
expect(screen.getByText("The channels of one Dot could not be read.")).toBeTruthy();
expect(screen.getByRole("article", { name: "Telegram of fares needs linking again" })).toBeTruthy();
expect(await screen.findByText("Channels may be missing")).toBeTruthy();
expect(screen.getByText("The channels could not be read.")).toBeTruthy();
});
it("says that nothing needs the person when nothing does, and loads without flashing that before it knows", async () => {
+12
View File
@@ -361,6 +361,18 @@ export class FakeControlPlane {
this.push(record.dot_id, "approval.resolved", { approval_id: id, decision, ...(record.task_id ? { task_id: record.task_id } : {}), ...(body.note ? { note: body.note } : {}), ...(body.always ? { always: true } : {}) });
return json(record);
}
if (pathname === "/api/tasks" && method === "GET") {
// The real route: the tasks of every Dot that ended in `status` after `finished_after`, the latest end first.
const status = searchParams.get("status");
const after = Date.parse(searchParams.get("finished_after") ?? "");
const ended = this.tasks.filter((t) => t.status === status && t.finished_at !== null && Date.parse(t.finished_at) > after);
return json({ tasks: ended.sort((a, b) => Date.parse(b.finished_at!) - Date.parse(a.finished_at!)) });
}
if (pathname === "/api/channels" && method === "GET") {
// The real route: every channel of every Dot, each with its Dot's id.
if (this.failChannels) return json({ error: this.failChannels.error, message: this.failChannels.message }, this.failChannels.status);
return json({ channels: Object.entries(this.channels).flatMap(([dotId, list]) => list.map((channel) => ({ dot_id: dotId, ...channel }))) });
}
const task = /^\/api\/tasks\/([^/]+)(\/cancel)?$/.exec(pathname);
if (task) {
const record = this.tasks.find((t) => t.id === decodeURIComponent(task[1]!));