修复游戏场景队列回读时序

重读未收口的场景权威快照

补充场景队列完成回归测试

同步场景链路与排障约定
This commit is contained in:
2026-08-06 14:42:13 +08:00
parent 447c23840c
commit a0b139ba34
4 changed files with 116 additions and 0 deletions
@@ -1728,6 +1728,109 @@ describe('useImageCanvasGenerationSubmissionWorkflow', () => {
);
});
it('reloads the authoritative scene project when the first queued snapshot is unresolved', async () => {
vi.useFakeTimers();
try {
const applyProjectSnapshot = vi.fn();
const unresolvedProject = {
projectId: 'editor-project-1',
title: '场景队列项目',
viewport: { x: 0, y: 0, scale: 1 },
layers: [
{
layerId: 'generation-dialog-scene-queued',
resourceId: 'generation-dialog-scene-queued',
itemType: 'generation-dialog',
dialog: {
id: 'dialog-scene-queued',
mode: 'scene',
status: 'generating',
},
},
],
resources: [],
updatedAt: '2026-08-06T08:00:00.000Z',
};
const completedProject = {
...unresolvedProject,
layers: [
{
layerId: 'layer-scene-result',
resourceId: 'resource-scene-result',
title: '游戏场景 1',
assetKind: 'scene',
},
{
layerId: 'generation-dialog-scene-queued',
resourceId: 'generation-dialog-scene-queued',
itemType: 'generation-dialog',
dialog: {
id: 'dialog-scene-queued',
mode: 'scene',
status: 'idle',
generatedLayerId: 'layer-scene-result',
},
},
],
updatedAt: '2026-08-06T08:00:01.000Z',
};
generateEditorSceneMock.mockResolvedValueOnce({
...createGenerated({ width: 1024, height: 576 }),
queueState: createQueueState(),
});
loadEditorProjectMock
.mockResolvedValueOnce(unresolvedProject)
.mockResolvedValueOnce(completedProject);
render(
<SubmissionWorkflowHarness
projectId="editor-project-1"
applyProjectSnapshot={applyProjectSnapshot}
initialDialog={{
id: 'dialog-scene-queued',
mode: 'scene',
prompt: '雨夜海边车站',
status: 'idle',
composerOpen: true,
sceneStylePreset: 'anime',
sceneCustomStyle: '',
generationReferences: [],
imageModel: 'gemini-3.1-flash-image-preview',
aspectRatio: '16:9',
imageSize: '1K',
placeholder: {
x: 100,
y: 100,
width: 1024,
height: 576,
originalWidth: 1024,
originalHeight: 576,
},
}}
/>,
);
fireEvent.click(screen.getByRole('button', { name: '设置初始对话' }));
fireEvent.click(screen.getByRole('button', { name: '提交当前生成' }));
await act(async () => {
await Promise.resolve();
await Promise.resolve();
});
expect(loadEditorProjectMock).toHaveBeenCalledTimes(1);
await act(async () => {
await vi.advanceTimersByTimeAsync(650);
});
expect(loadEditorProjectMock).toHaveBeenCalledTimes(2);
expect(applyProjectSnapshot).toHaveBeenNthCalledWith(
1,
unresolvedProject,
);
expect(applyProjectSnapshot).toHaveBeenNthCalledWith(2, completedProject);
} finally {
vi.useRealTimers();
}
});
it('does not add a local scene layer when the backend omits its project snapshot', async () => {
generateEditorSceneMock.mockResolvedValueOnce(
createGenerated({
@@ -2091,6 +2091,7 @@ export function useImageCanvasGenerationSubmissionWorkflow({
onQueuedGenerationTask,
onWalletBalanceMayHaveChanged,
onGenerationWarning,
canvasDialog?.id,
)
) {
return;