@lenne.tech/cli 1.0.1 → 1.1.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 (32) hide show
  1. package/build/commands/claude/install-commands.js +10 -5
  2. package/build/commands/claude/install-mcps.js +258 -0
  3. package/build/commands/claude/install-skills.js +90 -23
  4. package/build/lib/mcp-registry.js +80 -0
  5. package/build/templates/claude-commands/commit-message.md +21 -0
  6. package/build/templates/claude-commands/create-story.md +407 -0
  7. package/build/templates/claude-commands/skill-optimize.md +431 -90
  8. package/build/templates/claude-skills/building-stories-with-tdd/SKILL.md +265 -0
  9. package/build/templates/claude-skills/{story-tdd → building-stories-with-tdd}/code-quality.md +10 -0
  10. package/build/templates/claude-skills/{story-tdd → building-stories-with-tdd}/database-indexes.md +9 -0
  11. package/build/templates/claude-skills/{story-tdd → building-stories-with-tdd}/examples.md +115 -64
  12. package/build/templates/claude-skills/building-stories-with-tdd/handling-existing-tests.md +197 -0
  13. package/build/templates/claude-skills/{story-tdd → building-stories-with-tdd}/reference.md +276 -29
  14. package/build/templates/claude-skills/{story-tdd → building-stories-with-tdd}/security-review.md +8 -0
  15. package/build/templates/claude-skills/building-stories-with-tdd/workflow.md +1004 -0
  16. package/build/templates/claude-skills/generating-nest-servers/SKILL.md +303 -0
  17. package/build/templates/claude-skills/{nest-server-generator → generating-nest-servers}/configuration.md +6 -0
  18. package/build/templates/claude-skills/{nest-server-generator → generating-nest-servers}/declare-keyword-warning.md +9 -0
  19. package/build/templates/claude-skills/{nest-server-generator → generating-nest-servers}/description-management.md +9 -0
  20. package/build/templates/claude-skills/{nest-server-generator → generating-nest-servers}/examples.md +7 -0
  21. package/build/templates/claude-skills/generating-nest-servers/framework-guide.md +259 -0
  22. package/build/templates/claude-skills/{nest-server-generator → generating-nest-servers}/quality-review.md +9 -0
  23. package/build/templates/claude-skills/{nest-server-generator → generating-nest-servers}/reference.md +16 -0
  24. package/build/templates/claude-skills/{nest-server-generator → generating-nest-servers}/security-rules.md +13 -0
  25. package/build/templates/claude-skills/generating-nest-servers/verification-checklist.md +262 -0
  26. package/build/templates/claude-skills/generating-nest-servers/workflow-process.md +1061 -0
  27. package/build/templates/claude-skills/{lt-cli → using-lt-cli}/SKILL.md +22 -10
  28. package/build/templates/claude-skills/{lt-cli → using-lt-cli}/examples.md +7 -3
  29. package/build/templates/claude-skills/{lt-cli → using-lt-cli}/reference.md +10 -3
  30. package/package.json +2 -2
  31. package/build/templates/claude-skills/nest-server-generator/SKILL.md +0 -1891
  32. package/build/templates/claude-skills/story-tdd/SKILL.md +0 -1173
@@ -1,11 +1,34 @@
1
1
  ---
2
2
  name: story-tdd-reference
3
- version: 1.0.0
3
+ version: 1.0.1
4
4
  description: Quick reference guide for Test-Driven Development workflow
5
5
  ---
6
6
 
7
7
  # Story-Based TDD Quick Reference
8
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
+
9
32
  ## The 7-Step Workflow
10
33
 
11
34
  ```
@@ -19,7 +42,7 @@ description: Quick reference guide for Test-Driven Development workflow
19
42
 
20
43
  ┌─────────────────────────────────────────────────────────┐
21
44
  │ Step 2: Create Story Test │
22
- │ - Location: test/stories/feature-name.story.test.ts │
45
+ │ - Location: tests/stories/feature-name.story.test.ts │
23
46
  │ - Study existing test patterns │
24
47
  │ - Write comprehensive test scenarios │
25
48
  │ - Cover happy path, errors, edge cases │
@@ -78,7 +101,7 @@ description: Quick reference guide for Test-Driven Development workflow
78
101
  npm test
79
102
 
80
103
  # Run specific story test
81
- npm test -- test/stories/feature-name.story.test.ts
104
+ npm test -- tests/stories/feature-name.story.test.ts
82
105
 
83
106
  # Run tests with coverage
84
107
  npm run test:cov
@@ -105,6 +128,44 @@ lt server addProp Review rating:number --no-interactive
105
128
  lt server addProp Review comment:string? --no-interactive
106
129
  ```
107
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
+
108
169
  ## Test File Template
109
170
 
