import { afterEach, describe, expect, it, vi } from "vitest"; import { fetchDraft, fetchOutline } from "./server"; // 纯加载/降级逻辑(mock fetch,无网络/DOM):大纲解包/降级、草稿 404→null。 function mockFetch(status: number, body: unknown): void { vi.stubGlobal( "fetch", vi.fn(async () => new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" }, }), ), ); } afterEach(() => { vi.unstubAllGlobals(); }); describe("fetchOutline", () => { it("returns the persisted chapters on 200", async () => { mockFetch(200, { chapters: [{ no: 1, volume: 1, beats: ["开场"] }] }); const chapters = await fetchOutline("p1"); expect(chapters).toHaveLength(1); expect(chapters[0].no).toBe(1); }); it("returns empty array when the project has no outline", async () => { mockFetch(200, { chapters: [] }); expect(await fetchOutline("p1")).toEqual([]); }); it("falls back to empty array on any error (does not throw)", async () => { mockFetch(404, { error: { code: "NOT_FOUND", message: "no project" } }); expect(await fetchOutline("missing")).toEqual([]); }); }); describe("fetchDraft", () => { it("returns the saved draft view on 200", async () => { mockFetch(200, { project_id: "p1", chapter_no: 1, volume: 1, status: "draft", version: 2, content: "第一章正文", length: 5, }); const draft = await fetchDraft("p1", 1); expect(draft?.content).toBe("第一章正文"); expect(draft?.version).toBe(2); }); it("returns null when no draft exists (404 → empty editor)", async () => { mockFetch(404, { error: { code: "NOT_FOUND", message: "no draft" } }); expect(await fetchDraft("p1", 9)).toBeNull(); }); });