fix(data): 串行化 AppData 同 key 保存

This commit is contained in:
Script Generator
2026-07-07 18:22:06 +08:00
parent 0054854158
commit f1e2fb0b99
2 changed files with 96 additions and 1 deletions

View File

@@ -142,6 +142,87 @@ test('server data shares an in-flight load for the same AppData key', async () =
}
});
test('server data serializes same-key saves so later writes use the latest AppData version', async () => {
const originalFetch = globalThis.fetch;
const saveCalls: Array<{ value: unknown; version?: string | null }> = [];
let releaseFirstSave!: () => void;
let markFirstSaveStarted!: () => void;
const firstSaveStarted = new Promise<void>((resolve) => {
markFirstSaveStarted = resolve;
});
const firstSaveCanFinish = new Promise<void>((resolve) => {
releaseFirstSave = resolve;
});
__resetApiAvailabilityForTests();
globalThis.fetch = (async (input: RequestInfo | URL, init?: RequestInit) => {
const url = String(input);
if (url.endsWith('/config/ai')) {
return new Response(JSON.stringify({}), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (url.endsWith('/data/requirements') && (!init?.method || init.method === 'GET')) {
return new Response(JSON.stringify({
key: 'requirements',
value: { requirements: [] },
version: 'requirements-version-1',
}), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (url.endsWith('/data/requirements') && init?.method === 'PUT') {
const body = JSON.parse(String(init.body));
saveCalls.push(body);
if (body.value.marker === 'first') {
markFirstSaveStarted();
await firstSaveCanFinish;
return new Response(JSON.stringify({
key: 'requirements',
value: body.value,
version: 'requirements-version-2',
}), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
return new Response(JSON.stringify({
key: 'requirements',
value: body.value,
version: 'requirements-version-3',
}), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`Unexpected fetch ${init?.method ?? 'GET'} ${url}`);
}) as typeof fetch;
try {
await loadServerData('requirements', { force: true });
const firstSave = saveServerData('requirements', { marker: 'first' });
await firstSaveStarted;
const secondSave = saveServerData('requirements', { marker: 'second' });
await new Promise((resolve) => setTimeout(resolve, 0));
assert.equal(saveCalls.length, 1);
releaseFirstSave();
await Promise.all([firstSave, secondSave]);
assert.deepEqual(saveCalls, [
{ value: { marker: 'first' }, version: 'requirements-version-1' },
{ value: { marker: 'second' }, version: 'requirements-version-2' },
]);
} finally {
globalThis.fetch = originalFetch;
__resetApiAvailabilityForTests();
}
});
test('server data reuses a fresh cached load for repeated AppData reads', async () => {
const originalFetch = globalThis.fetch;
let dataRequests = 0;