@lenne.tech/cli 1.0.1 → 1.0.2
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 +256 -0
- package/build/commands/claude/install-skills.js +90 -23
- package/build/lib/mcp-registry.js +71 -0
- package/build/templates/claude-commands/commit-message.md +21 -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
|
@@ -0,0 +1,1004 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: story-tdd-workflow
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: Complete 7-step TDD workflow with detailed implementation steps, testing guidelines, and validation process
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Story-Based TDD Workflow - The Seven Steps
|
|
8
|
+
|
|
9
|
+
## Table of Contents
|
|
10
|
+
- [Step 1: Story Analysis & Validation](#step-1-story-analysis--validation)
|
|
11
|
+
- [Step 2: Create Story Test](#step-2-create-story-test)
|
|
12
|
+
- [Step 3: Run Tests & Analyze Failures](#step-3-run-tests--analyze-failures)
|
|
13
|
+
- [Step 3a: Fix Test Errors (if needed)](#step-3a-fix-test-errors-if-needed)
|
|
14
|
+
- [Step 4: Implement/Extend API Code](#step-4-implementextend-api-code)
|
|
15
|
+
- [Step 5: Validate & Iterate](#step-5-validate--iterate)
|
|
16
|
+
- [Step 5a: Code Quality & Refactoring Check](#step-5a-code-quality--refactoring-check)
|
|
17
|
+
- [Step 5b: Final Validation](#step-5b-final-validation)
|
|
18
|
+
|
|
19
|
+
This skill follows a rigorous 7-step iterative process (with Steps 5, 5a, 5b for final validation and refactoring):
|
|
20
|
+
|
|
21
|
+
## Step 1: Story Analysis & Validation
|
|
22
|
+
|
|
23
|
+
**Before writing ANY code or tests:**
|
|
24
|
+
|
|
25
|
+
1. **Read and analyze the complete user story/requirement**
|
|
26
|
+
- Identify all functional requirements
|
|
27
|
+
- List all acceptance criteria
|
|
28
|
+
- Note any technical constraints
|
|
29
|
+
|
|
30
|
+
2. **🔍 VERIFY existing API structure - NEVER assume!**
|
|
31
|
+
- **Read actual Controller files** to verify endpoints exist:
|
|
32
|
+
- Check HTTP methods (GET, POST, PUT, DELETE)
|
|
33
|
+
- Verify exact endpoint paths (e.g., `/api/users` vs `/users`)
|
|
34
|
+
- Confirm request/response structures
|
|
35
|
+
- **Read actual Resolver files** for GraphQL:
|
|
36
|
+
- Verify mutation/query names exist
|
|
37
|
+
- Check input types and field names
|
|
38
|
+
- Confirm return types
|
|
39
|
+
- **Read existing test files** to understand patterns:
|
|
40
|
+
- How are endpoints called in practice?
|
|
41
|
+
- What authentication is used?
|
|
42
|
+
- What response structure is expected?
|
|
43
|
+
- **Document what EXISTS vs what NEEDS to be created:**
|
|
44
|
+
- Existing: `/api/products` GET, POST (verified in product.controller.ts:45)
|
|
45
|
+
- Missing: `/api/products/:id/reviews` POST (needs implementation)
|
|
46
|
+
|
|
47
|
+
3. **Identify contradictions or ambiguities**
|
|
48
|
+
- Look for conflicting requirements
|
|
49
|
+
- Check for unclear specifications
|
|
50
|
+
- Verify if requirements match existing architecture
|
|
51
|
+
- **Verify assumed endpoints actually exist!**
|
|
52
|
+
|
|
53
|
+
4. **Ask developer for clarification IMMEDIATELY if needed**
|
|
54
|
+
- Don't assume or guess requirements
|
|
55
|
+
- Don't assume endpoints exist without verification
|
|
56
|
+
- Clarify contradictions BEFORE writing tests
|
|
57
|
+
- Get confirmation on architectural decisions
|
|
58
|
+
- Verify security/permission requirements
|
|
59
|
+
|
|
60
|
+
**⚠️ CRITICAL:** If you find ANY contradictions or ambiguities, STOP and use AskUserQuestion to clarify BEFORE proceeding to Step 2.
|
|
61
|
+
|
|
62
|
+
**⚠️ CRITICAL:** If you assume an endpoint exists but didn't verify it in the code, you are doing it WRONG! Always read the actual controller/resolver files first.
|
|
63
|
+
|
|
64
|
+
**Step 1 Checklist:**
|
|
65
|
+
- [ ] Story completely read and understood
|
|
66
|
+
- [ ] All functional requirements identified
|
|
67
|
+
- [ ] All acceptance criteria listed
|
|
68
|
+
- [ ] Existing API structure verified (Controllers/Resolvers read)
|
|
69
|
+
- [ ] Documented what EXISTS vs what NEEDS creation
|
|
70
|
+
- [ ] No contradictions or ambiguities (or clarified with developer)
|
|
71
|
+
- [ ] Ready for Step 2
|
|
72
|
+
|
|
73
|
+
## Step 2: Create Story Test
|
|
74
|
+
|
|
75
|
+
**🔍 BEFORE Creating New Tests - Check Existing Tests First!**
|
|
76
|
+
|
|
77
|
+
**CRITICAL:** Before writing ANY new test, verify that the functionality isn't already tested!
|
|
78
|
+
|
|
79
|
+
1. **Search existing tests** in `tests/` directory:
|
|
80
|
+
- Look for tests covering the same endpoints/mutations
|
|
81
|
+
- Check if existing tests already validate the behavior
|
|
82
|
+
- Identify tests that might need updates due to story changes
|
|
83
|
+
|
|
84
|
+
2. **If functionality is already tested:**
|
|
85
|
+
- ✅ **DO NOT** create duplicate tests
|
|
86
|
+
- ✅ **Extend** existing tests if new edge cases are needed
|
|
87
|
+
- ✅ **Update** existing tests if the story changes expected behavior
|
|
88
|
+
|
|
89
|
+
3. **If story changes require modifying existing tests:**
|
|
90
|
+
- ⚠️ **ALWAYS inform the user** about which tests will be modified and why
|
|
91
|
+
- ⚠️ **Only modify tests** when story requirements explicitly change the expected behavior
|
|
92
|
+
- ❌ **NEVER modify tests just because they fail** - failing tests indicate bugs in implementation!
|
|
93
|
+
|
|
94
|
+
**🚨 CRITICAL RULE: Tests Protect Against Unintended Side Effects!**
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
Test fails after your changes?
|
|
98
|
+
│
|
|
99
|
+
├─► Does the story EXPLICITLY require this behavior change?
|
|
100
|
+
│ │
|
|
101
|
+
│ ├─► YES (documented in story requirements):
|
|
102
|
+
│ │ └─► ✅ Update the test AND inform the user:
|
|
103
|
+
│ │ "Updating test X because story requires behavior Y"
|
|
104
|
+
│ │
|
|
105
|
+
│ └─► NO (not mentioned in story):
|
|
106
|
+
│ └─► ❌ DO NOT modify the test!
|
|
107
|
+
│ └─► Fix your implementation instead
|
|
108
|
+
│ (you introduced an unintended side effect)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Example - WRONG approach:**
|
|
112
|
+
```typescript
|
|
113
|
+
// Test fails: "expected status 200, got 401"
|
|
114
|
+
// ❌ WRONG: Just change the expected status
|
|
115
|
+
expect(response.status).toBe(401); // Changed from 200 to make test pass
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Example - CORRECT approach:**
|
|
119
|
+
```typescript
|
|
120
|
+
// Test fails: "expected status 200, got 401"
|
|
121
|
+
// ✅ CORRECT: Investigate WHY it fails
|
|
122
|
+
// → Found: Missing authentication token in new implementation
|
|
123
|
+
// → Fix: Add proper authentication, keep test expecting 200
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**When to inform user about test changes:**
|
|
127
|
+
- "Modifying `user-registration.story.test.ts` - story now requires email verification before login"
|
|
128
|
+
- "Updating expected response in `product-search.test.ts` - story adds new `category` field to response"
|
|
129
|
+
- "Adjusting test data in `order-processing.test.ts` - story changes minimum order amount from 10 to 20"
|
|
130
|
+
|
|
131
|
+
**📖 For detailed guidance on handling failing tests, see: `handling-existing-tests.md`**
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
**🚨 CRITICAL: ALWAYS TEST THROUGH API - NEVER DIRECT SERVICE/DB ACCESS! 🚨**
|
|
136
|
+
|
|
137
|
+
**FUNDAMENTAL RULE - Read This First:**
|
|
138
|
+
|
|
139
|
+
Tests MUST go through REST/GraphQL interfaces (Controller/Resolver) using TestHelper. Direct Service or Database access in test logic makes tests WORTHLESS because they bypass the actual API layer that users interact with.
|
|
140
|
+
|
|
141
|
+
**✅ ALWAYS DO:**
|
|
142
|
+
- ✅ Test via REST endpoints: `testHelper.rest('/api/users', { method: 'POST', ... })`
|
|
143
|
+
- ✅ Test via GraphQL: `testHelper.graphQl('mutation { createUser(...) }', { ... })`
|
|
144
|
+
- ✅ Use TestHelper for ALL functional testing
|
|
145
|
+
- ✅ Test the complete chain: Controller/Resolver → Guards → Service → Database
|
|
146
|
+
|
|
147
|
+
**❌ NEVER DO:**
|
|
148
|
+
- ❌ Direct Service calls: `userService.create()` - bypasses authentication!
|
|
149
|
+
- ❌ Direct DB queries in tests: `db.collection('users').findOne()` - bypasses business logic!
|
|
150
|
+
- ❌ Service instantiation: `new UserService()` - bypasses dependency injection!
|
|
151
|
+
- ❌ Mocking Controllers or Resolvers - defeats the purpose!
|
|
152
|
+
|
|
153
|
+
**Why This Rule Is Absolute:**
|
|
154
|
+
- **Security:** Direct Service access bypasses authentication, authorization, guards, decorators
|
|
155
|
+
- **Reality:** Tests must verify what actual users experience through the API
|
|
156
|
+
- **Business Logic:** Services might have additional validation that gets bypassed
|
|
157
|
+
- **Worthless Tests:** Tests that bypass the API cannot catch real bugs
|
|
158
|
+
|
|
159
|
+
**🔓 RARE Exceptions - Only for Test Setup/Cleanup (NOT for testing functionality):**
|
|
160
|
+
|
|
161
|
+
Direct database access is ONLY allowed in these specific cases:
|
|
162
|
+
|
|
163
|
+
**✅ Allowed in beforeAll/beforeEach/afterAll/afterEach:**
|
|
164
|
+
- Setting user roles: `await db.collection('users').updateOne({ _id: userId }, { $set: { roles: ['admin'] } })`
|
|
165
|
+
- Setting verified status: `await db.collection('users').updateOne({ _id: userId }, { $set: { verified: true } })`
|
|
166
|
+
- Cleanup: `await db.collection('products').deleteMany({ createdBy: testUserId })`
|
|
167
|
+
- Read-only verification when NO API endpoint exists: `const count = await db.collection('logs').countDocuments()`
|
|
168
|
+
|
|
169
|
+
**⚠️ Ask Yourself First:**
|
|
170
|
+
Before using direct DB/Service access, ask:
|
|
171
|
+
1. Can I do this via an API endpoint? → If YES, use the API!
|
|
172
|
+
2. Am I testing functionality? → If YES, MUST use API!
|
|
173
|
+
3. Is this just setup/cleanup? → Only then consider direct access
|
|
174
|
+
4. Am I setting roles/verified status? → Allowed exception
|
|
175
|
+
5. Am I reading data that has NO API endpoint? → Allowed, but prefer API
|
|
176
|
+
|
|
177
|
+
**❌ Still NEVER Allowed - Even in Setup:**
|
|
178
|
+
- ❌ Testing functionality via Services
|
|
179
|
+
- ❌ Creating test data via Services when API exists
|
|
180
|
+
- ❌ Verifying results via DB when API query exists
|
|
181
|
+
- ❌ Writing to DB for anything other than roles/verified/cleanup
|
|
182
|
+
|
|
183
|
+
**Example of correct usage:**
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
describe('User Registration Story', () => {
|
|
187
|
+
let testHelper: TestHelper;
|
|
188
|
+
let db: Db;
|
|
189
|
+
let createdUserId: string;
|
|
190
|
+
|
|
191
|
+
beforeAll(async () => {
|
|
192
|
+
testHelper = new TestHelper(app);
|
|
193
|
+
db = app.get<Connection>(getConnectionToken()).db;
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
afterAll(async () => {
|
|
197
|
+
// ✅ ALLOWED: Direct DB access for cleanup
|
|
198
|
+
if (createdUserId) {
|
|
199
|
+
await db.collection('users').deleteOne({ _id: new ObjectId(createdUserId) });
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('should allow new user to register with valid data', async () => {
|
|
204
|
+
// ✅ CORRECT: Test via API
|
|
205
|
+
const result = await testHelper.rest('/auth/signup', {
|
|
206
|
+
method: 'POST',
|
|
207
|
+
payload: {
|
|
208
|
+
email: 'newuser@test.com',
|
|
209
|
+
password: 'SecurePass123!',
|
|
210
|
+
firstName: 'John',
|
|
211
|
+
lastName: 'Doe'
|
|
212
|
+
},
|
|
213
|
+
statusCode: 201
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
expect(result.id).toBeDefined();
|
|
217
|
+
expect(result.email).toBe('newuser@test.com');
|
|
218
|
+
createdUserId = result.id;
|
|
219
|
+
|
|
220
|
+
// ✅ ALLOWED: Set verified flag for subsequent tests
|
|
221
|
+
await db.collection('users').updateOne(
|
|
222
|
+
{ _id: new ObjectId(createdUserId) },
|
|
223
|
+
{ $set: { verified: true } }
|
|
224
|
+
);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('should allow verified user to sign in', async () => {
|
|
228
|
+
// ✅ CORRECT: Test via API
|
|
229
|
+
const result = await testHelper.rest('/auth/signin', {
|
|
230
|
+
method: 'POST',
|
|
231
|
+
payload: {
|
|
232
|
+
email: 'newuser@test.com',
|
|
233
|
+
password: 'SecurePass123!'
|
|
234
|
+
},
|
|
235
|
+
statusCode: 201
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
expect(result.token).toBeDefined();
|
|
239
|
+
expect(result.user.email).toBe('newuser@test.com');
|
|
240
|
+
|
|
241
|
+
// ❌ WRONG: Don't verify via direct DB access
|
|
242
|
+
// const dbUser = await db.collection('users').findOne({ email: 'newuser@test.com' });
|
|
243
|
+
|
|
244
|
+
// ✅ CORRECT: Verify via API
|
|
245
|
+
const profile = await testHelper.rest('/api/users/me', {
|
|
246
|
+
method: 'GET',
|
|
247
|
+
token: result.token,
|
|
248
|
+
statusCode: 200
|
|
249
|
+
});
|
|
250
|
+
expect(profile.email).toBe('newuser@test.com');
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
**🔍 BEFORE Writing Any Tests - Study the TestHelper:**
|
|
258
|
+
|
|
259
|
+
**CRITICAL: Read the TestHelper source file to understand all available features!**
|
|
260
|
+
|
|
261
|
+
```
|
|
262
|
+
node_modules/@lenne.tech/nest-server/src/test/test.helper.ts
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
This file documents ALL TestHelper capabilities:
|
|
266
|
+
- `rest()` and `graphQl()` methods with all options
|
|
267
|
+
- File uploads via `attachments` option
|
|
268
|
+
- Debugging with `log` and `logError` options in `TestRestOptions`
|
|
269
|
+
- Custom headers, status code validation
|
|
270
|
+
- Authentication token handling
|
|
271
|
+
|
|
272
|
+
**Study this file BEFORE writing tests** to avoid reinventing functionality that already exists!
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
**Location:** `tests/stories/` directory (create if it doesn't exist)
|
|
277
|
+
|
|
278
|
+
**Directory Creation:**
|
|
279
|
+
If the `tests/stories/` directory doesn't exist yet, create it first:
|
|
280
|
+
```bash
|
|
281
|
+
mkdir -p tests/stories
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
**Naming Convention:** `{feature-name}.story.test.ts`
|
|
285
|
+
- Example: `user-registration.story.test.ts`
|
|
286
|
+
- Example: `product-search.story.test.ts`
|
|
287
|
+
- Example: `order-processing.story.test.ts`
|
|
288
|
+
|
|
289
|
+
**📁 File Organization - Avoid Too Many Files:**
|
|
290
|
+
|
|
291
|
+
**IMPORTANT:** Before creating a NEW test file, check if existing test files can be extended!
|
|
292
|
+
|
|
293
|
+
Story tests typically require significant setup (TestHelper, database connections, test users, etc.), so files naturally grow larger. A typical story test file ranges from 400-800 lines, with complex features reaching 1000+ lines.
|
|
294
|
+
|
|
295
|
+
**✅ PREFER extending existing files when:**
|
|
296
|
+
- The new tests relate to the same feature/module
|
|
297
|
+
- The existing file is not excessively large (< 1000 lines)
|
|
298
|
+
- The tests share similar setup/teardown logic
|
|
299
|
+
- It makes logical sense to group them together
|
|
300
|
+
|
|
301
|
+
**✅ CREATE new files when:**
|
|
302
|
+
- Testing a completely different feature/module
|
|
303
|
+
- The existing file would exceed ~1000-1200 lines
|
|
304
|
+
- The tests require significantly different setup
|
|
305
|
+
- It improves clarity and maintainability
|
|
306
|
+
|
|
307
|
+
**Example:**
|
|
308
|
+
```
|
|
309
|
+
tests/stories/
|
|
310
|
+
user-authentication.story.test.ts # Login, logout, password reset, session handling
|
|
311
|
+
user-profile.story.test.ts # Profile CRUD, settings, preferences
|
|
312
|
+
product-management.story.test.ts # Product CRUD, variants, pricing
|
|
313
|
+
order-processing.story.test.ts # Cart, checkout, payment, fulfillment
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
**Why this matters:**
|
|
317
|
+
- Too many small files → Hard to navigate, duplicate setup code, redundant boilerplate
|
|
318
|
+
- Too few large files → Hard to read, slow to run, merge conflicts
|
|
319
|
+
- Balance: Group related tests, split when files grow beyond ~1000 lines
|
|
320
|
+
|
|
321
|
+
**🔍 BEFORE Writing Tests - Verify Your Assumptions:**
|
|
322
|
+
|
|
323
|
+
**CRITICAL: Only write tests for endpoints that you have VERIFIED exist in the code!**
|
|
324
|
+
|
|
325
|
+
1. **For REST endpoints:**
|
|
326
|
+
```typescript
|
|
327
|
+
// ✅ CORRECT: Verified endpoint exists in user.controller.ts
|
|
328
|
+
await testHelper.rest('/api/users', { method: 'POST', ... });
|
|
329
|
+
|
|
330
|
+
// ❌ WRONG: Assumed endpoint without verification
|
|
331
|
+
await testHelper.rest('/api/users/profile', { method: 'PUT', ... }); // Does this exist?
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
2. **For GraphQL mutations/queries:**
|
|
335
|
+
```typescript
|
|
336
|
+
// ✅ CORRECT: Verified 'createUser' mutation exists in user.resolver.ts
|
|
337
|
+
await testHelper.graphQl({ name: 'createUser', type: TestGraphQLType.MUTATION, ... });
|
|
338
|
+
|
|
339
|
+
// ❌ WRONG: Assumed mutation without verification
|
|
340
|
+
await testHelper.graphQl({ name: 'updateUserProfile', ... }); // Does this exist?
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
3. **Document your verification:**
|
|
344
|
+
```typescript
|
|
345
|
+
// Test for user creation
|
|
346
|
+
// Verified: POST /api/users exists in src/server/modules/user/user.controller.ts:34
|
|
347
|
+
// Verified: Requires authentication (S_USER role)
|
|
348
|
+
// Verified: Returns User object with id, email, firstName, lastName
|
|
349
|
+
it('should create new user', async () => {
|
|
350
|
+
const result = await testHelper.rest('/api/users', {
|
|
351
|
+
method: 'POST',
|
|
352
|
+
payload: { email: 'test@example.com', ... },
|
|
353
|
+
token: adminToken,
|
|
354
|
+
statusCode: 201
|
|
355
|
+
});
|
|
356
|
+
// ...
|
|
357
|
+
});
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
**Test Structure:**
|
|
361
|
+
|
|
362
|
+
1. **Study existing story tests** (if any exist in `tests/stories/`)
|
|
363
|
+
- Follow established patterns and conventions
|
|
364
|
+
- Use similar setup/teardown approaches
|
|
365
|
+
- Match coding style and organization
|
|
366
|
+
|
|
367
|
+
2. **Study other test files** for patterns:
|
|
368
|
+
- Check `test/**/*.test.ts` files
|
|
369
|
+
- Understand authentication setup
|
|
370
|
+
- Learn data creation patterns
|
|
371
|
+
- See how API calls are made
|
|
372
|
+
|
|
373
|
+
3. **Write comprehensive story test** that includes:
|
|
374
|
+
- Clear test description matching the story
|
|
375
|
+
- Setup of test data and users
|
|
376
|
+
- All acceptance criteria as test cases
|
|
377
|
+
- Proper authentication/authorization
|
|
378
|
+
- Validation of responses and side effects
|
|
379
|
+
- Cleanup/teardown
|
|
380
|
+
|
|
381
|
+
4. **Ensure tests cover:**
|
|
382
|
+
- Happy path scenarios
|
|
383
|
+
- Edge cases
|
|
384
|
+
- Error conditions
|
|
385
|
+
- Security/permission checks
|
|
386
|
+
- Data validation
|
|
387
|
+
|
|
388
|
+
**Example test structure:**
|
|
389
|
+
```typescript
|
|
390
|
+
describe('User Registration Story', () => {
|
|
391
|
+
let createdUserIds: string[] = [];
|
|
392
|
+
let createdProductIds: string[] = [];
|
|
393
|
+
|
|
394
|
+
// Setup
|
|
395
|
+
beforeAll(async () => {
|
|
396
|
+
// Initialize test environment
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
afterAll(async () => {
|
|
400
|
+
// 🧹 CLEANUP: Delete ALL test data created during tests
|
|
401
|
+
// This prevents side effects on subsequent test runs
|
|
402
|
+
if (createdUserIds.length > 0) {
|
|
403
|
+
await db.collection('users').deleteMany({
|
|
404
|
+
_id: { $in: createdUserIds.map(id => new ObjectId(id)) }
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
if (createdProductIds.length > 0) {
|
|
408
|
+
await db.collection('products').deleteMany({
|
|
409
|
+
_id: { $in: createdProductIds.map(id => new ObjectId(id)) }
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
it('should allow new user to register with valid data', async () => {
|
|
415
|
+
// Test implementation
|
|
416
|
+
const user = await createUser(...);
|
|
417
|
+
createdUserIds.push(user.id); // Track for cleanup
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
it('should reject registration with invalid email', async () => {
|
|
421
|
+
// Test implementation
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it('should prevent duplicate email registration', async () => {
|
|
425
|
+
// Test implementation
|
|
426
|
+
});
|
|
427
|
+
});
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
**🚨 CRITICAL: Test Data Management for Parallel Execution**
|
|
431
|
+
|
|
432
|
+
**ALWAYS follow these rules to ensure tests can run in parallel safely!**
|
|
433
|
+
|
|
434
|
+
Tests run in parallel, so improper test data management causes:
|
|
435
|
+
- Conflicts between parallel tests (duplicate keys, race conditions)
|
|
436
|
+
- False positives/negatives in tests
|
|
437
|
+
- Flaky tests that pass/fail randomly
|
|
438
|
+
- Contaminated test database
|
|
439
|
+
- Hard-to-debug test failures
|
|
440
|
+
|
|
441
|
+
**📋 GOLDEN RULES for Test Data:**
|
|
442
|
+
|
|
443
|
+
1. **Email Addresses Must End with @test.com**
|
|
444
|
+
```typescript
|
|
445
|
+
// ✅ CORRECT: Will be excluded from external services (e2e.brevo.exclude)
|
|
446
|
+
// Includes timestamp + random suffix for uniqueness even within same millisecond
|
|
447
|
+
const testEmail = `user-${Date.now()}-${Math.random().toString(36).substring(2, 8)}@test.com`;
|
|
448
|
+
|
|
449
|
+
// ⚠️ LESS SAFE: Only timestamp (collision risk if tests run in same millisecond)
|
|
450
|
+
const testEmail = `user-${Date.now()}@test.com`;
|
|
451
|
+
|
|
452
|
+
// ❌ WRONG: Won't be excluded, may trigger external emails
|
|
453
|
+
const testEmail = 'testuser@example.com';
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
**Why:** Configuration in `src/config.env.ts` uses `e2e.brevo.exclude` to filter out @test.com emails from external services. The random suffix ensures uniqueness even when multiple tests run simultaneously.
|
|
457
|
+
|
|
458
|
+
2. **NEVER Reuse Same Data Across Test Files**
|
|
459
|
+
```typescript
|
|
460
|
+
// ❌ WRONG: user-story-1.test.ts and user-story-2.test.ts both use:
|
|
461
|
+
const email = 'admin@test.com'; // ❌ Conflict when running in parallel!
|
|
462
|
+
|
|
463
|
+
// ✅ CORRECT: Make data unique per test file with timestamp + random suffix
|
|
464
|
+
const email = `admin-user-story-1-${Date.now()}-${Math.random().toString(36).substring(2, 8)}@test.com`;
|
|
465
|
+
|
|
466
|
+
// ⚠️ LESS SAFE: Only timestamp
|
|
467
|
+
const email = `admin-user-story-1-${Date.now()}@test.com`;
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
**Why:** Tests run in parallel. Same email = duplicate key errors and race conditions. Random suffix prevents collisions within same millisecond.
|
|
471
|
+
|
|
472
|
+
3. **ONLY Delete What You Created in This Test File**
|
|
473
|
+
```typescript
|
|
474
|
+
// ❌ WRONG: Deletes ALL test users (affects parallel tests)
|
|
475
|
+
await db.collection('users').deleteMany({ email: /@test\.com$/ });
|
|
476
|
+
|
|
477
|
+
// ✅ CORRECT: Only delete tracked entities from THIS test
|
|
478
|
+
if (createdUserIds.length > 0) {
|
|
479
|
+
await db.collection('users').deleteMany({
|
|
480
|
+
_id: { $in: createdUserIds.map(id => new ObjectId(id)) }
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
**Why:** Deleting too much breaks parallel tests that are still running.
|
|
486
|
+
|
|
487
|
+
4. **ALL Created Entities Must Be Cleaned Up**
|
|
488
|
+
```typescript
|
|
489
|
+
// ✅ Track EVERY entity created
|
|
490
|
+
let createdUserIds: string[] = [];
|
|
491
|
+
let createdProductIds: string[] = [];
|
|
492
|
+
let createdOrderIds: string[] = [];
|
|
493
|
+
|
|
494
|
+
// ✅ Clean up ALL in afterAll
|
|
495
|
+
afterAll(async () => {
|
|
496
|
+
if (createdOrderIds.length > 0) {
|
|
497
|
+
await db.collection('orders').deleteMany({
|
|
498
|
+
_id: { $in: createdOrderIds.map(id => new ObjectId(id)) }
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
// ... clean up products, users, etc.
|
|
502
|
+
});
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
**Why:** Leftover data causes side effects in future test runs.
|
|
506
|
+
|
|
507
|
+
5. **NEVER Use Fixed Port Numbers**
|
|
508
|
+
```typescript
|
|
509
|
+
// ❌ WRONG: Fixed port causes conflicts in parallel tests
|
|
510
|
+
await app.listen(3000);
|
|
511
|
+
const response = await fetch('http://localhost:3000/api/users');
|
|
512
|
+
|
|
513
|
+
// ✅ CORRECT: NestJS assigns random ports automatically
|
|
514
|
+
await app.init(); // No port specified
|
|
515
|
+
// Use TestHelper - it handles ports automatically
|
|
516
|
+
const result = await testHelper.rest('/api/users', { ... });
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
**Why:** Parallel tests need different ports. NestJS assigns random available ports automatically. TestHelper abstracts this away.
|
|
520
|
+
|
|
521
|
+
**Cleanup Strategy:**
|
|
522
|
+
|
|
523
|
+
1. **Track all created entities:**
|
|
524
|
+
```typescript
|
|
525
|
+
let createdUserIds: string[] = [];
|
|
526
|
+
let createdProductIds: string[] = [];
|
|
527
|
+
let createdOrderIds: string[] = [];
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
2. **Add IDs immediately after creation:**
|
|
531
|
+
```typescript
|
|
532
|
+
const user = await testHelper.rest('/api/users', {
|
|
533
|
+
method: 'POST',
|
|
534
|
+
payload: userData,
|
|
535
|
+
token: adminToken,
|
|
536
|
+
});
|
|
537
|
+
createdUserIds.push(user.id); // ✅ Track for cleanup
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
3. **Delete ALL created entities in afterAll:**
|
|
541
|
+
```typescript
|
|
542
|
+
afterAll(async () => {
|
|
543
|
+
// Clean up all test data
|
|
544
|
+
if (createdOrderIds.length > 0) {
|
|
545
|
+
await db.collection('orders').deleteMany({
|
|
546
|
+
_id: { $in: createdOrderIds.map(id => new ObjectId(id)) }
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
if (createdProductIds.length > 0) {
|
|
550
|
+
await db.collection('products').deleteMany({
|
|
551
|
+
_id: { $in: createdProductIds.map(id => new ObjectId(id)) }
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
if (createdUserIds.length > 0) {
|
|
555
|
+
await db.collection('users').deleteMany({
|
|
556
|
+
_id: { $in: createdUserIds.map(id => new ObjectId(id)) }
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
await connection.close();
|
|
561
|
+
await app.close();
|
|
562
|
+
});
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
4. **Clean up in correct order:**
|
|
566
|
+
- Delete child entities first (e.g., Orders before Products)
|
|
567
|
+
- Delete parent entities last (e.g., Users last)
|
|
568
|
+
- Consider foreign key relationships
|
|
569
|
+
|
|
570
|
+
5. **Handle cleanup errors gracefully:**
|
|
571
|
+
```typescript
|
|
572
|
+
afterAll(async () => {
|
|
573
|
+
try {
|
|
574
|
+
// Cleanup operations
|
|
575
|
+
if (createdUserIds.length > 0) {
|
|
576
|
+
await db.collection('users').deleteMany({
|
|
577
|
+
_id: { $in: createdUserIds.map(id => new ObjectId(id)) }
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
} catch (error) {
|
|
581
|
+
console.error('Cleanup failed:', error);
|
|
582
|
+
// Don't throw - cleanup failures shouldn't fail the test suite
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
await connection.close();
|
|
586
|
+
await app.close();
|
|
587
|
+
});
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
**What to clean up:**
|
|
591
|
+
- ✅ Users created during tests
|
|
592
|
+
- ✅ Products/Resources created during tests
|
|
593
|
+
- ✅ Orders/Transactions created during tests
|
|
594
|
+
- ✅ Any relationships (comments, reviews, etc.)
|
|
595
|
+
- ✅ Files uploaded during tests
|
|
596
|
+
- ✅ Any other test data that persists
|
|
597
|
+
|
|
598
|
+
**What NOT to clean up:**
|
|
599
|
+
- ❌ Global test users created in `beforeAll` that are reused (clean these once at the end)
|
|
600
|
+
- ❌ Database connections (close these separately)
|
|
601
|
+
- ❌ The app instance (close this separately)
|
|
602
|
+
|
|
603
|
+
**Step 2 Checklist:**
|
|
604
|
+
- [ ] Test file created in tests/stories/
|
|
605
|
+
- [ ] Endpoints verified before writing tests
|
|
606
|
+
- [ ] ALL tests use TestHelper (rest() or graphQl())
|
|
607
|
+
- [ ] NO direct Service or DB access in test logic
|
|
608
|
+
- [ ] Existing test patterns studied and followed
|
|
609
|
+
- [ ] All acceptance criteria covered
|
|
610
|
+
- [ ] Cleanup implemented in afterAll
|
|
611
|
+
- [ ] All test entities tracked for cleanup
|
|
612
|
+
- [ ] Ready for Step 3
|
|
613
|
+
|
|
614
|
+
## Step 3: Run Tests & Analyze Failures
|
|
615
|
+
|
|
616
|
+
**Execute all tests:**
|
|
617
|
+
```bash
|
|
618
|
+
npm test
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
**Or run specific story test:**
|
|
622
|
+
```bash
|
|
623
|
+
npm test -- tests/stories/your-story.story.test.ts
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
**Analyze results:**
|
|
627
|
+
1. Record which tests fail and why
|
|
628
|
+
2. Identify if failures are due to:
|
|
629
|
+
- Missing implementation (expected)
|
|
630
|
+
- Test errors/bugs (needs fixing)
|
|
631
|
+
- Misunderstood requirements (needs clarification)
|
|
632
|
+
|
|
633
|
+
**Decision point:**
|
|
634
|
+
- If test has bugs/errors → Go to Step 3a
|
|
635
|
+
- If API implementation is missing/incomplete → Go to Step 4
|
|
636
|
+
|
|
637
|
+
**Debugging Test Failures:**
|
|
638
|
+
|
|
639
|
+
If test failures are unclear, enable debugging tools:
|
|
640
|
+
- **TestHelper:** Add `log: true, logError: true` to test options for detailed output
|
|
641
|
+
- **Server logging:** Set `logExceptions: true` in `src/config.env.ts`
|
|
642
|
+
- **Validation debugging:** Set `DEBUG_VALIDATION=true` environment variable
|
|
643
|
+
|
|
644
|
+
See **reference.md** for detailed debugging instructions and examples.
|
|
645
|
+
|
|
646
|
+
## Step 3a: Fix Test Errors (if needed)
|
|
647
|
+
|
|
648
|
+
**Only fix tests if:**
|
|
649
|
+
- Test logic is incorrect
|
|
650
|
+
- Test has programming errors
|
|
651
|
+
- Test makes nonsensical demands
|
|
652
|
+
- Test doesn't match actual requirements
|
|
653
|
+
|
|
654
|
+
**Do NOT "fix" tests by:**
|
|
655
|
+
- Removing security checks to make them pass
|
|
656
|
+
- Lowering expectations to match incomplete implementation
|
|
657
|
+
- Skipping test cases that should work
|
|
658
|
+
|
|
659
|
+
**After fixing tests:**
|
|
660
|
+
- Return to Step 3 (run tests again)
|
|
661
|
+
|
|
662
|
+
## Step 4: Implement/Extend API Code
|
|
663
|
+
|
|
664
|
+
**Use the `nest-server-generator` skill for implementation:**
|
|
665
|
+
|
|
666
|
+
1. **Analyze what's needed:**
|
|
667
|
+
- New modules? → Use `nest-server-generator`
|
|
668
|
+
- New objects? → Use `nest-server-generator`
|
|
669
|
+
- New properties? → Use `nest-server-generator`
|
|
670
|
+
- Code modifications? → Use `nest-server-generator`
|
|
671
|
+
|
|
672
|
+
2. **🔍 Understand existing codebase first - VERIFY before using:**
|
|
673
|
+
- **Read actual Service files** before calling methods:
|
|
674
|
+
- Verify method names and signatures
|
|
675
|
+
- Check required parameters and types
|
|
676
|
+
- Confirm return types
|
|
677
|
+
- Example: Read `user.service.ts` to verify `findById(id: string): Promise<User>` exists
|
|
678
|
+
- **Read actual Model files** to understand data structures:
|
|
679
|
+
- Verify field names and types
|
|
680
|
+
- Check validation rules
|
|
681
|
+
- Confirm relationships
|
|
682
|
+
- **Study @lenne.tech/nest-server patterns** (in `node_modules/@lenne.tech/nest-server/src`):
|
|
683
|
+
- Check CrudService base class for available methods (in `node_modules/@lenne.tech/nest-server/src/core/common/services/crud.service.ts`)
|
|
684
|
+
- Check RoleEnum (in the project or `node_modules/@lenne.tech/nest-server/src/core/common/enums/role.enum.ts`)
|
|
685
|
+
- Understand decorators: @Roles, @Restricted, @UnifiedField
|
|
686
|
+
- Study MapAndValidatePipe for validation logic (automatically activated via CoreModule - see `node_modules/@lenne.tech/nest-server/src/core/common/pipes/map-and-validate.pipe.ts`)
|
|
687
|
+
- **Review existing similar implementations** - don't assume, verify!
|
|
688
|
+
|
|
689
|
+
**⚠️ CRITICAL:** Don't assume methods or properties exist - READ THE CODE to verify!
|
|
690
|
+
|
|
691
|
+
2a. **🚨 CRITICAL: Property Descriptions with German Comments**
|
|
692
|
+
|
|
693
|
+
**When user provides German comments/descriptions for properties, you MUST preserve them correctly!**
|
|
694
|
+
|
|
695
|
+
**Rule: `ENGLISH (GERMAN)` format**
|
|
696
|
+
- German: `// Produktname` → Description: `'Product name (Produktname)'`
|
|
697
|
+
- German: `// Straße` → Description: `'Street (Straße)'`
|
|
698
|
+
- English: `// Product name` → Description: `'Product name'` (no translation)
|
|
699
|
+
|
|
700
|
+
**Process:**
|
|
701
|
+
1. ✅ Extract ALL comments from user requirements (after `//`)
|
|
702
|
+
2. ✅ Translate German to English, keep German in parentheses
|
|
703
|
+
3. ✅ Fix spelling errors but preserve exact wording
|
|
704
|
+
4. ✅ Apply SAME description to: Model, CreateInput, UpdateInput, @ObjectType, @InputType
|
|
705
|
+
5. ❌ NEVER change wording (e.g., `Straße` → `Straßenname` is WRONG!)
|
|
706
|
+
6. ❌ NEVER skip German original in parentheses
|
|
707
|
+
|
|
708
|
+
**Example from user requirements:**
|
|
709
|
+
```
|
|
710
|
+
Module: Product
|
|
711
|
+
- name: string // Produktname
|
|
712
|
+
- price: number // Price
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
**Correct implementation in ALL locations:**
|
|
716
|
+
```typescript
|
|
717
|
+
// In product.model.ts:
|
|
718
|
+
@UnifiedField({ description: 'Product name (Produktname)' })
|
|
719
|
+
name: string;
|
|
720
|
+
|
|
721
|
+
@UnifiedField({ description: 'Price' })
|
|
722
|
+
price: number;
|
|
723
|
+
|
|
724
|
+
// In product.input.ts (CreateInput, UpdateInput):
|
|
725
|
+
@UnifiedField({ description: 'Product name (Produktname)' })
|
|
726
|
+
name: string;
|
|
727
|
+
|
|
728
|
+
@UnifiedField({ description: 'Price' })
|
|
729
|
+
price: number;
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
**See `nest-server-generator` skill → `description-management.md` for complete details.**
|
|
733
|
+
|
|
734
|
+
3. **🚨 CRITICAL: ServiceOptions when calling other Services:**
|
|
735
|
+
|
|
736
|
+
**NEVER blindly pass all ServiceOptions when one Service calls another!**
|
|
737
|
+
|
|
738
|
+
When implementing Service methods that call other Services, analyze which options to pass:
|
|
739
|
+
|
|
740
|
+
**❌ WRONG:**
|
|
741
|
+
```typescript
|
|
742
|
+
// ❌ BAD: Blindly passing all serviceOptions
|
|
743
|
+
const product = await this.productService.findOne({ id: input.productId }, serviceOptions);
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
**✅ CORRECT:**
|
|
747
|
+
```typescript
|
|
748
|
+
// ✅ GOOD: Only pass what's needed (usually just currentUser)
|
|
749
|
+
const product = await this.productService.findOne(
|
|
750
|
+
{ id: input.productId },
|
|
751
|
+
{ currentUser: serviceOptions.currentUser }
|
|
752
|
+
);
|
|
753
|
+
|
|
754
|
+
// ✅ GOOD: Only set inputType if different Input class is needed
|
|
755
|
+
const user = await this.userService.findOne(
|
|
756
|
+
{ id: input.userId },
|
|
757
|
+
{
|
|
758
|
+
currentUser: serviceOptions.currentUser,
|
|
759
|
+
inputType: UserInput // Only if specific Input class needed (e.g., UserInput, UserInputCreate)
|
|
760
|
+
}
|
|
761
|
+
);
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
**Why this matters:**
|
|
765
|
+
- **inputType** specifies which Input class (DTO) to use for validation (e.g., `UserInput`, `UserInputCreate`)
|
|
766
|
+
- The inputType from outer service might be wrong for inner service
|
|
767
|
+
- **roles** might need to be different
|
|
768
|
+
- Other options (limit, skip, etc.) might not apply
|
|
769
|
+
- Can cause incorrect permission checks or wrong validation
|
|
770
|
+
|
|
771
|
+
**Before passing options:**
|
|
772
|
+
- Analyze what's in serviceOptions (currentUser, inputType, roles, etc.)
|
|
773
|
+
- Determine what the target Service actually needs
|
|
774
|
+
- Only pass required options (usually just currentUser)
|
|
775
|
+
- Only set inputType if a specific Input class (DTO) is needed (e.g., UserInput, UserInputCreate)
|
|
776
|
+
|
|
777
|
+
4. **Implement equivalently to existing code:**
|
|
778
|
+
- Use TestHelper for REST oder GraphQL requests (in `node_modules/@lenne.tech/nest-server/src/test/test.helper.ts`)
|
|
779
|
+
- Use `getStringIds()` and `getObjectIds()` from `@lenne.tech/nest-server` for ObjectId conversions
|
|
780
|
+
- Match coding style and patterns
|
|
781
|
+
- Use same architectural approaches
|
|
782
|
+
- Follow established conventions
|
|
783
|
+
- Reuse existing utilities
|
|
784
|
+
|
|
785
|
+
4a. **🔐 IMPORTANT: Guards in Controllers**
|
|
786
|
+
|
|
787
|
+
**DO NOT manually add `@UseGuards(AuthGuard(AuthGuardStrategy.JWT))` - it's automatically activated by `@Roles()`!**
|
|
788
|
+
|
|
789
|
+
```typescript
|
|
790
|
+
// ✅ CORRECT: @Roles automatically activates JWT guard
|
|
791
|
+
@Roles(RoleEnum.ADMIN)
|
|
792
|
+
@Get()
|
|
793
|
+
async findAll() {
|
|
794
|
+
return this.service.find();
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// ✅ CORRECT: @Restricted also activates guards automatically
|
|
798
|
+
@Restricted()
|
|
799
|
+
@Post()
|
|
800
|
+
async create(@Body() input: CreateDto) {
|
|
801
|
+
return this.service.create(input);
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
// ❌ WRONG: Redundant manual guard (already included by @Roles)
|
|
805
|
+
@UseGuards(AuthGuard(AuthGuardStrategy.JWT))
|
|
806
|
+
@Roles(RoleEnum.ADMIN)
|
|
807
|
+
@Get()
|
|
808
|
+
async findAll() {
|
|
809
|
+
return this.service.find();
|
|
810
|
+
}
|
|
811
|
+
```
|
|
812
|
+
|
|
813
|
+
**Why this matters:**
|
|
814
|
+
- `@Roles()` decorator automatically applies `@UseGuards(RolesGuard)`
|
|
815
|
+
- `RolesGuard` internally uses JWT authentication
|
|
816
|
+
- Adding `@UseGuards(AuthGuard(...))` manually is redundant and creates duplicate guards
|
|
817
|
+
- Existing controllers don't use manual guards - follow this pattern
|
|
818
|
+
|
|
819
|
+
5. **🔍 IMPORTANT: Database Indexes**
|
|
820
|
+
|
|
821
|
+
**Always define indexes directly in the @UnifiedField decorator via mongoose option!**
|
|
822
|
+
|
|
823
|
+
**Quick Guidelines:**
|
|
824
|
+
- Fields used in queries → Add `mongoose: { index: true, type: String }`
|
|
825
|
+
- Foreign keys → Add index
|
|
826
|
+
- Unique fields → Add `mongoose: { index: true, unique: true, type: String }`
|
|
827
|
+
- ⚠️ NEVER define indexes separately in schema files
|
|
828
|
+
|
|
829
|
+
**📖 For detailed index patterns and examples, see: `database-indexes.md`**
|
|
830
|
+
|
|
831
|
+
6. **Prefer existing packages:**
|
|
832
|
+
- Check if @lenne.tech/nest-server provides needed functionality
|
|
833
|
+
- Only add new npm packages as last resort
|
|
834
|
+
- If new package needed, verify:
|
|
835
|
+
- High quality and well-maintained
|
|
836
|
+
- Frequently used (npm downloads)
|
|
837
|
+
- Active maintenance
|
|
838
|
+
- Free license (preferably MIT)
|
|
839
|
+
- Long-term viability
|
|
840
|
+
|
|
841
|
+
## Step 5: Validate & Iterate
|
|
842
|
+
|
|
843
|
+
**Run ALL tests:**
|
|
844
|
+
```bash
|
|
845
|
+
npm test
|
|
846
|
+
```
|
|
847
|
+
|
|
848
|
+
**Check results:**
|
|
849
|
+
|
|
850
|
+
✅ **All tests pass?**
|
|
851
|
+
- Continue to Step 5a (Code Quality Check)
|
|
852
|
+
|
|
853
|
+
❌ **Some tests still fail?**
|
|
854
|
+
- Return to Step 3 (analyze failures)
|
|
855
|
+
- Continue iteration
|
|
856
|
+
|
|
857
|
+
## Step 5a: Code Quality & Refactoring Check
|
|
858
|
+
|
|
859
|
+
**BEFORE marking the task as complete, perform a code quality review!**
|
|
860
|
+
|
|
861
|
+
Once all tests are passing, analyze your implementation for code quality issues:
|
|
862
|
+
|
|
863
|
+
### 1-3. Code Quality Review
|
|
864
|
+
|
|
865
|
+
**Check for:**
|
|
866
|
+
- Code duplication (extract to private methods if used 2+ times)
|
|
867
|
+
- Common functionality (create helper functions)
|
|
868
|
+
- Similar code paths (consolidate with flexible parameters)
|
|
869
|
+
- Consistency with existing patterns
|
|
870
|
+
|
|
871
|
+
**📖 For detailed refactoring patterns and examples, see: `code-quality.md`**
|
|
872
|
+
|
|
873
|
+
### 4. Review for Consistency
|
|
874
|
+
|
|
875
|
+
**Ensure consistent patterns throughout your implementation:**
|
|
876
|
+
- Naming conventions match existing codebase
|
|
877
|
+
- Error handling follows project patterns
|
|
878
|
+
- Return types are consistent
|
|
879
|
+
- Similar operations use similar approaches
|
|
880
|
+
|
|
881
|
+
### 4a. Check Database Indexes
|
|
882
|
+
|
|
883
|
+
**Verify that indexes are defined where needed:**
|
|
884
|
+
|
|
885
|
+
**Quick check:**
|
|
886
|
+
- Fields used in find/filter → Has index?
|
|
887
|
+
- Foreign keys (userId, productId, etc.) → Has index?
|
|
888
|
+
- Unique fields (email, username) → Has unique: true?
|
|
889
|
+
- Fields used in sorting → Has index?
|
|
890
|
+
|
|
891
|
+
**If indexes are missing:**
|
|
892
|
+
- Add to @UnifiedField decorator (mongoose option)
|
|
893
|
+
- Re-run tests
|
|
894
|
+
- Document query pattern
|
|
895
|
+
|
|
896
|
+
**📖 For detailed verification checklist, see: `database-indexes.md`**
|
|
897
|
+
|
|
898
|
+
### 4b. Security Review
|
|
899
|
+
|
|
900
|
+
**🔐 CRITICAL: Perform security review before final testing!**
|
|
901
|
+
|
|
902
|
+
**ALWAYS review all code changes for security vulnerabilities.**
|
|
903
|
+
|
|
904
|
+
**Quick Security Check:**
|
|
905
|
+
- [ ] @Restricted/@Roles decorators NOT removed or weakened
|
|
906
|
+
- [ ] Ownership checks in place (users can only access own data)
|
|
907
|
+
- [ ] All inputs validated with proper DTOs
|
|
908
|
+
- [ ] Sensitive fields marked with hideField: true
|
|
909
|
+
- [ ] No injection vulnerabilities
|
|
910
|
+
- [ ] Error messages don't expose sensitive data
|
|
911
|
+
- [ ] Authorization tests pass
|
|
912
|
+
|
|
913
|
+
**Red Flags (STOP if found):**
|
|
914
|
+
- 🚩 @Restricted decorator removed
|
|
915
|
+
- 🚩 @Roles changed to more permissive
|
|
916
|
+
- 🚩 Missing ownership checks
|
|
917
|
+
- 🚩 Sensitive fields exposed
|
|
918
|
+
- 🚩 'any' type instead of DTO
|
|
919
|
+
|
|
920
|
+
**If ANY red flag found:**
|
|
921
|
+
1. STOP implementation
|
|
922
|
+
2. Fix security issue immediately
|
|
923
|
+
3. Re-run security checklist
|
|
924
|
+
4. Update tests to verify security
|
|
925
|
+
|
|
926
|
+
**📖 For complete security checklist with examples, see: `security-review.md`**
|
|
927
|
+
|
|
928
|
+
### 5. Refactoring Decision Tree
|
|
929
|
+
|
|
930
|
+
```
|
|
931
|
+
Code duplication detected?
|
|
932
|
+
│
|
|
933
|
+
├─► Used in 2+ places?
|
|
934
|
+
│ │
|
|
935
|
+
│ ├─► YES: Extract to private method
|
|
936
|
+
│ │ │
|
|
937
|
+
│ │ └─► Used across multiple services?
|
|
938
|
+
│ │ │
|
|
939
|
+
│ │ ├─► YES: Consider utility class/function
|
|
940
|
+
│ │ └─► NO: Keep as private method
|
|
941
|
+
│ │
|
|
942
|
+
│ └─► NO: Leave as-is (don't over-engineer)
|
|
943
|
+
│
|
|
944
|
+
└─► Complex logic block?
|
|
945
|
+
│
|
|
946
|
+
├─► Hard to understand?
|
|
947
|
+
│ └─► Extract to well-named method
|
|
948
|
+
│
|
|
949
|
+
└─► Simple and clear?
|
|
950
|
+
└─► Leave as-is
|
|
951
|
+
```
|
|
952
|
+
|
|
953
|
+
### 6. Run Tests After Refactoring & Security Review
|
|
954
|
+
|
|
955
|
+
**CRITICAL: After any refactoring, adding indexes, or security fixes:**
|
|
956
|
+
|
|
957
|
+
```bash
|
|
958
|
+
npm test
|
|
959
|
+
```
|
|
960
|
+
|
|
961
|
+
**Ensure:**
|
|
962
|
+
- ✅ All tests still pass
|
|
963
|
+
- ✅ No new failures introduced
|
|
964
|
+
- ✅ Code is more maintainable
|
|
965
|
+
- ✅ No functionality changed
|
|
966
|
+
- ✅ Indexes properly applied
|
|
967
|
+
- ✅ **Security checks still working (authorization tests pass)**
|
|
968
|
+
|
|
969
|
+
### 7. When to Skip Refactoring
|
|
970
|
+
|
|
971
|
+
**Don't refactor if:**
|
|
972
|
+
- Code is used in only ONE place
|
|
973
|
+
- Extraction would make code harder to understand
|
|
974
|
+
- The duplication is coincidental, not conceptual
|
|
975
|
+
- Time constraints don't allow for safe refactoring
|
|
976
|
+
|
|
977
|
+
**Remember:**
|
|
978
|
+
- **Working code > Perfect code**
|
|
979
|
+
- **Refactor only if it improves maintainability**
|
|
980
|
+
- **Always run tests after refactoring**
|
|
981
|
+
- **Always add indexes where queries are performed**
|
|
982
|
+
|
|
983
|
+
## Step 5b: Final Validation
|
|
984
|
+
|
|
985
|
+
**After refactoring (or deciding not to refactor):**
|
|
986
|
+
|
|
987
|
+
1. **Run ALL tests one final time:**
|
|
988
|
+
```bash
|
|
989
|
+
npm test
|
|
990
|
+
```
|
|
991
|
+
|
|
992
|
+
2. **Verify:**
|
|
993
|
+
- ✅ All tests pass
|
|
994
|
+
- ✅ Test coverage is adequate
|
|
995
|
+
- ✅ Code follows project patterns
|
|
996
|
+
- ✅ No obvious duplication
|
|
997
|
+
- ✅ Clean and maintainable
|
|
998
|
+
- ✅ **Security review completed**
|
|
999
|
+
- ✅ **No security vulnerabilities introduced**
|
|
1000
|
+
- ✅ **Authorization tests pass**
|
|
1001
|
+
|
|
1002
|
+
3. **Generate final report for developer**
|
|
1003
|
+
|
|
1004
|
+
4. **YOU'RE DONE!** 🎉
|