@lenne.tech/cli 1.2.0 → 1.3.0

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 (36) hide show
  1. package/build/commands/claude/install-plugin.js +339 -0
  2. package/package.json +1 -1
  3. package/build/commands/claude/install-commands.js +0 -337
  4. package/build/commands/claude/install-mcps.js +0 -258
  5. package/build/commands/claude/install-skills.js +0 -693
  6. package/build/lib/mcp-registry.js +0 -80
  7. package/build/templates/claude-commands/code-cleanup.md +0 -82
  8. package/build/templates/claude-commands/commit-message.md +0 -21
  9. package/build/templates/claude-commands/create-story.md +0 -435
  10. package/build/templates/claude-commands/mr-description-clipboard.md +0 -48
  11. package/build/templates/claude-commands/mr-description.md +0 -33
  12. package/build/templates/claude-commands/sec-review.md +0 -62
  13. package/build/templates/claude-commands/skill-optimize.md +0 -481
  14. package/build/templates/claude-commands/test-generate.md +0 -45
  15. package/build/templates/claude-skills/building-stories-with-tdd/SKILL.md +0 -265
  16. package/build/templates/claude-skills/building-stories-with-tdd/code-quality.md +0 -276
  17. package/build/templates/claude-skills/building-stories-with-tdd/database-indexes.md +0 -182
  18. package/build/templates/claude-skills/building-stories-with-tdd/examples.md +0 -1383
  19. package/build/templates/claude-skills/building-stories-with-tdd/handling-existing-tests.md +0 -197
  20. package/build/templates/claude-skills/building-stories-with-tdd/reference.md +0 -1427
  21. package/build/templates/claude-skills/building-stories-with-tdd/security-review.md +0 -307
  22. package/build/templates/claude-skills/building-stories-with-tdd/workflow.md +0 -1004
  23. package/build/templates/claude-skills/generating-nest-servers/SKILL.md +0 -303
  24. package/build/templates/claude-skills/generating-nest-servers/configuration.md +0 -285
  25. package/build/templates/claude-skills/generating-nest-servers/declare-keyword-warning.md +0 -133
  26. package/build/templates/claude-skills/generating-nest-servers/description-management.md +0 -226
  27. package/build/templates/claude-skills/generating-nest-servers/examples.md +0 -893
  28. package/build/templates/claude-skills/generating-nest-servers/framework-guide.md +0 -259
  29. package/build/templates/claude-skills/generating-nest-servers/quality-review.md +0 -864
  30. package/build/templates/claude-skills/generating-nest-servers/reference.md +0 -487
  31. package/build/templates/claude-skills/generating-nest-servers/security-rules.md +0 -371
  32. package/build/templates/claude-skills/generating-nest-servers/verification-checklist.md +0 -262
  33. package/build/templates/claude-skills/generating-nest-servers/workflow-process.md +0 -1061
  34. package/build/templates/claude-skills/using-lt-cli/SKILL.md +0 -284
  35. package/build/templates/claude-skills/using-lt-cli/examples.md +0 -546
  36. package/build/templates/claude-skills/using-lt-cli/reference.md +0 -513