110
171
  ```typescript
@@ -116,25 +177,25 @@ import {
116
177
  } from '@lenne.tech/nest-server';
117
178
  import { Test, TestingModule } from '@nestjs/testing';
118
179
  import { PubSub } from 'graphql-subscriptions';
119
- import { MongoClient } from 'mongodb';
180
+ import { MongoClient, ObjectId } from 'mongodb';
120
181
 
121
182
  import envConfig from '../../src/config.env';
122
183
  import { RoleEnum } from '../../src/server/common/enums/role.enum';
123
- import { YourService } from '../../src/server/modules/your-module/your.service';
124
184
  import { imports, ServerModule } from '../../src/server/server.module';
125
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
+
126
190
  describe('[Feature Name] Story', () => {
127
191
  // Test environment properties
128
192
  let app;
129
193
  let testHelper: TestHelper;
130
194
 
131
- // Database
195
+ // Database (only for setup/cleanup and setting roles/verified status)
132
196
  let connection;
133
197
  let db;
134
198
 
135
- // Services
136
- let yourService: YourService;
137
-
138
199
  // Global test data
139
200
  let gUserToken: string;
140
201
  let gUserId: string;
@@ -147,7 +208,6 @@ describe('[Feature Name] Story', () => {
147
208
  const moduleFixture: TestingModule = await Test.createTestingModule({
148
209
  imports: [...imports, ServerModule],
149
210
  providers: [
150
- YourService,
151
211
  {
152
212
  provide: 'PUB_SUB',
153
213
  useValue: new PubSub(),
@@ -162,15 +222,16 @@ describe('[Feature Name] Story', () => {
162
222
  await app.init();
163
223
 
164
224
  testHelper = new TestHelper(app);
165
- yourService = moduleFixture.get(YourService);
166
225
 
167
- // Connection to database
226
+ // Connection to database (ONLY for cleanup and setting roles/verified)
168
227
  connection = await MongoClient.connect(envConfig.mongoose.uri);
169
228
  db = await connection.db();
170
229
 
171
- // Create test user
230
+ // 🚨 CRITICAL: Create test user with @test.com email
172
231
  const password = Math.random().toString(36).substring(7);
173
- const email = `test-${password}@example.com`;
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`;
174
235
  const signUp = await testHelper.graphQl({
175
236
  arguments: {
176
237
  input: {
@@ -211,7 +272,11 @@ describe('[Feature Name] Story', () => {
211
272
  describe('Happy Path', () => {
212
273
  it('should [expected behavior]', async () => {
213
274
  // Arrange
214
- const data = { /* test data */ };
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
+ };
215
280
 
216
281
  // Act - Using REST
217
282
  const result = await testHelper.rest('/api/endpoint', {
@@ -225,7 +290,7 @@ describe('[Feature Name] Story', () => {
225
290
  // expected properties
226
291
  });
227
292
 
228
- // Track for cleanup
293
+ // Track for cleanup (CRITICAL for parallel-safe tests)
229
294
  createdEntityIds.push(result.id);
230
295
  });
231
296
  });
@@ -337,6 +402,17 @@ Before marking complete, verify:
337
402
 
338
403
  ## REST API Testing Patterns (using TestHelper)
339
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
+
340
416
  ```typescript
341
417
  // GET request
342
418
  const result = await testHelper.rest('/api/resource/123', {
@@ -382,6 +458,16 @@ const result = await testHelper.rest('/api/resource', {
382
458
  },
383
459
  token: userToken,
384
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
+ });
385
471
  ```
386
472
 
387
473
  ## GraphQL Testing Patterns (using TestHelper)
@@ -486,6 +572,54 @@ expect(() => fn()).toThrow();
486
572
  expect(() => fn()).toThrow('error message');
487
573
  ```
488
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
+
489
623
  ## Security Testing Checklist
490
624
 
491
625
  ```typescript
@@ -611,7 +745,7 @@ DEBUG_VALIDATION=true npm test
611
745
  process.env.DEBUG_VALIDATION = 'true';
612
746
  ```
613
747
 
614
- This activates console.debug statements in MapAndValidatePipe (`node_modules/@lenne.tech/nest-server/src/core/common/pipes/map-and-validate.pipe.ts`) to show detailed validation errors.
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.
615
749
 
616
750
  ### 4. Combined Debugging Setup
617
751
 
@@ -673,6 +807,24 @@ Test fails
673
807
  - Create shared validation helpers
674
808
  - Consolidate similar query builders
675
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
+
676
828
  3. **Database Indexes:**
677
829
  - Fields used in queries → Add `mongoose: { index: true, type: String }` to @UnifiedField
678
830
  - Foreign keys → Add index via mongoose option
@@ -738,7 +890,7 @@ Before marking complete:
738
890
  ## Story: [Name]
739
891
 
740
892
  ### Tests Created
741
- - Location: test/stories/[filename].story.test.ts
893
+ - Location: tests/stories/[filename].story.test.ts
742
894
  - Test cases: X scenarios
743
895
  - Coverage: X%
744
896
 
@@ -856,12 +1008,76 @@ git log -p --follow path/to/file.ts
856
1008
 
857
1009
  **ALWAYS implement comprehensive cleanup in your story tests!**
858
1010
 
859
- Test data that remains in the database can cause:
1011
+ Tests run in parallel, so improper test data management causes:
1012
+ - Conflicts between parallel tests (duplicate keys, race conditions)
860
1013
  - False positives/negatives in tests
861
1014
  - Flaky tests that pass/fail randomly
862
1015
  - Contaminated test database
863
1016
  - Hard-to-debug test failures
864
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
+
865
1081
  ### Between Test Suites - RECOMMENDED APPROACH
866
1082
 
867
1083
  **Track all created entities and delete them explicitly:**
@@ -875,13 +1091,19 @@ describe('Feature Story', () => {
875
1091
 
876
1092
  // In your tests, track IDs immediately after creation
877
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
+
878
1100
  const product = await testHelper.rest('/api/products', {
879
1101
  method: 'POST',
880
1102
  payload: productData,
881
1103
  token: adminToken,
882
1104
  });
883
1105
 
884
- // ✅ Track for cleanup
1106
+ // ✅ Track for cleanup (ONLY delete what we created)
885
1107
  createdProductIds.push(product.id);
886
1108
  });
887
1109
 
@@ -917,13 +1139,13 @@ describe('Feature Story', () => {
917
1139
  });
918
1140
  ```
919
1141
 
920
- ### Alternative: Pattern-Based Cleanup (Less Reliable)
1142
+ ### Alternative: Pattern-Based Cleanup (AVOID - Not Parallel-Safe!)
921
1143
 
922
- Only use this if you can't track IDs:
1144
+ **❌ DO NOT USE pattern-based cleanup - it breaks parallel test execution!**
923
1145
 
924
1146
  ```typescript
1147
+ // ❌ WRONG: Deletes ALL test users, even from parallel tests!
925
1148
  afterAll(async () => {
926
- // Clean up test data by pattern (less reliable)
927
1149
  await db.collection('users').deleteMany({ email: /@test\.com$/ });
928
1150
  await db.collection('products').deleteMany({ name: /^Test/ });
929
1151
 
@@ -932,10 +1154,21 @@ afterAll(async () => {
932
1154
  });
933
1155
  ```
934
1156
 
935
- **⚠️ Warning:** Pattern-based cleanup can:
936
- - Miss entities that don't match the pattern
937
- - Delete unintended entities if pattern is too broad
938
- - Be harder to debug when cleanup fails
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
+ ```
939
1172
 
940
1173
  ### Between Individual Tests
941
1174
 
@@ -970,7 +1203,9 @@ describe('Feature Tests', () => {
970
1203
  const signUp = await testHelper.graphQl({
971
1204
  arguments: {
972
1205
  input: {
973
- email: `test-${Date.now()}@example.com`, // Unique email
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
974
1209
  password: 'testpass123',
975
1210
  firstName: 'Test',
976
1211
  },
@@ -980,6 +1215,9 @@ const signUp = await testHelper.graphQl({
980
1215
  type: TestGraphQLType.MUTATION,
981
1216
  });
982
1217
  const token = signUp.token;
1218
+
1219
+ // ✅ Track for cleanup
1220
+ createdUserIds.push(signUp.user.id);
983
1221
  ```
984
1222
 
985
1223
  ### When to use signIn
@@ -1109,6 +1347,7 @@ await testHelper.rest('/api/resource', {
1109
1347
  - Skip test analysis step
1110
1348
  - **Weaken security for passing tests**
1111
1349
  - **Remove or weaken @Restricted/@Roles decorators**
1350
+ - **Add @UseGuards(AuthGuard(...)) manually (redundant with @Roles)**
1112
1351
  - **Skip security review before marking complete**
1113
1352
  - Add dependencies without checking existing
1114
1353
  - Ignore existing code patterns
@@ -1119,8 +1358,11 @@ await testHelper.rest('/api/resource', {
1119
1358
  - Create test interdependencies
1120
1359
  - **Forget to implement cleanup in afterAll**
1121
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)**
1122
1365
  - Clean up too aggressively (breaking other tests)
1123
- - Use pattern-based cleanup when ID tracking is possible
1124
1366
  - **Skip code quality check before marking complete**
1125
1367
  - **Leave obvious code duplication in place**
1126
1368
  - Over-engineer by extracting single-use code
@@ -1147,6 +1389,11 @@ await testHelper.rest('/api/resource', {
1147
1389
  - Use Promise.all() for parallel operations
1148
1390
  - **ALWAYS implement comprehensive cleanup in afterAll**
1149
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)**
1150
1397
  - Delete entities in correct order (children before parents)
1151
1398
  - **Check for code duplication before marking complete**
1152
1399
  - **Extract common functionality to helpers when used 2+ times**
@@ -6,6 +6,14 @@ description: Security review checklist for Test-Driven Development - ensures no
6
6
 
7
7
  # 🔐 Security Review Checklist
8
8
 
9
+ ## Table of Contents
10
+ - [Security Checklist](#security-checklist)
11
+ - [Security Decision Tree](#security-decision-tree)
12
+ - [Red Flags - STOP and Review](#red-flags---stop-and-review)
13
+ - [If ANY Red Flag Found](#if-any-red-flag-found)
14
+ - [Remember](#remember)
15
+ - [Quick Security Checklist](#quick-security-checklist)
16
+
9
17
  **CRITICAL: Perform security review before final testing!**
10
18
 
11
19
  **ALWAYS review all code changes for security vulnerabilities before marking complete.**