feat(canvas): 条件读取与历史 - 减少画布同步传输并校准版本
This commit is contained in:
31
web/test/canvas-conditional-get.test.ts
Normal file
31
web/test/canvas-conditional-get.test.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { afterEach, describe, expect, test } from "bun:test";
|
||||
|
||||
import { getRemoteCanvasProject } from "../src/services/api/user-data";
|
||||
import { apiClient } from "../src/services/api/request";
|
||||
import type { CanvasProject } from "../src/stores/canvas/use-canvas-store";
|
||||
|
||||
const originalAdapter = apiClient.defaults.adapter;
|
||||
|
||||
afterEach(() => {
|
||||
apiClient.defaults.adapter = originalAdapter;
|
||||
});
|
||||
|
||||
describe("conditional canvas reads", () => {
|
||||
test("reuses a verified project when the server returns 304", async () => {
|
||||
const known = {
|
||||
id: "canvas-1",
|
||||
title: "Cached canvas",
|
||||
revision: 4,
|
||||
updatedAt: "2026-09-28T12:00:00.000Z",
|
||||
} as CanvasProject;
|
||||
let requestEtag = "";
|
||||
apiClient.defaults.adapter = async (config) => {
|
||||
const headers = config.headers as { get?: (name: string) => string | undefined; [key: string]: unknown } | undefined;
|
||||
requestEtag = String(headers?.get?.("If-None-Match") ?? headers?.["If-None-Match"] ?? "");
|
||||
return { config, status: 304, statusText: "Not Modified", headers: {}, data: null };
|
||||
};
|
||||
|
||||
await expect(getRemoteCanvasProject(known.id, known)).resolves.toEqual({ project: known, notModified: true });
|
||||
expect(requestEtag).toBe('"canvas-4"');
|
||||
});
|
||||
});
|
||||
@@ -232,6 +232,20 @@ test("load latest, save and reload stay synced when Agent history replays an unv
|
||||
expect(requests.filter((request) => request.method === "put")).toHaveLength(1);
|
||||
});
|
||||
|
||||
test("a stale save auto-reconciles when the cloud already contains the identical local content", async () => {
|
||||
useCanvasStore.getState().renameProject("canvas", "same content saved elsewhere");
|
||||
beforePut = async () => {
|
||||
const current = useCanvasStore.getState().openProject("canvas")!;
|
||||
remote.set("canvas", { ...structuredClone(current), revision: current.revision! + 1 });
|
||||
};
|
||||
|
||||
await expect(saveRemoteUserDataNow("canvas")).resolves.toBeUndefined();
|
||||
|
||||
expect(useCanvasStore.getState().openProject("canvas")).toMatchObject({ title: "same content saved elsewhere", revision: 2 });
|
||||
expect(useSyncProgressStore.getState().syncingProjects.canvas).toMatchObject({ phase: "done", message: "云端内容一致,已自动校准版本" });
|
||||
expect(await readCanvasSyncDrafts("canvas")).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("a conflict preserves drafts, stops retries and does not block another canvas", async () => {
|
||||
remote.set("canvas", { ...addNode(canvas(), "remote-video"), revision: 2 });
|
||||
useCanvasStore.getState().updateProject("canvas", { nodes: addNode(canvas(), "local-video").nodes });
|
||||
|
||||
Reference in New Issue
Block a user