@@ -1,1427 +0,0 @@
1
- ---
2
- name: story-tdd-reference
3
- version: 1.0.1
4
- description: Quick reference guide for Test-Driven Development workflow
5
- ---
6
-
7
- # Story-Based TDD Quick Reference
8
-
9
- ## Table of Contents
10
- - [The 7-Step Workflow](#the-7-step-workflow)
11
- - [Commands Cheatsheet](#commands-cheatsheet)
12
- - [Test File Organization](#test-file-organization)
13
- - [Test File Template](#test-file-template)
14
- - [Database Indexes with @UnifiedField](#database-indexes-with-unifiedfield)
15
- - [REST API Testing Patterns](#rest-api-testing-patterns-using-testhelper)
16
- - [GraphQL Testing Patterns](#graphql-testing-patterns-using-testhelper)
17
- - [Common Test Assertions](#common-test-assertions)
18
- - [ObjectId Conversion Utilities](#objectid-conversion-utilities)
19
- - [Security Testing Checklist](#security-testing-checklist)
20
- - [When to Ask Developer](#when-to-ask-developer)
21
- - [Debugging Failed Tests](#debugging-failed-tests)
22
- - [Decision Tree: Test Failure Analysis](#decision-tree-test-failure-analysis)
23
- - [Code Quality, Security & Refactoring Check](#code-quality-security--refactoring-check)
24
- - [Final Report Template](#final-report-template)
25
- - [Handling Existing Tests](#-handling-existing-tests)
26
- - [CRITICAL: Git Commits](#-critical-git-commits)
27
- - [CRITICAL: Database Cleanup & Test Isolation](#-critical-database-cleanup--test-isolation)
28
- - [User Authentication: signUp vs signIn](#user-authentication-signup-vs-signin)
29
- - [Avoiding Test Interdependencies](#avoiding-test-interdependencies)
30
- - [Async/Await Best Practices](#asyncawait-best-practices)
31
-
32
- ## The 7-Step Workflow
33
-
34
- ```
35
- ┌─────────────────────────────────────────────────────────┐
36
- │ Step 1: Analyze Story & Clarify │
37
- │ - Read requirements thoroughly │
38
- │ - Check existing API structure │
39
- │ - Identify contradictions │
40
- │ - ASK DEVELOPER if anything unclear │
41
- └─────────────────────────────────────────────────────────┘
42
-
43
- ┌─────────────────────────────────────────────────────────┐
44
- │ Step 2: Create Story Test │
45
- │ - Location: tests/stories/feature-name.story.test.ts │
46
- │ - Study existing test patterns │
47
- │ - Write comprehensive test scenarios │
48
- │ - Cover happy path, errors, edge cases │
49
- └─────────────────────────────────────────────────────────┘
50
-
51
- ┌─────────────────────────────────────────────────────────┐
52
- │ Step 3: Run Tests & Analyze │
53
- │ - npm test │
54
- │ - Record failures and reasons │
55
- │ - Decide: Test bug OR Missing implementation │
56
- └─────────────────────────────────────────────────────────┘
57
-
58
- ┌──────┴──────┐
59
- │ │
60
- ┌──────────▼─┐ ┌───▼────────────┐
61
- │ Step 3a: │ │ Step 4: │
62
- │ Fix Test │ │ Implement Code │
63
- │ Errors │ │ (Use nest- │
64
- │ │ │ server- │
65
- │ │ │ generator) │
66
- └──────┬─────┘ └───┬────────────┘
67
- │ │
68
- └────────┬────────┘
69
-
70
- ┌─────────────────────────────────────────────────────────┐
71
- │ Step 5: Validate │
72
- │ - Run ALL tests │
73
- │ - All pass? → Go to Step 5a │
74
- │ - Some fail? → Back to Step 3 │
75
- └─────────────────────────────────────────────────────────┘
76
-
77
- ┌─────────────────────────────────────────────────────────┐
78
- │ Step 5a: Code Quality, Security & Refactoring Check │
79
- │ - Check for code duplication │
80
- │ - Extract common functionality │
81
- │ - Consolidate similar code paths │
82
- │ - Review for consistency │
83
- │ - Check database indexes │
84
- │ - 🔐 SECURITY REVIEW (CRITICAL) │
85
- │ - Run tests after refactoring │
86
- └─────────────────────────────────────────────────────────┘
87
-
88
- ┌─────────────────────────────────────────────────────────┐
89
- │ Step 5b: Final Validation │
90
- │ - Run ALL tests one final time │
91
- │ - Generate report → DONE! 🎉 │
92
- └─────────────────────────────────────────────────────────┘
93
- ```
94
-
95
- ## Commands Cheatsheet
96
-
97
- ### Running Tests
98
-
99
- ```bash
100
- # Run all tests
101
- npm test
102
-
103
- # Run specific story test
104
- npm test -- tests/stories/feature-name.story.test.ts
105
-
106
- # Run tests with coverage
107
- npm run test:cov
108
-
109
- # Run tests in watch mode
110
- npm run test:watch
111
- ```
112
-
113
- ### Using nest-server-generator Skill
114
-
115
- ```bash
116
- # Create module
117
- lt server module ModuleName --no-interactive
118
-
119
- # Create object
120
- lt server object ObjectName --no-interactive
121
-
122
- # Add property
123
- lt server addProp ModuleName propertyName:type --no-interactive
124
-
125
- # Examples:
126
- lt server module Review --no-interactive
127
- lt server addProp Review rating:number --no-interactive
128
- lt server addProp Review comment:string? --no-interactive
129
- ```
130
-
131
- ## Test File Organization
132
-
133
- ### Structuring Tests with Subfolders
134
-
135
- When many test files accumulate in `tests/stories/`, consider organizing them into subfolders for better clarity:
136
-
137
- **✅ DO use subfolders when:**
138
- - Multiple tests can be logically grouped (e.g., by feature, module, or domain)
139
- - Each subfolder contains at least 3-5 related test files
140
- - The grouping improves discoverability and navigation
141
-
142
- **❌ DON'T use subfolders when:**
143
- - Only 1-2 files would end up in each subfolder (defeats the purpose)
144
- - The grouping is arbitrary or unclear
145
- - Tests are already easy to find
146
-
147
- **Example folder structure:**
148
-
149
- ```
150
- tests/stories/
151
- ├── user-management/ # ✅ Good: 4 related tests
152
- │ ├── user-registration.story.test.ts
153
- │ ├── user-profile.story.test.ts
154
- │ ├── user-roles.story.test.ts
155
- │ └── user-deletion.story.test.ts
156
- ├── orders/ # ✅ Good: 3 related tests
157
- │ ├── order-creation.story.test.ts
158
- │ ├── order-fulfillment.story.test.ts
159
- │ └── order-cancellation.story.test.ts
160
- ├── auth/ # ❌ Bad: Only 1 file, should stay in root
161
- │ └── login.story.test.ts
162
- └── simple-feature.story.test.ts # ✅ OK: Single file stays in root
163
- ```
164
-
165
- **Rule of thumb:** If you can't fill a subfolder with at least 3 thematically related test files, keep them in the root `tests/stories/` directory.
166
-
167
- ---
168
-
169
- ## Test File Template
170
-
171
- ```typescript
172
- import {
173
- ConfigService,
174
- HttpExceptionLogFilter,
175
- TestGraphQLType,
176
- TestHelper,
177
- } from '@lenne.tech/nest-server';
178
- import { Test, TestingModule } from '@nestjs/testing';
179
- import { PubSub } from 'graphql-subscriptions';
180
- import { MongoClient, ObjectId } from 'mongodb';
181
-
182
- import envConfig from '../../src/config.env';
183
- import { RoleEnum } from '../../src/server/common/enums/role.enum';
184
- import { imports, ServerModule } from '../../src/server/server.module';
185
-
186
- // ⚠️ IMPORTANT: Do NOT import Services!
187
- // Tests must ONLY use API endpoints via TestHelper.
188
- // Services are accessed indirectly through Controllers/Resolvers.
189
-
190
- describe('[Feature Name] Story', () => {
191
- // Test environment properties
192
- let app;
193
- let testHelper: TestHelper;
194
-
195
- // Database (only for setup/cleanup and setting roles/verified status)
196
- let connection;
197
- let db;
198
-
199
- // Global test data
200
- let gUserToken: string;
201
- let gUserId: string;
202
-
203
- // Track created entities for cleanup
204
- let createdEntityIds: string[] = [];
205
-
206
- beforeAll(async () => {
207
- // Start server for testing
208
- const moduleFixture: TestingModule = await Test.createTestingModule({
209
- imports: [...imports, ServerModule],
210
- providers: [
211
- {
212
- provide: 'PUB_SUB',
213
- useValue: new PubSub(),
214
- },
215
- ],
216
- }).compile();
217
-
218
- app = moduleFixture.createNestApplication();
219
- app.useGlobalFilters(new HttpExceptionLogFilter());
220
- app.setBaseViewsDir(envConfig.templates.path);
221
- app.setViewEngine(envConfig.templates.engine);
222
- await app.init();
223
-
224
- testHelper = new TestHelper(app);
225
-
226
- // Connection to database (ONLY for cleanup and setting roles/verified)
227
- connection = await MongoClient.connect(envConfig.mongoose.uri);
228
- db = await connection.db();
229
-
230
- // 🚨 CRITICAL: Create test user with @test.com email
231
- const password = Math.random().toString(36).substring(7);
232
- // ✅ MUST end with @test.com for e2e.brevo.exclude filtering
233
- // Use timestamp + random suffix for guaranteed uniqueness
234
- const email = `test-${Date.now()}-${Math.random().toString(36).substring(2, 8)}@test.com`;
235
- const signUp = await testHelper.graphQl({
236
- arguments: {
237
- input: {
238
- email,
239
- firstName: 'Test',
240
- password,
241
- },
242
- },
243
- fields: ['token', { user: ['id', 'email'] }],
244
- name: 'signUp',
245
- type: TestGraphQLType.MUTATION,
246
- });
247
- gUserId = signUp.user.id;
248
- gUserToken = signUp.token;
249
- });
250
-
251
- afterAll(async () => {
252
- // 🧹 CLEANUP: Delete all test data created during tests
253
- try {
254
- if (createdEntityIds.length > 0) {
255
- await db.collection('entities').deleteMany({
256
- _id: { $in: createdEntityIds.map(id => new ObjectId(id)) }
257
- });
258
- }
259
-
260
- // Delete test user
261
- if (gUserId) {
262
- await db.collection('users').deleteOne({ _id: new ObjectId(gUserId) });
263
- }
264
- } catch (error) {
265
- console.error('Cleanup failed:', error);
266
- }
267
-
268
- await connection.close();
269
- await app.close();
270
- });
271
-
272
- describe('Happy Path', () => {
273
- it('should [expected behavior]', async () => {
274
- // Arrange
275
- // 🚨 IMPORTANT: Make data unique per test file to avoid conflicts
276
- const data = {
277
- email: `entity-feature-test-${Date.now()}-${Math.random().toString(36).substring(2, 8)}@test.com`, // ✅ @test.com + unique
278
- name: `Entity-FeatureTest-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`, // ✅ Unique per test file
279
- };
280
-
281
- // Act - Using REST
282
- const result = await testHelper.rest('/api/endpoint', {
283
- method: 'POST',
284
- payload: data,
285
- token: gUserToken,
286
- });
287
-
288
- // Assert
289
- expect(result).toMatchObject({
290
- // expected properties
291
- });
292
-
293
- // ✅ Track for cleanup (CRITICAL for parallel-safe tests)
294
- createdEntityIds.push(result.id);
295
- });
296
- });
297
-
298
- describe('Error Cases', () => {
299
- it('should reject invalid input', async () => {
300
- await testHelper.rest('/api/endpoint', {
301
- method: 'POST',
302
- payload: { /* invalid data */ },
303
- statusCode: 400,
304
- token: gUserToken,
305
- });
306
- });
307
-
308
- it('should require authentication', async () => {
309
- await testHelper.rest('/api/endpoint', {
310
- method: 'POST',
311
- payload: { /* data */ },
312
- statusCode: 401,
313
- });
314
- });
315
- });
316
- });
317
- ```
318
-
319
- ## Database Indexes with @UnifiedField
320
-
321
- ### When to Add Indexes
322
-
323
- **🔍 ALWAYS define indexes in @UnifiedField decorator via mongoose option!**
324
-
325
- ```typescript
326
- // ✅ CORRECT: Index in decorator mongoose option
327
- @UnifiedField({
328
- description: 'User email',
329
- mongoose: { index: true, unique: true, type: String }
330
- })
331
- email: string;
332
-
333
- // ❌ WRONG: Separate schema index (hard to find)
334
- UserSchema.index({ email: 1 }, { unique: true });
335
- ```
336
-
337
- ### Common Index Patterns
338
-
339
- **Single Field Index:**
340
- ```typescript
341
- @UnifiedField({
342
- description: 'Product category',
343
- mongoose: { index: true, type: String } // For queries like: find({ category: 'electronics' })
344
- })
345
- category: string;
346
- ```
347
-
348
- **Unique Index:**
349
- ```typescript
350
- @UnifiedField({
351
- description: 'Username',
352
- mongoose: { index: true, unique: true, type: String } // Prevents duplicates
353
- })
354
- username: string;
355
- ```
356
-
357
- **Foreign Key Index:**
358
- ```typescript
359
- @UnifiedField({
360
- description: 'User who created this',
361
- mongoose: { index: true, type: String } // For JOIN/population operations
362
- })
363
- createdBy: string;
364
- ```
365
-
366
- **Multiple Indexed Fields:**
367
- ```typescript
368
- @UnifiedField({
369
- description: 'Customer reference',
370
- mongoose: { index: true, type: String } // Indexed individually
371
- })
372
- customerId: string;
373
-
374
- @UnifiedField({
375
- description: 'Order status',
376
- mongoose: { index: true, type: String } // Indexed individually
377
- })
378
- status: string;
379
-
380
- // Both indexed for flexible querying
381
- ```
382
-
383
- **Text Search Index:**
384
- ```typescript
385
- @UnifiedField({
386
- description: 'Product name',
387
- mongoose: { type: String, text: true } // For full-text search
388
- })
389
- name: string;
390
- ```
391
-
392
- ### Index Checklist
393
-
394
- Before marking complete, verify:
395
-
396
- - [ ] Fields used in `find()` queries have indexes
397
- - [ ] Foreign keys (userId, productId, etc.) have indexes
398
- - [ ] Unique fields (email, username) marked with `unique: true`
399
- - [ ] Fields used in sorting have indexes
400
- - [ ] Compound queries use compound indexes
401
- - [ ] All indexes in @UnifiedField decorator (NOT separate schema)
402
-
403
- ## REST API Testing Patterns (using TestHelper)
404
-
405
- **🔍 IMPORTANT: Before writing tests, read the TestHelper source file to understand all available features:**
406
-
407
- ```
408
- node_modules/@lenne.tech/nest-server/src/test/test.helper.ts
409
- ```
410
-
411
- This file documents all TestHelper capabilities including:
412
- - File uploads via `attachments` option
413
- - Debugging with `log` and `logError` options in `TestRestOptions`
414
- - Custom headers, status code validation, and more
415
-
416
- ```typescript
417
- // GET request
418
- const result = await testHelper.rest('/api/resource/123', {
419
- token: userToken,
420
- });
421
-
422
- // GET request (public endpoint, no auth)
423
- const result = await testHelper.rest('/api/public');
424
-
425
- // POST request
426
- const result = await testHelper.rest('/api/resource', {
427
- method: 'POST',
428
- payload: data,
429
- token: userToken,
430
- });
431
-
432
- // PUT request
433
- const result = await testHelper.rest('/api/resource/123', {
434
- method: 'PUT',
435
- payload: updates,
436
- token: userToken,
437
- });
438
-
439
- // DELETE request
440
- const result = await testHelper.rest('/api/resource/123', {
441
- method: 'DELETE',
442
- token: userToken,
443
- });
444
-
445
- // Expect specific status code
446
- await testHelper.rest('/api/resource', {
447
- method: 'POST',
448
- payload: invalidData,
449
- statusCode: 400,
450
- token: userToken,
451
- });
452
-
453
- // With custom headers
454
- const result = await testHelper.rest('/api/resource', {
455
- headers: {
456
- 'Content-Type': 'application/json',
457
- 'X-Custom-Header': 'value',
458
- },
459
- token: userToken,
460
- });
461
-
462
- // File upload via attachments
463
- const result = await testHelper.rest('/api/upload', {
464
- method: 'POST',
465
- attachments: [
466
- { name: 'file', path: '/path/to/file.pdf' },
467
- { name: 'image', path: '/path/to/image.png' },
468
- ],
469
- token: userToken,
470
- });
471
- ```
472
-
473
- ## GraphQL Testing Patterns (using TestHelper)
474
-
475
- ```typescript
476
- import { TestGraphQLType, TestHelper } from '@lenne.tech/nest-server';
477
-
478
- // GraphQL Query
479
- const user = await testHelper.graphQl({
480
- arguments: {
481
- id: userId,
482
- },
483
- fields: ['id', 'email', 'firstName', { profile: ['bio', 'avatar'] }],
484
- name: 'getUser',
485
- type: TestGraphQLType.QUERY,
486
- }, { token: userToken });
487
-
488
- expect(user).toMatchObject({
489
- id: userId,
490
- email: 'test@example.com',
491
- });
492
-
493
- // GraphQL Mutation
494
- const result = await testHelper.graphQl({
495
- arguments: {
496
- input: {
497
- firstName: 'Updated',
498
- lastName: 'Name',
499
- },
500
- },
501
- fields: ['id', 'firstName', 'lastName'],
502
- name: 'updateUser',
503
- type: TestGraphQLType.MUTATION,
504
- }, { token: userToken });
505
-
506
- // GraphQL Mutation with nested objects
507
- const created = await testHelper.graphQl({
508
- arguments: {
509
- input: {
510
- title: 'New Post',
511
- content: 'Post content',
512
- tags: ['tag1', 'tag2'],
513
- },
514
- },
515
- fields: ['id', 'title', { author: ['id', 'email'] }, 'tags'],
516
- name: 'createPost',
517
- type: TestGraphQLType.MUTATION,
518
- }, { token: userToken });
519
-
520
- // GraphQL Query without auth (public)
521
- const publicData = await testHelper.graphQl({
522
- arguments: {},
523
- fields: ['version', 'status'],
524
- name: 'getPublicInfo',
525
- type: TestGraphQLType.QUERY,
526
- });
527
-
528
- // Expecting errors (e.g., unauthorized)
529
- const result = await testHelper.graphQl({
530
- arguments: { id: otherUserId },
531
- fields: ['id', 'email'],
532
- name: 'getUser',
533
- type: TestGraphQLType.QUERY,
534
- }, { token: userToken, statusCode: 200 });
535
-
536
- expect(result.errors).toBeDefined();
537
- expect(result.errors[0].message).toContain('Forbidden');
538
- ```
539
-
540
- ## Common Test Assertions
541
-
542
- ```typescript
543
- // Object matching
544
- expect(result).toMatchObject({ key: value });
545
-
546
- // Exact equality
547
- expect(result).toEqual(expected);
548
-
549
- // Array checks
550
- expect(array).toHaveLength(3);
551
- expect(array).toContain(item);
552
- expect(array).toBeInstanceOf(Array);
553
-
554
- // Existence checks
555
- expect(value).toBeDefined();
556
- expect(value).toBeUndefined();
557
- expect(value).toBeNull();
558
- expect(value).toBeTruthy();
559
- expect(value).toBeFalsy();
560
-
561
- // Number comparisons
562
- expect(number).toBeGreaterThan(5);
563
- expect(number).toBeLessThan(10);
564
- expect(number).toBeCloseTo(3.14, 2);
565
-
566
- // String matching
567
- expect(string).toContain('substring');
568
- expect(string).toMatch(/regex/);
569
-
570
- // Error checking
571
- expect(() => fn()).toThrow();
572
- expect(() => fn()).toThrow('error message');
573
- ```
574
-
575
- ## ObjectId Conversion Utilities
576
-
577
- **Use the utility functions from @lenne.tech/nest-server for ObjectId conversions:**
578
-
579
- ```typescript
580
- import { getStringIds, getObjectIds } from '@lenne.tech/nest-server';
581
-
582
- // Convert ObjectIds to strings (works with arrays OR single values)
583
- const stringIds = getStringIds(objectIds); // ObjectId[] → string[]
584
- const stringId = getStringIds(singleObjectId); // ObjectId → string
585
-
586
- // Convert strings to ObjectIds (works with arrays OR single values)
587
- const objectIds = getObjectIds(stringIds); // string[] → ObjectId[]
588
- const objectId = getObjectIds(singleStringId); // string → ObjectId
589
-
590
- // Pass objects directly - the functions extract IDs automatically!
591
- const stringIds = getStringIds(documents); // Extracts _id from each document
592
- const objectIds = getObjectIds(documents); // Extracts _id/id and converts
593
- ```
594
-
595
- **✅ ALWAYS use these utilities instead of manual conversion:**
596
-
597
- ```typescript
598
- // ✅ CORRECT: Use utility functions
599
- import { getStringIds, getObjectIds } from '@lenne.tech/nest-server';
600
-
601
- // For arrays of objects (no mapping needed - IDs are extracted automatically!)
602
- const stringIds = getStringIds(documents);
603
- const objectIds = getObjectIds(users);
604
-
605
- // For single values (no array needed!)
606
- const objectId = getObjectIds(userId);
607
- const stringId = getStringIds(document);
608
-
609
- // ❌ WRONG: Manual conversion
610
- const stringIds = documents.map(d => d._id.toString());
611
- const objectIds = inputIds.map(id => new ObjectId(id));
612
- const objectId = new ObjectId(userId);
613
- ```
614
-
615
- **Why use these utilities:**
616
- - Consistent behavior across the codebase
617
- - Works with both arrays and single values
618
- - Extracts IDs from objects automatically (no `.map()` needed)
619
- - Handles edge cases (null, undefined, invalid IDs)
620
- - Type-safe conversions
621
- - Easier to maintain and test
622
-
623
- ## Security Testing Checklist
624
-
625
- ```typescript
626
- // ✅ Create users with correct roles using TestHelper
627
- const userSignUp = await testHelper.graphQl({
628
- arguments: {
629
- input: {
630
- email: 'user@test.com',
631
- password: 'password123',
632
- firstName: 'Test',
633
- },
634
- },
635
- fields: ['token', { user: ['id'] }],
636
- name: 'signUp',
637
- type: TestGraphQLType.MUTATION,
638
- });
639
- const userToken = userSignUp.token;
640
-
641
- // ✅ Test with correct role
642
- await testHelper.rest('/api/resource', {
643
- token: userToken,
644
- });
645
-
646
- // ✅ Test without authentication
647
- await testHelper.rest('/api/resource', {
648
- statusCode: 401,
649
- });
650
-
651
- // ✅ Test with insufficient permissions
652
- await testHelper.rest('/api/admin/resource', {
653
- statusCode: 403,
654
- token: userToken, // Normal user trying admin endpoint
655
- });
656
-
657
- // ✅ Test access to own resources only
658
- await testHelper.rest(`/api/users/${userSignUp.user.id}/profile`, {
659
- method: 'PUT',
660
- payload: { firstName: 'Updated' },
661
- token: userToken,
662
- });
663
-
664
- await testHelper.rest(`/api/users/${otherUserId}/profile`, {
665
- method: 'PUT',
666
- payload: { firstName: 'Hacker' },
667
- statusCode: 403,
668
- token: userToken,
669
- });
670
-
671
- // ❌ NEVER do this
672
- // Don't remove @Restricted decorators
673
- // Don't change @Roles to more permissive
674
- // Don't disable security checks
675
- ```
676
-
677
- ## When to Ask Developer
678
-
679
- ```
680
- ❓ ASK when:
681
- - Story has contradictions or ambiguities
682
- - Need to change security decorators
683
- - Need to add new npm package
684
- - Multiple valid architectural approaches
685
- - Tests keep failing for unclear reasons
686
-
687
- ✅ DON'T ASK when:
688
- - Creating test files
689
- - Running tests
690
- - Analyzing failures
691
- - Implementing obvious features
692
- - Using nest-server-generator
693
- ```
694
-
695
- ## Debugging Failed Tests
696
-
697
- When tests fail, use these debugging tools to analyze the issue:
698
-
699
- ### 1. TestHelper Logging Options
700
-
701
- ```typescript
702
- // Enable detailed request/response logging
703
- const result = await testHelper.graphQl({
704
- arguments: { id: userId },
705
- fields: ['id', 'email'],
706
- name: 'getUser',
707
- type: TestGraphQLType.QUERY,
708
- }, {
709
- token: userToken,
710
- log: true, // Logs request details
711
- logError: true, // Logs errors when status >= 400
712
- });
713
-
714
- // For REST requests
715
- const result = await testHelper.rest('/api/endpoint', {
716
- method: 'POST',
717
- payload: data,
718
- token: userToken,
719
- log: true,
720
- logError: true,
721
- });
722
- ```
723
-
724
- ### 2. Server Exception Logging
725
-
726
- Enable in `src/config.env.ts`:
727
-
728
- ```typescript
729
- export default {
730
- // ... other config
731
- logExceptions: true, // Log all exceptions with stack traces
732
- // ...
733
- };
734
- ```
735
-
736
- ### 3. Validation Debug Logging
737
-
738
- Enable validation debugging via environment variable:
739
-
740
- ```bash
741
- # In your terminal or test script
742
- DEBUG_VALIDATION=true npm test
743
-
744
- # Or in your test file
745
- process.env.DEBUG_VALIDATION = 'true';
746
- ```
747
-
748
- This activates console.debug statements in MapAndValidatePipe (automatically activated via CoreModule - see `node_modules/@lenne.tech/nest-server/src/core/common/pipes/map-and-validate.pipe.ts`) to show detailed validation errors.
749
-
750
- ### 4. Combined Debugging Setup
751
-
752
- For comprehensive debugging, combine all three:
753
-
754
- ```typescript
755
- // In your test file beforeAll
756
- process.env.DEBUG_VALIDATION = 'true';
757
-
758
- // In src/config.env.ts
759
- export default {
760
- logExceptions: true,
761
- // ...
762
- };
763
-
764
- // In your tests
765
- const result = await testHelper.graphQl({
766
- // ... your test
767
- }, {
768
- log: true,
769
- logError: true,
770
- });
771
- ```
772
-
773
- ## Decision Tree: Test Failure Analysis
774
-
775
- ```
776
- Test fails
777
-
778
- ├─► Missing implementation?
779
- │ └─► Go to Step 4 (Implement)
780
-
781
- ├─► Test has bugs/errors?
782
- │ └─► Go to Step 3a (Fix test)
783
-
784
- ├─► Security blocking correctly?
785
- │ └─► Fix test to use proper auth
786
-
787
- ├─► Unclear error message?
788
- │ └─► Enable debugging (log, logError, logExceptions, DEBUG_VALIDATION)
789
-
790
- └─► Still unclear why failing?
791
- └─► Ask developer
792
- ```
793
-
794
- ## Code Quality, Security & Refactoring Check
795
-
796
- ### Quick Review Guide
797
-
798
- **Before marking complete, check for:**
799
-
800
- 1. **Code Duplication:**
801
- - Repeated validation logic → Extract to private method
802
- - Similar calculations in multiple places → Create helper function
803
- - Duplicated query patterns → Consolidate into flexible method
804
-
805
- 2. **Common Functionality:**
806
- - Extract repeated data transformations
807
- - Create shared validation helpers
808
- - Consolidate similar query builders
809
-
810
- 2a. **🔐 Guards in Controllers:**
811
- - DO NOT add `@UseGuards(AuthGuard(AuthGuardStrategy.JWT))` manually
812
- - `@Roles()` decorator automatically activates JWT authentication
813
- - `@Restricted()` decorator also activates guards automatically
814
- - Manual guards are redundant and create duplicates
815
- ```typescript
816
- // ✅ CORRECT
817
- @Roles(RoleEnum.ADMIN)
818
- @Get()
819
- async findAll() { ... }
820
-
821
- // ❌ WRONG: Redundant guard
822
- @UseGuards(AuthGuard(AuthGuardStrategy.JWT))
823
- @Roles(RoleEnum.ADMIN)
824
- @Get()
825
- async findAll() { ... }
826
- ```
827
-
828
- 3. **Database Indexes:**
829
- - Fields used in queries → Add `mongoose: { index: true, type: String }` to @UnifiedField
830
- - Foreign keys → Add index via mongoose option
831
- - Unique fields → Add `mongoose: { index: true, unique: true, type: String }`
832
- - Multiple query fields → Index each individually
833
-
834
- 4. **🔐 Security Review (CRITICAL):**
835
- - @Restricted/@Roles decorators NOT removed or weakened
836
- - Ownership checks in place for user data
837
- - All inputs validated with DTOs
838
- - Sensitive fields marked with `hideField: true`
839
- - No injection vulnerabilities
840
- - Error messages don't leak sensitive data
841
- - Authorization tests pass
842
-
843
- 5. **Refactoring Decision:**
844
- ```
845
- Used in 2+ places? → Extract to private method
846
- Used across services? → Consider utility class
847
- Only 1 usage? → Leave as-is (don't over-engineer)
848
- ```
849
-
850
- 6. **After Refactoring & Security Review:**
851
- ```bash
852
- npm test # MUST still pass!
853
- ```
854
-
855
- ### Code Quality Checklist
856
-
857
- Before marking complete:
858
-
859
- - [ ] All tests passing
860
- - [ ] **No obvious code duplication**
861
- - [ ] **Common functionality extracted to helpers**
862
- - [ ] **Consistent patterns throughout**
863
- - [ ] **Database indexes added to @UnifiedField decorators**
864
- - [ ] **Indexes match query patterns in services**
865
- - [ ] Test coverage adequate (80%+)
866
- - [ ] Code follows existing patterns
867
- - [ ] No unnecessary dependencies added
868
- - [ ] Proper error handling
869
- - [ ] Input validation implemented
870
- - [ ] Documentation/comments where needed
871
- - [ ] **Tests still pass after refactoring**
872
-
873
- **🔐 Security Checklist:**
874
-
875
- - [ ] **@Restricted/@Roles decorators NOT removed or weakened**
876
- - [ ] **Ownership checks in place (users can only access own data)**
877
- - [ ] **All inputs validated with proper DTOs**
878
- - [ ] **Sensitive fields marked with hideField: true**
879
- - [ ] **No SQL/NoSQL injection vulnerabilities**
880
- - [ ] **Error messages don't expose sensitive data**
881
- - [ ] **checkSecurity methods implemented in models**
882
- - [ ] **Authorization tests pass**
883
- - [ ] **No hardcoded secrets or credentials**
884
-
885
- ## Final Report Template
886
-
887
- ```markdown
888
- # Story Implementation Complete ✅
889
-
890
- ## Story: [Name]
891
-
892
- ### Tests Created
893
- - Location: tests/stories/[filename].story.test.ts
894
- - Test cases: X scenarios
895
- - Coverage: X%
896
-
897
- ### Implementation Summary
898
- - Modules: [list]
899
- - Objects: [list]
900
- - Properties: [list]
901
- - Other: [list]
902
-
903
- ### Test Results
904
- ✅ All X tests passing
905
-
906
- ### Code Quality
907
- - Patterns followed: ✅
908
- - Security preserved: ✅
909
- - Dependencies: None added ✅
910
- - Code duplication checked: ✅
911
- - Database indexes added: ✅
912
- - Refactoring performed: [Yes/No]
913
-
914
- ### Security Review
915
- - Authentication/Authorization: ✅
916
- - Input validation: ✅
917
- - Data exposure prevented: ✅
918
- - Ownership checks: ✅
919
- - Injection prevention: ✅
920
- - Authorization tests pass: ✅
921
-
922
- ### Refactoring (if performed)
923
- - Extracted helper functions: [list]
924
- - Consolidated code paths: [describe]
925
- - Removed duplication: [describe]
926
- - Tests still passing: ✅
927
-
928
- ### Files Modified
929
- 1. path/to/file.ts - description
930
- 2. path/to/file.ts - description
931
- ```
932
-
933
- ## 🔄 Handling Existing Tests
934
-
935
- **When your changes break existing tests:**
936
-
937
- ### Decision Tree
938
-
939
- ```
940
- Existing test fails
941
- ├─► Intentional breaking change? (e.g., added required field)
942
- │ └─► ✅ Update test to match new behavior
943
-
944
- └─► Unclear/unintended side effect?
945
- ├─► 🔍 Use git to investigate:
946
- │ - git show HEAD:path/to/file.ts
947
- │ - git diff HEAD path/to/file.ts
948
-
949
- └─► ⚠️ Fix code to satisfy BOTH old AND new tests
950
- ```
951
-
952
- ### Git Analysis (ALLOWED)
953
-
954
- ```bash
955
- # View old version of file
956
- git show HEAD:src/server/modules/user/user.service.ts
957
-
958
- # See what changed
959
- git diff HEAD src/server/modules/user/user.service.ts
960
-
961
- # View commit history
962
- git log -p --follow path/to/file.ts
963
- ```
964
-
965
- ### Guidelines
966
-
967
- **✅ Update tests when:**
968
- - Intentional API contract change
969
- - Removed deprecated functionality
970
- - Renamed fields/methods
971
- - Documented in story requirements
972
-
973
- **❌ Don't update tests when:**
974
- - Unclear why they're failing
975
- - Unrelated to your story
976
- - Multiple unrelated tests breaking
977
- - Testing important existing functionality
978
-
979
- **🚩 Red flags (investigate, don't update):**
980
- - Tests in different modules failing
981
- - Security/auth tests failing
982
- - 3+ unrelated tests failing
983
-
984
- **Remember:**
985
- - Existing tests = documentation of expected behavior
986
- - Use git freely for investigation (NOT commits!)
987
- - When in doubt, preserve backward compatibility
988
-
989
- ## ⛔ CRITICAL: Git Commits
990
-
991
- **🚨 NEVER create git commits unless explicitly requested by the developer.**
992
-
993
- - ❌ DO NOT use `git add`, `git commit`, or `git push` automatically
994
- - ❌ DO NOT commit changes when tests pass
995
- - ❌ DO NOT assume developer wants changes committed
996
- - ✅ ONLY commit when developer explicitly asks: "commit these changes"
997
-
998
- **Why:** Developers may want to review changes, commit in specific chunks, or have custom workflows.
999
-
1000
- **Your job:**
1001
- - ✅ Create/modify files
1002
- - ✅ Run tests
1003
- - ✅ Use git for analysis (git show, git diff, git log)
1004
- - ✅ Provide comprehensive report
1005
- - ❌ Never commit to git (unless explicitly requested)
1006
-
1007
- ## 🚨 CRITICAL: Database Cleanup & Test Isolation
1008
-
1009
- **ALWAYS implement comprehensive cleanup in your story tests!**
1010
-
1011
- Tests run in parallel, so improper test data management causes:
1012
- - Conflicts between parallel tests (duplicate keys, race conditions)
1013
- - False positives/negatives in tests
1014
- - Flaky tests that pass/fail randomly
1015
- - Contaminated test database
1016
- - Hard-to-debug test failures
1017
-
1018
- **📋 GOLDEN RULES for Parallel-Safe Test Data:**
1019
-
1020
- 1. **Email Addresses Must End with @test.com**
1021
- - Configuration in `src/config.env.ts` uses `e2e.brevo.exclude` to filter @test.com
1022
- - External services (email, etc.) will exclude these addresses
1023
- - Use timestamp + random suffix for guaranteed uniqueness
1024
- ```typescript
1025
- // ✅ CORRECT: Timestamp + 6-char random suffix
1026
- const email = `user-${Date.now()}-${Math.random().toString(36).substring(2, 8)}@test.com`;
1027
-
1028
- // ⚠️ LESS SAFE: Only timestamp (collision risk in same millisecond)
1029
- const email = `user-${Date.now()}@test.com`;
1030
-
1031
- // ❌ WRONG: No @test.com suffix
1032
- const email = 'testuser@example.com';
1033
- ```
1034
-
1035
- 2. **NEVER Reuse Same Data Across Test Files**
1036
- - Tests run in parallel = same data causes conflicts
1037
- - Make ALL data unique (emails, usernames, product names, etc.)
1038
- - Always use timestamp + random suffix
1039
- ```typescript
1040
- // ✅ CORRECT: Unique per test file with timestamp + random suffix
1041
- const email = `admin-product-test-${Date.now()}-${Math.random().toString(36).substring(2, 8)}@test.com`;
1042
-
1043
- // ⚠️ LESS SAFE: Only timestamp
1044
- const email = `admin-product-test-${Date.now()}@test.com`;
1045
-
1046
- // ❌ WRONG: Reused across multiple test files
1047
- const email = 'admin@test.com';
1048
- ```
1049
-
1050
- 3. **ONLY Delete Entities Created in This Test File**
1051
- - Track created IDs explicitly
1052
- - Delete ONLY tracked entities, not by pattern
1053
- ```typescript
1054
- // ✅ CORRECT: Only delete what we created
1055
- await db.collection('users').deleteMany({
1056
- _id: { $in: createdUserIds.map(id => new ObjectId(id)) }
1057
- });
1058
-
1059
- // ❌ WRONG: Deletes ALL test users (breaks parallel tests)
1060
- await db.collection('users').deleteMany({ email: /@test\.com$/ });
1061
- ```
1062
-
1063
- 4. **ALL Created Entities Must Be Cleaned Up**
1064
- - Track every created entity ID immediately
1065
- - Clean up in correct order (children before parents)
1066
- - Prevents side effects on future test runs
1067
-
1068
- 5. **NEVER Use Fixed Port Numbers**
1069
- - NestJS assigns random ports automatically for parallel execution
1070
- - Always use TestHelper - it abstracts port handling
1071
- ```typescript
1072
- // ✅ CORRECT: No port specified, TestHelper handles it
1073
- await app.init();
1074
- const result = await testHelper.rest('/api/users', { ... });
1075
-
1076
- // ❌ WRONG: Fixed port causes conflicts
1077
- await app.listen(3000);
1078
- const response = await fetch('http://localhost:3000/api/users');
1079
- ```
1080
-
1081
- ### Between Test Suites - RECOMMENDED APPROACH
1082
-
1083
- **Track all created entities and delete them explicitly:**
1084
-
1085
- ```typescript
1086
- describe('Feature Story', () => {
1087
- // Track created entities
1088
- let createdUserIds: string[] = [];
1089
- let createdProductIds: string[] = [];
1090
- let createdOrderIds: string[] = [];
1091
-
1092
- // In your tests, track IDs immediately after creation
1093
- it('should create product', async () => {
1094
- // 🚨 IMPORTANT: Use unique data per test file + @test.com for emails
1095
- const productData = {
1096
- name: `Product-FeatureStory-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`, // ✅ Unique per test file
1097
- ownerEmail: `owner-feature-${Date.now()}-${Math.random().toString(36).substring(2, 8)}@test.com`, // ✅ @test.com + unique
1098
- };
1099
-
1100
- const product = await testHelper.rest('/api/products', {
1101
- method: 'POST',
1102
- payload: productData,
1103
- token: adminToken,
1104
- });
1105
-
1106
- // ✅ Track for cleanup (ONLY delete what we created)
1107
- createdProductIds.push(product.id);
1108
- });
1109
-
1110
- afterAll(async () => {
1111
- // 🧹 CLEANUP: Delete ALL test data created during tests
1112
- try {
1113
- // Delete in correct order (child entities first)
1114
- if (createdOrderIds.length > 0) {
1115
- await db.collection('orders').deleteMany({
1116
- _id: { $in: createdOrderIds.map(id => new ObjectId(id)) }
1117
- });
1118
- }
1119
-
1120
- if (createdProductIds.length > 0) {
1121
- await db.collection('products').deleteMany({
1122
- _id: { $in: createdProductIds.map(id => new ObjectId(id)) }
1123
- });
1124
- }
1125
-
1126
- if (createdUserIds.length > 0) {
1127
- await db.collection('users').deleteMany({
1128
- _id: { $in: createdUserIds.map(id => new ObjectId(id)) }
1129
- });
1130
- }
1131
- } catch (error) {
1132
- console.error('Cleanup failed:', error);
1133
- // Don't throw - cleanup failures shouldn't fail the test suite
1134
- }
1135
-
1136
- await connection.close();
1137
- await app.close();
1138
- });
1139
- });
1140
- ```
1141
-
1142
- ### Alternative: Pattern-Based Cleanup (AVOID - Not Parallel-Safe!)
1143
-
1144
- **❌ DO NOT USE pattern-based cleanup - it breaks parallel test execution!**
1145
-
1146
- ```typescript
1147
- // ❌ WRONG: Deletes ALL test users, even from parallel tests!
1148
- afterAll(async () => {
1149
- await db.collection('users').deleteMany({ email: /@test\.com$/ });
1150
- await db.collection('products').deleteMany({ name: /^Test/ });
1151
-
1152
- await connection.close();
1153
- await app.close();
1154
- });
1155
- ```
1156
-
1157
- **⚠️ Why This is Dangerous:**
1158
- - **Breaks parallel tests:** Deletes entities from other tests that are still running
1159
- - **Race conditions:** Unpredictable failures when tests run simultaneously
1160
- - **Flaky tests:** Tests pass/fail randomly depending on execution order
1161
- - **Hard to debug:** Unclear why tests fail intermittently
1162
-
1163
- **✅ ALWAYS use ID-based cleanup instead:**
1164
- ```typescript
1165
- // ✅ CORRECT: Only deletes entities created in THIS test file
1166
- if (createdUserIds.length > 0) {
1167
- await db.collection('users').deleteMany({
1168
- _id: { $in: createdUserIds.map(id => new ObjectId(id)) }
1169
- });
1170
- }
1171
- ```
1172
-
1173
- ### Between Individual Tests
1174
-
1175
- Use `beforeEach`/`afterEach` only when necessary:
1176
-
1177
- ```typescript
1178
- describe('Feature Tests', () => {
1179
- let sharedResource;
1180
-
1181
- beforeEach(async () => {
1182
- // Reset state before each test if needed
1183
- sharedResource = await createFreshResource();
1184
- });
1185
-
1186
- afterEach(async () => {
1187
- // Clean up after each test if needed
1188
- await deleteResource(sharedResource.id);
1189
- });
1190
- });
1191
- ```
1192
-
1193
- ## User Authentication: signUp vs signIn
1194
-
1195
- ### When to use signUp
1196
-
1197
- - Creating new users in tests
1198
- - Full control over user data needed
1199
- - Testing user registration flows
1200
- - Most common in story tests
1201
-
1202
- ```typescript
1203
- const signUp = await testHelper.graphQl({
1204
- arguments: {
1205
- input: {
1206
- // 🚨 CRITICAL: MUST end with @test.com for e2e.brevo.exclude
1207
- // Use timestamp + random suffix for guaranteed uniqueness
1208
- email: `test-${Date.now()}-${Math.random().toString(36).substring(2, 8)}@test.com`, // ✅ Unique + @test.com
1209
- password: 'testpass123',
1210
- firstName: 'Test',
1211
- },
1212
- },
1213
- fields: ['token', { user: ['id', 'email'] }],
1214
- name: 'signUp',
1215
- type: TestGraphQLType.MUTATION,
1216
- });
1217
- const token = signUp.token;
1218
-
1219
- // ✅ Track for cleanup
1220
- createdUserIds.push(signUp.user.id);
1221
- ```
1222
-
1223
- ### When to use signIn
1224
-
1225
- - Authenticating existing users
1226
- - User already exists in database
1227
- - Testing login flows
1228
-
1229
- ```typescript
1230
- const signIn = await testHelper.rest('/auth/signin', {
1231
- method: 'POST',
1232
- payload: {
1233
- email: existingUserEmail,
1234
- password: existingUserPassword,
1235
- },
1236
- });
1237
- const token = signIn.token;
1238
- ```
1239
-
1240
- ## Avoiding Test Interdependencies
1241
-
1242
- ### ❌ DON'T: Shared state between tests
1243
-
1244
- ```typescript
1245
- // ❌ BAD: Test 2 depends on Test 1
1246
- let createdUserId;
1247
-
1248
- it('should create user', async () => {
1249
- const user = await createUser(...);
1250
- createdUserId = user.id; // ❌ Shared state!
1251
- });
1252
-
1253
- it('should update user', async () => {
1254
- await updateUser(createdUserId, ...); // ❌ Depends on Test 1!
1255
- });
1256
- ```
1257
-
1258
- ### ✅ DO: Independent tests
1259
-
1260
- ```typescript
1261
- // ✅ GOOD: Each test is independent
1262
- describe('User CRUD', () => {
1263
- let testUserId;
1264
-
1265
- beforeEach(async () => {
1266
- // Create fresh user for EACH test
1267
- const user = await createUser(...);
1268
- testUserId = user.id;
1269
- });
1270
-
1271
- afterEach(async () => {
1272
- // Clean up after each test
1273
- await deleteUser(testUserId);
1274
- });
1275
-
1276
- it('should update user', async () => {
1277
- await updateUser(testUserId, ...); // ✅ Independent!
1278
- });
1279
-
1280
- it('should delete user', async () => {
1281
- await deleteUser(testUserId, ...); // ✅ Independent!
1282
- });
1283
- });
1284
- ```
1285
-
1286
- ## Async/Await Best Practices
1287
-
1288
- ### Always await async operations
1289
-
1290
- ```typescript
1291
- // ❌ WRONG: Forgotten await
1292
- const user = testHelper.graphQl({...}); // Returns Promise, not user!
1293
- expect(user.email).toBe('test@example.com'); // FAILS!
1294
-
1295
- // ✅ CORRECT: With await
1296
- const user = await testHelper.graphQl({...});
1297
- expect(user.email).toBe('test@example.com'); // Works!
1298
- ```
1299
-
1300
- ### Parallel vs Sequential execution
1301
-
1302
- ```typescript
1303
- // ✅ Parallel execution (independent operations)
1304
- const [user1, user2, product] = await Promise.all([
1305
- testHelper.graphQl({...}), // Create user 1
1306
- testHelper.graphQl({...}), // Create user 2
1307
- testHelper.rest('/api/products', {...}), // Create product
1308
- ]);
1309
-
1310
- // ✅ Sequential execution (dependent operations)
1311
- const user = await testHelper.graphQl({...});
1312
- const product = await testHelper.rest('/api/products', {
1313
- token: user.token, // Depends on user being created first
1314
- payload: {...},
1315
- method: 'POST',
1316
- });
1317
-
1318
- // ❌ WRONG: Sequential when parallel is possible (slower)
1319
- const user1 = await testHelper.graphQl({...});
1320
- const user2 = await testHelper.graphQl({...}); // Could run in parallel!
1321
- const product = await testHelper.rest('/api/products', {...});
1322
- ```
1323
-
1324
- ### Handling errors with async/await
1325
-
1326
- ```typescript
1327
- // Test that async operation throws error
1328
- await expect(async () => {
1329
- await testHelper.rest('/api/resource', {
1330
- payload: invalidData,
1331
- token: userToken,
1332
- });
1333
- }).rejects.toThrow();
1334
-
1335
- // Or use statusCode option
1336
- await testHelper.rest('/api/resource', {
1337
- payload: invalidData,
1338
- statusCode: 400,
1339
- token: userToken,
1340
- });
1341
- ```
1342
-
1343
- ## Common Pitfalls to Avoid
1344
-
1345
- ❌ **Don't:**
1346
- - Write code before tests
1347
- - Skip test analysis step
1348
- - **Weaken security for passing tests**
1349
- - **Remove or weaken @Restricted/@Roles decorators**
1350
- - **Add @UseGuards(AuthGuard(...)) manually (redundant with @Roles)**
1351
- - **Skip security review before marking complete**
1352
- - Add dependencies without checking existing
1353
- - Ignore existing code patterns
1354
- - Batch test completions (mark complete immediately)
1355
- - Work on multiple tasks simultaneously
1356
- - **Create git commits without explicit request**
1357
- - Forget `await` on async calls
1358
- - Create test interdependencies
1359
- - **Forget to implement cleanup in afterAll**
1360
- - **Forget to track created entity IDs for cleanup**
1361
- - **Use pattern-based cleanup (deletes entities from parallel tests!)**
1362
- - **Reuse same test data across test files (causes parallel conflicts)**
1363
- - **Use emails without @test.com suffix (won't be excluded from external services)**
1364
- - **Use fixed port numbers (breaks parallel test execution)**
1365
- - Clean up too aggressively (breaking other tests)
1366
- - **Skip code quality check before marking complete**
1367
- - **Leave obvious code duplication in place**
1368
- - Over-engineer by extracting single-use code
1369
- - **Define indexes separately in schema files**
1370
- - **Forget to add indexes for queried fields**
1371
- - Add indexes to fields that are never queried
1372
- - **Expose sensitive fields without hideField**
1373
- - **Allow users to access others' data without checks**
1374
- - **Use 'any' type instead of proper DTOs**
1375
-
1376
- ✅ **Do:**
1377
- - Follow the 7-step process strictly (including Step 5a security & refactoring check)
1378
- - Ask for clarification early
1379
- - **Preserve all security mechanisms (CRITICAL)**
1380
- - **Perform security review before marking complete**
1381
- - Study existing code first
1382
- - Match existing patterns
1383
- - Mark todos complete as you finish them
1384
- - Focus on one step at a time
1385
- - **Wait for developer to commit changes**
1386
- - Always use `await` with async operations
1387
- - Make tests independent
1388
- - Use `beforeEach`/`afterEach` for test isolation
1389
- - Use Promise.all() for parallel operations
1390
- - **ALWAYS implement comprehensive cleanup in afterAll**
1391
- - **Track all created entity IDs immediately after creation**
1392
- - **ONLY delete entities created in THIS test file (parallel-safe)**
1393
- - **Use @test.com suffix for ALL test emails (e2e.brevo.exclude)**
1394
- - **Make ALL test data unique per test file (avoid parallel conflicts)**
1395
- - **NEVER use fixed ports - let NestJS assign random ports automatically**
1396
- - **Always use TestHelper for API calls (handles ports automatically)**
1397
- - Delete entities in correct order (children before parents)
1398
- - **Check for code duplication before marking complete**
1399
- - **Extract common functionality to helpers when used 2+ times**
1400
- - **Run tests again after refactoring**
1401
- - **Verify ownership checks for user data access**
1402
- - **Mark sensitive fields with hideField: true**
1403
- - **Use proper DTOs with validation decorators**
1404
- - **Ensure authorization tests pass**
1405
-
1406
- ## Integration Points
1407
-
1408
- ### With nest-server-generator
1409
- - Use for creating modules, objects, properties
1410
- - Use for understanding NestJS patterns
1411
- - Use for reading CrudService implementations
1412
-
1413
- ### With Existing Tests
1414
- - Study patterns in test/ directory
1415
- - Copy authentication setup approach
1416
- - Use same helper functions
1417
- - Match assertion style
1418
-
1419
- ### With API Documentation
1420
- - Check Controllers for REST endpoints
1421
- - Review Swagger annotations
1422
- - Understand existing data models
1423
- - Verify GraphQL schema if applicable
1424
-
1425
- ---
1426
-
1427
- **Remember:** Tests first, code second. Iterate until green. **Security review mandatory.** Refactor before done. Quality over speed.