@lenne.tech/cli 1.0.0 → 1.0.1

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