@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.
- package/build/commands/claude/install-commands.js +332 -0
- package/build/commands/claude/install-skills.js +5 -1
- package/build/commands/server/add-property.js +22 -41
- package/build/extensions/server.js +142 -46
- package/build/templates/claude-commands/code-cleanup.md +82 -0
- package/build/templates/claude-commands/mr-description-clipboard.md +48 -0
- package/build/templates/claude-commands/mr-description.md +33 -0
- package/build/templates/claude-commands/sec-review.md +62 -0
- package/build/templates/claude-commands/skill-optimize.md +140 -0
- package/build/templates/claude-commands/test-generate.md +45 -0
- package/build/templates/claude-skills/nest-server-generator/SKILL.md +372 -1314
- package/build/templates/claude-skills/nest-server-generator/configuration.md +279 -0
- package/build/templates/claude-skills/nest-server-generator/declare-keyword-warning.md +124 -0
- package/build/templates/claude-skills/nest-server-generator/description-management.md +217 -0
- package/build/templates/claude-skills/nest-server-generator/examples.md +131 -5
- package/build/templates/claude-skills/nest-server-generator/quality-review.md +855 -0
- package/build/templates/claude-skills/nest-server-generator/reference.md +67 -13
- package/build/templates/claude-skills/nest-server-generator/security-rules.md +358 -0
- package/build/templates/claude-skills/story-tdd/SKILL.md +1173 -0
- package/build/templates/claude-skills/story-tdd/code-quality.md +266 -0
- package/build/templates/claude-skills/story-tdd/database-indexes.md +173 -0
- package/build/templates/claude-skills/story-tdd/examples.md +1332 -0
- package/build/templates/claude-skills/story-tdd/reference.md +1180 -0
- package/build/templates/claude-skills/story-tdd/security-review.md +299 -0
- package/package.json +1 -1
|
@@ -0,0 +1,855 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nest-server-generator-quality-review
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: Comprehensive quality review guidelines before creating final report
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Phase 8: Pre-Report Quality Review
|
|
8
|
+
|
|
9
|
+
**CRITICAL**: Before creating the final report, you MUST perform a comprehensive quality review:
|
|
10
|
+
|
|
11
|
+
## Step 1: Identify All Changes
|
|
12
|
+
|
|
13
|
+
Use git to identify all created and modified files:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
git status --short
|
|
17
|
+
git diff --name-only
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
For each file, review:
|
|
21
|
+
- All newly created files
|
|
22
|
+
- All modified files
|
|
23
|
+
- File structure and organization
|
|
24
|
+
|
|
25
|
+
## Step 2: Test Management
|
|
26
|
+
|
|
27
|
+
**CRITICAL**: Ensure tests are created/updated for all changes:
|
|
28
|
+
|
|
29
|
+
### Step 2.1: Analyze Existing Tests FIRST
|
|
30
|
+
|
|
31
|
+
**BEFORE creating or modifying ANY tests, you MUST thoroughly analyze existing tests**:
|
|
32
|
+
|
|
33
|
+
1. **Identify all existing test files**:
|
|
34
|
+
```bash
|
|
35
|
+
# List all test directories and files
|
|
36
|
+
ls -la tests/
|
|
37
|
+
ls -la tests/modules/
|
|
38
|
+
find tests -name "*.e2e-spec.ts" -type f
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
2. **Read multiple existing test files completely**:
|
|
42
|
+
```bash
|
|
43
|
+
# Read at least 2-3 different module tests to understand patterns
|
|
44
|
+
cat tests/modules/user.e2e-spec.ts
|
|
45
|
+
cat tests/modules/<another-module>.e2e-spec.ts
|
|
46
|
+
|
|
47
|
+
# Also check the common and project test files
|
|
48
|
+
cat tests/common.e2e-spec.ts
|
|
49
|
+
cat tests/project.e2e-spec.ts
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
3. **CRITICAL: Understand the TestHelper thoroughly**:
|
|
53
|
+
|
|
54
|
+
**Before creating any tests, you MUST understand the TestHelper from @lenne.tech/nest-server**:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# Read the TestHelper source code to understand its capabilities
|
|
58
|
+
cat node_modules/@lenne.tech/nest-server/src/test/test.helper.ts
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Analyze the TestHelper to understand**:
|
|
62
|
+
- **Available methods**: What methods does TestHelper provide?
|
|
63
|
+
- **Configuration options**: How can TestHelper be configured?
|
|
64
|
+
- **GraphQL support**: How to use `graphQl()` method? What parameters does it accept?
|
|
65
|
+
- **REST support**: How to use `rest()` method? What parameters does it accept?
|
|
66
|
+
- **Authentication**: How does TestHelper handle tokens and authentication?
|
|
67
|
+
- **Request building**: How are requests constructed? What options are available?
|
|
68
|
+
- **Response handling**: How are responses processed? What format is returned?
|
|
69
|
+
- **Error handling**: How does TestHelper handle errors and failures?
|
|
70
|
+
- **Helper utilities**: What additional utilities are available?
|
|
71
|
+
|
|
72
|
+
**Document your findings**:
|
|
73
|
+
```typescript
|
|
74
|
+
// Example: Understanding TestHelper.graphQl()
|
|
75
|
+
// Method signature: graphQl(options: GraphQLOptions, config?: RequestConfig)
|
|
76
|
+
// GraphQLOptions: { name, type (QUERY/MUTATION), arguments, fields }
|
|
77
|
+
// RequestConfig: { token, statusCode, headers }
|
|
78
|
+
// Returns: Parsed response data or error
|
|
79
|
+
|
|
80
|
+
// Example: Understanding TestHelper.rest()
|
|
81
|
+
// Method signature: rest(method: HttpMethod, path: string, options?: RestOptions)
|
|
82
|
+
// HttpMethod: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
|
|
83
|
+
// RestOptions: { body, token, statusCode, headers }
|
|
84
|
+
// Returns: Response data or error
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Common TestHelper patterns to understand**:
|
|
88
|
+
- How to execute GraphQL queries/mutations with `graphQl()`
|
|
89
|
+
- How to execute REST requests with `rest()`
|
|
90
|
+
- How to pass authentication tokens (same for both methods)
|
|
91
|
+
- How to handle expected errors (statusCode parameter)
|
|
92
|
+
- How to work with response data
|
|
93
|
+
- How to structure test data
|
|
94
|
+
- When to use GraphQL vs REST methods
|
|
95
|
+
|
|
96
|
+
**Only after fully understanding TestHelper, proceed to next step.**
|
|
97
|
+
|
|
98
|
+
4. **Understand the testing approach used**:
|
|
99
|
+
- Which test framework? (Jest, Mocha, etc.)
|
|
100
|
+
- Which testing utilities? (@lenne.tech/nest-server testHelper, custom helpers)
|
|
101
|
+
- How is the test app initialized? (beforeAll setup)
|
|
102
|
+
- How are test users/auth handled?
|
|
103
|
+
- How is test data created and cleaned up?
|
|
104
|
+
- What assertion library? (expect, should, etc.)
|
|
105
|
+
- Are there custom matchers?
|
|
106
|
+
|
|
107
|
+
5. **Document the patterns you observe**:
|
|
108
|
+
- **Import patterns**: Which modules are imported? In what order?
|
|
109
|
+
- **Setup patterns**: How is beforeAll/beforeEach structured?
|
|
110
|
+
- **Auth patterns**: How do tests authenticate? Token handling?
|
|
111
|
+
- **Test structure**: Describe blocks organization? Test naming conventions?
|
|
112
|
+
- **CRUD patterns**: How are create/read/update/delete tested?
|
|
113
|
+
- **Assertion patterns**: What assertions are used? How detailed?
|
|
114
|
+
- **Cleanup patterns**: How is afterAll/afterEach structured?
|
|
115
|
+
- **Error testing**: How are failures/validations tested?
|
|
116
|
+
|
|
117
|
+
6. **Verify existing tests run successfully**:
|
|
118
|
+
```bash
|
|
119
|
+
# Run existing tests to ensure they pass
|
|
120
|
+
npm run test:e2e
|
|
121
|
+
|
|
122
|
+
# If any fail, understand why before proceeding
|
|
123
|
+
# Your new/modified tests MUST NOT break existing tests
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
7. **Create a mental checklist**:
|
|
127
|
+
- [ ] I have read and understand the TestHelper source code
|
|
128
|
+
- [ ] I understand TestHelper methods and configuration
|
|
129
|
+
- [ ] I understand how to use graphQl() method (GraphQL queries/mutations)
|
|
130
|
+
- [ ] I understand how to use rest() method (REST endpoints)
|
|
131
|
+
- [ ] I understand when to use graphQl() vs rest()
|
|
132
|
+
- [ ] I understand TestHelper authentication and error handling
|
|
133
|
+
- [ ] I understand which test helpers/utilities are used
|
|
134
|
+
- [ ] I understand the authentication/authorization pattern
|
|
135
|
+
- [ ] I understand the test data lifecycle (create/cleanup)
|
|
136
|
+
- [ ] I understand the assertion patterns
|
|
137
|
+
- [ ] I understand the error testing approach
|
|
138
|
+
- [ ] All existing tests pass before I make changes
|
|
139
|
+
|
|
140
|
+
**Only after completing this analysis, proceed to create or modify tests.**
|
|
141
|
+
|
|
142
|
+
### Step 2.1.1: Understanding Permissions and User Rights in Tests
|
|
143
|
+
|
|
144
|
+
**CRITICAL**: Before creating tests, you MUST understand the 3-layer permission system:
|
|
145
|
+
|
|
146
|
+
**Important Definitions**:
|
|
147
|
+
|
|
148
|
+
- **Admin User**: A user whose `roles` array contains `'admin'`
|
|
149
|
+
```typescript
|
|
150
|
+
// Example admin user
|
|
151
|
+
{
|
|
152
|
+
id: '123',
|
|
153
|
+
email: 'admin@test.com',
|
|
154
|
+
roles: ['admin', 'user'] // ← Contains 'admin'
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
- **Creator**: The user who created an object, identified by matching IDs
|
|
159
|
+
```typescript
|
|
160
|
+
// User who created the object
|
|
161
|
+
const user = { id: 'user-123', email: 'creator@test.com' };
|
|
162
|
+
|
|
163
|
+
// Object created by this user
|
|
164
|
+
const product = {
|
|
165
|
+
id: 'product-456',
|
|
166
|
+
name: 'Test Product',
|
|
167
|
+
createdBy: 'user-123' // ← Matches user.id → This user is the CREATOR
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
// Different user (NOT the creator)
|
|
171
|
+
const otherUser = { id: 'user-789', email: 'other@test.com' };
|
|
172
|
+
// otherUser.id !== product.createdBy → NOT the creator!
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**The Three Permission Layers**:
|
|
176
|
+
|
|
177
|
+
1. **Controller/Resolver Layer** (`@Roles()` decorator):
|
|
178
|
+
- Controls WHO can call the endpoint
|
|
179
|
+
- Example: `@Roles(RoleEnum.ADMIN)` → Only admins can call this endpoint
|
|
180
|
+
- Example: `@Roles(RoleEnum.S_USER)` → All signed-in users can call
|
|
181
|
+
|
|
182
|
+
2. **Service Layer** (`serviceOptions.roles` parameter):
|
|
183
|
+
- Controls what permissions are checked during service processing
|
|
184
|
+
- Example: Update/Delete often require `[RoleEnum.ADMIN, RoleEnum.S_CREATOR]`
|
|
185
|
+
- The creator can update/delete their own items
|
|
186
|
+
|
|
187
|
+
3. **Model Layer** (`securityCheck()` method):
|
|
188
|
+
- Controls WHAT data is returned to the user
|
|
189
|
+
- Standard implementation:
|
|
190
|
+
```typescript
|
|
191
|
+
securityCheck(user: User, force?: boolean) {
|
|
192
|
+
// Admins see everything (user.roles contains 'admin')
|
|
193
|
+
if (force || user?.hasRole(RoleEnum.ADMIN)) {
|
|
194
|
+
return this;
|
|
195
|
+
}
|
|
196
|
+
// Only creator can see their own data (user.id === this.createdBy)
|
|
197
|
+
if (!equalIds(user, this.createdBy)) {
|
|
198
|
+
return undefined; // Non-creator gets nothing!
|
|
199
|
+
}
|
|
200
|
+
return this;
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
- **Key checks**:
|
|
204
|
+
- `user?.hasRole(RoleEnum.ADMIN)` → Returns `true` if `user.roles.includes('admin')`
|
|
205
|
+
- `equalIds(user, this.createdBy)` → Returns `true` if `user.id === this.createdBy`
|
|
206
|
+
|
|
207
|
+
**Default Permission Behavior**:
|
|
208
|
+
- **Create**: Usually accessible to signed-in users (`RoleEnum.S_USER`)
|
|
209
|
+
- **Read/List**: Usually accessible to signed-in users, but securityCheck filters results
|
|
210
|
+
- **Update**: Only ADMIN or CREATOR (via `serviceOptions.roles` check)
|
|
211
|
+
- **Delete**: Only ADMIN or CREATOR (via `serviceOptions.roles` check)
|
|
212
|
+
|
|
213
|
+
**Analyzing Permissions Before Creating Tests**:
|
|
214
|
+
|
|
215
|
+
Before writing tests, check these 3 locations:
|
|
216
|
+
|
|
217
|
+
1. **Check Controller/Resolver decorators**:
|
|
218
|
+
```typescript
|
|
219
|
+
// In product.resolver.ts
|
|
220
|
+
@Roles(RoleEnum.ADMIN) // ← WHO can call this?
|
|
221
|
+
@Query(() => Product)
|
|
222
|
+
async getProduct(@Args('id') id: string) { ... }
|
|
223
|
+
|
|
224
|
+
@Roles(RoleEnum.S_USER) // ← All signed-in users
|
|
225
|
+
@Mutation(() => Product)
|
|
226
|
+
async createProduct(@Args('input') input: ProductCreateInput) { ... }
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
2. **Check Model/Object `@Restricted` decorators**:
|
|
230
|
+
```typescript
|
|
231
|
+
// In product.model.ts
|
|
232
|
+
@Restricted(RoleEnum.ADMIN) // ← Model-level restriction
|
|
233
|
+
export class Product extends CoreModel {
|
|
234
|
+
|
|
235
|
+
@Restricted(RoleEnum.ADMIN) // ← Property-level restriction
|
|
236
|
+
@UnifiedField()
|
|
237
|
+
internalNotes?: string;
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
3. **Check Model `securityCheck()` logic**:
|
|
242
|
+
```typescript
|
|
243
|
+
// In product.model.ts
|
|
244
|
+
securityCheck(user: User, force?: boolean) {
|
|
245
|
+
// Admin check: user.roles contains 'admin'
|
|
246
|
+
if (force || user?.hasRole(RoleEnum.ADMIN)) {
|
|
247
|
+
return this; // Admin sees all
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Custom logic: Allow public products for everyone
|
|
251
|
+
if (this.isPublic) {
|
|
252
|
+
return this;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Creator check: user.id === this.createdBy
|
|
256
|
+
if (!equalIds(user, this.createdBy)) {
|
|
257
|
+
return undefined; // Non-creator gets nothing
|
|
258
|
+
}
|
|
259
|
+
return this; // Creator sees their own product
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
**Creating Appropriate Test Users**:
|
|
264
|
+
|
|
265
|
+
Based on permission analysis, create appropriate test users:
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
describe('Product Module', () => {
|
|
269
|
+
let testHelper: TestHelper;
|
|
270
|
+
let adminToken: string;
|
|
271
|
+
let userToken: string;
|
|
272
|
+
let otherUserToken: string;
|
|
273
|
+
let createdProductId: string;
|
|
274
|
+
|
|
275
|
+
beforeAll(async () => {
|
|
276
|
+
testHelper = new TestHelper(app);
|
|
277
|
+
|
|
278
|
+
// Admin user (user.roles contains 'admin')
|
|
279
|
+
const adminAuth = await testHelper.graphQl({
|
|
280
|
+
name: 'signIn',
|
|
281
|
+
type: TestGraphQLType.MUTATION,
|
|
282
|
+
arguments: { email: 'admin@test.com', password: 'admin' },
|
|
283
|
+
fields: ['token', 'user { id email roles }']
|
|
284
|
+
});
|
|
285
|
+
adminToken = adminAuth.token;
|
|
286
|
+
// adminAuth.user.roles = ['admin', 'user'] ← Contains 'admin'
|
|
287
|
+
|
|
288
|
+
// Regular user (will be the creator of test objects)
|
|
289
|
+
const userAuth = await testHelper.graphQl({
|
|
290
|
+
name: 'signIn',
|
|
291
|
+
type: TestGraphQLType.MUTATION,
|
|
292
|
+
arguments: { email: 'user@test.com', password: 'user' },
|
|
293
|
+
fields: ['token', 'user { id email roles }']
|
|
294
|
+
});
|
|
295
|
+
userToken = userAuth.token;
|
|
296
|
+
// When this user creates an object → object.createdBy = userAuth.user.id
|
|
297
|
+
|
|
298
|
+
// Another regular user (will NOT be the creator)
|
|
299
|
+
const otherUserAuth = await testHelper.graphQl({
|
|
300
|
+
name: 'signIn',
|
|
301
|
+
type: TestGraphQLType.MUTATION,
|
|
302
|
+
arguments: { email: 'other@test.com', password: 'other' },
|
|
303
|
+
fields: ['token', 'user { id email roles }']
|
|
304
|
+
});
|
|
305
|
+
otherUserToken = otherUserAuth.token;
|
|
306
|
+
// otherUserAuth.user.id !== object.createdBy → NOT the creator
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
**Test Structure Based on Permissions**:
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
describe('Product Module', () => {
|
|
315
|
+
// ... setup with adminToken, userToken, otherUserToken
|
|
316
|
+
|
|
317
|
+
describe('Create Product', () => {
|
|
318
|
+
it('should create product as regular user', async () => {
|
|
319
|
+
const result = await testHelper.graphQl({
|
|
320
|
+
name: 'createProduct',
|
|
321
|
+
type: TestGraphQLType.MUTATION,
|
|
322
|
+
arguments: { input: { name: 'Test Product', price: 99.99 } },
|
|
323
|
+
fields: ['id', 'name', 'createdBy { id }']
|
|
324
|
+
}, { token: userToken }); // ← Created by userToken
|
|
325
|
+
|
|
326
|
+
expect(result.name).toBe('Test Product');
|
|
327
|
+
// result.createdBy.id now equals userAuth.user.id
|
|
328
|
+
// → userToken is the CREATOR of this product
|
|
329
|
+
createdProductId = result.id;
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
describe('Update Product', () => {
|
|
334
|
+
it('should update product as creator', async () => {
|
|
335
|
+
const result = await testHelper.graphQl({
|
|
336
|
+
name: 'updateProduct',
|
|
337
|
+
type: TestGraphQLType.MUTATION,
|
|
338
|
+
arguments: {
|
|
339
|
+
id: createdProductId,
|
|
340
|
+
input: { price: 89.99 }
|
|
341
|
+
},
|
|
342
|
+
fields: ['id', 'price']
|
|
343
|
+
}, { token: userToken }); // ← Creator: userAuth.user.id === product.createdBy
|
|
344
|
+
|
|
345
|
+
expect(result.price).toBe(89.99);
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it('should update product as admin', async () => {
|
|
349
|
+
const result = await testHelper.graphQl({
|
|
350
|
+
name: 'updateProduct',
|
|
351
|
+
type: TestGraphQLType.MUTATION,
|
|
352
|
+
arguments: {
|
|
353
|
+
id: createdProductId,
|
|
354
|
+
input: { price: 79.99 }
|
|
355
|
+
},
|
|
356
|
+
fields: ['id', 'price']
|
|
357
|
+
}, { token: adminToken }); // ← Admin: adminAuth.user.roles contains 'admin'
|
|
358
|
+
|
|
359
|
+
expect(result.price).toBe(79.99);
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
it('should fail to update product as non-creator', async () => {
|
|
363
|
+
const result = await testHelper.graphQl({
|
|
364
|
+
name: 'updateProduct',
|
|
365
|
+
type: TestGraphQLType.MUTATION,
|
|
366
|
+
arguments: {
|
|
367
|
+
id: createdProductId,
|
|
368
|
+
input: { price: 69.99 }
|
|
369
|
+
},
|
|
370
|
+
fields: ['id']
|
|
371
|
+
}, { token: otherUserToken, statusCode: 403 }); // ← Not creator: otherUserAuth.user.id !== product.createdBy
|
|
372
|
+
|
|
373
|
+
expect(result.errors).toBeDefined();
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
describe('Delete Product', () => {
|
|
378
|
+
it('should delete product as creator', async () => {
|
|
379
|
+
// First create a new product to delete
|
|
380
|
+
const created = await testHelper.graphQl({
|
|
381
|
+
name: 'createProduct',
|
|
382
|
+
type: TestGraphQLType.MUTATION,
|
|
383
|
+
arguments: { input: { name: 'To Delete', price: 50 } },
|
|
384
|
+
fields: ['id']
|
|
385
|
+
}, { token: userToken });
|
|
386
|
+
|
|
387
|
+
// Delete as creator
|
|
388
|
+
const result = await testHelper.graphQl({
|
|
389
|
+
name: 'deleteProduct',
|
|
390
|
+
type: TestGraphQLType.MUTATION,
|
|
391
|
+
arguments: { id: created.id },
|
|
392
|
+
fields: ['id']
|
|
393
|
+
}, { token: userToken }); // ← Creator: userAuth.user.id === created.createdBy
|
|
394
|
+
|
|
395
|
+
expect(result.id).toBe(created.id);
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
it('should fail to delete product as non-creator', async () => {
|
|
399
|
+
const result = await testHelper.graphQl({
|
|
400
|
+
name: 'deleteProduct',
|
|
401
|
+
type: TestGraphQLType.MUTATION,
|
|
402
|
+
arguments: { id: createdProductId },
|
|
403
|
+
fields: ['id']
|
|
404
|
+
}, { token: otherUserToken, statusCode: 403 }); // ← Not creator: otherUserAuth.user.id !== product.createdBy
|
|
405
|
+
|
|
406
|
+
expect(result.errors).toBeDefined();
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
it('should delete any product as admin', async () => {
|
|
410
|
+
const result = await testHelper.graphQl({
|
|
411
|
+
name: 'deleteProduct',
|
|
412
|
+
type: TestGraphQLType.MUTATION,
|
|
413
|
+
arguments: { id: createdProductId },
|
|
414
|
+
fields: ['id']
|
|
415
|
+
}, { token: adminToken }); // ← Admin: adminAuth.user.roles contains 'admin'
|
|
416
|
+
|
|
417
|
+
expect(result.id).toBe(createdProductId);
|
|
418
|
+
});
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
**Permission Testing Checklist**:
|
|
424
|
+
|
|
425
|
+
Before creating tests, verify:
|
|
426
|
+
|
|
427
|
+
- [ ] I have checked the `@Roles()` decorators in controllers/resolvers
|
|
428
|
+
- [ ] I have checked the `@Restricted()` decorators in models/objects
|
|
429
|
+
- [ ] I have reviewed the `securityCheck()` logic in models
|
|
430
|
+
- [ ] I understand who can CREATE items (usually S_USER)
|
|
431
|
+
- [ ] I understand who can READ items (S_USER + securityCheck filtering)
|
|
432
|
+
- [ ] I understand who can UPDATE items (usually ADMIN + S_CREATOR)
|
|
433
|
+
- [ ] I understand who can DELETE items (usually ADMIN + S_CREATOR)
|
|
434
|
+
- [ ] I have created appropriate test users (admin, creator, non-creator)
|
|
435
|
+
- [ ] My tests use the CREATOR token (user.id === object.createdBy) for update/delete operations
|
|
436
|
+
- [ ] My tests verify that non-creators (user.id !== object.createdBy) CANNOT update/delete
|
|
437
|
+
- [ ] My tests verify that admins (user.roles contains 'admin') CAN update/delete everything
|
|
438
|
+
|
|
439
|
+
**Common Permission Test Patterns**:
|
|
440
|
+
|
|
441
|
+
1. **Test with creator** (user.id === object.createdBy) → Should succeed
|
|
442
|
+
2. **Test with admin** (user.roles contains 'admin') → Should succeed
|
|
443
|
+
3. **Test with other user** (user.id !== object.createdBy) → Should fail (403)
|
|
444
|
+
|
|
445
|
+
**Only after understanding permissions, proceed to create tests.**
|
|
446
|
+
|
|
447
|
+
### Step 2.2: For Newly Created Modules
|
|
448
|
+
|
|
449
|
+
**CRITICAL: Follow the correct test folder structure**:
|
|
450
|
+
|
|
451
|
+
The project uses a specific test organization:
|
|
452
|
+
|
|
453
|
+
1. **Module tests** (for modules in `src/server/modules/`):
|
|
454
|
+
```
|
|
455
|
+
tests/modules/<module-name>.e2e-spec.ts
|
|
456
|
+
```
|
|
457
|
+
- Each module gets its own test file directly in `tests/modules/`
|
|
458
|
+
- Examples: `tests/modules/user.e2e-spec.ts`, `tests/modules/book.e2e-spec.ts`
|
|
459
|
+
|
|
460
|
+
2. **Common tests** (for common functionality in `src/server/common/`):
|
|
461
|
+
```
|
|
462
|
+
tests/common.e2e-spec.ts
|
|
463
|
+
```
|
|
464
|
+
- All common functionality (enums, objects, helpers) tested here
|
|
465
|
+
- Single file for all common-related tests
|
|
466
|
+
|
|
467
|
+
3. **Project tests** (for everything else - root level, config, etc.):
|
|
468
|
+
```
|
|
469
|
+
tests/project.e2e-spec.ts
|
|
470
|
+
```
|
|
471
|
+
- General project-level tests
|
|
472
|
+
- Configuration tests
|
|
473
|
+
- Integration tests
|
|
474
|
+
|
|
475
|
+
**Determine correct test location**:
|
|
476
|
+
|
|
477
|
+
```bash
|
|
478
|
+
# BEFORE creating a test, ask yourself:
|
|
479
|
+
# - Is this a module in src/server/modules/? → tests/modules/<name>.e2e-spec.ts
|
|
480
|
+
# - Is this common functionality? → Add to tests/common.e2e-spec.ts
|
|
481
|
+
# - Is this project-level? → Add to tests/project.e2e-spec.ts
|
|
482
|
+
|
|
483
|
+
# Check existing test structure to confirm:
|
|
484
|
+
ls -la tests/
|
|
485
|
+
ls tests/modules/
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
**Create new test files** for modules following the patterns you identified:
|
|
489
|
+
|
|
490
|
+
```bash
|
|
491
|
+
# For a new module (e.g., Book)
|
|
492
|
+
tests/modules/book.e2e-spec.ts
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
**IMPORTANT**: Your new test file MUST:
|
|
496
|
+
1. **Match the exact structure** of existing test files
|
|
497
|
+
2. **Use the same imports** as existing tests
|
|
498
|
+
3. **Follow the same setup/cleanup pattern** (beforeAll, afterAll)
|
|
499
|
+
4. **Use the same test helpers/utilities** you observed
|
|
500
|
+
5. **Follow the same authentication pattern**
|
|
501
|
+
6. **Use the same assertion style**
|
|
502
|
+
7. **Follow the same naming conventions** for describe/it blocks
|
|
503
|
+
|
|
504
|
+
**Each new test file must include**:
|
|
505
|
+
1. All CRUD operations (create, find all, find by ID, update, delete)
|
|
506
|
+
2. Authorization tests (unauthorized should fail, authorized should succeed)
|
|
507
|
+
3. Required field validation (missing required fields should fail)
|
|
508
|
+
4. Proper test data setup and cleanup (beforeAll, afterAll)
|
|
509
|
+
5. Tests for any custom methods or relationships
|
|
510
|
+
|
|
511
|
+
**Ensure all prerequisites are met by analyzing existing tests**:
|
|
512
|
+
|
|
513
|
+
Before writing test code, identify ALL prerequisites from existing test files:
|
|
514
|
+
|
|
515
|
+
```bash
|
|
516
|
+
# Read an existing module test to understand prerequisites
|
|
517
|
+
cat tests/modules/user.e2e-spec.ts
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
**Common prerequisites to check**:
|
|
521
|
+
1. **Test data dependencies**:
|
|
522
|
+
- Does the module reference other modules? (e.g., Book → User for borrowedBy)
|
|
523
|
+
- Do you need to create related test data first?
|
|
524
|
+
- Example: To test Book with borrowedBy: User, create test User first
|
|
525
|
+
|
|
526
|
+
2. **Authentication requirements**:
|
|
527
|
+
- What roles/permissions are needed?
|
|
528
|
+
- Do test users need to be created with specific roles?
|
|
529
|
+
- Example: Admin user for create operations, regular user for read operations
|
|
530
|
+
|
|
531
|
+
3. **Database setup**:
|
|
532
|
+
- Are there database constraints or required collections?
|
|
533
|
+
- Do embedded objects or enums need to exist?
|
|
534
|
+
|
|
535
|
+
4. **Configuration**:
|
|
536
|
+
- Are environment variables or config values needed?
|
|
537
|
+
- Example: JWT secrets, database connections
|
|
538
|
+
|
|
539
|
+
**Pattern from existing tests**:
|
|
540
|
+
```typescript
|
|
541
|
+
// Example structure you should follow:
|
|
542
|
+
beforeAll(async () => {
|
|
543
|
+
// 1. Initialize test app/module
|
|
544
|
+
// 2. Set up database connection
|
|
545
|
+
// 3. Create prerequisite test data (users, roles, etc.)
|
|
546
|
+
// 4. Authenticate and get tokens
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
describe('Module Tests', () => {
|
|
550
|
+
// Tests here
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
afterAll(async () => {
|
|
554
|
+
// 1. Delete created test data (in reverse order)
|
|
555
|
+
// 2. Clean up connections
|
|
556
|
+
// 3. Close app
|
|
557
|
+
});
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
**CRITICAL**: Look at how existing tests handle prerequisites and replicate the exact same approach.
|
|
561
|
+
|
|
562
|
+
### Step 2.3: For Modified Existing Modules
|
|
563
|
+
|
|
564
|
+
**Update existing test files** when you modify modules:
|
|
565
|
+
|
|
566
|
+
1. **FIRST: Read the existing test file completely**:
|
|
567
|
+
```bash
|
|
568
|
+
# Find and read the test file for the module you modified
|
|
569
|
+
find tests -name "*<module-name>*.e2e-spec.ts"
|
|
570
|
+
cat tests/modules/<module-name>.e2e-spec.ts
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
2. **Understand what the existing tests cover**:
|
|
574
|
+
- Which operations are tested?
|
|
575
|
+
- Which properties are validated?
|
|
576
|
+
- What edge cases are covered?
|
|
577
|
+
- How is test data structured?
|
|
578
|
+
|
|
579
|
+
3. **Run existing tests to ensure they pass BEFORE your changes**:
|
|
580
|
+
```bash
|
|
581
|
+
npm run test:e2e
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
4. **Review and update tests**:
|
|
585
|
+
- **Added properties**: Add tests verifying new properties work correctly
|
|
586
|
+
- **Changed validation**: Update tests to reflect new validation rules
|
|
587
|
+
- **Added relationships**: Add tests for new references/embedded objects
|
|
588
|
+
- **Changed required fields**: Update CreateInput tests accordingly
|
|
589
|
+
- **Removed properties**: Remove related test assertions
|
|
590
|
+
|
|
591
|
+
5. **Verify test coverage**:
|
|
592
|
+
- All new properties are tested
|
|
593
|
+
- Changed behavior is verified
|
|
594
|
+
- Edge cases are covered
|
|
595
|
+
- Authorization still works correctly
|
|
596
|
+
|
|
597
|
+
6. **Run tests again to ensure your changes don't break anything**:
|
|
598
|
+
```bash
|
|
599
|
+
npm run test:e2e
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
## Step 3: Compare with Existing Code
|
|
603
|
+
|
|
604
|
+
**Compare generated code with existing project code**:
|
|
605
|
+
|
|
606
|
+
1. **Read existing similar modules** to understand project patterns:
|
|
607
|
+
```bash
|
|
608
|
+
# Example: If you created a User module, check existing modules
|
|
609
|
+
ls src/server/modules/
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
2. **Check for consistency**:
|
|
613
|
+
- Code style (indentation, spacing, formatting)
|
|
614
|
+
- Import ordering and organization
|
|
615
|
+
- Naming conventions (camelCase, PascalCase, kebab-case)
|
|
616
|
+
- File structure and directory organization
|
|
617
|
+
- Comment style and documentation
|
|
618
|
+
- Decorator usage (@Field, @Prop, etc.)
|
|
619
|
+
- Error handling patterns
|
|
620
|
+
- Validation patterns
|
|
621
|
+
|
|
622
|
+
3. **Review property ordering**:
|
|
623
|
+
- Verify alphabetical order in models
|
|
624
|
+
- Verify alphabetical order in inputs
|
|
625
|
+
- Verify alphabetical order in outputs
|
|
626
|
+
- Check decorator consistency
|
|
627
|
+
|
|
628
|
+
## Step 4: Critical Analysis
|
|
629
|
+
|
|
630
|
+
**Analyze each file critically**:
|
|
631
|
+
|
|
632
|
+
1. **Style consistency**:
|
|
633
|
+
- Does the code match the project's existing style?
|
|
634
|
+
- Are imports grouped and ordered correctly?
|
|
635
|
+
- Is indentation consistent with the project?
|
|
636
|
+
- Are naming conventions followed?
|
|
637
|
+
|
|
638
|
+
2. **Structural consistency**:
|
|
639
|
+
- Are decorators in the same order as existing code?
|
|
640
|
+
- Is the file structure identical to existing modules?
|
|
641
|
+
- Are descriptions formatted the same way?
|
|
642
|
+
- Are relationships implemented consistently?
|
|
643
|
+
|
|
644
|
+
3. **Code quality**:
|
|
645
|
+
- Are there any redundant imports?
|
|
646
|
+
- Are there any missing imports?
|
|
647
|
+
- Are descriptions meaningful and complete?
|
|
648
|
+
- Are TypeScript types correctly used?
|
|
649
|
+
|
|
650
|
+
4. **Best practices**:
|
|
651
|
+
- Are required fields properly marked?
|
|
652
|
+
- Are nullable fields correctly configured?
|
|
653
|
+
- Are references properly typed?
|
|
654
|
+
- Are arrays correctly configured?
|
|
655
|
+
|
|
656
|
+
## Step 5: Automated Optimizations
|
|
657
|
+
|
|
658
|
+
**Apply automatic improvements**:
|
|
659
|
+
|
|
660
|
+
1. **Fix import ordering**:
|
|
661
|
+
- External imports first (alphabetically)
|
|
662
|
+
- @lenne.tech/nest-server imports next
|
|
663
|
+
- Local imports last (alphabetically by path depth)
|
|
664
|
+
|
|
665
|
+
2. **Fix property ordering**:
|
|
666
|
+
- Reorder all properties alphabetically in models
|
|
667
|
+
- Reorder all properties alphabetically in inputs
|
|
668
|
+
- Reorder all properties alphabetically in outputs
|
|
669
|
+
|
|
670
|
+
3. **Fix formatting**:
|
|
671
|
+
- Ensure consistent indentation
|
|
672
|
+
- Remove extra blank lines
|
|
673
|
+
- Add missing blank lines between sections
|
|
674
|
+
|
|
675
|
+
4. **Fix descriptions**:
|
|
676
|
+
- Ensure all follow "ENGLISH (DEUTSCH)" format
|
|
677
|
+
- Add missing descriptions
|
|
678
|
+
- Improve unclear descriptions
|
|
679
|
+
|
|
680
|
+
5. **Fix common patterns**:
|
|
681
|
+
- Standardize decorator usage
|
|
682
|
+
- Standardize validation patterns
|
|
683
|
+
- Standardize error handling
|
|
684
|
+
|
|
685
|
+
## Step 6: Pre-Report Testing
|
|
686
|
+
|
|
687
|
+
**MANDATORY**: Run all tests before reporting:
|
|
688
|
+
|
|
689
|
+
```bash
|
|
690
|
+
# Run TypeScript compilation
|
|
691
|
+
npm run build
|
|
692
|
+
|
|
693
|
+
# Run linting
|
|
694
|
+
npm run lint
|
|
695
|
+
|
|
696
|
+
# Run all tests
|
|
697
|
+
npm run test:e2e
|
|
698
|
+
|
|
699
|
+
# If any fail, fix issues and repeat
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
**If tests fail**:
|
|
703
|
+
1. Analyze the error
|
|
704
|
+
2. Fix the issue
|
|
705
|
+
3. Re-run tests
|
|
706
|
+
4. Repeat until all tests pass
|
|
707
|
+
|
|
708
|
+
### Debugging Failed Tests - Important Guidelines
|
|
709
|
+
|
|
710
|
+
**When tests fail, use systematic debugging with console.log statements:**
|
|
711
|
+
|
|
712
|
+
1. **Add debug messages in Controllers/Resolvers**:
|
|
713
|
+
```typescript
|
|
714
|
+
// In controller/resolver - BEFORE service call
|
|
715
|
+
console.log('🔵 [Controller] createProduct - Input:', input);
|
|
716
|
+
console.log('🔵 [Controller] createProduct - User:', serviceOptions?.user);
|
|
717
|
+
|
|
718
|
+
const result = await this.productService.create(input, serviceOptions);
|
|
719
|
+
|
|
720
|
+
// AFTER service call
|
|
721
|
+
console.log('🔵 [Controller] createProduct - Result:', result);
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
2. **Add debug messages in Services**:
|
|
725
|
+
```typescript
|
|
726
|
+
// In service method
|
|
727
|
+
console.log('🟢 [Service] create - Input:', input);
|
|
728
|
+
console.log('🟢 [Service] create - ServiceOptions:', serviceOptions);
|
|
729
|
+
|
|
730
|
+
const created = await super.create(input, serviceOptions);
|
|
731
|
+
|
|
732
|
+
console.log('🟢 [Service] create - Created:', created);
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
3. **Understand the permissions system**:
|
|
736
|
+
- **Controllers/Resolvers**: `@Roles()` decorator controls WHO can call the endpoint
|
|
737
|
+
- **Services**: `serviceOptions.roles` controls what the service checks during processing
|
|
738
|
+
- **Models**: `securityCheck()` method determines what data is returned to the user
|
|
739
|
+
|
|
740
|
+
4. **Default permission behavior**:
|
|
741
|
+
- Only **Admin users** (user.roles contains 'admin') OR the **creator** (user.id === object.createdBy) of an element can access it
|
|
742
|
+
- This is enforced in the `securityCheck()` method in models:
|
|
743
|
+
```typescript
|
|
744
|
+
securityCheck(user: User, force?: boolean) {
|
|
745
|
+
// Admin: user.roles contains 'admin'
|
|
746
|
+
if (force || user?.hasRole(RoleEnum.ADMIN)) {
|
|
747
|
+
return this; // Admin sees everything
|
|
748
|
+
}
|
|
749
|
+
// Creator: user.id === this.createdBy
|
|
750
|
+
if (!equalIds(user, this.createdBy)) {
|
|
751
|
+
return undefined; // Non-creator (user.id !== this.createdBy) gets nothing
|
|
752
|
+
}
|
|
753
|
+
return this; // Creator sees their own data
|
|
754
|
+
}
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
5. **Debugging strategy for permission issues**:
|
|
758
|
+
|
|
759
|
+
**Step 1**: Run failing test with Admin user first
|
|
760
|
+
```typescript
|
|
761
|
+
// In test setup
|
|
762
|
+
const adminToken = await testHelper.signIn('admin@test.com', 'admin-password');
|
|
763
|
+
|
|
764
|
+
// Use admin token in test
|
|
765
|
+
const result = await testHelper.graphQl({...}, { token: adminToken });
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
**Step 2**: Analyze results
|
|
769
|
+
- ✅ **Works with Admin, fails with normal user** → Permission issue (check Roles, securityCheck)
|
|
770
|
+
- ❌ **Fails with Admin too** → Different issue (check logic, data, validation)
|
|
771
|
+
|
|
772
|
+
6. **Common permission issues and solutions**:
|
|
773
|
+
|
|
774
|
+
| Problem | Cause | Solution |
|
|
775
|
+
|---------|-------|----------|
|
|
776
|
+
| 401/403 on endpoint | `@Roles()` too restrictive | Adjust decorator in controller/resolver |
|
|
777
|
+
| Empty result despite data existing | `securityCheck()` returns undefined | Modify securityCheck logic or use Admin |
|
|
778
|
+
| Service throws permission error | `serviceOptions.roles` check fails | Pass correct roles in serviceOptions |
|
|
779
|
+
|
|
780
|
+
7. **Remove debug messages after fixing**:
|
|
781
|
+
```bash
|
|
782
|
+
# After tests pass, remove all console.log statements
|
|
783
|
+
# Search for debug patterns
|
|
784
|
+
grep -r "console.log" src/server/modules/your-module/
|
|
785
|
+
|
|
786
|
+
# Remove them manually or with sed
|
|
787
|
+
# Then verify tests still pass
|
|
788
|
+
npm run test:e2e
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
**Debugging workflow example**:
|
|
792
|
+
```typescript
|
|
793
|
+
// 1. Test fails - add debugging
|
|
794
|
+
@Mutation(() => Product)
|
|
795
|
+
async createProduct(@Args('input') input: ProductCreateInput, @GraphQLServiceOptions() opts) {
|
|
796
|
+
console.log('🔵 START createProduct', { input, user: opts?.user?.email });
|
|
797
|
+
|
|
798
|
+
const result = await this.productService.create(input, opts);
|
|
799
|
+
|
|
800
|
+
console.log('🔵 END createProduct', { result: result?.id });
|
|
801
|
+
return result;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
// In service
|
|
805
|
+
async create(input: ProductCreateInput, serviceOptions?: ServiceOptions) {
|
|
806
|
+
console.log('🟢 Service create', { input, user: serviceOptions?.user?.email });
|
|
807
|
+
|
|
808
|
+
const created = await super.create(input, serviceOptions);
|
|
809
|
+
|
|
810
|
+
console.log('🟢 Service created', { id: created?.id, createdBy: created?.createdBy });
|
|
811
|
+
return created;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
// 2. Run test - observe output:
|
|
815
|
+
// 🔵 START createProduct { input: {...}, user: 'test@test.com' }
|
|
816
|
+
// 🟢 Service create { input: {...}, user: 'test@test.com' }
|
|
817
|
+
// 🟢 Service created { id: '123', createdBy: '456' }
|
|
818
|
+
// 🔵 END createProduct { result: undefined } ← AHA! Result is undefined!
|
|
819
|
+
|
|
820
|
+
// 3. Check model securityCheck() - likely returns undefined for non-creator (user.id !== object.createdBy)
|
|
821
|
+
// 4. Fix: Either use Admin user (user.roles contains 'admin') or adjust securityCheck logic
|
|
822
|
+
// 5. Test passes → Remove console.log statements
|
|
823
|
+
// 6. Verify tests still pass
|
|
824
|
+
```
|
|
825
|
+
|
|
826
|
+
**Do not proceed to final report if**:
|
|
827
|
+
- TypeScript compilation fails
|
|
828
|
+
- Linting fails
|
|
829
|
+
- Any tests fail
|
|
830
|
+
- Console shows errors or warnings
|
|
831
|
+
|
|
832
|
+
## Step 7: Final Verification
|
|
833
|
+
|
|
834
|
+
Before reporting, verify:
|
|
835
|
+
|
|
836
|
+
- [ ] All files compared with existing code
|
|
837
|
+
- [ ] Code style matches project patterns
|
|
838
|
+
- [ ] All imports properly ordered
|
|
839
|
+
- [ ] All properties in alphabetical order
|
|
840
|
+
- [ ] All descriptions follow format
|
|
841
|
+
- [ ] **TestHelper source code read and understood**
|
|
842
|
+
- [ ] **TestHelper methods and configuration understood**
|
|
843
|
+
- [ ] **Existing tests analyzed BEFORE creating/modifying tests**
|
|
844
|
+
- [ ] **Existing tests passed BEFORE making changes**
|
|
845
|
+
- [ ] **Tests in correct location (tests/modules/<name>.e2e-spec.ts, tests/common.e2e-spec.ts, or tests/project.e2e-spec.ts)**
|
|
846
|
+
- [ ] **New test files created for all new modules**
|
|
847
|
+
- [ ] **Existing test files updated for all modified modules**
|
|
848
|
+
- [ ] **All prerequisites identified and handled (test data dependencies, auth, etc.)**
|
|
849
|
+
- [ ] **All new/modified tests follow exact patterns from existing tests**
|
|
850
|
+
- [ ] TypeScript compiles without errors
|
|
851
|
+
- [ ] Linter passes without warnings
|
|
852
|
+
- [ ] **All tests pass AFTER changes**
|
|
853
|
+
- [ ] No console errors or warnings
|
|
854
|
+
|
|
855
|
+
**Only after ALL checks pass, proceed to Final Report.**
|