Skip to content

Commit ae9fbaa

Browse files
committed
```
test(game): 为游戏逻辑钩子添加单元测试 为 useGame 目录下的核心钩子和工作流添加了全面的 Vitest 单元测试,以提高代码质量和可维护性。 覆盖范围包括: - 图像生成工作流 (NPC, 场景, 后台监控) - 状态管理 (回档快照, 故事状态) - 提示词与上下文处理 (运行时提示词, 上下文快照) - NPC 逻辑 (记忆, 上下文) - 变量校准与工作流协调 chore(ci): 优化部署工作流 - 将 `npm ci` 替换为 `npm install --no-fund --no-audit` 以提高安装速度和鲁棒性。 - 将 `npm run build` 替换为 `npx vite build` 以直接执行构建命令。 ```
1 parent baf2175 commit ae9fbaa

20 files changed

Lines changed: 3880 additions & 0 deletions
Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
import { describe, it, expect, vi, beforeEach } from 'vitest';
2+
import { renderHook, act } from '@testing-library/react';
3+
import { 创建后台生图监控 } from './backgroundImageMonitor';
4+
5+
describe('backgroundImageMonitor', () => {
6+
beforeEach(() => {
7+
vi.clearAllMocks();
8+
});
9+
10+
describe('创建后台生图监控', () => {
11+
const makeDeps = (overrides: any = {}) => ({
12+
推送右下角提示: vi.fn(),
13+
NPC生图任务队列: [],
14+
场景生图任务队列: [],
15+
...overrides,
16+
});
17+
18+
it('returns refs and recording functions', () => {
19+
const deps = makeDeps();
20+
const { result } = renderHook(() => 创建后台生图监控(deps));
21+
expect(result.current.refs).toBeDefined();
22+
expect(typeof result.current.记录后台场景监控).toBe('function');
23+
expect(typeof result.current.记录后台手动生图监控).toBe('function');
24+
expect(typeof result.current.记录后台私密生图监控).toBe('function');
25+
});
26+
27+
it('records manual image generation monitor', () => {
28+
const deps = makeDeps();
29+
const { result } = renderHook(() => 创建后台生图监控(deps));
30+
result.current.记录后台手动生图监控({ npcId: 'npc_1', since: Date.now(), npcName: '张三', 构图: '头像' });
31+
expect(result.current.refs.后台手动生图监控Ref.current).toHaveLength(1);
32+
});
33+
34+
it('records private image generation monitor', () => {
35+
const deps = makeDeps();
36+
const { result } = renderHook(() => 创建后台生图监控(deps));
37+
result.current.记录后台私密生图监控({ npcId: 'npc_1', since: Date.now(), npcName: '张三', 部位: '胸部' });
38+
expect(result.current.refs.后台私密生图监控Ref.current).toHaveLength(1);
39+
});
40+
41+
it('records scene image generation monitor', () => {
42+
const deps = makeDeps();
43+
const { result } = renderHook(() => 创建后台生图监控(deps));
44+
result.current.记录后台场景监控({ since: Date.now(), 摘要: '场景摘要' });
45+
expect(result.current.refs.后台场景生图监控Ref.current).toHaveLength(1);
46+
});
47+
48+
it('shows success toast when manual task completes', () => {
49+
const task = {
50+
id: 'task_1',
51+
NPC标识: 'npc_1',
52+
来源: 'manual' as const,
53+
状态: 'success' as const,
54+
创建时间: Date.now(),
55+
构图: '头像' as const,
56+
};
57+
const deps = makeDeps();
58+
const { result, rerender } = renderHook(({ deps }) => 创建后台生图监控(deps), { initialProps: { deps } });
59+
result.current.记录后台手动生图监控({ npcId: 'npc_1', since: task.创建时间 - 1000, npcName: '张三', 构图: '头像' });
60+
act(() => {
61+
deps.NPC生图任务队列 = [task];
62+
rerender({ deps });
63+
});
64+
expect(deps.推送右下角提示).toHaveBeenCalledWith(
65+
expect.objectContaining({ title: '手动生图完成', tone: 'success' })
66+
);
67+
});
68+
69+
it('shows error toast when manual task fails', () => {
70+
const task = {
71+
id: 'task_1',
72+
NPC标识: 'npc_1',
73+
来源: 'manual' as const,
74+
状态: 'failed' as const,
75+
创建时间: Date.now(),
76+
构图: '立绘' as const,
77+
错误信息: '超时',
78+
};
79+
const deps = makeDeps();
80+
const { result, rerender } = renderHook(({ deps }) => 创建后台生图监控(deps), { initialProps: { deps } });
81+
result.current.记录后台手动生图监控({ npcId: 'npc_1', since: task.创建时间 - 1000, npcName: '张三', 构图: '立绘' });
82+
act(() => {
83+
deps.NPC生图任务队列 = [task];
84+
rerender({ deps });
85+
});
86+
expect(deps.推送右下角提示).toHaveBeenCalledWith(
87+
expect.objectContaining({ title: '手动生图失败', tone: 'error' })
88+
);
89+
});
90+
91+
it('ignores tasks that are still running', () => {
92+
const task = {
93+
id: 'task_1',
94+
NPC标识: 'npc_1',
95+
来源: 'manual' as const,
96+
状态: 'running' as const,
97+
创建时间: Date.now(),
98+
构图: '头像' as const,
99+
};
100+
const deps = makeDeps();
101+
const { result, rerender } = renderHook(({ deps }) => 创建后台生图监控(deps), { initialProps: { deps } });
102+
result.current.记录后台手动生图监控({ npcId: 'npc_1', since: task.创建时间 - 1000, npcName: '张三', 构图: '头像' });
103+
act(() => {
104+
deps.NPC生图任务队列 = [task];
105+
rerender({ deps });
106+
});
107+
expect(deps.推送右下角提示).not.toHaveBeenCalled();
108+
});
109+
110+
it('matches tasks by id: prefix', () => {
111+
const task = {
112+
id: 'task_1',
113+
NPC标识: 'id:npc_1',
114+
来源: 'manual' as const,
115+
状态: 'success' as const,
116+
创建时间: Date.now(),
117+
构图: '头像' as const,
118+
};
119+
const deps = makeDeps();
120+
const { result, rerender } = renderHook(({ deps }) => 创建后台生图监控(deps), { initialProps: { deps } });
121+
result.current.记录后台手动生图监控({ npcId: 'npc_1', since: task.创建时间 - 1000, npcName: '张三', 构图: '头像' });
122+
act(() => {
123+
deps.NPC生图任务队列 = [task];
124+
rerender({ deps });
125+
});
126+
expect(deps.推送右下角提示).toHaveBeenCalledWith(
127+
expect.objectContaining({ title: '手动生图完成' })
128+
);
129+
});
130+
131+
it('shows private part completion toast', () => {
132+
const task = {
133+
id: 'task_1',
134+
NPC标识: 'npc_1',
135+
来源: 'manual' as const,
136+
状态: 'success' as const,
137+
创建时间: Date.now(),
138+
构图: '部位特写' as const,
139+
部位: '胸部' as const,
140+
};
141+
const deps = makeDeps();
142+
const { result, rerender } = renderHook(({ deps }) => 创建后台生图监控(deps), { initialProps: { deps } });
143+
result.current.记录后台私密生图监控({ npcId: 'npc_1', since: task.创建时间 - 1000, npcName: '张三', 部位: '胸部' });
144+
act(() => {
145+
deps.NPC生图任务队列 = [task];
146+
rerender({ deps });
147+
});
148+
expect(deps.推送右下角提示).toHaveBeenCalledWith(
149+
expect.objectContaining({ title: '私密特写完成', tone: 'success' })
150+
);
151+
});
152+
153+
it('shows scene completion toast', () => {
154+
const task = {
155+
id: 'scene_task_1',
156+
来源: 'manual' as const,
157+
状态: 'success' as const,
158+
创建时间: Date.now(),
159+
};
160+
const deps = makeDeps();
161+
const { result, rerender } = renderHook(({ deps }) => 创建后台生图监控(deps), { initialProps: { deps } });
162+
result.current.记录后台场景监控({ since: task.创建时间 - 1000, 摘要: '竹林夜色' });
163+
act(() => {
164+
deps.场景生图任务队列 = [task];
165+
rerender({ deps });
166+
});
167+
expect(deps.推送右下角提示).toHaveBeenCalledWith(
168+
expect.objectContaining({ title: '场景生图完成', tone: 'success' })
169+
);
170+
});
171+
172+
it('shows scene failure toast with fallback text', () => {
173+
const task = {
174+
id: 'scene_task_1',
175+
来源: 'manual' as const,
176+
状态: 'failed' as const,
177+
创建时间: Date.now(),
178+
错误信息: '网络错误',
179+
};
180+
const deps = makeDeps();
181+
const { result, rerender } = renderHook(({ deps }) => 创建后台生图监控(deps), { initialProps: { deps } });
182+
result.current.记录后台场景监控({ since: task.创建时间 - 1000, 摘要: '' });
183+
act(() => {
184+
deps.场景生图任务队列 = [task];
185+
rerender({ deps });
186+
});
187+
expect(deps.推送右下角提示).toHaveBeenCalledWith(
188+
expect.objectContaining({ title: '场景生图失败' })
189+
);
190+
expect(deps.推送右下角提示).toHaveBeenCalledWith(
191+
expect.objectContaining({ message: expect.stringContaining('当前正文场景') })
192+
);
193+
});
194+
195+
it('does not trigger for non-manual tasks', () => {
196+
const task = {
197+
id: 'task_1',
198+
NPC标识: 'npc_1',
199+
来源: 'auto' as const,
200+
状态: 'success' as const,
201+
创建时间: Date.now(),
202+
构图: '头像' as const,
203+
};
204+
const deps = makeDeps();
205+
const { result, rerender } = renderHook(({ deps }) => 创建后台生图监控(deps), { initialProps: { deps } });
206+
result.current.记录后台手动生图监控({ npcId: 'npc_1', since: task.创建时间 - 1000, npcName: '张三', 构图: '头像' });
207+
act(() => {
208+
deps.NPC生图任务队列 = [task];
209+
rerender({ deps });
210+
});
211+
expect(deps.推送右下角提示).not.toHaveBeenCalled();
212+
});
213+
214+
it('deduplicates completed task notifications', () => {
215+
const task = {
216+
id: 'task_1',
217+
NPC标识: 'npc_1',
218+
来源: 'manual' as const,
219+
状态: 'success' as const,
220+
创建时间: Date.now(),
221+
构图: '头像' as const,
222+
};
223+
const deps = makeDeps();
224+
const { result, rerender } = renderHook(({ deps }) => 创建后台生图监控(deps), { initialProps: { deps } });
225+
result.current.记录后台手动生图监控({ npcId: 'npc_1', since: task.创建时间 - 1000, npcName: '张三', 构图: '头像' });
226+
act(() => {
227+
deps.NPC生图任务队列 = [task];
228+
rerender({ deps });
229+
});
230+
expect(deps.推送右下角提示).toHaveBeenCalledTimes(1);
231+
232+
// Second rerender with same task should not trigger again
233+
act(() => {
234+
rerender({ deps });
235+
});
236+
expect(deps.推送右下角提示).toHaveBeenCalledTimes(1);
237+
});
238+
});
239+
});

0 commit comments

Comments
 (0)