@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.
- package/build/commands/claude/install-commands.js +10 -5
- package/build/commands/claude/install-mcps.js +258 -0
- package/build/commands/claude/install-skills.js +90 -23
- package/build/lib/mcp-registry.js +80 -0
- package/build/templates/claude-commands/commit-message.md +21 -0
- package/build/templates/claude-commands/create-story.md +407 -0
- package/build/templates/claude-commands/skill-optimize.md +431 -90
- package/build/templates/claude-skills/building-stories-with-tdd/SKILL.md +265 -0
- package/build/templates/claude-skills/{story-tdd → building-stories-with-tdd}/code-quality.md +10 -0
- package/build/templates/claude-skills/{story-tdd → building-stories-with-tdd}/database-indexes.md +9 -0
- package/build/templates/claude-skills/{story-tdd → building-stories-with-tdd}/examples.md +115 -64
- package/build/templates/claude-skills/building-stories-with-tdd/handling-existing-tests.md +197 -0
- package/build/templates/claude-skills/{story-tdd → building-stories-with-tdd}/reference.md +276 -29
- package/build/templates/claude-skills/{story-tdd → building-stories-with-tdd}/security-review.md +8 -0
- package/build/templates/claude-skills/building-stories-with-tdd/workflow.md +1004 -0
- package/build/templates/claude-skills/generating-nest-servers/SKILL.md +303 -0
- package/build/templates/claude-skills/{nest-server-generator → generating-nest-servers}/configuration.md +6 -0
- package/build/templates/claude-skills/{nest-server-generator → generating-nest-servers}/declare-keyword-warning.md +9 -0
- package/build/templates/claude-skills/{nest-server-generator → generating-nest-servers}/description-management.md +9 -0
- package/build/templates/claude-skills/{nest-server-generator → generating-nest-servers}/examples.md +7 -0
- package/build/templates/claude-skills/generating-nest-servers/framework-guide.md +259 -0
- package/build/templates/claude-skills/{nest-server-generator → generating-nest-servers}/quality-review.md +9 -0
- package/build/templates/claude-skills/{nest-server-generator → generating-nest-servers}/reference.md +16 -0
- package/build/templates/claude-skills/{nest-server-generator → generating-nest-servers}/security-rules.md +13 -0
- package/build/templates/claude-skills/generating-nest-servers/verification-checklist.md +262 -0
- package/build/templates/claude-skills/generating-nest-servers/workflow-process.md +1061 -0
- package/build/templates/claude-skills/{lt-cli → using-lt-cli}/SKILL.md +22 -10
- package/build/templates/claude-skills/{lt-cli → using-lt-cli}/examples.md +7 -3
- package/build/templates/claude-skills/{lt-cli → using-lt-cli}/reference.md +10 -3
- package/package.json +2 -2
- package/build/templates/claude-skills/nest-server-generator/SKILL.md +0 -1891
- package/build/templates/claude-skills/story-tdd/SKILL.md +0 -1173
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: story-tdd-examples
|
|
3
|
-
version: 1.0.
|
|
3
|
+
version: 1.0.1
|
|
4
4
|
description: Complete examples for Test-Driven Development workflow with NestJS story tests
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Story-Based TDD Examples
|
|
8
8
|
|
|
9
|
+
## Table of Contents
|
|
10
|
+
- [Example 1: Simple CRUD Feature - Product Reviews](#example-1-simple-crud-feature---product-reviews)
|
|
11
|
+
- [Example 2: Complex Business Logic - Order Processing](#example-2-complex-business-logic---order-processing)
|
|
12
|
+
- [Example 3: GraphQL Mutation - User Profile Update](#example-3-graphql-mutation---user-profile-update)
|
|
13
|
+
- [Debugging Test Failures](#debugging-test-failures)
|
|
14
|
+
- [Key Takeaways from Examples](#key-takeaways-from-examples)
|
|
15
|
+
|
|
9
16
|
This document provides complete examples of the TDD workflow for different types of user stories.
|
|
10
17
|
|
|
11
18
|
## Example 1: Simple CRUD Feature - Product Reviews
|
|
@@ -36,13 +43,36 @@ Acceptance Criteria:
|
|
|
36
43
|
- Can users review a product multiple times? (Assuming NO)
|
|
37
44
|
- What validation for rating? (Assuming 1-5 integer)
|
|
38
45
|
|
|
46
|
+
**🔍 Verification of existing API (CRITICAL - Do this BEFORE writing tests!):**
|
|
47
|
+
|
|
48
|
+
1. **Check Product endpoints:**
|
|
49
|
+
```
|
|
50
|
+
✅ Read src/server/modules/product/product.controller.ts
|
|
51
|
+
✅ Verified: GET /api/products exists (line 23)
|
|
52
|
+
✅ Verified: POST /api/products exists (line 45)
|
|
53
|
+
✅ Verified: Returns Product with id, name, price
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
2. **Check Review endpoints:**
|
|
57
|
+
```
|
|
58
|
+
❌ Review module does NOT exist yet
|
|
59
|
+
❌ POST /api/reviews needs to be implemented
|
|
60
|
+
❌ GET /api/products/:id/reviews needs to be implemented
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
3. **Plan implementation:**
|
|
64
|
+
- Need to create Review module (service, controller, model)
|
|
65
|
+
- Need to add POST /api/reviews endpoint
|
|
66
|
+
- Need to add GET /api/products/:id/reviews endpoint
|
|
67
|
+
|
|
39
68
|
### Step 2: Create Story Test
|
|
40
69
|
|
|
41
|
-
**File:** `
|
|
70
|
+
**File:** `tests/stories/product-review.story.test.ts`
|
|
42
71
|
|
|
43
72
|
```typescript
|
|
44
73
|
import {
|
|
45
74
|
ConfigService,
|
|
75
|
+
getObjectIds,
|
|
46
76
|
HttpExceptionLogFilter,
|
|
47
77
|
TestGraphQLType,
|
|
48
78
|
TestHelper,
|
|
@@ -53,11 +83,11 @@ import { MongoClient, ObjectId } from 'mongodb';
|
|
|
53
83
|
|
|
54
84
|
import envConfig from '../../src/config.env';
|
|
55
85
|
import { RoleEnum } from '../../src/server/common/enums/role.enum';
|
|
56
|
-
import { ProductService } from '../../src/server/modules/product/product.service';
|
|
57
|
-
import { ReviewService } from '../../src/server/modules/review/review.service';
|
|
58
|
-
import { UserService } from '../../src/server/modules/user/user.service';
|
|
59
86
|
import { imports, ServerModule } from '../../src/server/server.module';
|
|
60
87
|
|
|
88
|
+
// ⚠️ NOTE: No Service imports! Tests must use API endpoints only.
|
|
89
|
+
// Services are only accessed indirectly through Controllers/Resolvers.
|
|
90
|
+
|
|
61
91
|
describe('Product Review Story', () => {
|
|
62
92
|
// Test environment properties
|
|
63
93
|
let app;
|
|
@@ -67,11 +97,6 @@ describe('Product Review Story', () => {
|
|
|
67
97
|
let connection;
|
|
68
98
|
let db;
|
|
69
99
|
|
|
70
|
-
// Services
|
|
71
|
-
let userService: UserService;
|
|
72
|
-
let productService: ProductService;
|
|
73
|
-
let reviewService: ReviewService;
|
|
74
|
-
|
|
75
100
|
// Global test data
|
|
76
101
|
let gAdminToken: string;
|
|
77
102
|
let gAdminId: string;
|
|
@@ -87,9 +112,6 @@ describe('Product Review Story', () => {
|
|
|
87
112
|
const moduleFixture: TestingModule = await Test.createTestingModule({
|
|
88
113
|
imports: [...imports, ServerModule],
|
|
89
114
|
providers: [
|
|
90
|
-
UserService,
|
|
91
|
-
ProductService,
|
|
92
|
-
ReviewService,
|
|
93
115
|
{
|
|
94
116
|
provide: 'PUB_SUB',
|
|
95
117
|
useValue: new PubSub(),
|
|
@@ -104,15 +126,12 @@ describe('Product Review Story', () => {
|
|
|
104
126
|
await app.init();
|
|
105
127
|
|
|
106
128
|
testHelper = new TestHelper(app);
|
|
107
|
-
userService = moduleFixture.get(UserService);
|
|
108
|
-
productService = moduleFixture.get(ProductService);
|
|
109
|
-
reviewService = moduleFixture.get(ReviewService);
|
|
110
129
|
|
|
111
130
|
// Connection to database
|
|
112
131
|
connection = await MongoClient.connect(envConfig.mongoose.uri);
|
|
113
132
|
db = await connection.db();
|
|
114
133
|
|
|
115
|
-
// Create admin user
|
|
134
|
+
// Create admin user via API
|
|
116
135
|
const adminPassword = Math.random().toString(36).substring(7);
|
|
117
136
|
const adminEmail = `admin-${adminPassword}@test.com`;
|
|
118
137
|
const adminSignUp = await testHelper.graphQl({
|
|
@@ -130,8 +149,11 @@ describe('Product Review Story', () => {
|
|
|
130
149
|
gAdminId = adminSignUp.user.id;
|
|
131
150
|
gAdminToken = adminSignUp.token;
|
|
132
151
|
|
|
133
|
-
// Set admin role
|
|
134
|
-
await
|
|
152
|
+
// ✅ ALLOWED EXCEPTION: Set admin role via direct DB access (no API endpoint for this)
|
|
153
|
+
await db.collection('users').updateOne(
|
|
154
|
+
{ _id: new ObjectId(gAdminId) },
|
|
155
|
+
{ $set: { roles: [RoleEnum.ADMIN] } }
|
|
156
|
+
);
|
|
135
157
|
|
|
136
158
|
// Create normal user
|
|
137
159
|
const userPassword = Math.random().toString(36).substring(7);
|
|
@@ -166,24 +188,24 @@ describe('Product Review Story', () => {
|
|
|
166
188
|
afterAll(async () => {
|
|
167
189
|
// 🧹 CLEANUP: Delete all test data created during tests
|
|
168
190
|
try {
|
|
169
|
-
// Delete all created reviews
|
|
191
|
+
// Delete all created reviews (using getObjectIds for array conversion)
|
|
170
192
|
if (createdReviewIds.length > 0) {
|
|
171
193
|
await db.collection('reviews').deleteMany({
|
|
172
|
-
_id: { $in: createdReviewIds
|
|
194
|
+
_id: { $in: getObjectIds(createdReviewIds) }
|
|
173
195
|
});
|
|
174
196
|
}
|
|
175
197
|
|
|
176
|
-
// Delete test product
|
|
198
|
+
// Delete test product (using getObjectIds for single value - no array needed!)
|
|
177
199
|
if (gProductId) {
|
|
178
|
-
await db.collection('products').deleteOne({ _id:
|
|
200
|
+
await db.collection('products').deleteOne({ _id: getObjectIds(gProductId) });
|
|
179
201
|
}
|
|
180
202
|
|
|
181
|
-
// Delete test users
|
|
203
|
+
// Delete test users (using getObjectIds for single values)
|
|
182
204
|
if (gUserId) {
|
|
183
|
-
await db.collection('users').deleteOne({ _id:
|
|
205
|
+
await db.collection('users').deleteOne({ _id: getObjectIds(gUserId) });
|
|
184
206
|
}
|
|
185
207
|
if (gAdminId) {
|
|
186
|
-
await db.collection('users').deleteOne({ _id:
|
|
208
|
+
await db.collection('users').deleteOne({ _id: getObjectIds(gAdminId) });
|
|
187
209
|
}
|
|
188
210
|
} catch (error) {
|
|
189
211
|
console.error('Cleanup failed:', error);
|
|
@@ -390,11 +412,12 @@ Acceptance Criteria:
|
|
|
390
412
|
|
|
391
413
|
### Step 2: Create Story Test
|
|
392
414
|
|
|
393
|
-
**File:** `
|
|
415
|
+
**File:** `tests/stories/order-processing.story.test.ts`
|
|
394
416
|
|
|
395
417
|
```typescript
|
|
396
418
|
import {
|
|
397
419
|
ConfigService,
|
|
420
|
+
getObjectIds,
|
|
398
421
|
HttpExceptionLogFilter,
|
|
399
422
|
TestGraphQLType,
|
|
400
423
|
TestHelper,
|
|
@@ -405,11 +428,11 @@ import { MongoClient, ObjectId } from 'mongodb';
|
|
|
405
428
|
|
|
406
429
|
import envConfig from '../../src/config.env';
|
|
407
430
|
import { RoleEnum } from '../../src/server/common/enums/role.enum';
|
|
408
|
-
import { OrderService } from '../../src/server/modules/order/order.service';
|
|
409
|
-
import { ProductService } from '../../src/server/modules/product/product.service';
|
|
410
|
-
import { UserService } from '../../src/server/modules/user/user.service';
|
|
411
431
|
import { imports, ServerModule } from '../../src/server/server.module';
|
|
412
432
|
|
|
433
|
+
// ⚠️ NOTE: No Service imports! Tests must use API endpoints only.
|
|
434
|
+
// Services are only accessed indirectly through Controllers/Resolvers.
|
|
435
|
+
|
|
413
436
|
describe('Order Processing Story', () => {
|
|
414
437
|
// Test environment properties
|
|
415
438
|
let app;
|
|
@@ -419,11 +442,6 @@ describe('Order Processing Story', () => {
|
|
|
419
442
|
let connection;
|
|
420
443
|
let db;
|
|
421
444
|
|
|
422
|
-
// Services
|
|
423
|
-
let userService: UserService;
|
|
424
|
-
let productService: ProductService;
|
|
425
|
-
let orderService: OrderService;
|
|
426
|
-
|
|
427
445
|
// Global test data
|
|
428
446
|
let gAdminToken: string;
|
|
429
447
|
let gAdminId: string;
|
|
@@ -442,9 +460,6 @@ describe('Order Processing Story', () => {
|
|
|
442
460
|
const moduleFixture: TestingModule = await Test.createTestingModule({
|
|
443
461
|
imports: [...imports, ServerModule],
|
|
444
462
|
providers: [
|
|
445
|
-
UserService,
|
|
446
|
-
ProductService,
|
|
447
|
-
OrderService,
|
|
448
463
|
{
|
|
449
464
|
provide: 'PUB_SUB',
|
|
450
465
|
useValue: new PubSub(),
|
|
@@ -459,15 +474,12 @@ describe('Order Processing Story', () => {
|
|
|
459
474
|
await app.init();
|
|
460
475
|
|
|
461
476
|
testHelper = new TestHelper(app);
|
|
462
|
-
userService = moduleFixture.get(UserService);
|
|
463
|
-
productService = moduleFixture.get(ProductService);
|
|
464
|
-
orderService = moduleFixture.get(OrderService);
|
|
465
477
|
|
|
466
478
|
// Connection to database
|
|
467
479
|
connection = await MongoClient.connect(envConfig.mongoose.uri);
|
|
468
480
|
db = await connection.db();
|
|
469
481
|
|
|
470
|
-
// Create admin user
|
|
482
|
+
// Create admin user via API
|
|
471
483
|
const adminPassword = Math.random().toString(36).substring(7);
|
|
472
484
|
const adminEmail = `admin-${adminPassword}@test.com`;
|
|
473
485
|
const adminSignUp = await testHelper.graphQl({
|
|
@@ -485,8 +497,11 @@ describe('Order Processing Story', () => {
|
|
|
485
497
|
gAdminId = adminSignUp.user.id;
|
|
486
498
|
gAdminToken = adminSignUp.token;
|
|
487
499
|
|
|
488
|
-
// Set admin role
|
|
489
|
-
await
|
|
500
|
+
// ✅ ALLOWED EXCEPTION: Set admin role via direct DB access (no API endpoint for this)
|
|
501
|
+
await db.collection('users').updateOne(
|
|
502
|
+
{ _id: new ObjectId(gAdminId) },
|
|
503
|
+
{ $set: { roles: [RoleEnum.ADMIN] } }
|
|
504
|
+
);
|
|
490
505
|
|
|
491
506
|
// Create customer user
|
|
492
507
|
const customerPassword = Math.random().toString(36).substring(7);
|
|
@@ -537,26 +552,26 @@ describe('Order Processing Story', () => {
|
|
|
537
552
|
afterAll(async () => {
|
|
538
553
|
// 🧹 CLEANUP: Delete all test data created during tests
|
|
539
554
|
try {
|
|
540
|
-
// Delete all created orders first (child entities)
|
|
555
|
+
// Delete all created orders first (child entities) - using getObjectIds for arrays
|
|
541
556
|
if (createdOrderIds.length > 0) {
|
|
542
557
|
await db.collection('orders').deleteMany({
|
|
543
|
-
_id: { $in: createdOrderIds
|
|
558
|
+
_id: { $in: getObjectIds(createdOrderIds) }
|
|
544
559
|
});
|
|
545
560
|
}
|
|
546
561
|
|
|
547
|
-
// Delete all created products
|
|
562
|
+
// Delete all created products - using getObjectIds for arrays
|
|
548
563
|
if (createdProductIds.length > 0) {
|
|
549
564
|
await db.collection('products').deleteMany({
|
|
550
|
-
_id: { $in: createdProductIds
|
|
565
|
+
_id: { $in: getObjectIds(createdProductIds) }
|
|
551
566
|
});
|
|
552
567
|
}
|
|
553
568
|
|
|
554
|
-
// Delete test users
|
|
569
|
+
// Delete test users - using getObjectIds for single values (no array needed!)
|
|
555
570
|
if (gCustomerId) {
|
|
556
|
-
await db.collection('users').deleteOne({ _id:
|
|
571
|
+
await db.collection('users').deleteOne({ _id: getObjectIds(gCustomerId) });
|
|
557
572
|
}
|
|
558
573
|
if (gAdminId) {
|
|
559
|
-
await db.collection('users').deleteOne({ _id:
|
|
574
|
+
await db.collection('users').deleteOne({ _id: getObjectIds(gAdminId) });
|
|
560
575
|
}
|
|
561
576
|
} catch (error) {
|
|
562
577
|
console.error('Cleanup failed:', error);
|
|
@@ -930,24 +945,27 @@ Acceptance Criteria:
|
|
|
930
945
|
|
|
931
946
|
### Step 2: Create Story Test (GraphQL)
|
|
932
947
|
|
|
933
|
-
**File:** `
|
|
948
|
+
**File:** `tests/stories/profile-update.story.test.ts`
|
|
934
949
|
|
|
935
950
|
```typescript
|
|
936
951
|
import {
|
|
937
952
|
ConfigService,
|
|
953
|
+
getObjectIds,
|
|
938
954
|
HttpExceptionLogFilter,
|
|
939
955
|
TestGraphQLType,
|
|
940
956
|
TestHelper,
|
|
941
957
|
} from '@lenne.tech/nest-server';
|
|
942
958
|
import { Test, TestingModule } from '@nestjs/testing';
|
|
943
959
|
import { PubSub } from 'graphql-subscriptions';
|
|
944
|
-
import { MongoClient } from 'mongodb';
|
|
960
|
+
import { MongoClient, ObjectId } from 'mongodb';
|
|
945
961
|
|
|
946
962
|
import envConfig from '../../src/config.env';
|
|
947
963
|
import { RoleEnum } from '../../src/server/common/enums/role.enum';
|
|
948
|
-
import { UserService } from '../../src/server/modules/user/user.service';
|
|
949
964
|
import { imports, ServerModule } from '../../src/server/server.module';
|
|
950
965
|
|
|
966
|
+
// ⚠️ NOTE: No Service imports! Tests must use API endpoints only.
|
|
967
|
+
// Services are only accessed indirectly through Controllers/Resolvers.
|
|
968
|
+
|
|
951
969
|
describe('Profile Update Story (GraphQL)', () => {
|
|
952
970
|
// Test environment properties
|
|
953
971
|
let app;
|
|
@@ -957,9 +975,6 @@ describe('Profile Update Story (GraphQL)', () => {
|
|
|
957
975
|
let connection;
|
|
958
976
|
let db;
|
|
959
977
|
|
|
960
|
-
// Services
|
|
961
|
-
let userService: UserService;
|
|
962
|
-
|
|
963
978
|
// Global test data
|
|
964
979
|
let gNormalUserId: string;
|
|
965
980
|
let gNormalUserToken: string;
|
|
@@ -977,7 +992,6 @@ describe('Profile Update Story (GraphQL)', () => {
|
|
|
977
992
|
const moduleFixture: TestingModule = await Test.createTestingModule({
|
|
978
993
|
imports: [...imports, ServerModule],
|
|
979
994
|
providers: [
|
|
980
|
-
UserService,
|
|
981
995
|
{
|
|
982
996
|
provide: 'PUB_SUB',
|
|
983
997
|
useValue: new PubSub(),
|
|
@@ -992,7 +1006,6 @@ describe('Profile Update Story (GraphQL)', () => {
|
|
|
992
1006
|
await app.init();
|
|
993
1007
|
|
|
994
1008
|
testHelper = new TestHelper(app);
|
|
995
|
-
userService = moduleFixture.get(UserService);
|
|
996
1009
|
|
|
997
1010
|
// Connection to database
|
|
998
1011
|
connection = await MongoClient.connect(envConfig.mongoose.uri);
|
|
@@ -1062,17 +1075,20 @@ describe('Profile Update Story (GraphQL)', () => {
|
|
|
1062
1075
|
// Track for cleanup
|
|
1063
1076
|
createdUserIds.push(gAdminUserId);
|
|
1064
1077
|
|
|
1065
|
-
// Set admin role
|
|
1066
|
-
await
|
|
1078
|
+
// ✅ ALLOWED EXCEPTION: Set admin role via direct DB access (no API endpoint for this)
|
|
1079
|
+
await db.collection('users').updateOne(
|
|
1080
|
+
{ _id: new ObjectId(gAdminUserId) },
|
|
1081
|
+
{ $set: { roles: [RoleEnum.ADMIN] } }
|
|
1082
|
+
);
|
|
1067
1083
|
});
|
|
1068
1084
|
|
|
1069
1085
|
afterAll(async () => {
|
|
1070
1086
|
// 🧹 CLEANUP: Delete all test data created during tests
|
|
1071
1087
|
try {
|
|
1072
|
-
// Delete all created users
|
|
1088
|
+
// Delete all created users - using getObjectIds
|
|
1073
1089
|
if (createdUserIds.length > 0) {
|
|
1074
1090
|
await db.collection('users').deleteMany({
|
|
1075
|
-
_id: { $in: createdUserIds
|
|
1091
|
+
_id: { $in: getObjectIds(createdUserIds) }
|
|
1076
1092
|
});
|
|
1077
1093
|
}
|
|
1078
1094
|
} catch (error) {
|
|
@@ -1211,6 +1227,14 @@ describe('Profile Update Story (GraphQL)', () => {
|
|
|
1211
1227
|
|
|
1212
1228
|
When your tests fail and error messages are unclear, enable debugging:
|
|
1213
1229
|
|
|
1230
|
+
**🔍 TIP: For all TestHelper options, read the source file:**
|
|
1231
|
+
|
|
1232
|
+
```
|
|
1233
|
+
node_modules/@lenne.tech/nest-server/src/test/test.helper.ts
|
|
1234
|
+
```
|
|
1235
|
+
|
|
1236
|
+
This file documents all capabilities including `log`, `logError`, file uploads via `attachments`, and more.
|
|
1237
|
+
|
|
1214
1238
|
### TestHelper Debugging Options
|
|
1215
1239
|
|
|
1216
1240
|
```typescript
|
|
@@ -1262,7 +1286,7 @@ beforeAll(async () => {
|
|
|
1262
1286
|
});
|
|
1263
1287
|
```
|
|
1264
1288
|
|
|
1265
|
-
This enables detailed console.debug output from MapAndValidatePipe (`node_modules/@lenne.tech/nest-server/src/core/common/pipes/map-and-validate.pipe.ts`).
|
|
1289
|
+
This enables detailed console.debug output from MapAndValidatePipe (automatically activated via CoreModule - see `node_modules/@lenne.tech/nest-server/src/core/common/pipes/map-and-validate.pipe.ts`).
|
|
1266
1290
|
|
|
1267
1291
|
### Full Debugging Setup Example
|
|
1268
1292
|
|
|
@@ -1288,6 +1312,33 @@ describe('My Story Test', () => {
|
|
|
1288
1312
|
|
|
1289
1313
|
**Remember to disable debugging logs before committing** to keep test output clean in CI/CD.
|
|
1290
1314
|
|
|
1315
|
+
### File Upload Testing
|
|
1316
|
+
|
|
1317
|
+
TestHelper supports file uploads via the `attachments` option:
|
|
1318
|
+
|
|
1319
|
+
```typescript
|
|
1320
|
+
// Upload a single file
|
|
1321
|
+
const result = await testHelper.rest('/api/upload', {
|
|
1322
|
+
method: 'POST',
|
|
1323
|
+
attachments: [
|
|
1324
|
+
{ name: 'document', path: '/path/to/document.pdf' },
|
|
1325
|
+
],
|
|
1326
|
+
token: userToken,
|
|
1327
|
+
});
|
|
1328
|
+
|
|
1329
|
+
// Upload multiple files
|
|
1330
|
+
const result = await testHelper.rest('/api/upload-multiple', {
|
|
1331
|
+
method: 'POST',
|
|
1332
|
+
attachments: [
|
|
1333
|
+
{ name: 'avatar', path: '/path/to/avatar.png' },
|
|
1334
|
+
{ name: 'resume', path: '/path/to/resume.pdf' },
|
|
1335
|
+
],
|
|
1336
|
+
token: userToken,
|
|
1337
|
+
});
|
|
1338
|
+
```
|
|
1339
|
+
|
|
1340
|
+
**See `node_modules/@lenne.tech/nest-server/src/test/test.helper.ts` for all available options.**
|
|
1341
|
+
|
|
1291
1342
|
---
|
|
1292
1343
|
|
|
1293
1344
|
## Key Takeaways from Examples
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: story-tdd-handling-existing-tests
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: Complete guide for handling existing tests when modifying code in TDD workflow - decision trees, git analysis, examples, and guidelines for determining when to update tests vs fix code
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 🔄 Handling Existing Tests When Modifying Code
|
|
8
|
+
|
|
9
|
+
## Table of Contents
|
|
10
|
+
- [Analysis Decision Tree](#analysis-decision-tree)
|
|
11
|
+
- [Using Git for Analysis (ALLOWED)](#using-git-for-analysis-allowed)
|
|
12
|
+
- [Examples](#examples)
|
|
13
|
+
- [Guidelines](#guidelines)
|
|
14
|
+
- [Process](#process)
|
|
15
|
+
- [Red Flags](#red-flags)
|
|
16
|
+
- [Remember](#remember)
|
|
17
|
+
|
|
18
|
+
**CRITICAL RULE:** When your code changes cause existing (non-story) tests to fail, you MUST analyze and handle this properly.
|
|
19
|
+
|
|
20
|
+
## Analysis Decision Tree
|
|
21
|
+
|
|
22
|
+
When existing tests fail after your changes:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
Existing test fails
|
|
26
|
+
│
|
|
27
|
+
├─► Was this change intentional and breaking?
|
|
28
|
+
│ │
|
|
29
|
+
│ ├─► YES: Change was deliberate and it's clear why tests break
|
|
30
|
+
│ │ └─► ✅ Update the existing tests to reflect new behavior
|
|
31
|
+
│ │ - Modify test expectations
|
|
32
|
+
│ │ - Update test data/setup if needed
|
|
33
|
+
│ │ - Document why test was changed
|
|
34
|
+
│ │
|
|
35
|
+
│ └─► NO/UNCLEAR: Not sure why tests are breaking
|
|
36
|
+
│ └─► 🔍 Investigate potential side effect
|
|
37
|
+
│ │
|
|
38
|
+
│ ├─► Use git to review previous state:
|
|
39
|
+
│ │ - git show HEAD:path/to/file.ts
|
|
40
|
+
│ │ - git diff HEAD path/to/test.ts
|
|
41
|
+
│ │ - git log -p path/to/file.ts
|
|
42
|
+
│ │
|
|
43
|
+
│ ├─► Compare old vs new behavior
|
|
44
|
+
│ │
|
|
45
|
+
│ └─► ⚠️ Likely unintended side effect!
|
|
46
|
+
│ └─► Fix code to satisfy BOTH old AND new tests
|
|
47
|
+
│ - Refine implementation
|
|
48
|
+
│ - Add conditional logic if needed
|
|
49
|
+
│ - Ensure backward compatibility
|
|
50
|
+
│ - Keep existing functionality intact
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Using Git for Analysis (ALLOWED)
|
|
54
|
+
|
|
55
|
+
**✅ Git commands are EXPLICITLY ALLOWED for analysis:**
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# View old version of a file
|
|
59
|
+
git show HEAD:src/server/modules/user/user.service.ts
|
|
60
|
+
|
|
61
|
+
# See what changed in a file
|
|
62
|
+
git diff HEAD src/server/modules/user/user.service.ts
|
|
63
|
+
|
|
64
|
+
# View file from specific commit
|
|
65
|
+
git show abc123:path/to/file.ts
|
|
66
|
+
|
|
67
|
+
# See commit history for a file
|
|
68
|
+
git log -p --follow path/to/file.ts
|
|
69
|
+
|
|
70
|
+
# Compare branches
|
|
71
|
+
git diff main..HEAD path/to/file.ts
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**These commands help you understand:**
|
|
75
|
+
- What the code looked like before your changes
|
|
76
|
+
- What the previous test expectations were
|
|
77
|
+
- Why existing tests were written a certain way
|
|
78
|
+
- Whether your change introduces regression
|
|
79
|
+
|
|
80
|
+
## Examples
|
|
81
|
+
|
|
82
|
+
### Example 1: Intentional Breaking Change
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
// Scenario: You added a required field to User model
|
|
86
|
+
// Old test expects: { email, firstName }
|
|
87
|
+
// New behavior requires: { email, firstName, lastName }
|
|
88
|
+
|
|
89
|
+
// ✅ CORRECT: Update the test
|
|
90
|
+
it('should create user', async () => {
|
|
91
|
+
const user = await userService.create({
|
|
92
|
+
email: 'test@example.com',
|
|
93
|
+
firstName: 'John',
|
|
94
|
+
lastName: 'Doe', // ✅ Added required field
|
|
95
|
+
});
|
|
96
|
+
// ...
|
|
97
|
+
});
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Example 2: Unintended Side Effect
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
// Scenario: You changed authentication logic for new feature
|
|
104
|
+
// Old tests for different feature now fail unexpectedly
|
|
105
|
+
|
|
106
|
+
// ❌ WRONG: Just update the failing tests
|
|
107
|
+
// ✅ CORRECT: Investigate and fix the code
|
|
108
|
+
|
|
109
|
+
// 1. Use git to see old implementation
|
|
110
|
+
// git show HEAD:src/server/modules/auth/auth.service.ts
|
|
111
|
+
|
|
112
|
+
// 2. Identify the unintended side effect
|
|
113
|
+
// 3. Refine your code to avoid breaking existing functionality
|
|
114
|
+
|
|
115
|
+
// Example fix: Add conditional logic
|
|
116
|
+
async authenticate(user: User, options?: AuthOptions) {
|
|
117
|
+
// Your new feature logic
|
|
118
|
+
if (options?.useNewBehavior) {
|
|
119
|
+
return this.newAuthMethod(user);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Preserve existing behavior for backward compatibility
|
|
123
|
+
return this.existingAuthMethod(user);
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Guidelines
|
|
128
|
+
|
|
129
|
+
**✅ DO update existing tests when:**
|
|
130
|
+
- You intentionally changed an API contract
|
|
131
|
+
- You removed deprecated functionality
|
|
132
|
+
- You renamed fields/methods
|
|
133
|
+
- The old behavior is being replaced (not extended)
|
|
134
|
+
- It's documented in your story requirements
|
|
135
|
+
|
|
136
|
+
**❌ DON'T update existing tests when:**
|
|
137
|
+
- You're not sure why they're failing
|
|
138
|
+
- The failure seems unrelated to your story
|
|
139
|
+
- Multiple unrelated tests are breaking
|
|
140
|
+
- The test was testing important existing functionality
|
|
141
|
+
|
|
142
|
+
**🔍 INVESTIGATE when:**
|
|
143
|
+
- More than 2-3 existing tests fail
|
|
144
|
+
- Tests in unrelated modules fail
|
|
145
|
+
- Test failure messages are unclear
|
|
146
|
+
- You suspect a side effect
|
|
147
|
+
|
|
148
|
+
## Process
|
|
149
|
+
|
|
150
|
+
1. **Run ALL tests** (not just story tests)
|
|
151
|
+
```bash
|
|
152
|
+
npm test
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
2. **If existing tests fail:**
|
|
156
|
+
```bash
|
|
157
|
+
# Identify which tests failed
|
|
158
|
+
# For each failing test, decide:
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
3. **For intentional changes:**
|
|
162
|
+
- Update test expectations
|
|
163
|
+
- Document change in commit message (when developer commits)
|
|
164
|
+
- Verify all tests pass
|
|
165
|
+
|
|
166
|
+
4. **For unclear failures:**
|
|
167
|
+
- Use `git show` to see old code
|
|
168
|
+
- Use `git diff` to see your changes
|
|
169
|
+
- Compare old vs new behavior
|
|
170
|
+
- Refine code to fix both old AND new tests
|
|
171
|
+
|
|
172
|
+
5. **Validate:**
|
|
173
|
+
```bash
|
|
174
|
+
# All tests (old + new) should pass
|
|
175
|
+
npm test
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Red Flags
|
|
179
|
+
|
|
180
|
+
🚩 **Warning signs of unintended side effects:**
|
|
181
|
+
- Tests in different modules failing
|
|
182
|
+
- Security/auth tests failing
|
|
183
|
+
- Tests that worked in `main` branch now fail
|
|
184
|
+
- Tests with names unrelated to your story failing
|
|
185
|
+
|
|
186
|
+
**When you see red flags:**
|
|
187
|
+
1. STOP updating tests
|
|
188
|
+
2. Use git to investigate
|
|
189
|
+
3. Fix the code, not the tests
|
|
190
|
+
4. Ask developer if uncertain
|
|
191
|
+
|
|
192
|
+
## Remember
|
|
193
|
+
|
|
194
|
+
- **Existing tests are documentation** of expected behavior
|
|
195
|
+
- **Don't break working functionality** to make new tests pass
|
|
196
|
+
- **Use git freely** for investigation (NOT for commits)
|
|
197
|
+
- **When in doubt, preserve backward compatibility**
|