@gadmin2n/schematics 0.0.88 → 0.0.89

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/dist/lib/application/files/gadmin2-game-angle-demo/.dockerignore +16 -2
  2. package/dist/lib/application/files/gadmin2-game-angle-demo/Dockerfile.codegen +40 -0
  3. package/dist/lib/application/files/gadmin2-game-angle-demo/Dockerfile.server +76 -0
  4. package/dist/lib/application/files/gadmin2-game-angle-demo/Dockerfile.web +53 -0
  5. package/dist/lib/application/files/gadmin2-game-angle-demo/Jenkinsfile +219 -33
  6. package/dist/lib/application/files/gadmin2-game-angle-demo/compose-ctl.sh +250 -0
  7. package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/workflow.prisma +4 -1
  8. package/dist/lib/application/files/gadmin2-game-angle-demo/dev/postgres/init.sql +12 -0
  9. package/dist/lib/application/files/gadmin2-game-angle-demo/docker-compose.md +170 -0
  10. package/dist/lib/application/files/gadmin2-game-angle-demo/docker-compose.yml +254 -0
  11. package/dist/lib/application/files/gadmin2-game-angle-demo/server/package.json +8 -7
  12. package/dist/lib/application/files/gadmin2-game-angle-demo/server/scripts/lib/page-helpers.ts +1 -1
  13. package/dist/lib/application/files/gadmin2-game-angle-demo/server/scripts/prismaModels.ts +1 -1
  14. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/agenda.seed.ts +39 -0
  15. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/audit.seed.ts +40 -0
  16. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/bootstrap.ts +56 -0
  17. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/canvas.seed.ts +39 -0
  18. package/dist/lib/application/files/gadmin2-game-angle-demo/server/{scripts/sync-data-mngt-pages.ts → seed/data-mngt.seed.ts} +36 -20
  19. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/game.seed.ts +44 -0
  20. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/index.ts +30 -6
  21. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/permission.seed.ts +130 -0
  22. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow-event-trigger.ts +60 -0
  23. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow-node-types.ts +11 -25
  24. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow.seed.ts +108 -0
  25. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/main.ts +1 -0
  26. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agendaJob/agendaJob.controller.spec.ts +31 -2
  27. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/audit/audit.controller.spec.ts +31 -2
  28. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/audit/audit.service.spec.ts +41 -57
  29. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/game/game.controller.spec.ts +31 -2
  30. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/game/game.service.spec.ts +309 -1
  31. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/page/page.controller.spec.ts +31 -2
  32. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/page/page.service.spec.ts +315 -1
  33. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/pageResource/pageResource.controller.spec.ts +31 -2
  34. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/pageResource/pageResource.service.spec.ts +312 -2
  35. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/resource/resource.controller.spec.ts +31 -2
  36. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/resource/resource.service.spec.ts +317 -1
  37. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/role/role.controller.spec.ts +31 -2
  38. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/role/role.service.spec.ts +309 -1
  39. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/rolePages/rolePages.controller.spec.ts +31 -2
  40. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/rolePages/rolePages.service.spec.ts +299 -1
  41. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/roleResource/roleResource.controller.spec.ts +31 -2
  42. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/roleResource/roleResource.service.spec.ts +307 -1
  43. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.controller.spec.ts +31 -2
  44. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.service.spec.ts +309 -1
  45. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/dsl-validate.util.spec.ts +205 -0
  46. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/dsl-validate.util.ts +116 -0
  47. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/temporal.service.spec.ts +158 -0
  48. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/temporal.service.ts +110 -1
  49. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/webhook-signature.util.spec.ts +79 -0
  50. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/webhook-signature.util.ts +54 -0
  51. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.controller.ts +34 -0
  52. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.spec.ts +457 -0
  53. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.ts +241 -4
  54. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowEventOutbox/workflowEventOutbox.controller.spec.ts +34 -2
  55. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowEventOutbox/workflowEventOutbox.service.spec.ts +24 -30
  56. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeInstance/workflowNodeInstance.controller.spec.ts +34 -2
  57. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeInstance/workflowNodeInstance.service.spec.ts +36 -36
  58. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeType/workflowNodeType.controller.spec.ts +34 -2
  59. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeType/workflowNodeType.service.spec.ts +48 -24
  60. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/README.md +312 -3
  61. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/TODO.md +152 -0
  62. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/.dockerignore +12 -0
  63. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/Dockerfile +79 -0
  64. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/GRACEFUL-DEPLOYMENT.md +270 -0
  65. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/index.ts +1 -1
  66. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/reporting.ts +23 -0
  67. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/index.ts +70 -5
  68. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/outbox-poller.ts +246 -90
  69. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/tests/cron-trigger-workflow.test.ts +20 -0
  70. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/workflows/dsl-workflow.ts +96 -8
  71. package/dist/lib/application/files/gadmin2-game-angle-demo/web/nginx.conf +74 -0
  72. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/agentPanel/ElementInspector.tsx +18 -0
  73. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/agentPanel/promptGenerator.ts +1 -1
  74. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/helpers/form.tsx +1 -1
  75. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/en/common.json +3 -3
  76. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/zh_CN/common.json +3 -3
  77. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/plugins/devShellPlugin.ts +4 -1
  78. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasEditPage.tsx +9 -0
  79. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasListPage.tsx +156 -139
  80. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasPage.tsx +14 -2
  81. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasToolbar.tsx +62 -0
  82. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/PublishModal.tsx +4 -6
  83. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/canvasApi.ts +18 -27
  84. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/canvasDefaults.ts +32 -11
  85. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/demos.ts +48 -61
  86. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas-page/index.tsx +3 -6
  87. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/DslView.tsx +16 -16
  88. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/editor.tsx +28 -35
  89. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/instance-detail.tsx +34 -3
  90. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/show.tsx +1 -1
  91. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/types.ts +1 -1
  92. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/antd.css +6 -0
  93. package/package.json +1 -1
  94. package/dist/lib/application/files/gadmin2-game-angle-demo/.gitattributes +0 -2
  95. package/dist/lib/application/files/gadmin2-game-angle-demo/Dockerfile +0 -63
  96. package/dist/lib/application/files/gadmin2-game-angle-demo/server/scripts/sync-resources.ts +0 -100
  97. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/permissions.ts +0 -302
  98. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/canvas/canvas.controller.spec.ts +0 -20
  99. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/sql/create-event-trigger.sql +0 -87
  100. /package/dist/lib/application/files/gadmin2-game-angle-demo/{GRACEFUL-DEPLOYMENT.md → server/GRACEFUL-DEPLOYMENT.md} +0 -0
