simpledi-app-generator 0.0.7 → 0.0.9
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.
- package/README.md +9 -9
- package/dist/cli.js +3 -4
- package/dist/cli.js.map +1 -1
- package/dist/create_module.d.ts.map +1 -1
- package/dist/create_module.js +3 -0
- package/dist/create_module.js.map +1 -1
- package/dist/generate_crud_use_cases.d.ts.map +1 -1
- package/dist/generate_crud_use_cases.js +571 -130
- package/dist/generate_crud_use_cases.js.map +1 -1
- package/dist/generate_skeleton.d.ts.map +1 -1
- package/dist/generate_skeleton.js +57 -2
- package/dist/generate_skeleton.js.map +1 -1
- package/dist/templates/schema.ts +2 -0
- package/dist/templates/src/core/user/IUserRepository.ts +8 -0
- package/dist/templates/src/core/user/IUserService.ts +8 -0
- package/dist/templates/src/core/user/User.ts +71 -0
- package/dist/templates/src/core/user/UserModule.ts +7 -0
- package/dist/templates/src/core/user/UserRepository.spec.ts +63 -0
- package/dist/templates/src/core/user/UserRepository.ts +31 -0
- package/dist/templates/src/core/user/UserRepositoryModule.ts +15 -0
- package/dist/templates/src/core/user/UserService.ts +34 -0
- package/dist/templates/src/core/user/UserServiceModule.ts +14 -0
- package/dist/templates/src/core/user/baseZodUserSchema.ts +31 -0
- package/dist/templates/src/lib/functions/getContextUser.ts +5 -0
- package/dist/templates/src/lib/functions/test-related/createSignedUpUser.ts +44 -0
- package/dist/templates/src/lib/functions/test-related/getOneUserSignupData.ts +28 -0
- package/dist/templates/src/lib/functions/test-related/getTestServer.ts +28 -0
- package/dist/templates/src/lib/types/AdminRoleEnum.ts +6 -0
- package/dist/templates/src/lib/types/AnyRoleEnum.ts +5 -0
- package/dist/templates/src/lib/types/PhoneNumberTypeEnum.ts +7 -0
- package/dist/templates/src/lib/types/UserRoleEnum.ts +4 -0
- package/dist/templates/src/lib/types/UserTypeEnum.ts +4 -0
- package/dist/templates/src/middlewares/authGuard.ts +46 -0
- package/dist/templates/src/middlewares/index.ts +2 -0
- package/dist/templates/src/middlewares/roleGuard.ts +16 -0
- package/package.json +1 -1
- package/user-guide.md +16 -4
|
@@ -24,14 +24,19 @@ export async function generateCrudUseCases(EntityName, entityName, kebabName, sr
|
|
|
24
24
|
await generateDeleteUseCase(useCaseBaseDir, config);
|
|
25
25
|
// Generate aggregator module
|
|
26
26
|
await generateUseCaseAggregatorModule(useCaseBaseDir, config);
|
|
27
|
-
// Auto-register in UseCaseModule.ts
|
|
27
|
+
// Auto-register in UseCaseModule.ts
|
|
28
28
|
await registerInUseCaseModule(srcDir, config);
|
|
29
|
-
|
|
29
|
+
// Phase 3: Generate Scaffolded E2E Tests
|
|
30
|
+
await generateCreateE2ETest(useCaseBaseDir, config);
|
|
31
|
+
await generateUpdateE2ETest(useCaseBaseDir, config);
|
|
32
|
+
await generateGetE2ETest(useCaseBaseDir, config);
|
|
33
|
+
await generateListE2ETest(useCaseBaseDir, config);
|
|
34
|
+
await generateDeleteE2ETest(useCaseBaseDir, config);
|
|
30
35
|
console.log(`\n✅ CRUD use cases generated for ${EntityName}!`);
|
|
31
36
|
}
|
|
32
37
|
// ============ CREATE USE CASE ============
|
|
33
38
|
async function generateCreateUseCase(baseDir, config) {
|
|
34
|
-
const { EntityName, entityName, kebabName, TOKEN_BASE } = config;
|
|
39
|
+
const { EntityName, entityName, kebabName, TOKEN_BASE, pluralKebab } = config;
|
|
35
40
|
const useCaseName = `Create${EntityName}`;
|
|
36
41
|
const useCaseDir = join(baseDir, `create-${kebabName}`);
|
|
37
42
|
const inputsDir = join(useCaseDir, 'inputs');
|
|
@@ -111,28 +116,42 @@ import { StatusCodes } from 'http-status-codes';
|
|
|
111
116
|
import { inject } from '@kanian77/simple-di';
|
|
112
117
|
import { ${useCaseName}, ${toUpperSnakeCase(useCaseName)}_USE_CASE_TOKEN } from './${useCaseName}';
|
|
113
118
|
import { ${useCaseName}Failure } from './outputs/${useCaseName}Failure';
|
|
114
|
-
import
|
|
119
|
+
import { ${EntityName}InsertSchema } from '@root/core/${kebabName}/${EntityName}';
|
|
120
|
+
import { getContextUser } from '@root/lib/functions/getContextUser';
|
|
121
|
+
import { authGuard } from '@root/middlewares/authGuard';
|
|
122
|
+
import { roleGuard } from '@root/middlewares/roleGuard';
|
|
123
|
+
import { AdminRoleEnum } from '@root/lib';
|
|
115
124
|
|
|
116
125
|
const ${toCamelCase(useCaseName)}Routes = new Hono();
|
|
126
|
+
const ${toCamelCase(useCaseName)}RoutesPath = '/${pluralKebab}';
|
|
117
127
|
|
|
118
|
-
${toCamelCase(useCaseName)}Routes.post(
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
});
|
|
128
|
+
${toCamelCase(useCaseName)}Routes.post(
|
|
129
|
+
${toCamelCase(useCaseName)}RoutesPath,
|
|
130
|
+
authGuard(),
|
|
131
|
+
roleGuard([AdminRoleEnum.ADMIN]),
|
|
132
|
+
async (c) => {
|
|
133
|
+
try {
|
|
134
|
+
const rawInput = await c.req.json();
|
|
135
|
+
const user = getContextUser(c);
|
|
136
|
+
if (user) {
|
|
137
|
+
rawInput.createdBy = user.id;
|
|
138
|
+
}
|
|
132
139
|
|
|
133
|
-
const ${
|
|
140
|
+
const input = ${EntityName}InsertSchema.parse(rawInput);
|
|
141
|
+
const useCase = inject<${useCaseName}>(${toUpperSnakeCase(useCaseName)}_USE_CASE_TOKEN);
|
|
142
|
+
const result = await useCase.execute(input);
|
|
143
|
+
return c.json(result, StatusCodes.CREATED);
|
|
144
|
+
} catch (e) {
|
|
145
|
+
console.error('Error in ${toCamelCase(useCaseName)}Routes:', e);
|
|
146
|
+
return c.json(
|
|
147
|
+
new ${useCaseName}Failure('Internal Server Error'),
|
|
148
|
+
StatusCodes.INTERNAL_SERVER_ERROR,
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
);
|
|
134
153
|
|
|
135
|
-
export { ${toCamelCase(useCaseName)}Routes, ${toCamelCase(useCaseName)}RoutesPath };
|
|
154
|
+
export { ${toCamelCase(useCaseName)}Routes as Route, ${toCamelCase(useCaseName)}RoutesPath as Path };
|
|
136
155
|
`;
|
|
137
156
|
await writeFile(join(inputsDir, `${useCaseName}Input.ts`), inputContent);
|
|
138
157
|
await writeFile(join(outputsDir, `${useCaseName}Success.ts`), successContent);
|
|
@@ -143,7 +162,7 @@ export { ${toCamelCase(useCaseName)}Routes, ${toCamelCase(useCaseName)}RoutesPat
|
|
|
143
162
|
}
|
|
144
163
|
// ============ UPDATE USE CASE ============
|
|
145
164
|
async function generateUpdateUseCase(baseDir, config) {
|
|
146
|
-
const { EntityName, entityName, kebabName, TOKEN_BASE } = config;
|
|
165
|
+
const { EntityName, entityName, kebabName, TOKEN_BASE, pluralKebab } = config;
|
|
147
166
|
const useCaseName = `Update${EntityName}`;
|
|
148
167
|
const useCaseDir = join(baseDir, `update-${kebabName}`);
|
|
149
168
|
const inputsDir = join(useCaseDir, 'inputs');
|
|
@@ -227,29 +246,45 @@ import { inject } from '@kanian77/simple-di';
|
|
|
227
246
|
import { ${useCaseName}, ${toUpperSnakeCase(useCaseName)}_USE_CASE_TOKEN } from './${useCaseName}';
|
|
228
247
|
import { ${useCaseName}Failure } from './outputs/${useCaseName}Failure';
|
|
229
248
|
import type { ${useCaseName}Input } from './inputs/${useCaseName}Input';
|
|
249
|
+
import { ${EntityName}UpdateSchema } from '@root/core/${kebabName}/${EntityName}';
|
|
250
|
+
import { getContextUser } from '@root/lib/functions/getContextUser';
|
|
251
|
+
import { authGuard } from '@root/middlewares/authGuard';
|
|
252
|
+
import { roleGuard } from '@root/middlewares/roleGuard';
|
|
253
|
+
import { AdminRoleEnum } from '@root/lib';
|
|
230
254
|
|
|
231
255
|
const ${toCamelCase(useCaseName)}Routes = new Hono();
|
|
256
|
+
const ${toCamelCase(useCaseName)}RoutesPath = '/${pluralKebab}/:id';
|
|
232
257
|
|
|
233
|
-
${toCamelCase(useCaseName)}Routes.put(
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
} catch (e) {
|
|
242
|
-
console.error('Error in ${toCamelCase(useCaseName)}Routes:', e);
|
|
243
|
-
return c.json(
|
|
244
|
-
new ${useCaseName}Failure('Internal Server Error'),
|
|
245
|
-
StatusCodes.INTERNAL_SERVER_ERROR,
|
|
246
|
-
);
|
|
247
|
-
}
|
|
248
|
-
});
|
|
258
|
+
${toCamelCase(useCaseName)}Routes.put(
|
|
259
|
+
${toCamelCase(useCaseName)}RoutesPath,
|
|
260
|
+
authGuard(),
|
|
261
|
+
roleGuard([AdminRoleEnum.ADMIN]),
|
|
262
|
+
async (c) => {
|
|
263
|
+
try {
|
|
264
|
+
const id = c.req.param('id');
|
|
265
|
+
const rawBody = await c.req.json();
|
|
249
266
|
|
|
250
|
-
const
|
|
267
|
+
const user = getContextUser(c);
|
|
268
|
+
if (user) {
|
|
269
|
+
rawBody.updatedBy = user.id;
|
|
270
|
+
}
|
|
251
271
|
|
|
252
|
-
|
|
272
|
+
const data = ${EntityName}UpdateSchema.parse(rawBody);
|
|
273
|
+
const input: ${useCaseName}Input = { id, data };
|
|
274
|
+
const useCase = inject<${useCaseName}>(${toUpperSnakeCase(useCaseName)}_USE_CASE_TOKEN);
|
|
275
|
+
const result = await useCase.execute(input);
|
|
276
|
+
return c.json(result, StatusCodes.OK);
|
|
277
|
+
} catch (e) {
|
|
278
|
+
console.error('Error in ${toCamelCase(useCaseName)}Routes:', e);
|
|
279
|
+
return c.json(
|
|
280
|
+
new ${useCaseName}Failure('Internal Server Error'),
|
|
281
|
+
StatusCodes.INTERNAL_SERVER_ERROR,
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
export { ${toCamelCase(useCaseName)}Routes as Route, ${toCamelCase(useCaseName)}RoutesPath as Path };
|
|
253
288
|
`;
|
|
254
289
|
await writeFile(join(inputsDir, `${useCaseName}Input.ts`), inputContent);
|
|
255
290
|
await writeFile(join(outputsDir, `${useCaseName}Success.ts`), successContent);
|
|
@@ -260,7 +295,7 @@ export { ${toCamelCase(useCaseName)}Routes, ${toCamelCase(useCaseName)}RoutesPat
|
|
|
260
295
|
}
|
|
261
296
|
// ============ GET USE CASE ============
|
|
262
297
|
async function generateGetUseCase(baseDir, config) {
|
|
263
|
-
const { EntityName, entityName, kebabName, TOKEN_BASE } = config;
|
|
298
|
+
const { EntityName, entityName, kebabName, TOKEN_BASE, pluralKebab } = config;
|
|
264
299
|
const useCaseName = `Get${EntityName}`;
|
|
265
300
|
const useCaseDir = join(baseDir, `get-${kebabName}`);
|
|
266
301
|
const outputsDir = join(useCaseDir, 'outputs');
|
|
@@ -334,8 +369,9 @@ import { ${useCaseName}, ${toUpperSnakeCase(useCaseName)}_USE_CASE_TOKEN } from
|
|
|
334
369
|
import { ${useCaseName}Failure } from './outputs/${useCaseName}Failure';
|
|
335
370
|
|
|
336
371
|
const ${toCamelCase(useCaseName)}Routes = new Hono();
|
|
372
|
+
const ${toCamelCase(useCaseName)}RoutesPath = '/${pluralKebab}/:id';
|
|
337
373
|
|
|
338
|
-
${toCamelCase(useCaseName)}Routes.get(
|
|
374
|
+
${toCamelCase(useCaseName)}Routes.get(${toCamelCase(useCaseName)}RoutesPath, async (c) => {
|
|
339
375
|
try {
|
|
340
376
|
const id = c.req.param('id');
|
|
341
377
|
const useCase = inject<${useCaseName}>(${toUpperSnakeCase(useCaseName)}_USE_CASE_TOKEN);
|
|
@@ -350,9 +386,7 @@ ${toCamelCase(useCaseName)}Routes.get('/:id', async (c) => {
|
|
|
350
386
|
}
|
|
351
387
|
});
|
|
352
388
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
export { ${toCamelCase(useCaseName)}Routes, ${toCamelCase(useCaseName)}RoutesPath };
|
|
389
|
+
export { ${toCamelCase(useCaseName)}Routes as Route, ${toCamelCase(useCaseName)}RoutesPath as Path };
|
|
356
390
|
`;
|
|
357
391
|
await writeFile(join(outputsDir, `${useCaseName}Success.ts`), successContent);
|
|
358
392
|
await writeFile(join(outputsDir, `${useCaseName}Failure.ts`), failureContent);
|
|
@@ -362,7 +396,7 @@ export { ${toCamelCase(useCaseName)}Routes, ${toCamelCase(useCaseName)}RoutesPat
|
|
|
362
396
|
}
|
|
363
397
|
// ============ LIST USE CASE ============
|
|
364
398
|
async function generateListUseCase(baseDir, config) {
|
|
365
|
-
const { EntityName, entityName, kebabName, TOKEN_BASE, pluralPascal } = config;
|
|
399
|
+
const { EntityName, entityName, kebabName, TOKEN_BASE, pluralPascal, pluralKebab, } = config;
|
|
366
400
|
const useCaseName = `List${pluralPascal}`;
|
|
367
401
|
const useCaseDir = join(baseDir, `list-${config.pluralKebab}`);
|
|
368
402
|
const outputsDir = join(useCaseDir, 'outputs');
|
|
@@ -436,8 +470,9 @@ import { ${useCaseName}, ${toUpperSnakeCase(useCaseName)}_USE_CASE_TOKEN } from
|
|
|
436
470
|
import { ${useCaseName}Failure } from './outputs/${useCaseName}Failure';
|
|
437
471
|
|
|
438
472
|
const ${toCamelCase(useCaseName)}Routes = new Hono();
|
|
473
|
+
const ${toCamelCase(useCaseName)}RoutesPath = '/${pluralKebab}';
|
|
439
474
|
|
|
440
|
-
${toCamelCase(useCaseName)}Routes.get(
|
|
475
|
+
${toCamelCase(useCaseName)}Routes.get(${toCamelCase(useCaseName)}RoutesPath, async (c) => {
|
|
441
476
|
try {
|
|
442
477
|
const useCase = inject<${useCaseName}>(${toUpperSnakeCase(useCaseName)}_USE_CASE_TOKEN);
|
|
443
478
|
const result = await useCase.execute();
|
|
@@ -451,9 +486,7 @@ ${toCamelCase(useCaseName)}Routes.get('/', async (c) => {
|
|
|
451
486
|
}
|
|
452
487
|
});
|
|
453
488
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
export { ${toCamelCase(useCaseName)}Routes, ${toCamelCase(useCaseName)}RoutesPath };
|
|
489
|
+
export { ${toCamelCase(useCaseName)}Routes as Route, ${toCamelCase(useCaseName)}RoutesPath as Path };
|
|
457
490
|
`;
|
|
458
491
|
await writeFile(join(outputsDir, `${useCaseName}Success.ts`), successContent);
|
|
459
492
|
await writeFile(join(outputsDir, `${useCaseName}Failure.ts`), failureContent);
|
|
@@ -463,7 +496,7 @@ export { ${toCamelCase(useCaseName)}Routes, ${toCamelCase(useCaseName)}RoutesPat
|
|
|
463
496
|
}
|
|
464
497
|
// ============ DELETE USE CASE ============
|
|
465
498
|
async function generateDeleteUseCase(baseDir, config) {
|
|
466
|
-
const { EntityName, entityName, kebabName, TOKEN_BASE } = config;
|
|
499
|
+
const { EntityName, entityName, kebabName, TOKEN_BASE, pluralKebab } = config;
|
|
467
500
|
const useCaseName = `Delete${EntityName}`;
|
|
468
501
|
const useCaseDir = join(baseDir, `delete-${kebabName}`);
|
|
469
502
|
const outputsDir = join(useCaseDir, 'outputs');
|
|
@@ -472,7 +505,7 @@ async function generateDeleteUseCase(baseDir, config) {
|
|
|
472
505
|
// Success Output
|
|
473
506
|
const successContent = `import { SuccessfullOperation } from '@root/lib';
|
|
474
507
|
|
|
475
|
-
export type ${useCaseName}Payload = { id: string; deleted: boolean };
|
|
508
|
+
export type ${useCaseName}Payload = { id: string; deleted: boolean; softDelete: boolean };
|
|
476
509
|
|
|
477
510
|
export class ${useCaseName}Success extends SuccessfullOperation {
|
|
478
511
|
constructor(
|
|
@@ -512,9 +545,9 @@ export class ${useCaseName} implements IUseCase {
|
|
|
512
545
|
private readonly ${entityName}Service: I${EntityName}Service,
|
|
513
546
|
) {}
|
|
514
547
|
|
|
515
|
-
async execute(id: string): Promise<${useCaseName}Success> {
|
|
516
|
-
await this.${entityName}Service.delete(id);
|
|
517
|
-
const result: ${useCaseName}Payload = { id, deleted: true };
|
|
548
|
+
async execute(id: string, softDelete: boolean = true): Promise<${useCaseName}Success> {
|
|
549
|
+
await this.${entityName}Service.delete(id, softDelete);
|
|
550
|
+
const result: ${useCaseName}Payload = { id, deleted: true, softDelete };
|
|
518
551
|
return new ${useCaseName}Success(result);
|
|
519
552
|
}
|
|
520
553
|
}
|
|
@@ -535,27 +568,45 @@ import { StatusCodes } from 'http-status-codes';
|
|
|
535
568
|
import { inject } from '@kanian77/simple-di';
|
|
536
569
|
import { ${useCaseName}, ${toUpperSnakeCase(useCaseName)}_USE_CASE_TOKEN } from './${useCaseName}';
|
|
537
570
|
import { ${useCaseName}Failure } from './outputs/${useCaseName}Failure';
|
|
571
|
+
import { authGuard } from '@root/middlewares/authGuard';
|
|
572
|
+
import { roleGuard } from '@root/middlewares/roleGuard';
|
|
573
|
+
import { AdminRoleEnum } from '@root/lib';
|
|
538
574
|
|
|
539
575
|
const ${toCamelCase(useCaseName)}Routes = new Hono();
|
|
576
|
+
const ${toCamelCase(useCaseName)}RoutesPath = '/${pluralKebab}/:id';
|
|
540
577
|
|
|
541
|
-
${toCamelCase(useCaseName)}Routes.delete(
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
}
|
|
578
|
+
${toCamelCase(useCaseName)}Routes.delete(
|
|
579
|
+
${toCamelCase(useCaseName)}RoutesPath,
|
|
580
|
+
authGuard(),
|
|
581
|
+
roleGuard([AdminRoleEnum.ADMIN]),
|
|
582
|
+
async (c) => {
|
|
583
|
+
try {
|
|
584
|
+
const id = c.req.param('id');
|
|
585
|
+
|
|
586
|
+
let softDelete = true;
|
|
587
|
+
try {
|
|
588
|
+
const body = await c.req.json();
|
|
589
|
+
if (typeof body.softDelete === 'boolean') {
|
|
590
|
+
softDelete = body.softDelete;
|
|
591
|
+
}
|
|
592
|
+
} catch (e) {
|
|
593
|
+
// Body is likely empty, default to softDelete = true
|
|
594
|
+
}
|
|
555
595
|
|
|
556
|
-
const ${
|
|
596
|
+
const useCase = inject<${useCaseName}>(${toUpperSnakeCase(useCaseName)}_USE_CASE_TOKEN);
|
|
597
|
+
const result = await useCase.execute(id, softDelete);
|
|
598
|
+
return c.json(result, StatusCodes.OK);
|
|
599
|
+
} catch (e) {
|
|
600
|
+
console.error('Error in ${toCamelCase(useCaseName)}Routes:', e);
|
|
601
|
+
return c.json(
|
|
602
|
+
new ${useCaseName}Failure('Internal Server Error'),
|
|
603
|
+
StatusCodes.INTERNAL_SERVER_ERROR,
|
|
604
|
+
);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
);
|
|
557
608
|
|
|
558
|
-
export { ${toCamelCase(useCaseName)}Routes, ${toCamelCase(useCaseName)}RoutesPath };
|
|
609
|
+
export { ${toCamelCase(useCaseName)}Routes as Route, ${toCamelCase(useCaseName)}RoutesPath as Path };
|
|
559
610
|
`;
|
|
560
611
|
await writeFile(join(outputsDir, `${useCaseName}Success.ts`), successContent);
|
|
561
612
|
await writeFile(join(outputsDir, `${useCaseName}Failure.ts`), failureContent);
|
|
@@ -644,66 +695,456 @@ async function registerInUseCaseModule(srcDir, config) {
|
|
|
644
695
|
await writeFile(useCaseModulePath, content);
|
|
645
696
|
console.log(`Updated: src/use-case/UseCaseModule.ts`);
|
|
646
697
|
}
|
|
647
|
-
// ============
|
|
648
|
-
async function
|
|
649
|
-
const { EntityName, entityName, kebabName,
|
|
650
|
-
const
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
import {
|
|
659
|
-
import {
|
|
660
|
-
import
|
|
661
|
-
import
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
698
|
+
// ============ E2E TESTS GENERATION ============
|
|
699
|
+
async function generateCreateE2ETest(baseDir, config) {
|
|
700
|
+
const { EntityName, entityName, kebabName, pluralKebab } = config;
|
|
701
|
+
const useCaseDir = join(baseDir, `create-${kebabName}`);
|
|
702
|
+
const testContent = `import { afterAll, beforeAll, describe, expect, it } from 'bun:test';
|
|
703
|
+
import { EnvFileNames, UserRoleEnum, UserTypeEnum } from '@root/lib';
|
|
704
|
+
import { bootstrap, inject, Module } from '@kanian77/simple-di';
|
|
705
|
+
import { UseCaseModule } from '@root/use-case/UseCaseModule';
|
|
706
|
+
import { getConfigModule } from 'config/getConfigModule';
|
|
707
|
+
import { getDbModule } from 'db/getDbModule';
|
|
708
|
+
import { CoreModule } from '@root/core/CoreModule';
|
|
709
|
+
import { DB_SERVICE, type DbService } from 'db/DbService';
|
|
710
|
+
import { APP, AppModule } from '@root/AppModule';
|
|
711
|
+
import type { Hono } from 'hono';
|
|
712
|
+
import * as schema from '@root/schema';
|
|
713
|
+
import { eq } from 'drizzle-orm';
|
|
714
|
+
import { getTestServer, type TestServer } from '@root/lib/functions/test-related/getTestServer';
|
|
715
|
+
import { createOneSignedUpUser, deleteCreatedSignedUsers } from '@root/lib/functions/test-related/createSignedUpUser';
|
|
716
|
+
import { USER_REPOSITORY_INTERFACE, type UserRepository } from '@root/core/user/IUserRepository';
|
|
717
|
+
import { UserModule } from '@root/core/user/UserModule';
|
|
718
|
+
|
|
719
|
+
describe('Create${EntityName} e2e', () => {
|
|
720
|
+
let dbService: DbService;
|
|
721
|
+
let server: TestServer;
|
|
722
|
+
let app: Hono;
|
|
723
|
+
let userRepository: UserRepository;
|
|
724
|
+
|
|
725
|
+
beforeAll(async () => {
|
|
726
|
+
const TestModule = new Module({
|
|
727
|
+
imports: [
|
|
728
|
+
AppModule,
|
|
729
|
+
getConfigModule(EnvFileNames.TESTING),
|
|
730
|
+
getDbModule(),
|
|
731
|
+
CoreModule,
|
|
732
|
+
UserModule,
|
|
733
|
+
UseCaseModule,
|
|
734
|
+
],
|
|
735
|
+
});
|
|
736
|
+
bootstrap(TestModule);
|
|
737
|
+
dbService = inject<DbService>(DB_SERVICE);
|
|
738
|
+
app = inject<Hono>(APP);
|
|
739
|
+
userRepository = inject(USER_REPOSITORY_INTERFACE);
|
|
740
|
+
server = await getTestServer(app);
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
afterAll(async () => {
|
|
744
|
+
await dbService.getDb().delete(schema.${entityName}Schema).execute();
|
|
745
|
+
await deleteCreatedSignedUsers(userRepository);
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
it('fails if unauthenticated', async () => {
|
|
749
|
+
const req = await server.client.request('/${pluralKebab}', {
|
|
750
|
+
method: 'POST',
|
|
751
|
+
body: JSON.stringify({ /* TODO: Add valid payload */ }),
|
|
752
|
+
headers: { 'Content-Type': 'application/json' },
|
|
753
|
+
});
|
|
754
|
+
expect(req.status).toBe(401);
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
it('fails with fake generated ID for createdBy', async () => {
|
|
758
|
+
const { token } = await createOneSignedUpUser(userRepository, {
|
|
759
|
+
userType: UserTypeEnum.ADMIN,
|
|
760
|
+
role: UserRoleEnum.AUTHENTICATED,
|
|
761
|
+
});
|
|
762
|
+
|
|
763
|
+
// Attempting to forge createdBy should be overwritten by the backend Context Injection
|
|
764
|
+
const maliciousPayload = {
|
|
765
|
+
// TODO: Add required payload properties
|
|
766
|
+
createdBy: 'fake-malicious-id'
|
|
767
|
+
};
|
|
768
|
+
|
|
769
|
+
const req = await server.client.request('/${pluralKebab}', {
|
|
770
|
+
method: 'POST',
|
|
771
|
+
body: JSON.stringify(maliciousPayload),
|
|
772
|
+
headers: {
|
|
773
|
+
'Content-Type': 'application/json',
|
|
774
|
+
Authorization: \`Bearer \${token}\`,
|
|
775
|
+
},
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
expect(req.status).toBe(201);
|
|
779
|
+
const body: any = await req.json();
|
|
780
|
+
|
|
781
|
+
// Verify creation succeeded but createdBy was securely overwritten
|
|
782
|
+
expect(body.result.createdBy).not.toBe('fake-malicious-id');
|
|
783
|
+
|
|
784
|
+
// Direct DB check
|
|
785
|
+
const row = await dbService.getDb().query.${entityName}Schema.findFirst({
|
|
786
|
+
where: eq(schema.${entityName}Schema.id, body.result.id)
|
|
787
|
+
});
|
|
788
|
+
expect(row).toBeDefined();
|
|
789
|
+
expect(row!.createdBy).not.toBe('fake-malicious-id');
|
|
790
|
+
});
|
|
791
|
+
});
|
|
691
792
|
`;
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
793
|
+
await writeFile(join(useCaseDir, `Create${EntityName}.e2e.spec.ts`), testContent);
|
|
794
|
+
}
|
|
795
|
+
async function generateUpdateE2ETest(baseDir, config) {
|
|
796
|
+
const { EntityName, entityName, kebabName, pluralKebab } = config;
|
|
797
|
+
const useCaseDir = join(baseDir, `update-${kebabName}`);
|
|
798
|
+
const testContent = `import { afterAll, beforeAll, describe, expect, it } from 'bun:test';
|
|
799
|
+
import { EnvFileNames, UserRoleEnum, UserTypeEnum } from '@root/lib';
|
|
800
|
+
import { bootstrap, inject, Module } from '@kanian77/simple-di';
|
|
801
|
+
import { UseCaseModule } from '@root/use-case/UseCaseModule';
|
|
802
|
+
import { getConfigModule } from 'config/getConfigModule';
|
|
803
|
+
import { getDbModule } from 'db/getDbModule';
|
|
804
|
+
import { CoreModule } from '@root/core/CoreModule';
|
|
805
|
+
import { DB_SERVICE, type DbService } from 'db/DbService';
|
|
806
|
+
import { APP, AppModule } from '@root/AppModule';
|
|
807
|
+
import type { Hono } from 'hono';
|
|
808
|
+
import * as schema from '@root/schema';
|
|
809
|
+
import { eq } from 'drizzle-orm';
|
|
810
|
+
import { getTestServer, type TestServer } from '@root/lib/functions/test-related/getTestServer';
|
|
811
|
+
import { createOneSignedUpUser, deleteCreatedSignedUsers } from '@root/lib/functions/test-related/createSignedUpUser';
|
|
812
|
+
import { USER_REPOSITORY_INTERFACE, type UserRepository } from '@root/core/user/IUserRepository';
|
|
813
|
+
import { UserModule } from '@root/core/user/UserModule';
|
|
814
|
+
|
|
815
|
+
describe('Update${EntityName} e2e', () => {
|
|
816
|
+
let dbService: DbService;
|
|
817
|
+
let server: TestServer;
|
|
818
|
+
let app: Hono;
|
|
819
|
+
let targetId: string;
|
|
820
|
+
let userRepository: UserRepository;
|
|
821
|
+
|
|
822
|
+
beforeAll(async () => {
|
|
823
|
+
const TestModule = new Module({
|
|
824
|
+
imports: [
|
|
825
|
+
AppModule,
|
|
826
|
+
getConfigModule(EnvFileNames.TESTING),
|
|
827
|
+
getDbModule(),
|
|
828
|
+
CoreModule,
|
|
829
|
+
UserModule,
|
|
830
|
+
UseCaseModule,
|
|
831
|
+
],
|
|
832
|
+
});
|
|
833
|
+
bootstrap(TestModule);
|
|
834
|
+
dbService = inject<DbService>(DB_SERVICE);
|
|
835
|
+
app = inject<Hono>(APP);
|
|
836
|
+
userRepository = inject(USER_REPOSITORY_INTERFACE);
|
|
837
|
+
server = await getTestServer(app);
|
|
838
|
+
|
|
839
|
+
// TODO: Seed a target entity here and save targetId
|
|
840
|
+
});
|
|
841
|
+
|
|
842
|
+
afterAll(async () => {
|
|
843
|
+
await dbService.getDb().delete(schema.${entityName}Schema).execute();
|
|
844
|
+
await deleteCreatedSignedUsers(userRepository);
|
|
845
|
+
});
|
|
846
|
+
|
|
847
|
+
it('fails if unauthenticated', async () => {
|
|
848
|
+
const req = await server.client.request(\`/\${pluralKebab}/\${targetId}\`, {
|
|
849
|
+
method: 'PUT',
|
|
850
|
+
body: JSON.stringify({ /* TODO: valid update payload */ }),
|
|
851
|
+
headers: { 'Content-Type': 'application/json' },
|
|
852
|
+
});
|
|
853
|
+
expect(req.status).toBe(401);
|
|
854
|
+
});
|
|
855
|
+
|
|
856
|
+
it('successfully updates and overrides updatedBy context', async () => {
|
|
857
|
+
const { token } = await createOneSignedUpUser(userRepository, {
|
|
858
|
+
userType: UserTypeEnum.ADMIN,
|
|
859
|
+
role: UserRoleEnum.AUTHENTICATED,
|
|
860
|
+
});
|
|
861
|
+
|
|
862
|
+
const payload = {
|
|
863
|
+
// TODO: Add properties to update
|
|
864
|
+
updatedBy: 'hacker-id' // Should be discarded
|
|
865
|
+
};
|
|
866
|
+
|
|
867
|
+
const req = await server.client.request(\`/\${pluralKebab}/\${targetId}\`, {
|
|
868
|
+
method: 'PUT',
|
|
869
|
+
body: JSON.stringify(payload),
|
|
870
|
+
headers: {
|
|
871
|
+
'Content-Type': 'application/json',
|
|
872
|
+
Authorization: \`Bearer \${token}\`,
|
|
873
|
+
},
|
|
874
|
+
});
|
|
875
|
+
|
|
876
|
+
expect(req.status).toBe(200);
|
|
877
|
+
const body: any = await req.json();
|
|
878
|
+
|
|
879
|
+
expect(body.result.updatedBy).not.toBe('hacker-id');
|
|
880
|
+
|
|
881
|
+
const row = await dbService.getDb().query.${entityName}Schema.findFirst({
|
|
882
|
+
where: eq(schema.${entityName}Schema.id, targetId)
|
|
883
|
+
});
|
|
884
|
+
expect(row!.updatedBy).not.toBe('hacker-id');
|
|
885
|
+
});
|
|
886
|
+
});
|
|
887
|
+
`;
|
|
888
|
+
await writeFile(join(useCaseDir, `Update${EntityName}.e2e.spec.ts`), testContent);
|
|
889
|
+
}
|
|
890
|
+
async function generateGetE2ETest(baseDir, config) {
|
|
891
|
+
const { EntityName, entityName, kebabName, pluralKebab } = config;
|
|
892
|
+
const useCaseDir = join(baseDir, `get-${kebabName}`);
|
|
893
|
+
const testContent = `import { afterAll, beforeAll, describe, expect, it } from 'bun:test';
|
|
894
|
+
import { EnvFileNames, UserRoleEnum, UserTypeEnum } from '@root/lib';
|
|
895
|
+
import { bootstrap, inject, Module } from '@kanian77/simple-di';
|
|
896
|
+
import { UseCaseModule } from '@root/use-case/UseCaseModule';
|
|
897
|
+
import { getConfigModule } from 'config/getConfigModule';
|
|
898
|
+
import { getDbModule } from 'db/getDbModule';
|
|
899
|
+
import { CoreModule } from '@root/core/CoreModule';
|
|
900
|
+
import { DB_SERVICE, type DbService } from 'db/DbService';
|
|
901
|
+
import { APP, AppModule } from '@root/AppModule';
|
|
902
|
+
import type { Hono } from 'hono';
|
|
903
|
+
import * as schema from '@root/schema';
|
|
904
|
+
import { getTestServer, type TestServer } from '@root/lib/functions/test-related/getTestServer';
|
|
905
|
+
import { createOneSignedUpUser, deleteCreatedSignedUsers } from '@root/lib/functions/test-related/createSignedUpUser';
|
|
906
|
+
import { USER_REPOSITORY_INTERFACE, type UserRepository } from '@root/core/user/IUserRepository';
|
|
907
|
+
import { UserModule } from '@root/core/user/UserModule';
|
|
908
|
+
|
|
909
|
+
describe('Get${EntityName} e2e', () => {
|
|
910
|
+
let dbService: DbService;
|
|
911
|
+
let server: TestServer;
|
|
912
|
+
let app: Hono;
|
|
913
|
+
let targetId: string;
|
|
914
|
+
let userRepository: UserRepository;
|
|
915
|
+
|
|
916
|
+
beforeAll(async () => {
|
|
917
|
+
const TestModule = new Module({
|
|
918
|
+
imports: [
|
|
919
|
+
AppModule,
|
|
920
|
+
getConfigModule(EnvFileNames.TESTING),
|
|
921
|
+
getDbModule(),
|
|
922
|
+
CoreModule,
|
|
923
|
+
UserModule,
|
|
924
|
+
UseCaseModule,
|
|
925
|
+
],
|
|
926
|
+
});
|
|
927
|
+
bootstrap(TestModule);
|
|
928
|
+
dbService = inject<DbService>(DB_SERVICE);
|
|
929
|
+
app = inject<Hono>(APP);
|
|
930
|
+
userRepository = inject(USER_REPOSITORY_INTERFACE);
|
|
931
|
+
server = await getTestServer(app);
|
|
932
|
+
|
|
933
|
+
// TODO: Seed an entity here and assign targetId
|
|
934
|
+
});
|
|
935
|
+
|
|
936
|
+
afterAll(async () => {
|
|
937
|
+
await dbService.getDb().delete(schema.${entityName}Schema).execute();
|
|
938
|
+
await deleteCreatedSignedUsers(userRepository);
|
|
939
|
+
});
|
|
940
|
+
|
|
941
|
+
it('fails if unauthenticated (assuming route requires auth)', async () => {
|
|
942
|
+
// Note: Adjust depending on whether the GET route should be public or private
|
|
943
|
+
// const req = await server.client.request(\`/\${pluralKebab}/\${targetId}\`);
|
|
944
|
+
// expect(req.status).toBe(401);
|
|
945
|
+
});
|
|
946
|
+
|
|
947
|
+
it('retrieves the entity', async () => {
|
|
948
|
+
const { token } = await createOneSignedUpUser(userRepository, {
|
|
949
|
+
userType: UserTypeEnum.ADMIN,
|
|
950
|
+
role: UserRoleEnum.AUTHENTICATED,
|
|
951
|
+
});
|
|
952
|
+
|
|
953
|
+
const req = await server.client.request(\`/\${pluralKebab}/\${targetId}\`, {
|
|
954
|
+
method: 'GET',
|
|
955
|
+
headers: {
|
|
956
|
+
Authorization: \`Bearer \${token}\`,
|
|
957
|
+
},
|
|
958
|
+
});
|
|
959
|
+
|
|
960
|
+
expect(req.status).toBe(200);
|
|
961
|
+
const body: any = await req.json();
|
|
962
|
+
expect(body.result.id).toBe(targetId);
|
|
963
|
+
});
|
|
964
|
+
});
|
|
965
|
+
`;
|
|
966
|
+
await writeFile(join(useCaseDir, `Get${EntityName}.e2e.spec.ts`), testContent);
|
|
967
|
+
}
|
|
968
|
+
async function generateListE2ETest(baseDir, config) {
|
|
969
|
+
const { EntityName, entityName, kebabName, pluralKebab, pluralPascal } = config;
|
|
970
|
+
const useCaseDir = join(baseDir, `list-${pluralKebab}`);
|
|
971
|
+
const testContent = `import { afterAll, beforeAll, describe, expect, it } from 'bun:test';
|
|
972
|
+
import { EnvFileNames, UserRoleEnum, UserTypeEnum } from '@root/lib';
|
|
973
|
+
import { bootstrap, inject, Module } from '@kanian77/simple-di';
|
|
974
|
+
import { UseCaseModule } from '@root/use-case/UseCaseModule';
|
|
975
|
+
import { getConfigModule } from 'config/getConfigModule';
|
|
976
|
+
import { getDbModule } from 'db/getDbModule';
|
|
977
|
+
import { CoreModule } from '@root/core/CoreModule';
|
|
978
|
+
import { DB_SERVICE, type DbService } from 'db/DbService';
|
|
979
|
+
import { APP, AppModule } from '@root/AppModule';
|
|
980
|
+
import type { Hono } from 'hono';
|
|
981
|
+
import * as schema from '@root/schema';
|
|
982
|
+
import { getTestServer, type TestServer } from '@root/lib/functions/test-related/getTestServer';
|
|
983
|
+
import { createOneSignedUpUser, deleteCreatedSignedUsers } from '@root/lib/functions/test-related/createSignedUpUser';
|
|
984
|
+
import { USER_REPOSITORY_INTERFACE, type UserRepository } from '@root/core/user/IUserRepository';
|
|
985
|
+
import { UserModule } from '@root/core/user/UserModule';
|
|
986
|
+
|
|
987
|
+
describe('List${pluralPascal} e2e', () => {
|
|
988
|
+
let dbService: DbService;
|
|
989
|
+
let server: TestServer;
|
|
990
|
+
let app: Hono;
|
|
991
|
+
let userRepository: UserRepository;
|
|
992
|
+
|
|
993
|
+
beforeAll(async () => {
|
|
994
|
+
const TestModule = new Module({
|
|
995
|
+
imports: [
|
|
996
|
+
AppModule,
|
|
997
|
+
getConfigModule(EnvFileNames.TESTING),
|
|
998
|
+
getDbModule(),
|
|
999
|
+
CoreModule,
|
|
1000
|
+
UserModule,
|
|
1001
|
+
UseCaseModule,
|
|
1002
|
+
],
|
|
1003
|
+
});
|
|
1004
|
+
bootstrap(TestModule);
|
|
1005
|
+
dbService = inject<DbService>(DB_SERVICE);
|
|
1006
|
+
app = inject<Hono>(APP);
|
|
1007
|
+
userRepository = inject(USER_REPOSITORY_INTERFACE);
|
|
1008
|
+
server = await getTestServer(app);
|
|
1009
|
+
|
|
1010
|
+
// TODO: Seed multiple entities here
|
|
1011
|
+
});
|
|
1012
|
+
|
|
1013
|
+
afterAll(async () => {
|
|
1014
|
+
await dbService.getDb().delete(schema.${entityName}Schema).execute();
|
|
1015
|
+
await deleteCreatedSignedUsers(userRepository);
|
|
1016
|
+
});
|
|
1017
|
+
|
|
1018
|
+
it('lists entities successfully', async () => {
|
|
1019
|
+
const { token } = await createOneSignedUpUser(userRepository, {
|
|
1020
|
+
userType: UserTypeEnum.ADMIN,
|
|
1021
|
+
role: UserRoleEnum.AUTHENTICATED,
|
|
1022
|
+
});
|
|
1023
|
+
|
|
1024
|
+
const req = await server.client.request('/${pluralKebab}', {
|
|
1025
|
+
method: 'GET',
|
|
1026
|
+
headers: {
|
|
1027
|
+
Authorization: \`Bearer \${token}\`,
|
|
1028
|
+
},
|
|
1029
|
+
});
|
|
1030
|
+
|
|
1031
|
+
expect(req.status).toBe(200);
|
|
1032
|
+
const body: any = await req.json();
|
|
1033
|
+
expect(Array.isArray(body.result)).toBe(true);
|
|
1034
|
+
});
|
|
1035
|
+
});
|
|
1036
|
+
`;
|
|
1037
|
+
await writeFile(join(useCaseDir, `List${pluralPascal}.e2e.spec.ts`), testContent);
|
|
1038
|
+
}
|
|
1039
|
+
async function generateDeleteE2ETest(baseDir, config) {
|
|
1040
|
+
const { EntityName, entityName, kebabName, pluralKebab } = config;
|
|
1041
|
+
const useCaseDir = join(baseDir, `delete-${kebabName}`);
|
|
1042
|
+
const testContent = `import { afterAll, beforeAll, describe, expect, it } from 'bun:test';
|
|
1043
|
+
import { EnvFileNames, UserRoleEnum, UserTypeEnum } from '@root/lib';
|
|
1044
|
+
import { bootstrap, inject, Module } from '@kanian77/simple-di';
|
|
1045
|
+
import { UseCaseModule } from '@root/use-case/UseCaseModule';
|
|
1046
|
+
import { getConfigModule } from 'config/getConfigModule';
|
|
1047
|
+
import { getDbModule } from 'db/getDbModule';
|
|
1048
|
+
import { CoreModule } from '@root/core/CoreModule';
|
|
1049
|
+
import { DB_SERVICE, type DbService } from 'db/DbService';
|
|
1050
|
+
import { APP, AppModule } from '@root/AppModule';
|
|
1051
|
+
import type { Hono } from 'hono';
|
|
1052
|
+
import * as schema from '@root/schema';
|
|
1053
|
+
import { eq } from 'drizzle-orm';
|
|
1054
|
+
import { getTestServer, type TestServer } from '@root/lib/functions/test-related/getTestServer';
|
|
1055
|
+
import { createOneSignedUpUser, deleteCreatedSignedUsers } from '@root/lib/functions/test-related/createSignedUpUser';
|
|
1056
|
+
import { USER_REPOSITORY_INTERFACE, type UserRepository } from '@root/core/user/IUserRepository';
|
|
1057
|
+
import { UserModule } from '@root/core/user/UserModule';
|
|
1058
|
+
|
|
1059
|
+
describe('Delete${EntityName} e2e', () => {
|
|
1060
|
+
let dbService: DbService;
|
|
1061
|
+
let server: TestServer;
|
|
1062
|
+
let app: Hono;
|
|
1063
|
+
let targetIdSoft: string;
|
|
1064
|
+
let targetIdHard: string;
|
|
1065
|
+
let userRepository: UserRepository;
|
|
1066
|
+
|
|
1067
|
+
beforeAll(async () => {
|
|
1068
|
+
const TestModule = new Module({
|
|
1069
|
+
imports: [
|
|
1070
|
+
AppModule,
|
|
1071
|
+
getConfigModule(EnvFileNames.TESTING),
|
|
1072
|
+
getDbModule(),
|
|
1073
|
+
CoreModule,
|
|
1074
|
+
UserModule,
|
|
1075
|
+
UseCaseModule,
|
|
1076
|
+
],
|
|
1077
|
+
});
|
|
1078
|
+
bootstrap(TestModule);
|
|
1079
|
+
dbService = inject<DbService>(DB_SERVICE);
|
|
1080
|
+
app = inject<Hono>(APP);
|
|
1081
|
+
userRepository = inject(USER_REPOSITORY_INTERFACE);
|
|
1082
|
+
server = await getTestServer(app);
|
|
1083
|
+
|
|
1084
|
+
// TODO: Seed two entities, assign to targetIdSoft and targetIdHard
|
|
1085
|
+
});
|
|
1086
|
+
|
|
1087
|
+
afterAll(async () => {
|
|
1088
|
+
await dbService.getDb().delete(schema.${entityName}Schema).execute();
|
|
1089
|
+
});
|
|
1090
|
+
|
|
1091
|
+
it('fails if unauthenticated', async () => {
|
|
1092
|
+
const req = await server.client.request(\`/\${pluralKebab}/\${targetIdSoft}\`, {
|
|
1093
|
+
method: 'DELETE',
|
|
1094
|
+
});
|
|
1095
|
+
expect(req.status).toBe(401);
|
|
1096
|
+
});
|
|
1097
|
+
|
|
1098
|
+
it('soft deletes by default', async () => {
|
|
1099
|
+
const { token } = await createOneSignedUpUser(userRepository, {
|
|
1100
|
+
userType: UserTypeEnum.ADMIN,
|
|
1101
|
+
role: UserRoleEnum.AUTHENTICATED,
|
|
1102
|
+
});
|
|
1103
|
+
|
|
1104
|
+
const req = await server.client.request(\`/\${pluralKebab}/\${targetIdSoft}\`, {
|
|
1105
|
+
method: 'DELETE',
|
|
1106
|
+
headers: {
|
|
1107
|
+
Authorization: \`Bearer \${token}\`,
|
|
1108
|
+
},
|
|
1109
|
+
});
|
|
1110
|
+
|
|
1111
|
+
expect(req.status).toBe(200);
|
|
1112
|
+
|
|
1113
|
+
// Direct DB check for soft delete
|
|
1114
|
+
const row = await dbService.getDb().query.${entityName}Schema.findFirst({
|
|
1115
|
+
where: eq(schema.${entityName}Schema.id, targetIdSoft)
|
|
1116
|
+
});
|
|
1117
|
+
expect(row).toBeDefined();
|
|
1118
|
+
expect(row!.deleted).toBe(true);
|
|
1119
|
+
expect(row!.deletedAt).not.toBeNull();
|
|
1120
|
+
});
|
|
1121
|
+
|
|
1122
|
+
it('hard deletes when flag is false', async () => {
|
|
1123
|
+
const { token } = await createOneSignedUpUser(userRepository, {
|
|
1124
|
+
userType: UserTypeEnum.ADMIN,
|
|
1125
|
+
role: UserRoleEnum.AUTHENTICATED,
|
|
1126
|
+
});
|
|
1127
|
+
|
|
1128
|
+
const req = await server.client.request(\`/\${pluralKebab}/\${targetIdHard}\`, {
|
|
1129
|
+
method: 'DELETE',
|
|
1130
|
+
body: JSON.stringify({ softDelete: false }),
|
|
1131
|
+
headers: {
|
|
1132
|
+
'Content-Type': 'application/json',
|
|
1133
|
+
Authorization: \`Bearer \${token}\`,
|
|
1134
|
+
},
|
|
1135
|
+
});
|
|
1136
|
+
|
|
1137
|
+
expect(req.status).toBe(200);
|
|
1138
|
+
|
|
1139
|
+
// Direct DB check for physical row missing
|
|
1140
|
+
const row = await dbService.getDb().query.${entityName}Schema.findFirst({
|
|
1141
|
+
where: eq(schema.${entityName}Schema.id, targetIdHard)
|
|
1142
|
+
});
|
|
1143
|
+
expect(row).toBeUndefined();
|
|
1144
|
+
});
|
|
1145
|
+
});
|
|
1146
|
+
`;
|
|
1147
|
+
await writeFile(join(useCaseDir, `Delete${EntityName}.e2e.spec.ts`), testContent);
|
|
708
1148
|
}
|
|
1149
|
+
// END ROUTES
|
|
709
1150
|
//# sourceMappingURL=generate_crud_use_cases.js.map
|