diff --git a/apps/ai-game-creator-shell/tests/conversationModelSelect.test.tsx b/apps/ai-game-creator-shell/tests/conversationModelSelect.test.tsx index 27f8b7c1a..25b1372f8 100644 --- a/apps/ai-game-creator-shell/tests/conversationModelSelect.test.tsx +++ b/apps/ai-game-creator-shell/tests/conversationModelSelect.test.tsx @@ -400,3 +400,73 @@ test('reuses a single in-flight catalog request', async () => { await waitFor(() => expect(loadClientLlmModels).toHaveBeenCalledTimes(2)); expect(loadClientLlmModels).toHaveBeenCalledTimes(2); }); + +test('keeps refreshing when the server omits the catalog revision', async () => { + vi.mocked(loadClientLlmModels).mockResolvedValue({ + defaultModelId: 'quality', + models: [ + { id: 'quality', displayName: '高质量' }, + { id: 'fast', displayName: '快速' }, + ], + revision: undefined as unknown as number, + }); + const onReady = vi.fn(); + render(); + await waitFor(() => expect(onReady).toHaveBeenLastCalledWith(true)); + + // 旧服务端不返回 revision 时,两次响应的 revision 都是 undefined, + // 不能因此判定「未变化」而停止刷新。 + vi.mocked(loadClientLlmModels).mockResolvedValue({ + defaultModelId: 'quality', + models: [ + { id: 'quality', displayName: '高质量' }, + { id: 'vision', displayName: '视觉' }, + ], + revision: undefined as unknown as number, + }); + fireEvent(window, new Event('focus')); + await waitFor(() => expect(loadClientLlmModels).toHaveBeenCalledTimes(2)); + fireEvent.click(screen.getByRole('button', { name: '对话模型' })); + expect(await screen.findByRole('option', { name: '视觉' })).not.toBeNull(); +}); + +test('applies a catalog when the revision goes backwards', async () => { + const onReady = vi.fn(); + render(); + await waitFor(() => expect(onReady).toHaveBeenLastCalledWith(true)); + + // 服务端目录重建后 revision 可能回退,仍要按「已变化」处理。 + vi.mocked(loadClientLlmModels).mockResolvedValue({ + defaultModelId: 'quality', + models: [ + { id: 'quality', displayName: '高质量' }, + { id: 'vision', displayName: '视觉' }, + ], + revision: 0, + }); + fireEvent(window, new Event('focus')); + await waitFor(() => expect(loadClientLlmModels).toHaveBeenCalledTimes(2)); + fireEvent.click(screen.getByRole('button', { name: '对话模型' })); + expect(await screen.findByRole('option', { name: '视觉' })).not.toBeNull(); +}); + +test('keeps the applied catalog when the revision is unchanged', async () => { + const onReady = vi.fn(); + render(); + await waitFor(() => expect(onReady).toHaveBeenLastCalledWith(true)); + + // revision 未变化时不更新界面,沿用已应用的目录。 + vi.mocked(loadClientLlmModels).mockResolvedValue({ + defaultModelId: 'quality', + models: [ + { id: 'quality', displayName: '高质量' }, + { id: 'vision', displayName: '视觉' }, + ], + revision: 1, + }); + fireEvent(window, new Event('focus')); + await waitFor(() => expect(loadClientLlmModels).toHaveBeenCalledTimes(2)); + fireEvent.click(screen.getByRole('button', { name: '对话模型' })); + expect(screen.queryByRole('option', { name: '视觉' })).toBeNull(); + expect(screen.getByRole('option', { name: '快速' })).not.toBeNull(); +});