Fix declared server config file reads

This commit is contained in:
npc0-hue
2026-09-21 10:05:50 +08:00
parent b481b1b798
commit 20008b4043
16 changed files with 166 additions and 33 deletions
@@ -80,4 +80,64 @@ describe("ServerConfigEditor", () => {
expect(apiMocks.readServerFile).toHaveBeenCalledTimes(1);
expect(apiMocks.getServerFileReadSnapshot).toHaveBeenCalledTimes(2);
});
it("surfaces a terminal Run read failure without polling for thirty seconds", async () => {
apiMocks.getServerFileReadSnapshot.mockResolvedValue({ serverInstanceId: "server-1", pluginId: "game.scum", key: "scum-server-settings", state: "failed", reason: "Run 文件读取失败:file is missing" });
container = document.createElement("div");
document.body.append(container);
root = createRoot(container);
const operations: OperationTracker = { operations: [], begin: () => "operation-read", update: () => undefined, succeed: () => undefined, fail: () => undefined, isPending: () => false };
await act(async () => root?.render(<ServerConfigEditor instance={instance} operations={operations} requester="Operator" onClose={() => undefined} />));
await act(async () => { await Promise.resolve(); await Promise.resolve(); });
expect(apiMocks.readServerFile).not.toHaveBeenCalled();
expect(apiMocks.getServerFileReadSnapshot).toHaveBeenCalledTimes(1);
expect(container.textContent).toContain("file is missing");
expect(container.textContent).toContain("重试");
});
it("does not dispatch a second read while an existing Run read is pending", async () => {
apiMocks.getServerFileReadSnapshot.mockResolvedValue({ serverInstanceId: "server-1", pluginId: "game.scum", key: "scum-server-settings", state: "pending", reason: "等待运行端完成文件读取。" });
container = document.createElement("div");
document.body.append(container);
root = createRoot(container);
const operations: OperationTracker = { operations: [], begin: () => "operation-read", update: () => undefined, succeed: () => undefined, fail: () => undefined, isPending: () => false };
await act(async () => root?.render(<ServerConfigEditor instance={instance} operations={operations} requester="Operator" onClose={() => undefined} />));
await act(async () => { await Promise.resolve(); await Promise.resolve(); });
expect(apiMocks.readServerFile).not.toHaveBeenCalled();
expect(apiMocks.getServerFileReadSnapshot).toHaveBeenCalledTimes(1);
await act(async () => { await vi.advanceTimersByTimeAsync(500); });
expect(apiMocks.readServerFile).not.toHaveBeenCalled();
expect(apiMocks.getServerFileReadSnapshot).toHaveBeenCalledTimes(2);
});
it("retries a failed read with a new idempotency key and loads the file", async () => {
let retryRequested = false;
apiMocks.getServerFileReadSnapshot.mockImplementation(async () => retryRequested
? { serverInstanceId: "server-1", pluginId: "game.scum", key: "scum-server-settings", state: "ready", content: "[General]\nscum.MaxPlayers=63\n", version: 1, checksum: "sha256:test" }
: { serverInstanceId: "server-1", pluginId: "game.scum", key: "scum-server-settings", state: "failed", reason: "Run 文件读取失败:file is missing" });
apiMocks.readServerFile.mockImplementation(async (_serverId: string, request: { idempotencyKey: string }) => {
retryRequested = true;
return { status: "queued", serverInstanceId: "server-1", pluginId: "game.scum", operation: "read", key: "scum-server-settings", job: { id: "job-read-retry", state: "queued" }, request };
});
container = document.createElement("div");
document.body.append(container);
root = createRoot(container);
const operations: OperationTracker = { operations: [], begin: () => "operation-read", update: () => undefined, succeed: () => undefined, fail: () => undefined, isPending: () => false };
await act(async () => root?.render(<ServerConfigEditor instance={instance} operations={operations} requester="Operator" onClose={() => undefined} />));
await act(async () => { await Promise.resolve(); await Promise.resolve(); });
const retry = [...(container.querySelectorAll("button"))].find((button) => button.textContent?.includes("重试"));
expect(retry).toBeDefined();
await act(async () => retry?.click());
await act(async () => { await vi.advanceTimersByTimeAsync(500); await Promise.resolve(); });
expect(apiMocks.readServerFile).toHaveBeenCalledTimes(1);
expect(apiMocks.readServerFile.mock.calls[0][1].idempotencyKey).toBe("web:server-config:read:server-1:scum-server-settings:1");
expect(container.textContent).toContain("已读取 ServerSettings.ini");
});
});