fix(data): 清理旧数据并保护服务端写入

This commit is contained in:
Script Generator
2026-07-02 09:30:50 +08:00
parent 28e0c6de19
commit 916bd17a48
38 changed files with 1346 additions and 194 deletions

View File

@@ -0,0 +1,92 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { loadServerData, saveServerData, ServerDataConflictError } from './server-data';
type MockResponse = {
status: number;
body: unknown;
};
test('server data saves include the latest loaded AppData version', async () => {
const originalFetch = globalThis.fetch;
const calls: Array<{ url: string; method: string; body?: unknown }> = [];
const responses: MockResponse[] = [
{ status: 200, body: {} },
{
status: 200,
body: { key: 'dev-tasks', value: [{ id: 'old' }], version: 'version-1' },
},
{
status: 200,
body: { key: 'dev-tasks', value: [{ id: 'next' }], version: 'version-2' },
},
{
status: 200,
body: { key: 'dev-tasks', value: [{ id: 'again' }], version: 'version-3' },
},
{
status: 200,
body: { key: 'bugs', value: [], version: 'bugs-version-1' },
},
{
status: 409,
body: {
code: 'APP_DATA_CONFLICT',
key: 'dev-tasks',
currentValue: [{ id: 'server-copy' }],
currentVersion: 'version-4',
},
},
];
globalThis.fetch = (async (input: RequestInfo | URL, init?: RequestInit) => {
const response = responses.shift();
if (!response) throw new Error('Unexpected fetch call');
calls.push({
url: String(input),
method: init?.method ?? 'GET',
body: init?.body ? JSON.parse(String(init.body)) : undefined,
});
return new Response(JSON.stringify(response.body), {
status: response.status,
headers: { 'Content-Type': 'application/json' },
});
}) as typeof fetch;
try {
await assert.doesNotReject(async () => {
const loaded = await loadServerData<Array<{ id: string }>>('dev-tasks');
assert.deepEqual(loaded, [{ id: 'old' }]);
});
await saveServerData('dev-tasks', [{ id: 'next' }]);
assert.deepEqual(calls[2].body, {
value: [{ id: 'next' }],
version: 'version-1',
});
await saveServerData('dev-tasks', [{ id: 'again' }]);
assert.deepEqual(calls[3].body, {
value: [{ id: 'again' }],
version: 'version-2',
});
await saveServerData('bugs', []);
assert.deepEqual(calls[4].body, { value: [] });
await assert.rejects(
() => saveServerData('dev-tasks', [{ id: 'stale' }]),
(error: unknown) => {
if (!(error instanceof ServerDataConflictError)) return false;
return error.key === 'dev-tasks' && error.currentVersion === 'version-4';
},
);
assert.deepEqual(calls[5].body, {
value: [{ id: 'stale' }],
version: 'version-3',
});
} finally {
globalThis.fetch = originalFetch;
}
});