@@ -71,14 +71,11 @@ describe('AuditService', () => {
71
71
  it('should create a single audit record', async () => {
72
72
  const createArgs = {
73
73
  data: {
74
- action: 'LOGIN',
75
- authorId: 'user123',
76
- authorName: 'Test User',
77
- resource: 'auth',
78
- resourceId: '1',
74
+ creator: 'test_creator',
75
+ authorId: 'test_authorId',
79
76
  },
80
- };
81
- const expectedResult = { id: BigInt(1), ...createArgs.data };
77
+ } as any;
78
+ const expectedResult = { id: 1, ...createArgs.data };
82
79
 
83
80
  prisma.audit.create.mockResolvedValue(expectedResult);
84
81
 
@@ -92,13 +89,10 @@ describe('AuditService', () => {
92
89
  it('should throw error when create fails', async () => {
93
90
  const createArgs = {
94
91
  data: {
95
- action: 'LOGIN',
96
- authorId: 'user123',
97
- authorName: 'Test User',
98
- resource: 'auth',
99
- resourceId: '1',
92
+ creator: 'test_creator',
93
+ authorId: 'test_authorId',
100
94
  },
101
- };
95
+ } as any;
102
96
  const error = new Error('Database connection failed');
103
97
 
104
98
  prisma.audit.create.mockRejectedValue(error);
@@ -113,20 +107,14 @@ describe('AuditService', () => {
113
107
  it('should create multiple audit records with skipDuplicates', async () => {
114
108
  const data = [
115
109
  {
116
- action: 'LOGIN',
117
- authorId: 'user1',
118
- authorName: 'User 1',
119
- resource: 'auth',
120
- resourceId: '1',
110
+ creator: 'test_creator',
111
+ authorId: 'test_authorId',
121
112
  },
122
113
  {
123
- action: 'LOGOUT',
124
- authorId: 'user2',
125
- authorName: 'User 2',
126
- resource: 'auth',
127
- resourceId: '2',
114
+ creator: 'test_creator',
115
+ authorId: 'test_authorId',
128
116
  },
129
- ];
117
+ ] as any;
130
118
  const expectedResult = { count: 2 };
131
119
 
132
120
  prisma.audit.createMany.mockResolvedValue(expectedResult);
@@ -144,14 +132,11 @@ describe('AuditService', () => {
144
132
  describe('findMany', () => {
145
133
  it('should return entities with itemCount', async () => {
146
134
  const findArgs = {
147
- where: { authorId: 'user123' },
135
+ where: {},
148
136
  take: 10,
149
137
  skip: 0,
150
- };
151
- const mockEntities = [
152
- { id: BigInt(1), action: 'LOGIN', authorId: 'user123' },
153
- { id: BigInt(2), action: 'LOGOUT', authorId: 'user123' },
154
- ];
138
+ } as any;
139
+ const mockEntities = [{ id: 1 }, { id: 2 }];
155
140
 
156
141
  prisma.audit.count.mockResolvedValue(2);
157
142
  prisma.audit.findMany.mockResolvedValue(mockEntities);
@@ -169,7 +154,7 @@ describe('AuditService', () => {
169
154
  });
170
155
 
171
156
  it('should return empty result when no records found', async () => {
172
- const findArgs = { where: { authorId: 'nonexistent' } };
157
+ const findArgs = { where: {} } as any;
173
158
 
174
159
  prisma.audit.count.mockResolvedValue(0);
175
160
  prisma.audit.findMany.mockResolvedValue([]);
@@ -183,12 +168,8 @@ describe('AuditService', () => {
183
168
  describe('findUnique', () => {
184
169
  it('should find a single audit record by id', async () => {
185
170
  const id = 1;
186
- const select = { id: true, action: true, authorId: true };
187
- const expectedResult = {
188
- id: BigInt(1),
189
- action: 'LOGIN',
190
- authorId: 'user123',
191
- };
171
+ const select = { id: true };
172
+ const expectedResult = { id: 1 };
192
173
 
193
174
  prisma.audit.findUnique.mockResolvedValue(expectedResult);
194
175
 
@@ -204,7 +185,7 @@ describe('AuditService', () => {
204
185
  it('should return null when record not found', async () => {
205
186
  prisma.audit.findUnique.mockResolvedValue(null);
206
187
 
207
- const result = await service.findUnique(999, { id: true });
188
+ const result = await service.findUnique(1, { id: true });
208
189
 
209
190
  expect(result).toBeNull();
210
191
  });
@@ -213,8 +194,11 @@ describe('AuditService', () => {
213
194
  describe('updateUnique', () => {
214
195
  it('should update a single audit record', async () => {
215
196
  const id = 1;
216
- const data = { action: 'UPDATED_ACTION' };
217
- const expectedResult = { id: BigInt(1), action: 'UPDATED_ACTION' };
197
+ const data = {
198
+ creator: 'test_creator',
199
+ authorId: 'test_authorId',
200
+ } as any;
201
+ const expectedResult = { id: 1, ...data };
218
202
 
219
203
  prisma.audit.update.mockResolvedValue(expectedResult);
220
204
 
@@ -231,9 +215,12 @@ describe('AuditService', () => {
231
215
  describe('updateMany', () => {
232
216
  it('should update multiple audit records', async () => {
233
217
  const updateArgs = {
234
- where: { action: 'LOGIN' },
235
- data: { resource: 'updated_resource' },
236
- };
218
+ where: {},
219
+ data: {
220
+ creator: 'test_creator',
221
+ authorId: 'test_authorId',
222
+ },
223
+ } as any;
237
224
  const expectedResult = { count: 5 };
238
225
 
239
226
  prisma.audit.updateMany.mockResolvedValue(expectedResult);
@@ -249,7 +236,7 @@ describe('AuditService', () => {
249
236
  it('should delete a single audit record and return count', async () => {
250
237
  const id = 1;
251
238
 
252
- prisma.audit.delete.mockResolvedValue({ id: BigInt(1) });
239
+ prisma.audit.delete.mockResolvedValue({ id: 1 });
253
240
 
254
241
  const result = await service.deleteUnique(id);
255
242
 
@@ -260,7 +247,7 @@ describe('AuditService', () => {
260
247
 
261
248
  describe('deleteMany', () => {
262
249
  it('should delete multiple audit records', async () => {
263
- const deleteArgs = { where: { action: 'LOGIN' } };
250
+ const deleteArgs = { where: {} } as any;
264
251
  const expectedResult = { count: 3 };
265
252
 
266
253
  prisma.audit.deleteMany.mockResolvedValue(expectedResult);
@@ -274,7 +261,7 @@ describe('AuditService', () => {
274
261
 
275
262
  describe('count', () => {
276
263
  it('should count audit records with filter', async () => {
277
- const countArgs = { where: { action: 'LOGIN' } };
264
+ const countArgs = { where: {} } as any;
278
265
 
279
266
  prisma.audit.count.mockResolvedValue(10);
280
267
 
@@ -297,17 +284,14 @@ describe('AuditService', () => {
297
284
  describe('groupBy', () => {
298
285
  it('should group audit records by specified field', async () => {
299
286
  const groupByArgs = {
300
- by: ['action'],
301
- _count: { action: true },
302
- };
303
- const expectedResult = [
304
- { action: 'LOGIN', _count: { action: 50 } },
305
- { action: 'LOGOUT', _count: { action: 30 } },
306
- ];
287
+ by: ['id'],
288
+ _count: { id: true },
289
+ } as any;
290
+ const expectedResult = [{ id: 1, _count: { id: 50 } }];
307
291
 
308
292
  prisma.audit.groupBy.mockResolvedValue(expectedResult);
309
293
 
310
- const result = await service.groupBy(groupByArgs as any);
294
+ const result = await service.groupBy(groupByArgs);
311
295
 
312
296
  expect(prisma.audit.groupBy).toHaveBeenCalledWith(groupByArgs);
313
297
  expect(result).toEqual(expectedResult);
@@ -320,11 +304,11 @@ describe('AuditService', () => {
320
304
  _count: true,
321
305
  _max: { id: true },
322
306
  _min: { id: true },
323
- } as const;
307
+ } as any;
324
308
  const expectedResult = {
325
309
  _count: 100,
326
- _max: { id: BigInt(100) },
327
- _min: { id: BigInt(1) },
310
+ _max: { id: 1 },
311
+ _min: { id: 1 },
328
312
  };
329
313
 
330
314
  prisma.audit.aggregate.mockResolvedValue(expectedResult);
@@ -1,15 +1,44 @@
1
+ /// <reference types="jest" />
2
+ import { ConfigService } from '@nestjs/config';
1
3
  import { Test, TestingModule } from '@nestjs/testing';
4
+ import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
2
5
  import { GameController } from './game.controller';
3
6
  import { GameService } from './game.service';
4
7
 
8
+ // Mock GameService:用空对象屏蔽真实 Service 的依赖(PrismaService 等),
9
+ // 避免 NestJS 在测试模块编译时去解析 Service 的构造参数。
10
+ const mockGameService = {};
11
+
12
+ // Mock ConfigService
13
+ const mockConfigService = {
14
+ get: jest.fn(),
15
+ };
16
+
17
+ // Mock Logger
18
+ const mockLogger = {
19
+ child: jest.fn().mockReturnThis(),
20
+ info: jest.fn(),
21
+ error: jest.fn(),
22
+ warn: jest.fn(),
23
+ debug: jest.fn(),
24
+ };
25
+
5
26
  describe('GameController', () => {
6
27
  let controller: GameController;
7
28
 
8
29
  beforeEach(async () => {
9
30
  const module: TestingModule = await Test.createTestingModule({
10
31
  controllers: [GameController],
11
- providers: [GameService],
12
- }).compile();
32
+ providers: [
33
+ { provide: GameService, useValue: mockGameService },
34
+ { provide: ConfigService, useValue: mockConfigService },
35
+ { provide: WINSTON_MODULE_PROVIDER, useValue: mockLogger },
36
+ ],
37
+ })
38
+ // 兜底:自动 mock 任何未显式提供的依赖
39
+ // (例如 ACGuard 内部的 __roles_builder__ token)
40
+ .useMocker(() => ({}))
41
+ .compile();
13
42
 
14
43
  controller = module.get<GameController>(GameController);
15
44
  });
@@ -1,18 +1,326 @@
1
+ /// <reference types="jest" />
2
+ import { ConfigService } from '@nestjs/config';
1
3
  import { Test, TestingModule } from '@nestjs/testing';
4
+ import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
5
+
6
+ // Mock nestjs-prisma 模块
7
+ jest.mock('nestjs-prisma', () => ({
8
+ PrismaService: jest.fn(),
9
+ }));
10
+
11
+ import { PrismaService } from 'nestjs-prisma';
2
12
  import { GameService } from './game.service';
3
13
 
14
+ // Mock PrismaService
15
+ const mockPrismaService = {
16
+ game: {
17
+ create: jest.fn(),
18
+ createMany: jest.fn(),
19
+ findMany: jest.fn(),
20
+ findUnique: jest.fn(),
21
+ update: jest.fn(),
22
+ updateMany: jest.fn(),
23
+ delete: jest.fn(),
24
+ deleteMany: jest.fn(),
25
+ count: jest.fn(),
26
+ groupBy: jest.fn(),
27
+ aggregate: jest.fn(),
28
+ },
29
+ };
30
+
31
+ // Mock Logger
32
+ const mockLogger = {
33
+ child: jest.fn().mockReturnThis(),
34
+ info: jest.fn(),
35
+ error: jest.fn(),
36
+ warn: jest.fn(),
37
+ debug: jest.fn(),
38
+ };
39
+
40
+ // Mock ConfigService
41
+ const mockConfigService = {
42
+ get: jest.fn(),
43
+ };
44
+
4
45
  describe('GameService', () => {
5
46
  let service: GameService;
47
+ let prisma: typeof mockPrismaService;
6
48
 
7
49
  beforeEach(async () => {
50
+ // 每个测试前重置所有 mock
51
+ jest.clearAllMocks();
52
+
8
53
  const module: TestingModule = await Test.createTestingModule({
9
- providers: [GameService],
54
+ providers: [
55
+ GameService,
56
+ { provide: PrismaService, useValue: mockPrismaService },
57
+ { provide: ConfigService, useValue: mockConfigService },
58
+ { provide: WINSTON_MODULE_PROVIDER, useValue: mockLogger },
59
+ ],
10
60
  }).compile();
11
61
 
12
62
  service = module.get<GameService>(GameService);
63
+ prisma = mockPrismaService;
13
64
  });
14
65
 
15
66
  it('should be defined', () => {
16
67
  expect(service).toBeDefined();
17
68
  });
69
+
70
+ describe('createOne', () => {
71
+ it('should create a single game record', async () => {
72
+ const createArgs = {
73
+ data: {
74
+ unified_editionid: 'test_unified_editionid',
75
+ name: 'test_name',
76
+ creator: 'test_creator',
77
+ },
78
+ } as any;
79
+ const expectedResult = { id: 1, ...createArgs.data };
80
+
81
+ prisma.game.create.mockResolvedValue(expectedResult);
82
+
83
+ const result = await service.createOne(createArgs);
84
+
85
+ expect(prisma.game.create).toHaveBeenCalledWith(createArgs);
86
+ expect(prisma.game.create).toHaveBeenCalledTimes(1);
87
+ expect(result).toEqual(expectedResult);
88
+ });
89
+
90
+ it('should throw error when create fails', async () => {
91
+ const createArgs = {
92
+ data: {
93
+ unified_editionid: 'test_unified_editionid',
94
+ name: 'test_name',
95
+ creator: 'test_creator',
96
+ },
97
+ } as any;
98
+ const error = new Error('Database connection failed');
99
+
100
+ prisma.game.create.mockRejectedValue(error);
101
+
102
+ await expect(service.createOne(createArgs)).rejects.toThrow(
103
+ 'Database connection failed',
104
+ );
105
+ });
106
+ });
107
+
108
+ describe('createMany', () => {
109
+ it('should create multiple game records with skipDuplicates', async () => {
110
+ const data = [
111
+ {
112
+ unified_editionid: 'test_unified_editionid',
113
+ name: 'test_name',
114
+ creator: 'test_creator',
115
+ },
116
+ {
117
+ unified_editionid: 'test_unified_editionid',
118
+ name: 'test_name',
119
+ creator: 'test_creator',
120
+ },
121
+ ] as any;
122
+ const expectedResult = { count: 2 };
123
+
124
+ prisma.game.createMany.mockResolvedValue(expectedResult);
125
+
126
+ const result = await service.createMany(data);
127
+
128
+ expect(prisma.game.createMany).toHaveBeenCalledWith({
129
+ data,
130
+ skipDuplicates: true,
131
+ });
132
+ expect(result).toEqual(expectedResult);
133
+ });
134
+ });
135
+
136
+ describe('findMany', () => {
137
+ it('should return entities with itemCount', async () => {
138
+ const findArgs = {
139
+ where: {},
140
+ take: 10,
141
+ skip: 0,
142
+ } as any;
143
+ const mockEntities = [{ id: 1 }, { id: 2 }];
144
+
145
+ prisma.game.count.mockResolvedValue(2);
146
+ prisma.game.findMany.mockResolvedValue(mockEntities);
147
+
148
+ const result = await service.findMany(findArgs);
149
+
150
+ expect(prisma.game.count).toHaveBeenCalledWith({ where: findArgs.where });
151
+ expect(prisma.game.findMany).toHaveBeenCalledWith(findArgs);
152
+ expect(result).toEqual({
153
+ itemCount: 2,
154
+ entities: mockEntities,
155
+ });
156
+ });
157
+
158
+ it('should return empty result when no records found', async () => {
159
+ const findArgs = { where: {} } as any;
160
+
161
+ prisma.game.count.mockResolvedValue(0);
162
+ prisma.game.findMany.mockResolvedValue([]);
163
+
164
+ const result = await service.findMany(findArgs);
165
+
166
+ expect(result).toEqual({ itemCount: 0, entities: [] });
167
+ });
168
+ });
169
+
170
+ describe('findUnique', () => {
171
+ it('should find a single game record by id', async () => {
172
+ const id = 1;
173
+ const select = { id: true };
174
+ const expectedResult = { id: 1 };
175
+
176
+ prisma.game.findUnique.mockResolvedValue(expectedResult);
177
+
178
+ const result = await service.findUnique(id, select);
179
+
180
+ expect(prisma.game.findUnique).toHaveBeenCalledWith({
181
+ where: { id },
182
+ select,
183
+ });
184
+ expect(result).toEqual(expectedResult);
185
+ });
186
+
187
+ it('should return null when record not found', async () => {
188
+ prisma.game.findUnique.mockResolvedValue(null);
189
+
190
+ const result = await service.findUnique(1, { id: true });
191
+
192
+ expect(result).toBeNull();
193
+ });
194
+ });
195
+
196
+ describe('updateUnique', () => {
197
+ it('should update a single game record', async () => {
198
+ const id = 1;
199
+ const data = {
200
+ unified_editionid: 'test_unified_editionid',
201
+ name: 'test_name',
202
+ creator: 'test_creator',
203
+ } as any;
204
+ const expectedResult = { id: 1, ...data };
205
+
206
+ prisma.game.update.mockResolvedValue(expectedResult);
207
+
208
+ const result = await service.updateUnique(id, data);
209
+
210
+ expect(prisma.game.update).toHaveBeenCalledWith({
211
+ where: { id },
212
+ data,
213
+ });
214
+ expect(result).toEqual(expectedResult);
215
+ });
216
+ });
217
+
218
+ describe('updateMany', () => {
219
+ it('should update multiple game records', async () => {
220
+ const updateArgs = {
221
+ where: {},
222
+ data: {
223
+ unified_editionid: 'test_unified_editionid',
224
+ name: 'test_name',
225
+ creator: 'test_creator',
226
+ },
227
+ } as any;
228
+ const expectedResult = { count: 5 };
229
+
230
+ prisma.game.updateMany.mockResolvedValue(expectedResult);
231
+
232
+ const result = await service.updateMany(updateArgs);
233
+
234
+ expect(prisma.game.updateMany).toHaveBeenCalledWith(updateArgs);
235
+ expect(result).toEqual(expectedResult);
236
+ });
237
+ });
238
+
239
+ describe('deleteUnique', () => {
240
+ it('should delete a single game record and return count', async () => {
241
+ const id = 1;
242
+
243
+ prisma.game.delete.mockResolvedValue({ id: 1 });
244
+
245
+ const result = await service.deleteUnique(id);
246
+
247
+ expect(prisma.game.delete).toHaveBeenCalledWith({ where: { id } });
248
+ expect(result).toEqual({ count: 1 });
249
+ });
250
+ });
251
+
252
+ describe('deleteMany', () => {
253
+ it('should delete multiple game records', async () => {
254
+ const deleteArgs = { where: {} } as any;
255
+ const expectedResult = { count: 3 };
256
+
257
+ prisma.game.deleteMany.mockResolvedValue(expectedResult);
258
+
259
+ const result = await service.deleteMany(deleteArgs);
260
+
261
+ expect(prisma.game.deleteMany).toHaveBeenCalledWith(deleteArgs);
262
+ expect(result).toEqual(expectedResult);
263
+ });
264
+ });
265
+
266
+ describe('count', () => {
267
+ it('should count game records with filter', async () => {
268
+ const countArgs = { where: {} } as any;
269
+
270
+ prisma.game.count.mockResolvedValue(10);
271
+
272
+ const result = await service.count(countArgs);
273
+
274
+ expect(prisma.game.count).toHaveBeenCalledWith(countArgs);
275
+ expect(result).toBe(10);
276
+ });
277
+
278
+ it('should count all records when no filter provided', async () => {
279
+ prisma.game.count.mockResolvedValue(100);
280
+
281
+ const result = await service.count();
282
+
283
+ expect(prisma.game.count).toHaveBeenCalledWith(undefined);
284
+ expect(result).toBe(100);
285
+ });
286
+ });
287
+
288
+ describe('groupBy', () => {
289
+ it('should group game records by specified field', async () => {
290
+ const groupByArgs = {
291
+ by: ['id'],
292
+ _count: { id: true },
293
+ } as any;
294
+ const expectedResult = [{ id: 1, _count: { id: 50 } }];
295
+
296
+ prisma.game.groupBy.mockResolvedValue(expectedResult);
297
+
298
+ const result = await service.groupBy(groupByArgs);
299
+
300
+ expect(prisma.game.groupBy).toHaveBeenCalledWith(groupByArgs);
301
+ expect(result).toEqual(expectedResult);
302
+ });
303
+ });
304
+
305
+ describe('aggregate', () => {
306
+ it('should aggregate game records', async () => {
307
+ const aggregateArgs = {
308
+ _count: true,
309
+ _max: { id: true },
310
+ _min: { id: true },
311
+ } as any;
312
+ const expectedResult = {
313
+ _count: 100,
314
+ _max: { id: 1 },
315
+ _min: { id: 1 },
316
+ };
317
+
318
+ prisma.game.aggregate.mockResolvedValue(expectedResult);
319
+
320
+ const result = await service.aggregate(aggregateArgs);
321
+
322
+ expect(prisma.game.aggregate).toHaveBeenCalledWith(aggregateArgs);
323
+ expect(result).toEqual(expectedResult);
324
+ });
325
+ });
18
326
  });
@@ -1,15 +1,44 @@
1
+ /// <reference types="jest" />
2
+ import { ConfigService } from '@nestjs/config';
1
3
  import { Test, TestingModule } from '@nestjs/testing';
4
+ import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
2
5
  import { PageController } from './page.controller';
3
6
  import { PageService } from './page.service';
4
7
 
8
+ // Mock PageService:用空对象屏蔽真实 Service 的依赖(PrismaService 等),
9
+ // 避免 NestJS 在测试模块编译时去解析 Service 的构造参数。
10
+ const mockPageService = {};
11
+
12
+ // Mock ConfigService
13
+ const mockConfigService = {
14
+ get: jest.fn(),
15
+ };
16
+
17
+ // Mock Logger
18
+ const mockLogger = {
19
+ child: jest.fn().mockReturnThis(),
20
+ info: jest.fn(),
21
+ error: jest.fn(),
22
+ warn: jest.fn(),
23
+ debug: jest.fn(),
24
+ };
25
+
5
26
  describe('PageController', () => {
6
27
  let controller: PageController;
7
28
 
8
29
  beforeEach(async () => {
9
30
  const module: TestingModule = await Test.createTestingModule({
10
31
  controllers: [PageController],
11
- providers: [PageService],
12
- }).compile();
32
+ providers: [
33
+ { provide: PageService, useValue: mockPageService },
34
+ { provide: ConfigService, useValue: mockConfigService },
35
+ { provide: WINSTON_MODULE_PROVIDER, useValue: mockLogger },
36
+ ],
37
+ })
38
+ // 兜底:自动 mock 任何未显式提供的依赖
39
+ // (例如 ACGuard 内部的 __roles_builder__ token)
40
+ .useMocker(() => ({}))
41
+ .compile();
13
42
 
14
43
  controller = module.get<PageController>(PageController);
15
44
  });