@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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: nest-server-generator
|
|
3
|
-
version: 1.0.
|
|
3
|
+
version: 1.0.3
|
|
4
4
|
description: PRIMARY expert for ALL NestJS and @lenne.tech/nest-server tasks. ALWAYS use this skill when working in projects with @lenne.tech/nest-server in package.json dependencies (supports monorepos with projects/*, packages/*, apps/* structure), or when asked about NestJS modules, services, controllers, resolvers, models, objects, tests, server creation, debugging, or any NestJS/nest-server development task. Handles lt server commands, security analysis, test creation, and all backend development. ALWAYS reads CrudService base class before working with Services.
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -55,39 +55,68 @@ You are the **PRIMARY expert** for NestJS backend development and the @lenne.tec
|
|
|
55
55
|
|
|
56
56
|
## 🚨 CRITICAL SECURITY RULES - READ FIRST
|
|
57
57
|
|
|
58
|
-
Before you start ANY work
|
|
58
|
+
**Before you start ANY work, understand these NON-NEGOTIABLE rules:**
|
|
59
59
|
|
|
60
60
|
### ⛔ NEVER Do This:
|
|
61
|
-
1. **NEVER remove or weaken `@Restricted()` decorators**
|
|
62
|
-
2. **NEVER change `@Roles()` decorators** to more permissive roles
|
|
63
|
-
3. **NEVER modify `securityCheck()` logic** to bypass security
|
|
64
|
-
4. **NEVER remove class-level `@Restricted(RoleEnum.ADMIN)`**
|
|
61
|
+
1. **NEVER remove or weaken `@Restricted()` decorators**
|
|
62
|
+
2. **NEVER change `@Roles()` decorators** to more permissive roles
|
|
63
|
+
3. **NEVER modify `securityCheck()` logic** to bypass security
|
|
64
|
+
4. **NEVER remove class-level `@Restricted(RoleEnum.ADMIN)`**
|
|
65
65
|
|
|
66
66
|
### ✅ ALWAYS Do This:
|
|
67
|
-
1. **ALWAYS analyze permissions BEFORE writing tests**
|
|
67
|
+
1. **ALWAYS analyze permissions BEFORE writing tests**
|
|
68
68
|
2. **ALWAYS test with the LEAST privileged user** who is authorized
|
|
69
|
-
3. **ALWAYS
|
|
70
|
-
4. **ALWAYS
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
3. **ALWAYS adapt tests to security requirements**, never vice versa
|
|
70
|
+
4. **ALWAYS ask developer for approval** before changing ANY security decorator
|
|
71
|
+
|
|
72
|
+
**📖 For complete security rules, testing guidelines, and examples, see: `security-rules.md`**
|
|
73
|
+
|
|
74
|
+
## 🚨 CRITICAL: NEVER USE `declare` KEYWORD FOR PROPERTIES
|
|
75
|
+
|
|
76
|
+
**⚠️ DO NOT use the `declare` keyword when defining properties in classes!**
|
|
77
|
+
|
|
78
|
+
### Quick Rule
|
|
73
79
|
|
|
74
|
-
### 🔑 Permission Hierarchy (Specific Overrides General):
|
|
75
80
|
```typescript
|
|
76
|
-
|
|
77
|
-
export class
|
|
78
|
-
|
|
79
|
-
|
|
81
|
+
// ❌ WRONG
|
|
82
|
+
export class ProductCreateInput extends ProductInput {
|
|
83
|
+
declare name: string; // Decorator won't work!
|
|
84
|
+
}
|
|
80
85
|
|
|
81
|
-
|
|
86
|
+
// ✅ CORRECT
|
|
87
|
+
export class ProductCreateInput extends ProductInput {
|
|
88
|
+
@UnifiedField({ description: 'Product name' })
|
|
89
|
+
name: string; // Decorator works properly
|
|
82
90
|
}
|
|
83
91
|
```
|
|
84
92
|
|
|
85
|
-
**Why
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
93
|
+
**Why**: `declare` prevents decorators from being applied, breaking the decorator system.
|
|
94
|
+
|
|
95
|
+
**📖 For detailed explanation and correct patterns, see: `declare-keyword-warning.md`**
|
|
96
|
+
|
|
97
|
+
## 🚨 CRITICAL: DESCRIPTION MANAGEMENT
|
|
98
|
+
|
|
99
|
+
**⚠️ COMMON MISTAKE:** Descriptions are often applied inconsistently. You MUST follow this process for EVERY component.
|
|
89
100
|
|
|
90
|
-
|
|
101
|
+
### 3-Step Process
|
|
102
|
+
|
|
103
|
+
**1. Extract descriptions** from user's `// comments`
|
|
104
|
+
|
|
105
|
+
**2. Format correctly:**
|
|
106
|
+
- English input → `'Product name'`
|
|
107
|
+
- German input → `'Product name (Produktname)'`
|
|
108
|
+
- **⚠️ Fix typos ONLY, NEVER change wording!**
|
|
109
|
+
|
|
110
|
+
**3. Apply EVERYWHERE:**
|
|
111
|
+
- Model file
|
|
112
|
+
- Create Input file
|
|
113
|
+
- Update Input file
|
|
114
|
+
- Object files (if SubObject)
|
|
115
|
+
- Class-level @ObjectType() decorators
|
|
116
|
+
|
|
117
|
+
**📖 For detailed formatting rules, examples, and verification checklist, see: `description-management.md`**
|
|
118
|
+
|
|
119
|
+
---
|
|
91
120
|
|
|
92
121
|
## Core Responsibilities
|
|
93
122
|
|
|
@@ -190,38 +219,22 @@ export class ProductService extends CrudService<Product> {
|
|
|
190
219
|
|
|
191
220
|
## Configuration File (lt.config.json)
|
|
192
221
|
|
|
193
|
-
The lenne.tech CLI supports project-level configuration via `lt.config.json` files
|
|
222
|
+
The lenne.tech CLI supports project-level configuration via `lt.config.json` files to set default values for commands.
|
|
194
223
|
|
|
195
|
-
|
|
224
|
+
**📖 For complete configuration guide including structure, options, and examples, see: `configuration.md`**
|
|
196
225
|
|
|
197
|
-
|
|
198
|
-
- **
|
|
199
|
-
- **Priority
|
|
200
|
-
|
|
201
|
-
2. Config from parent directories (higher up = lower priority)
|
|
202
|
-
3. Config from current directory
|
|
203
|
-
4. CLI parameters (`--flag value`)
|
|
204
|
-
5. Interactive user input
|
|
205
|
-
|
|
206
|
-
### Configuration Structure
|
|
226
|
+
**Quick reference:**
|
|
227
|
+
- **Location**: Project root or parent directories
|
|
228
|
+
- **Priority**: CLI parameters > Interactive input > Config file > Defaults
|
|
229
|
+
- **Key option**: `commands.server.module.controller` - Sets default controller type ("Rest" | "GraphQL" | "Both" | "auto")
|
|
207
230
|
|
|
231
|
+
**Example config:**
|
|
208
232
|
```json
|
|
209
233
|
{
|
|
210
|
-
"meta": {
|
|
211
|
-
"version": "1.0.0",
|
|
212
|
-
"name": "My Project",
|
|
213
|
-
"description": "Optional project description"
|
|
214
|
-
},
|
|
215
234
|
"commands": {
|
|
216
235
|
"server": {
|
|
217
236
|
"module": {
|
|
218
|
-
"controller": "
|
|
219
|
-
"skipLint": false
|
|
220
|
-
},
|
|
221
|
-
"object": {
|
|
222
|
-
"skipLint": false
|
|
223
|
-
},
|
|
224
|
-
"addProp": {
|
|
237
|
+
"controller": "Rest",
|
|
225
238
|
"skipLint": false
|
|
226
239
|
}
|
|
227
240
|
}
|
|
@@ -229,238 +242,8 @@ The lenne.tech CLI supports project-level configuration via `lt.config.json` fil
|
|
|
229
242
|
}
|
|
230
243
|
```
|
|
231
244
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
**Server Module Configuration (`commands.server.module`)**:
|
|
235
|
-
- `controller`: Default controller type (`"Rest"` | `"GraphQL"` | `"Both"` | `"auto"`)
|
|
236
|
-
- `skipLint`: Skip lint prompt after module creation (boolean)
|
|
237
|
-
|
|
238
|
-
**Server Object Configuration (`commands.server.object`)**:
|
|
239
|
-
- `skipLint`: Skip lint prompt after object creation (boolean)
|
|
240
|
-
|
|
241
|
-
**Server AddProp Configuration (`commands.server.addProp`)**:
|
|
242
|
-
- `skipLint`: Skip lint prompt after adding property (boolean)
|
|
243
|
-
|
|
244
|
-
### Using Configuration in Commands
|
|
245
|
-
|
|
246
|
-
**Example 1: Configure controller type globally**
|
|
247
|
-
```json
|
|
248
|
-
{
|
|
249
|
-
"commands": {
|
|
250
|
-
"server": {
|
|
251
|
-
"module": {
|
|
252
|
-
"controller": "Rest"
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
Now all `lt server module` commands will default to REST controllers:
|
|
260
|
-
```bash
|
|
261
|
-
# Uses "Rest" from config (no prompt)
|
|
262
|
-
lt server module --name Product --prop-name-0 name --prop-type-0 string
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
**Example 2: Override config with CLI parameter**
|
|
266
|
-
```bash
|
|
267
|
-
# Ignores config, uses GraphQL
|
|
268
|
-
lt server module --name Product --controller GraphQL
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
**Example 3: Auto-detect from config**
|
|
272
|
-
```json
|
|
273
|
-
{
|
|
274
|
-
"commands": {
|
|
275
|
-
"server": {
|
|
276
|
-
"module": {
|
|
277
|
-
"controller": "auto"
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
```
|
|
283
|
-
|
|
284
|
-
Now the CLI will auto-detect controller type from existing modules without prompting.
|
|
285
|
-
|
|
286
|
-
### Managing Configuration
|
|
287
|
-
|
|
288
|
-
**Initialize configuration**:
|
|
289
|
-
```bash
|
|
290
|
-
lt config init
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
**Show current configuration** (merged from all hierarchy levels):
|
|
294
|
-
```bash
|
|
295
|
-
lt config show
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
**Get help**:
|
|
299
|
-
```bash
|
|
300
|
-
lt config help
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
### When to Use Configuration
|
|
304
|
-
|
|
305
|
-
**✅ Use configuration when:**
|
|
306
|
-
- Creating multiple modules with the same controller type
|
|
307
|
-
- Working in a team with agreed-upon conventions
|
|
308
|
-
- Automating module generation in CI/CD
|
|
309
|
-
- You want to skip repetitive prompts
|
|
310
|
-
|
|
311
|
-
**❌ Don't use configuration when:**
|
|
312
|
-
- Creating a single module with specific requirements
|
|
313
|
-
- Each module needs a different controller type
|
|
314
|
-
- You're just testing or experimenting
|
|
315
|
-
|
|
316
|
-
### Best Practices
|
|
317
|
-
|
|
318
|
-
1. **Project Root**: Place `lt.config.json` in your project root
|
|
319
|
-
2. **Version Control**: Commit the config file to share with your team
|
|
320
|
-
3. **Documentation**: Add a README note explaining the config choices
|
|
321
|
-
4. **Override When Needed**: Use CLI parameters to override for special cases
|
|
322
|
-
|
|
323
|
-
### 🎯 IMPORTANT: Configuration After Server Creation
|
|
324
|
-
|
|
325
|
-
**CRITICAL WORKFLOW**: After creating a new server with `lt server create`, you **MUST** initialize the configuration file to set project conventions.
|
|
326
|
-
|
|
327
|
-
#### Automatic Post-Creation Setup
|
|
328
|
-
|
|
329
|
-
When you create a new NestJS server, immediately follow these steps:
|
|
330
|
-
|
|
331
|
-
1. **Navigate to the API directory**:
|
|
332
|
-
```bash
|
|
333
|
-
cd projects/api
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
2. **Create the configuration file manually**:
|
|
337
|
-
```bash
|
|
338
|
-
# Create lt.config.json with controller preference
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
3. **Ask the developer for their preference** (if not already specified):
|
|
342
|
-
```
|
|
343
|
-
What controller type do you prefer for new modules in this project?
|
|
344
|
-
1. Rest - REST controllers only
|
|
345
|
-
2. GraphQL - GraphQL resolvers only
|
|
346
|
-
3. Both - Both REST and GraphQL
|
|
347
|
-
4. auto - Auto-detect from existing modules
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
4. **Write the configuration** based on the answer:
|
|
351
|
-
```json
|
|
352
|
-
{
|
|
353
|
-
"meta": {
|
|
354
|
-
"version": "1.0.0"
|
|
355
|
-
},
|
|
356
|
-
"commands": {
|
|
357
|
-
"server": {
|
|
358
|
-
"module": {
|
|
359
|
-
"controller": "Rest"
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
```
|
|
365
|
-
|
|
366
|
-
#### Why This Is Important
|
|
367
|
-
|
|
368
|
-
- ✅ **Consistency**: All modules will follow the same pattern
|
|
369
|
-
- ✅ **No Prompts**: Developers won't be asked for controller type repeatedly
|
|
370
|
-
- ✅ **Team Alignment**: Everyone uses the same conventions
|
|
371
|
-
- ✅ **Automation**: Scripts and CI/CD can create modules without interaction
|
|
372
|
-
|
|
373
|
-
#### Example Workflow
|
|
374
|
-
|
|
375
|
-
```bash
|
|
376
|
-
# User creates new server
|
|
377
|
-
lt server create --name MyAPI
|
|
378
|
-
|
|
379
|
-
# You (Claude) navigate to API directory
|
|
380
|
-
cd projects/api
|
|
381
|
-
|
|
382
|
-
# You ask the user
|
|
383
|
-
"I've created the server. What controller type would you like to use for modules?"
|
|
384
|
-
"1. Rest (REST only)"
|
|
385
|
-
"2. GraphQL (GraphQL only)"
|
|
386
|
-
"3. Both (REST + GraphQL)"
|
|
387
|
-
"4. auto (Auto-detect)"
|
|
388
|
-
|
|
389
|
-
# User answers: "Rest"
|
|
390
|
-
|
|
391
|
-
# You create lt.config.json
|
|
392
|
-
{
|
|
393
|
-
"meta": {
|
|
394
|
-
"version": "1.0.0"
|
|
395
|
-
},
|
|
396
|
-
"commands": {
|
|
397
|
-
"server": {
|
|
398
|
-
"module": {
|
|
399
|
-
"controller": "Rest"
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
# Confirm to user
|
|
406
|
-
"✅ Configuration saved! All new modules will default to REST controllers."
|
|
407
|
-
"You can change this anytime by editing lt.config.json or running 'lt config init'."
|
|
408
|
-
```
|
|
409
|
-
|
|
410
|
-
#### Configuration Options Explained
|
|
411
|
-
|
|
412
|
-
**"Rest"**:
|
|
413
|
-
- ✅ Creates REST controllers (`@Controller()`)
|
|
414
|
-
- ❌ No GraphQL resolvers
|
|
415
|
-
- ❌ No PubSub integration
|
|
416
|
-
- **Best for**: Traditional REST APIs, microservices
|
|
417
|
-
|
|
418
|
-
**"GraphQL"**:
|
|
419
|
-
- ❌ No REST controllers
|
|
420
|
-
- ✅ Creates GraphQL resolvers (`@Resolver()`)
|
|
421
|
-
- ✅ Includes PubSub for subscriptions
|
|
422
|
-
- **Best for**: GraphQL-first APIs, real-time apps
|
|
423
|
-
|
|
424
|
-
**"Both"**:
|
|
425
|
-
- ✅ Creates REST controllers
|
|
426
|
-
- ✅ Creates GraphQL resolvers
|
|
427
|
-
- ✅ Includes PubSub
|
|
428
|
-
- **Best for**: Hybrid APIs, gradual migration
|
|
429
|
-
|
|
430
|
-
**"auto"**:
|
|
431
|
-
- 🤖 Analyzes existing modules
|
|
432
|
-
- 🤖 Detects pattern automatically
|
|
433
|
-
- 🤖 No user prompt
|
|
434
|
-
- **Best for**: Following existing conventions
|
|
435
|
-
|
|
436
|
-
#### When NOT to Create Config
|
|
437
|
-
|
|
438
|
-
Skip config creation if:
|
|
439
|
-
- ❌ User is just testing/experimenting
|
|
440
|
-
- ❌ User explicitly says "no configuration"
|
|
441
|
-
- ❌ Project already has lt.config.json
|
|
442
|
-
|
|
443
|
-
### Integration with Commands
|
|
444
|
-
|
|
445
|
-
When generating code, **ALWAYS check for configuration**:
|
|
446
|
-
1. Load config via `lt config show` or check for `lt.config.json`
|
|
447
|
-
2. Use configured values in command construction
|
|
448
|
-
3. Only pass CLI parameters when overriding config
|
|
449
|
-
|
|
450
|
-
**Example: Generating module with config**
|
|
451
|
-
```bash
|
|
452
|
-
# Check if config exists and what controller type is configured
|
|
453
|
-
# If config has "controller": "Rest", use it
|
|
454
|
-
lt server module --name Product --prop-name-0 name --prop-type-0 string
|
|
455
|
-
|
|
456
|
-
# If config has "controller": "auto", let CLI detect
|
|
457
|
-
lt server module --name Order --prop-name-0 total --prop-type-0 number
|
|
458
|
-
|
|
459
|
-
# Override config when needed
|
|
460
|
-
lt server module --name User --controller Both
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
## Command Syntax Reference
|
|
245
|
+
**Initialize config**: `lt config init`
|
|
246
|
+
**Show current config**: `lt config show`
|
|
464
247
|
|
|
465
248
|
### lt server module
|
|
466
249
|
Creates a complete NestJS module with model, service, controller/resolver, and DTOs.
|
|
@@ -876,29 +659,177 @@ export class BuyerProfile extends Profile { ... }
|
|
|
876
659
|
|
|
877
660
|
### Phase 5: Description Management
|
|
878
661
|
|
|
879
|
-
|
|
662
|
+
**⚠️ CRITICAL PHASE - Refer to "CRITICAL: DESCRIPTION MANAGEMENT" section at the top of this document!**
|
|
663
|
+
|
|
664
|
+
This phase is often done incorrectly. Follow these steps EXACTLY:
|
|
665
|
+
|
|
666
|
+
#### Step 5.1: Extract Descriptions from User Input
|
|
667
|
+
|
|
668
|
+
**BEFORE applying any descriptions, review the original specification:**
|
|
669
|
+
|
|
670
|
+
Go back to the user's original specification and extract ALL comments that appear after `//`:
|
|
671
|
+
|
|
672
|
+
```
|
|
673
|
+
Module: Product
|
|
674
|
+
- name: string // Product name
|
|
675
|
+
- price: number // Produktpreis
|
|
676
|
+
- description?: string // Produktbeschreibung
|
|
677
|
+
- stock: number // Current inventory
|
|
678
|
+
|
|
679
|
+
SubObject: Address
|
|
680
|
+
- street: string // Straße
|
|
681
|
+
- city: string // City name
|
|
682
|
+
- zipCode: string // Postleitzahl
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
**Create a mapping**:
|
|
686
|
+
```
|
|
687
|
+
Product.name → "Product name" (English)
|
|
688
|
+
Product.price → "Produktpreis" (German)
|
|
689
|
+
Product.description → "Produktbeschreibung" (German)
|
|
690
|
+
Product.stock → "Current inventory" (English)
|
|
691
|
+
Address.street → "Straße" (German)
|
|
692
|
+
Address.city → "City name" (English)
|
|
693
|
+
Address.zipCode → "Postleitzahl" (German)
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
#### Step 5.2: Format Descriptions
|
|
697
|
+
|
|
698
|
+
**Rule**: `"ENGLISH_DESCRIPTION (DEUTSCHE_BESCHREIBUNG)"`
|
|
880
699
|
|
|
881
|
-
|
|
700
|
+
Apply formatting rules:
|
|
882
701
|
|
|
883
702
|
1. **If comment is in English**:
|
|
884
703
|
```
|
|
885
|
-
//
|
|
704
|
+
// Product name
|
|
886
705
|
```
|
|
887
|
-
→ Use as: `description: '
|
|
706
|
+
→ Use as: `description: 'Product name'`
|
|
707
|
+
|
|
708
|
+
Fix typos if needed:
|
|
709
|
+
```
|
|
710
|
+
// Prodcut name (typo)
|
|
711
|
+
```
|
|
712
|
+
→ Use as: `description: 'Product name'` (typo corrected)
|
|
888
713
|
|
|
889
714
|
2. **If comment is in German**:
|
|
715
|
+
```
|
|
716
|
+
// Produktpreis
|
|
717
|
+
```
|
|
718
|
+
→ Translate and add original: `description: 'Product price (Produktpreis)'`
|
|
719
|
+
|
|
890
720
|
```
|
|
891
721
|
// Straße
|
|
892
722
|
```
|
|
893
723
|
→ Translate and add original: `description: 'Street (Straße)'`
|
|
894
724
|
|
|
895
|
-
|
|
896
|
-
|
|
725
|
+
Fix typos in original:
|
|
726
|
+
```
|
|
727
|
+
// Postleizahl (typo: missing 't')
|
|
728
|
+
```
|
|
729
|
+
→ Translate and add corrected: `description: 'Postal code (Postleitzahl)'`
|
|
730
|
+
|
|
731
|
+
3. **If no comment provided**:
|
|
732
|
+
→ Create meaningful English description: `description: 'User email address'`
|
|
733
|
+
|
|
734
|
+
**⚠️ CRITICAL - Preserve Original Wording**:
|
|
735
|
+
|
|
736
|
+
- ✅ **DO:** Fix spelling/typos only
|
|
737
|
+
- ❌ **DON'T:** Rephrase, expand, or improve wording
|
|
738
|
+
- ❌ **DON'T:** Change terms (they may be predefined/referenced by external systems)
|
|
739
|
+
|
|
740
|
+
**Examples**:
|
|
741
|
+
```
|
|
742
|
+
✅ CORRECT:
|
|
743
|
+
// Straße → 'Street (Straße)' (preserve word)
|
|
744
|
+
// Produkt → 'Product (Produkt)' (don't add "name")
|
|
745
|
+
// Status → 'Status (Status)' (same in both languages)
|
|
746
|
+
|
|
747
|
+
❌ WRONG:
|
|
748
|
+
// Straße → 'Street name (Straßenname)' (changed word!)
|
|
749
|
+
// Produkt → 'Product name (Produktname)' (added word!)
|
|
750
|
+
// Status → 'Current status (Aktueller Status)' (added word!)
|
|
751
|
+
```
|
|
752
|
+
|
|
753
|
+
#### Step 5.3: Apply Descriptions EVERYWHERE
|
|
754
|
+
|
|
755
|
+
**🚨 MOST IMPORTANT: Apply SAME description to ALL files!**
|
|
756
|
+
|
|
757
|
+
For **EVERY property in EVERY Module**:
|
|
758
|
+
|
|
759
|
+
1. Open `<module>.model.ts` → Add description to property
|
|
760
|
+
2. Open `inputs/<module>-create.input.ts` → Add SAME description to property
|
|
761
|
+
3. Open `inputs/<module>.input.ts` → Add SAME description to property
|
|
762
|
+
|
|
763
|
+
For **EVERY property in EVERY SubObject**:
|
|
764
|
+
|
|
765
|
+
1. Open `objects/<object>/<object>.object.ts` → Add description to property
|
|
766
|
+
2. Open `objects/<object>/<object>-create.input.ts` → Add SAME description to property
|
|
767
|
+
3. Open `objects/<object>/<object>.input.ts` → Add SAME description to property
|
|
768
|
+
|
|
769
|
+
**Example for Module "Product" with property "price"**:
|
|
770
|
+
|
|
771
|
+
```typescript
|
|
772
|
+
// File: src/server/modules/product/product.model.ts
|
|
773
|
+
@UnifiedField({ description: 'Product price (Produktpreis)' })
|
|
774
|
+
price: number;
|
|
775
|
+
|
|
776
|
+
// File: src/server/modules/product/inputs/product-create.input.ts
|
|
777
|
+
@UnifiedField({ description: 'Product price (Produktpreis)' })
|
|
778
|
+
price: number;
|
|
779
|
+
|
|
780
|
+
// File: src/server/modules/product/inputs/product.input.ts
|
|
781
|
+
@UnifiedField({ description: 'Product price (Produktpreis)' })
|
|
782
|
+
price?: number;
|
|
783
|
+
```
|
|
784
|
+
|
|
785
|
+
**Example for SubObject "Address" with property "street"**:
|
|
786
|
+
|
|
787
|
+
```typescript
|
|
788
|
+
// File: src/server/common/objects/address/address.object.ts
|
|
789
|
+
@UnifiedField({ description: 'Street (Straße)' })
|
|
790
|
+
street: string;
|
|
791
|
+
|
|
792
|
+
// File: src/server/common/objects/address/address-create.input.ts
|
|
793
|
+
@UnifiedField({ description: 'Street (Straße)' })
|
|
794
|
+
street: string;
|
|
795
|
+
|
|
796
|
+
// File: src/server/common/objects/address/address.input.ts
|
|
797
|
+
@UnifiedField({ description: 'Street (Straße)' })
|
|
798
|
+
street?: string;
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
#### Step 5.4: Add Class-Level Descriptions
|
|
802
|
+
|
|
803
|
+
Also add descriptions to the `@ObjectType()` and `@InputType()` decorators:
|
|
804
|
+
|
|
805
|
+
```typescript
|
|
806
|
+
@ObjectType({ description: 'Product entity (Produkt-Entität)' })
|
|
807
|
+
export class Product extends CoreModel { ... }
|
|
808
|
+
|
|
809
|
+
@InputType({ description: 'Product creation data (Produkt-Erstellungsdaten)' })
|
|
810
|
+
export class ProductCreateInput { ... }
|
|
811
|
+
|
|
812
|
+
@InputType({ description: 'Product update data (Produkt-Aktualisierungsdaten)' })
|
|
813
|
+
export class ProductInput { ... }
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
#### Step 5.5: Verify Consistency
|
|
817
|
+
|
|
818
|
+
After applying all descriptions, verify:
|
|
819
|
+
|
|
820
|
+
- [ ] All user-provided comments extracted and processed
|
|
821
|
+
- [ ] All German descriptions translated to format: `ENGLISH (DEUTSCH)`
|
|
822
|
+
- [ ] All English descriptions kept as-is
|
|
823
|
+
- [ ] Module Model has descriptions on all properties
|
|
824
|
+
- [ ] Module CreateInput has SAME descriptions on all properties
|
|
825
|
+
- [ ] Module UpdateInput has SAME descriptions on all properties
|
|
826
|
+
- [ ] SubObject has descriptions on all properties
|
|
827
|
+
- [ ] SubObject CreateInput has SAME descriptions on all properties
|
|
828
|
+
- [ ] SubObject UpdateInput has SAME descriptions on all properties
|
|
829
|
+
- [ ] Class-level decorators have descriptions
|
|
830
|
+
- [ ] NO inconsistencies (same property, different descriptions)
|
|
897
831
|
|
|
898
|
-
|
|
899
|
-
- Model property
|
|
900
|
-
- Input property (both create and update)
|
|
901
|
-
- Output property
|
|
832
|
+
**If ANY checkbox is unchecked, STOP and fix before continuing to Phase 6!**
|
|
902
833
|
|
|
903
834
|
### Phase 6: Enum File Creation
|
|
904
835
|
|
|
@@ -920,6 +851,99 @@ export enum StatusEnum {
|
|
|
920
851
|
|
|
921
852
|
### Phase 7: API Test Creation
|
|
922
853
|
|
|
854
|
+
**⚠️ CRITICAL: Test Type Requirement**
|
|
855
|
+
|
|
856
|
+
**ONLY create API tests using TestHelper - NEVER create direct Service tests!**
|
|
857
|
+
|
|
858
|
+
- ✅ **DO:** Create tests that call REST endpoints or GraphQL queries/mutations using `TestHelper`
|
|
859
|
+
- ✅ **DO:** Test through the API layer (Controller/Resolver → Service → Database)
|
|
860
|
+
- ❌ **DON'T:** Create tests that directly instantiate or call Service methods
|
|
861
|
+
- ❌ **DON'T:** Create unit tests for Services (e.g., `user.service.spec.ts`)
|
|
862
|
+
- ❌ **DON'T:** Mock dependencies or bypass the API layer
|
|
863
|
+
|
|
864
|
+
**Why API tests only?**
|
|
865
|
+
- API tests validate the complete security model (decorators, guards, permissions)
|
|
866
|
+
- Direct Service tests bypass authentication and authorization checks
|
|
867
|
+
- TestHelper provides all necessary tools for comprehensive API testing
|
|
868
|
+
|
|
869
|
+
**Exception: Direct database/service access for test setup/cleanup ONLY**
|
|
870
|
+
|
|
871
|
+
Direct database or service access is ONLY allowed for:
|
|
872
|
+
|
|
873
|
+
- ✅ **Test Setup (beforeAll/beforeEach)**:
|
|
874
|
+
- Setting user roles in database: `await db.collection('users').updateOne({ _id: userId }, { $set: { roles: ['admin'] } })`
|
|
875
|
+
- Setting verified flag: `await db.collection('users').updateOne({ _id: userId }, { $set: { verified: true } })`
|
|
876
|
+
- Creating prerequisite test data that can't be created via API
|
|
877
|
+
|
|
878
|
+
- ✅ **Test Cleanup (afterAll/afterEach)**:
|
|
879
|
+
- Deleting test objects: `await db.collection('products').deleteMany({ createdBy: testUserId })`
|
|
880
|
+
- Cleaning up test data: `await db.collection('users').deleteOne({ email: 'test@example.com' })`
|
|
881
|
+
|
|
882
|
+
- ❌ **NEVER for testing functionality**:
|
|
883
|
+
- Don't call `userService.create()` to test user creation - use API endpoint!
|
|
884
|
+
- Don't call `productService.update()` to test updates - use API endpoint!
|
|
885
|
+
- Don't access database to verify results - query via API instead!
|
|
886
|
+
|
|
887
|
+
**Example of correct usage:**
|
|
888
|
+
|
|
889
|
+
```typescript
|
|
890
|
+
describe('Product Tests', () => {
|
|
891
|
+
let adminToken: string;
|
|
892
|
+
let userId: string;
|
|
893
|
+
|
|
894
|
+
beforeAll(async () => {
|
|
895
|
+
// ✅ ALLOWED: Direct DB access for setup
|
|
896
|
+
const user = await testHelper.rest('/auth/signup', {
|
|
897
|
+
method: 'POST',
|
|
898
|
+
payload: { email: 'admin@test.com', password: 'password' }
|
|
899
|
+
});
|
|
900
|
+
userId = user.id;
|
|
901
|
+
|
|
902
|
+
// ✅ ALLOWED: Direct DB manipulation for test setup
|
|
903
|
+
await db.collection('users').updateOne(
|
|
904
|
+
{ _id: new ObjectId(userId) },
|
|
905
|
+
{ $set: { roles: ['admin'], verified: true } }
|
|
906
|
+
);
|
|
907
|
+
|
|
908
|
+
// Get token via API
|
|
909
|
+
const auth = await testHelper.rest('/auth/signin', {
|
|
910
|
+
method: 'POST',
|
|
911
|
+
payload: { email: 'admin@test.com', password: 'password' }
|
|
912
|
+
});
|
|
913
|
+
adminToken = auth.token;
|
|
914
|
+
});
|
|
915
|
+
|
|
916
|
+
it('should create product', async () => {
|
|
917
|
+
// ✅ CORRECT: Test via API
|
|
918
|
+
const result = await testHelper.rest('/api/products', {
|
|
919
|
+
method: 'POST',
|
|
920
|
+
payload: { name: 'Test Product' },
|
|
921
|
+
token: adminToken
|
|
922
|
+
});
|
|
923
|
+
|
|
924
|
+
expect(result.name).toBe('Test Product');
|
|
925
|
+
|
|
926
|
+
// ❌ WRONG: Don't verify via DB
|
|
927
|
+
// const dbProduct = await db.collection('products').findOne({ _id: result.id });
|
|
928
|
+
|
|
929
|
+
// ✅ CORRECT: Verify via API
|
|
930
|
+
const fetched = await testHelper.rest(`/api/products/${result.id}`, {
|
|
931
|
+
method: 'GET',
|
|
932
|
+
token: adminToken
|
|
933
|
+
});
|
|
934
|
+
expect(fetched.name).toBe('Test Product');
|
|
935
|
+
});
|
|
936
|
+
|
|
937
|
+
afterAll(async () => {
|
|
938
|
+
// ✅ ALLOWED: Direct DB access for cleanup
|
|
939
|
+
await db.collection('products').deleteMany({ createdBy: userId });
|
|
940
|
+
await db.collection('users').deleteOne({ _id: new ObjectId(userId) });
|
|
941
|
+
});
|
|
942
|
+
});
|
|
943
|
+
```
|
|
944
|
+
|
|
945
|
+
---
|
|
946
|
+
|
|
923
947
|
**⚠️ CRITICAL: Test Creation Process**
|
|
924
948
|
|
|
925
949
|
Creating API tests is NOT just about testing functionality - it's about **validating the security model**. You MUST follow this exact process:
|
|
@@ -1594,7 +1618,20 @@ After generation, verify:
|
|
|
1594
1618
|
- [ ] All Objects created
|
|
1595
1619
|
- [ ] All Modules created
|
|
1596
1620
|
- [ ] All properties in alphabetical order
|
|
1597
|
-
- [ ]
|
|
1621
|
+
- [ ] **DESCRIPTIONS (Critical - check thoroughly):**
|
|
1622
|
+
- [ ] All user-provided comments (after `//`) extracted from specification
|
|
1623
|
+
- [ ] All German descriptions translated to format: `ENGLISH (DEUTSCH)`
|
|
1624
|
+
- [ ] All English descriptions kept as-is (spelling corrected)
|
|
1625
|
+
- [ ] ALL Module Models have descriptions on all properties
|
|
1626
|
+
- [ ] ALL Module CreateInputs have SAME descriptions
|
|
1627
|
+
- [ ] ALL Module UpdateInputs have SAME descriptions
|
|
1628
|
+
- [ ] ALL SubObjects have descriptions on all properties
|
|
1629
|
+
- [ ] ALL SubObject CreateInputs have SAME descriptions
|
|
1630
|
+
- [ ] ALL SubObject UpdateInputs have SAME descriptions
|
|
1631
|
+
- [ ] ALL `@ObjectType()` decorators have descriptions
|
|
1632
|
+
- [ ] ALL `@InputType()` decorators have descriptions
|
|
1633
|
+
- [ ] NO inconsistencies (same property, different descriptions in different files)
|
|
1634
|
+
- [ ] NO German-only descriptions (must be translated)
|
|
1598
1635
|
- [ ] Inheritance properly implemented
|
|
1599
1636
|
- [ ] Required fields correctly set in CreateInputs
|
|
1600
1637
|
- [ ] Enum files created in `src/server/common/enums/`
|
|
@@ -1747,1036 +1784,57 @@ import { User } from '../../user/user.model';
|
|
|
1747
1784
|
|
|
1748
1785
|
## ⚠️ CRITICAL: Security & Test Coverage Rules
|
|
1749
1786
|
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
**❌ ABSOLUTELY FORBIDDEN:**
|
|
1753
|
-
```typescript
|
|
1754
|
-
// BEFORE (secure):
|
|
1755
|
-
@Restricted(RoleEnum.ADMIN)
|
|
1756
|
-
export class ProductController {
|
|
1757
|
-
@Roles(RoleEnum.S_USER)
|
|
1758
|
-
async createProduct() { ... }
|
|
1759
|
-
}
|
|
1760
|
-
|
|
1761
|
-
// AFTER (FORBIDDEN - security weakened!):
|
|
1762
|
-
// @Restricted(RoleEnum.ADMIN) ← NEVER remove this!
|
|
1763
|
-
export class ProductController {
|
|
1764
|
-
@Roles(RoleEnum.S_USER)
|
|
1765
|
-
async createProduct() { ... }
|
|
1766
|
-
}
|
|
1767
|
-
```
|
|
1768
|
-
|
|
1769
|
-
**🚨 CRITICAL RULE:**
|
|
1770
|
-
- **NEVER remove or weaken `@Restricted()` decorators** on Controllers, Resolvers, Models, or Objects
|
|
1771
|
-
- **NEVER change `@Roles()` decorators** to more permissive roles just to make tests pass
|
|
1772
|
-
- **NEVER modify `securityCheck()` logic** to bypass security for testing
|
|
1773
|
-
|
|
1774
|
-
**If tests fail due to permissions:**
|
|
1775
|
-
1. ✅ **CORRECT**: Adjust the test to use the appropriate user/token
|
|
1776
|
-
2. ✅ **CORRECT**: Create test users with the required roles
|
|
1777
|
-
3. ❌ **WRONG**: Weaken security to make tests pass
|
|
1778
|
-
|
|
1779
|
-
**Any security changes MUST:**
|
|
1780
|
-
- Be discussed with the developer FIRST
|
|
1781
|
-
- Have a solid business justification
|
|
1782
|
-
- Be explicitly approved by the developer
|
|
1783
|
-
- Be documented with the reason
|
|
1784
|
-
|
|
1785
|
-
### Rule 2: Understanding Permission Hierarchy
|
|
1786
|
-
|
|
1787
|
-
**⭐ Key Concept: Specific Overrides General**
|
|
1788
|
-
|
|
1789
|
-
The `@Restricted()` decorator on a class acts as a **security fallback** - if a method/property doesn't specify permissions, it inherits the class-level restriction. This is a **security-by-default** pattern.
|
|
1790
|
-
|
|
1791
|
-
**Example - Controller/Resolver:**
|
|
1792
|
-
```typescript
|
|
1793
|
-
@Restricted(RoleEnum.ADMIN) // ← FALLBACK: Protects everything by default
|
|
1794
|
-
export class ProductController {
|
|
1795
|
-
|
|
1796
|
-
@Roles(RoleEnum.S_EVERYONE) // ← SPECIFIC: This method is MORE open
|
|
1797
|
-
async getPublicProducts() {
|
|
1798
|
-
// Anyone can access this
|
|
1799
|
-
}
|
|
1800
|
-
|
|
1801
|
-
@Roles(RoleEnum.S_USER) // ← SPECIFIC: This method is MORE open
|
|
1802
|
-
async getMyProducts() {
|
|
1803
|
-
// Any signed-in user can access this
|
|
1804
|
-
}
|
|
1805
|
-
|
|
1806
|
-
async adminOnlyMethod() { // ← NO DECORATOR: Falls back to @Restricted(ADMIN)
|
|
1807
|
-
// Only admins can access this
|
|
1808
|
-
}
|
|
1809
|
-
}
|
|
1810
|
-
```
|
|
1811
|
-
|
|
1812
|
-
**Why `@Restricted(RoleEnum.ADMIN)` MUST stay:**
|
|
1813
|
-
- **Fail-safe**: If someone forgets `@Roles()` on a new method, it's protected by default
|
|
1814
|
-
- **Security**: Without it, a method without `@Roles()` would be open to everyone
|
|
1815
|
-
- **Intent**: Shows the class is security-sensitive
|
|
1816
|
-
|
|
1817
|
-
**Example - Model/Object:**
|
|
1818
|
-
```typescript
|
|
1819
|
-
@Restricted(RoleEnum.ADMIN) // ← FALLBACK: Protects all properties by default
|
|
1820
|
-
export class Product extends CoreModel {
|
|
1821
|
-
|
|
1822
|
-
@Restricted(RoleEnum.S_EVERYONE) // ← SPECIFIC: This field is MORE open
|
|
1823
|
-
name: string;
|
|
1824
|
-
|
|
1825
|
-
@Restricted(RoleEnum.S_USER) // ← SPECIFIC: This field is MORE open
|
|
1826
|
-
description: string;
|
|
1827
|
-
|
|
1828
|
-
secretInternalNotes: string; // ← NO DECORATOR: Falls back to ADMIN only
|
|
1829
|
-
|
|
1830
|
-
securityCheck(user: User) {
|
|
1831
|
-
// Controls who can see the entire object
|
|
1832
|
-
if (user?.hasRole(RoleEnum.ADMIN)) return this;
|
|
1833
|
-
if (this.isPublic) return this;
|
|
1834
|
-
return undefined;
|
|
1835
|
-
}
|
|
1836
|
-
}
|
|
1837
|
-
```
|
|
1838
|
-
|
|
1839
|
-
### Rule 3: Test Coverage Strategy
|
|
1787
|
+
**This section provides detailed rules for security and testing. It's duplicated at the beginning of this file for visibility.**
|
|
1840
1788
|
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
**Approach:**
|
|
1844
|
-
1. **Analyze permissions thoroughly** (as described in Phase 7)
|
|
1845
|
-
2. **Create appropriate test users** for each permission level:
|
|
1846
|
-
```typescript
|
|
1847
|
-
const adminUser = await testHelper.createUser({ roles: [RoleEnum.ADMIN] });
|
|
1848
|
-
const regularUser = await testHelper.createUser({ roles: [RoleEnum.S_USER] });
|
|
1849
|
-
const creatorUser = await testHelper.createUser({ roles: [RoleEnum.S_USER] });
|
|
1850
|
-
```
|
|
1851
|
-
3. **Test with the LEAST privileged user** who should have access
|
|
1852
|
-
4. **Also test with UNAUTHORIZED users** to verify security
|
|
1853
|
-
5. **Document why certain operations require certain roles**
|
|
1854
|
-
|
|
1855
|
-
**Coverage Priorities:**
|
|
1856
|
-
1. **100% coverage** for all security-critical code (securityCheck, guards)
|
|
1857
|
-
2. **100% coverage** for all endpoints (both success and failure cases)
|
|
1858
|
-
3. **100% coverage** for all permission combinations
|
|
1859
|
-
4. **90%+ coverage** for business logic
|
|
1860
|
-
5. **Complete coverage** of error paths and edge cases
|
|
1861
|
-
|
|
1862
|
-
**Testing Strategy:**
|
|
1863
|
-
```typescript
|
|
1864
|
-
describe('ProductController', () => {
|
|
1865
|
-
describe('createProduct', () => {
|
|
1866
|
-
// Test with minimum required privilege
|
|
1867
|
-
it('should create product as S_USER', async () => {
|
|
1868
|
-
// ✅ Tests with least privileged user who should succeed
|
|
1869
|
-
});
|
|
1789
|
+
**📖 For the complete content with all rules, examples, and testing strategies, see: `security-rules.md`**
|
|
1870
1790
|
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1791
|
+
**Quick reminder - Core rules:**
|
|
1792
|
+
1. **NEVER weaken @Restricted or @Roles decorators** to make tests pass
|
|
1793
|
+
2. **NEVER modify securityCheck() logic** to bypass security
|
|
1794
|
+
3. **ALWAYS test with least privileged user** who is authorized
|
|
1795
|
+
4. **ALWAYS create appropriate test users** for each permission level
|
|
1796
|
+
5. **ALWAYS ask developer** before changing ANY security decorator
|
|
1797
|
+
6. **Aim for 80-100% test coverage** without compromising security
|
|
1876
1798
|
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1799
|
+
**Testing approach:**
|
|
1800
|
+
- Analyze permissions first
|
|
1801
|
+
- Create test users for each role
|
|
1802
|
+
- Test happy path with appropriate user
|
|
1803
|
+
- Test security failures (403 responses)
|
|
1804
|
+
- Document why certain roles are required
|
|
1881
1805
|
|
|
1882
|
-
|
|
1883
|
-
// ✅ Verifies fallback to @Restricted(ADMIN) works
|
|
1884
|
-
});
|
|
1806
|
+
## Phase 8: Pre-Report Quality Review
|
|
1885
1807
|
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
B) Change to @Restricted(RoleEnum.S_USER) with your approval
|
|
1917
|
-
C) Something else?"
|
|
1918
|
-
```
|
|
1919
|
-
5. **Wait for explicit approval** before changing ANY security decorator
|
|
1920
|
-
6. **Document the decision** in code comments:
|
|
1921
|
-
```typescript
|
|
1922
|
-
// Changed from ADMIN to S_USER on 2024-01-15
|
|
1923
|
-
// Reason: Users need to create their own products
|
|
1924
|
-
// Approved by: [Developer Name]
|
|
1925
|
-
@Restricted(RoleEnum.S_USER)
|
|
1926
|
-
```
|
|
1927
|
-
|
|
1928
|
-
**Remember:**
|
|
1929
|
-
- Security is NOT negotiable for test convenience
|
|
1930
|
-
- Tests must adapt to security requirements, not vice versa
|
|
1931
|
-
- When in doubt, keep it secure and ask
|
|
1932
|
-
|
|
1933
|
-
## Phase 8: Pre-Report Quality Review
|
|
1934
|
-
|
|
1935
|
-
**CRITICAL**: Before creating the final report, you MUST perform a comprehensive quality review:
|
|
1936
|
-
|
|
1937
|
-
### Step 1: Identify All Changes
|
|
1938
|
-
|
|
1939
|
-
Use git to identify all created and modified files:
|
|
1940
|
-
|
|
1941
|
-
```bash
|
|
1942
|
-
git status --short
|
|
1943
|
-
git diff --name-only
|
|
1944
|
-
```
|
|
1945
|
-
|
|
1946
|
-
For each file, review:
|
|
1947
|
-
- All newly created files
|
|
1948
|
-
- All modified files
|
|
1949
|
-
- File structure and organization
|
|
1950
|
-
|
|
1951
|
-
### Step 2: Test Management
|
|
1952
|
-
|
|
1953
|
-
**CRITICAL**: Ensure tests are created/updated for all changes:
|
|
1954
|
-
|
|
1955
|
-
#### Step 2.1: Analyze Existing Tests FIRST
|
|
1956
|
-
|
|
1957
|
-
**BEFORE creating or modifying ANY tests, you MUST thoroughly analyze existing tests**:
|
|
1958
|
-
|
|
1959
|
-
1. **Identify all existing test files**:
|
|
1960
|
-
```bash
|
|
1961
|
-
# List all test directories and files
|
|
1962
|
-
ls -la tests/
|
|
1963
|
-
ls -la tests/modules/
|
|
1964
|
-
find tests -name "*.e2e-spec.ts" -type f
|
|
1965
|
-
```
|
|
1966
|
-
|
|
1967
|
-
2. **Read multiple existing test files completely**:
|
|
1968
|
-
```bash
|
|
1969
|
-
# Read at least 2-3 different module tests to understand patterns
|
|
1970
|
-
cat tests/modules/user.e2e-spec.ts
|
|
1971
|
-
cat tests/modules/<another-module>.e2e-spec.ts
|
|
1972
|
-
|
|
1973
|
-
# Also check the common and project test files
|
|
1974
|
-
cat tests/common.e2e-spec.ts
|
|
1975
|
-
cat tests/project.e2e-spec.ts
|
|
1976
|
-
```
|
|
1977
|
-
|
|
1978
|
-
3. **CRITICAL: Understand the TestHelper thoroughly**:
|
|
1979
|
-
|
|
1980
|
-
**Before creating any tests, you MUST understand the TestHelper from @lenne.tech/nest-server**:
|
|
1981
|
-
|
|
1982
|
-
```bash
|
|
1983
|
-
# Read the TestHelper source code to understand its capabilities
|
|
1984
|
-
cat node_modules/@lenne.tech/nest-server/src/test/test.helper.ts
|
|
1985
|
-
```
|
|
1986
|
-
|
|
1987
|
-
**Analyze the TestHelper to understand**:
|
|
1988
|
-
- **Available methods**: What methods does TestHelper provide?
|
|
1989
|
-
- **Configuration options**: How can TestHelper be configured?
|
|
1990
|
-
- **GraphQL support**: How to use `graphQl()` method? What parameters does it accept?
|
|
1991
|
-
- **REST support**: How to use `rest()` method? What parameters does it accept?
|
|
1992
|
-
- **Authentication**: How does TestHelper handle tokens and authentication?
|
|
1993
|
-
- **Request building**: How are requests constructed? What options are available?
|
|
1994
|
-
- **Response handling**: How are responses processed? What format is returned?
|
|
1995
|
-
- **Error handling**: How does TestHelper handle errors and failures?
|
|
1996
|
-
- **Helper utilities**: What additional utilities are available?
|
|
1997
|
-
|
|
1998
|
-
**Document your findings**:
|
|
1999
|
-
```typescript
|
|
2000
|
-
// Example: Understanding TestHelper.graphQl()
|
|
2001
|
-
// Method signature: graphQl(options: GraphQLOptions, config?: RequestConfig)
|
|
2002
|
-
// GraphQLOptions: { name, type (QUERY/MUTATION), arguments, fields }
|
|
2003
|
-
// RequestConfig: { token, statusCode, headers }
|
|
2004
|
-
// Returns: Parsed response data or error
|
|
2005
|
-
|
|
2006
|
-
// Example: Understanding TestHelper.rest()
|
|
2007
|
-
// Method signature: rest(method: HttpMethod, path: string, options?: RestOptions)
|
|
2008
|
-
// HttpMethod: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
|
|
2009
|
-
// RestOptions: { body, token, statusCode, headers }
|
|
2010
|
-
// Returns: Response data or error
|
|
2011
|
-
```
|
|
2012
|
-
|
|
2013
|
-
**Common TestHelper patterns to understand**:
|
|
2014
|
-
- How to execute GraphQL queries/mutations with `graphQl()`
|
|
2015
|
-
- How to execute REST requests with `rest()`
|
|
2016
|
-
- How to pass authentication tokens (same for both methods)
|
|
2017
|
-
- How to handle expected errors (statusCode parameter)
|
|
2018
|
-
- How to work with response data
|
|
2019
|
-
- How to structure test data
|
|
2020
|
-
- When to use GraphQL vs REST methods
|
|
2021
|
-
|
|
2022
|
-
**Only after fully understanding TestHelper, proceed to next step.**
|
|
2023
|
-
|
|
2024
|
-
4. **Understand the testing approach used**:
|
|
2025
|
-
- Which test framework? (Jest, Mocha, etc.)
|
|
2026
|
-
- Which testing utilities? (@lenne.tech/nest-server testHelper, custom helpers)
|
|
2027
|
-
- How is the test app initialized? (beforeAll setup)
|
|
2028
|
-
- How are test users/auth handled?
|
|
2029
|
-
- How is test data created and cleaned up?
|
|
2030
|
-
- What assertion library? (expect, should, etc.)
|
|
2031
|
-
- Are there custom matchers?
|
|
2032
|
-
|
|
2033
|
-
5. **Document the patterns you observe**:
|
|
2034
|
-
- **Import patterns**: Which modules are imported? In what order?
|
|
2035
|
-
- **Setup patterns**: How is beforeAll/beforeEach structured?
|
|
2036
|
-
- **Auth patterns**: How do tests authenticate? Token handling?
|
|
2037
|
-
- **Test structure**: Describe blocks organization? Test naming conventions?
|
|
2038
|
-
- **CRUD patterns**: How are create/read/update/delete tested?
|
|
2039
|
-
- **Assertion patterns**: What assertions are used? How detailed?
|
|
2040
|
-
- **Cleanup patterns**: How is afterAll/afterEach structured?
|
|
2041
|
-
- **Error testing**: How are failures/validations tested?
|
|
2042
|
-
|
|
2043
|
-
6. **Verify existing tests run successfully**:
|
|
2044
|
-
```bash
|
|
2045
|
-
# Run existing tests to ensure they pass
|
|
2046
|
-
npm run test:e2e
|
|
2047
|
-
|
|
2048
|
-
# If any fail, understand why before proceeding
|
|
2049
|
-
# Your new/modified tests MUST NOT break existing tests
|
|
2050
|
-
```
|
|
2051
|
-
|
|
2052
|
-
7. **Create a mental checklist**:
|
|
2053
|
-
- [ ] I have read and understand the TestHelper source code
|
|
2054
|
-
- [ ] I understand TestHelper methods and configuration
|
|
2055
|
-
- [ ] I understand how to use graphQl() method (GraphQL queries/mutations)
|
|
2056
|
-
- [ ] I understand how to use rest() method (REST endpoints)
|
|
2057
|
-
- [ ] I understand when to use graphQl() vs rest()
|
|
2058
|
-
- [ ] I understand TestHelper authentication and error handling
|
|
2059
|
-
- [ ] I understand which test helpers/utilities are used
|
|
2060
|
-
- [ ] I understand the authentication/authorization pattern
|
|
2061
|
-
- [ ] I understand the test data lifecycle (create/cleanup)
|
|
2062
|
-
- [ ] I understand the assertion patterns
|
|
2063
|
-
- [ ] I understand the error testing approach
|
|
2064
|
-
- [ ] All existing tests pass before I make changes
|
|
2065
|
-
|
|
2066
|
-
**Only after completing this analysis, proceed to create or modify tests.**
|
|
2067
|
-
|
|
2068
|
-
#### Step 2.1.1: Understanding Permissions and User Rights in Tests
|
|
2069
|
-
|
|
2070
|
-
**CRITICAL**: Before creating tests, you MUST understand the 3-layer permission system:
|
|
2071
|
-
|
|
2072
|
-
**Important Definitions**:
|
|
2073
|
-
|
|
2074
|
-
- **Admin User**: A user whose `roles` array contains `'admin'`
|
|
2075
|
-
```typescript
|
|
2076
|
-
// Example admin user
|
|
2077
|
-
{
|
|
2078
|
-
id: '123',
|
|
2079
|
-
email: 'admin@test.com',
|
|
2080
|
-
roles: ['admin', 'user'] // ← Contains 'admin'
|
|
2081
|
-
}
|
|
2082
|
-
```
|
|
2083
|
-
|
|
2084
|
-
- **Creator**: The user who created an object, identified by matching IDs
|
|
2085
|
-
```typescript
|
|
2086
|
-
// User who created the object
|
|
2087
|
-
const user = { id: 'user-123', email: 'creator@test.com' };
|
|
2088
|
-
|
|
2089
|
-
// Object created by this user
|
|
2090
|
-
const product = {
|
|
2091
|
-
id: 'product-456',
|
|
2092
|
-
name: 'Test Product',
|
|
2093
|
-
createdBy: 'user-123' // ← Matches user.id → This user is the CREATOR
|
|
2094
|
-
};
|
|
2095
|
-
|
|
2096
|
-
// Different user (NOT the creator)
|
|
2097
|
-
const otherUser = { id: 'user-789', email: 'other@test.com' };
|
|
2098
|
-
// otherUser.id !== product.createdBy → NOT the creator!
|
|
2099
|
-
```
|
|
2100
|
-
|
|
2101
|
-
**The Three Permission Layers**:
|
|
2102
|
-
|
|
2103
|
-
1. **Controller/Resolver Layer** (`@Roles()` decorator):
|
|
2104
|
-
- Controls WHO can call the endpoint
|
|
2105
|
-
- Example: `@Roles(RoleEnum.ADMIN)` → Only admins can call this endpoint
|
|
2106
|
-
- Example: `@Roles(RoleEnum.S_USER)` → All signed-in users can call
|
|
2107
|
-
|
|
2108
|
-
2. **Service Layer** (`serviceOptions.roles` parameter):
|
|
2109
|
-
- Controls what permissions are checked during service processing
|
|
2110
|
-
- Example: Update/Delete often require `[RoleEnum.ADMIN, RoleEnum.S_CREATOR]`
|
|
2111
|
-
- The creator can update/delete their own items
|
|
2112
|
-
|
|
2113
|
-
3. **Model Layer** (`securityCheck()` method):
|
|
2114
|
-
- Controls WHAT data is returned to the user
|
|
2115
|
-
- Standard implementation:
|
|
2116
|
-
```typescript
|
|
2117
|
-
securityCheck(user: User, force?: boolean) {
|
|
2118
|
-
// Admins see everything (user.roles contains 'admin')
|
|
2119
|
-
if (force || user?.hasRole(RoleEnum.ADMIN)) {
|
|
2120
|
-
return this;
|
|
2121
|
-
}
|
|
2122
|
-
// Only creator can see their own data (user.id === this.createdBy)
|
|
2123
|
-
if (!equalIds(user, this.createdBy)) {
|
|
2124
|
-
return undefined; // Non-creator gets nothing!
|
|
2125
|
-
}
|
|
2126
|
-
return this;
|
|
2127
|
-
}
|
|
2128
|
-
```
|
|
2129
|
-
- **Key checks**:
|
|
2130
|
-
- `user?.hasRole(RoleEnum.ADMIN)` → Returns `true` if `user.roles.includes('admin')`
|
|
2131
|
-
- `equalIds(user, this.createdBy)` → Returns `true` if `user.id === this.createdBy`
|
|
2132
|
-
|
|
2133
|
-
**Default Permission Behavior**:
|
|
2134
|
-
- **Create**: Usually accessible to signed-in users (`RoleEnum.S_USER`)
|
|
2135
|
-
- **Read/List**: Usually accessible to signed-in users, but securityCheck filters results
|
|
2136
|
-
- **Update**: Only ADMIN or CREATOR (via `serviceOptions.roles` check)
|
|
2137
|
-
- **Delete**: Only ADMIN or CREATOR (via `serviceOptions.roles` check)
|
|
2138
|
-
|
|
2139
|
-
**Analyzing Permissions Before Creating Tests**:
|
|
2140
|
-
|
|
2141
|
-
Before writing tests, check these 3 locations:
|
|
2142
|
-
|
|
2143
|
-
1. **Check Controller/Resolver decorators**:
|
|
2144
|
-
```typescript
|
|
2145
|
-
// In product.resolver.ts
|
|
2146
|
-
@Roles(RoleEnum.ADMIN) // ← WHO can call this?
|
|
2147
|
-
@Query(() => Product)
|
|
2148
|
-
async getProduct(@Args('id') id: string) { ... }
|
|
2149
|
-
|
|
2150
|
-
@Roles(RoleEnum.S_USER) // ← All signed-in users
|
|
2151
|
-
@Mutation(() => Product)
|
|
2152
|
-
async createProduct(@Args('input') input: ProductCreateInput) { ... }
|
|
2153
|
-
```
|
|
2154
|
-
|
|
2155
|
-
2. **Check Model/Object `@Restricted` decorators**:
|
|
2156
|
-
```typescript
|
|
2157
|
-
// In product.model.ts
|
|
2158
|
-
@Restricted(RoleEnum.ADMIN) // ← Model-level restriction
|
|
2159
|
-
export class Product extends CoreModel {
|
|
2160
|
-
|
|
2161
|
-
@Restricted(RoleEnum.ADMIN) // ← Property-level restriction
|
|
2162
|
-
@UnifiedField()
|
|
2163
|
-
internalNotes?: string;
|
|
2164
|
-
}
|
|
2165
|
-
```
|
|
2166
|
-
|
|
2167
|
-
3. **Check Model `securityCheck()` logic**:
|
|
2168
|
-
```typescript
|
|
2169
|
-
// In product.model.ts
|
|
2170
|
-
securityCheck(user: User, force?: boolean) {
|
|
2171
|
-
// Admin check: user.roles contains 'admin'
|
|
2172
|
-
if (force || user?.hasRole(RoleEnum.ADMIN)) {
|
|
2173
|
-
return this; // Admin sees all
|
|
2174
|
-
}
|
|
2175
|
-
|
|
2176
|
-
// Custom logic: Allow public products for everyone
|
|
2177
|
-
if (this.isPublic) {
|
|
2178
|
-
return this;
|
|
2179
|
-
}
|
|
2180
|
-
|
|
2181
|
-
// Creator check: user.id === this.createdBy
|
|
2182
|
-
if (!equalIds(user, this.createdBy)) {
|
|
2183
|
-
return undefined; // Non-creator gets nothing
|
|
2184
|
-
}
|
|
2185
|
-
return this; // Creator sees their own product
|
|
2186
|
-
}
|
|
2187
|
-
```
|
|
2188
|
-
|
|
2189
|
-
**Creating Appropriate Test Users**:
|
|
2190
|
-
|
|
2191
|
-
Based on permission analysis, create appropriate test users:
|
|
2192
|
-
|
|
2193
|
-
```typescript
|
|
2194
|
-
describe('Product Module', () => {
|
|
2195
|
-
let testHelper: TestHelper;
|
|
2196
|
-
let adminToken: string;
|
|
2197
|
-
let userToken: string;
|
|
2198
|
-
let otherUserToken: string;
|
|
2199
|
-
let createdProductId: string;
|
|
2200
|
-
|
|
2201
|
-
beforeAll(async () => {
|
|
2202
|
-
testHelper = new TestHelper(app);
|
|
2203
|
-
|
|
2204
|
-
// Admin user (user.roles contains 'admin')
|
|
2205
|
-
const adminAuth = await testHelper.graphQl({
|
|
2206
|
-
name: 'signIn',
|
|
2207
|
-
type: TestGraphQLType.MUTATION,
|
|
2208
|
-
arguments: { email: 'admin@test.com', password: 'admin' },
|
|
2209
|
-
fields: ['token', 'user { id email roles }']
|
|
2210
|
-
});
|
|
2211
|
-
adminToken = adminAuth.token;
|
|
2212
|
-
// adminAuth.user.roles = ['admin', 'user'] ← Contains 'admin'
|
|
2213
|
-
|
|
2214
|
-
// Regular user (will be the creator of test objects)
|
|
2215
|
-
const userAuth = await testHelper.graphQl({
|
|
2216
|
-
name: 'signIn',
|
|
2217
|
-
type: TestGraphQLType.MUTATION,
|
|
2218
|
-
arguments: { email: 'user@test.com', password: 'user' },
|
|
2219
|
-
fields: ['token', 'user { id email roles }']
|
|
2220
|
-
});
|
|
2221
|
-
userToken = userAuth.token;
|
|
2222
|
-
// When this user creates an object → object.createdBy = userAuth.user.id
|
|
2223
|
-
|
|
2224
|
-
// Another regular user (will NOT be the creator)
|
|
2225
|
-
const otherUserAuth = await testHelper.graphQl({
|
|
2226
|
-
name: 'signIn',
|
|
2227
|
-
type: TestGraphQLType.MUTATION,
|
|
2228
|
-
arguments: { email: 'other@test.com', password: 'other' },
|
|
2229
|
-
fields: ['token', 'user { id email roles }']
|
|
2230
|
-
});
|
|
2231
|
-
otherUserToken = otherUserAuth.token;
|
|
2232
|
-
// otherUserAuth.user.id !== object.createdBy → NOT the creator
|
|
2233
|
-
});
|
|
2234
|
-
});
|
|
2235
|
-
```
|
|
2236
|
-
|
|
2237
|
-
**Test Structure Based on Permissions**:
|
|
2238
|
-
|
|
2239
|
-
```typescript
|
|
2240
|
-
describe('Product Module', () => {
|
|
2241
|
-
// ... setup with adminToken, userToken, otherUserToken
|
|
2242
|
-
|
|
2243
|
-
describe('Create Product', () => {
|
|
2244
|
-
it('should create product as regular user', async () => {
|
|
2245
|
-
const result = await testHelper.graphQl({
|
|
2246
|
-
name: 'createProduct',
|
|
2247
|
-
type: TestGraphQLType.MUTATION,
|
|
2248
|
-
arguments: { input: { name: 'Test Product', price: 99.99 } },
|
|
2249
|
-
fields: ['id', 'name', 'createdBy { id }']
|
|
2250
|
-
}, { token: userToken }); // ← Created by userToken
|
|
2251
|
-
|
|
2252
|
-
expect(result.name).toBe('Test Product');
|
|
2253
|
-
// result.createdBy.id now equals userAuth.user.id
|
|
2254
|
-
// → userToken is the CREATOR of this product
|
|
2255
|
-
createdProductId = result.id;
|
|
2256
|
-
});
|
|
2257
|
-
});
|
|
2258
|
-
|
|
2259
|
-
describe('Update Product', () => {
|
|
2260
|
-
it('should update product as creator', async () => {
|
|
2261
|
-
const result = await testHelper.graphQl({
|
|
2262
|
-
name: 'updateProduct',
|
|
2263
|
-
type: TestGraphQLType.MUTATION,
|
|
2264
|
-
arguments: {
|
|
2265
|
-
id: createdProductId,
|
|
2266
|
-
input: { price: 89.99 }
|
|
2267
|
-
},
|
|
2268
|
-
fields: ['id', 'price']
|
|
2269
|
-
}, { token: userToken }); // ← Creator: userAuth.user.id === product.createdBy
|
|
2270
|
-
|
|
2271
|
-
expect(result.price).toBe(89.99);
|
|
2272
|
-
});
|
|
2273
|
-
|
|
2274
|
-
it('should update product as admin', async () => {
|
|
2275
|
-
const result = await testHelper.graphQl({
|
|
2276
|
-
name: 'updateProduct',
|
|
2277
|
-
type: TestGraphQLType.MUTATION,
|
|
2278
|
-
arguments: {
|
|
2279
|
-
id: createdProductId,
|
|
2280
|
-
input: { price: 79.99 }
|
|
2281
|
-
},
|
|
2282
|
-
fields: ['id', 'price']
|
|
2283
|
-
}, { token: adminToken }); // ← Admin: adminAuth.user.roles contains 'admin'
|
|
2284
|
-
|
|
2285
|
-
expect(result.price).toBe(79.99);
|
|
2286
|
-
});
|
|
2287
|
-
|
|
2288
|
-
it('should fail to update product as non-creator', async () => {
|
|
2289
|
-
const result = await testHelper.graphQl({
|
|
2290
|
-
name: 'updateProduct',
|
|
2291
|
-
type: TestGraphQLType.MUTATION,
|
|
2292
|
-
arguments: {
|
|
2293
|
-
id: createdProductId,
|
|
2294
|
-
input: { price: 69.99 }
|
|
2295
|
-
},
|
|
2296
|
-
fields: ['id']
|
|
2297
|
-
}, { token: otherUserToken, statusCode: 403 }); // ← Not creator: otherUserAuth.user.id !== product.createdBy
|
|
2298
|
-
|
|
2299
|
-
expect(result.errors).toBeDefined();
|
|
2300
|
-
});
|
|
2301
|
-
});
|
|
2302
|
-
|
|
2303
|
-
describe('Delete Product', () => {
|
|
2304
|
-
it('should delete product as creator', async () => {
|
|
2305
|
-
// First create a new product to delete
|
|
2306
|
-
const created = await testHelper.graphQl({
|
|
2307
|
-
name: 'createProduct',
|
|
2308
|
-
type: TestGraphQLType.MUTATION,
|
|
2309
|
-
arguments: { input: { name: 'To Delete', price: 50 } },
|
|
2310
|
-
fields: ['id']
|
|
2311
|
-
}, { token: userToken });
|
|
2312
|
-
|
|
2313
|
-
// Delete as creator
|
|
2314
|
-
const result = await testHelper.graphQl({
|
|
2315
|
-
name: 'deleteProduct',
|
|
2316
|
-
type: TestGraphQLType.MUTATION,
|
|
2317
|
-
arguments: { id: created.id },
|
|
2318
|
-
fields: ['id']
|
|
2319
|
-
}, { token: userToken }); // ← Creator: userAuth.user.id === created.createdBy
|
|
2320
|
-
|
|
2321
|
-
expect(result.id).toBe(created.id);
|
|
2322
|
-
});
|
|
2323
|
-
|
|
2324
|
-
it('should fail to delete product as non-creator', async () => {
|
|
2325
|
-
const result = await testHelper.graphQl({
|
|
2326
|
-
name: 'deleteProduct',
|
|
2327
|
-
type: TestGraphQLType.MUTATION,
|
|
2328
|
-
arguments: { id: createdProductId },
|
|
2329
|
-
fields: ['id']
|
|
2330
|
-
}, { token: otherUserToken, statusCode: 403 }); // ← Not creator: otherUserAuth.user.id !== product.createdBy
|
|
2331
|
-
|
|
2332
|
-
expect(result.errors).toBeDefined();
|
|
2333
|
-
});
|
|
2334
|
-
|
|
2335
|
-
it('should delete any product as admin', async () => {
|
|
2336
|
-
const result = await testHelper.graphQl({
|
|
2337
|
-
name: 'deleteProduct',
|
|
2338
|
-
type: TestGraphQLType.MUTATION,
|
|
2339
|
-
arguments: { id: createdProductId },
|
|
2340
|
-
fields: ['id']
|
|
2341
|
-
}, { token: adminToken }); // ← Admin: adminAuth.user.roles contains 'admin'
|
|
2342
|
-
|
|
2343
|
-
expect(result.id).toBe(createdProductId);
|
|
2344
|
-
});
|
|
2345
|
-
});
|
|
2346
|
-
});
|
|
2347
|
-
```
|
|
2348
|
-
|
|
2349
|
-
**Permission Testing Checklist**:
|
|
2350
|
-
|
|
2351
|
-
Before creating tests, verify:
|
|
2352
|
-
|
|
2353
|
-
- [ ] I have checked the `@Roles()` decorators in controllers/resolvers
|
|
2354
|
-
- [ ] I have checked the `@Restricted()` decorators in models/objects
|
|
2355
|
-
- [ ] I have reviewed the `securityCheck()` logic in models
|
|
2356
|
-
- [ ] I understand who can CREATE items (usually S_USER)
|
|
2357
|
-
- [ ] I understand who can READ items (S_USER + securityCheck filtering)
|
|
2358
|
-
- [ ] I understand who can UPDATE items (usually ADMIN + S_CREATOR)
|
|
2359
|
-
- [ ] I understand who can DELETE items (usually ADMIN + S_CREATOR)
|
|
2360
|
-
- [ ] I have created appropriate test users (admin, creator, non-creator)
|
|
2361
|
-
- [ ] My tests use the CREATOR token (user.id === object.createdBy) for update/delete operations
|
|
2362
|
-
- [ ] My tests verify that non-creators (user.id !== object.createdBy) CANNOT update/delete
|
|
2363
|
-
- [ ] My tests verify that admins (user.roles contains 'admin') CAN update/delete everything
|
|
2364
|
-
|
|
2365
|
-
**Common Permission Test Patterns**:
|
|
2366
|
-
|
|
2367
|
-
1. **Test with creator** (user.id === object.createdBy) → Should succeed
|
|
2368
|
-
2. **Test with admin** (user.roles contains 'admin') → Should succeed
|
|
2369
|
-
3. **Test with other user** (user.id !== object.createdBy) → Should fail (403)
|
|
2370
|
-
|
|
2371
|
-
**Only after understanding permissions, proceed to create tests.**
|
|
2372
|
-
|
|
2373
|
-
#### Step 2.2: For Newly Created Modules
|
|
2374
|
-
|
|
2375
|
-
**CRITICAL: Follow the correct test folder structure**:
|
|
2376
|
-
|
|
2377
|
-
The project uses a specific test organization:
|
|
2378
|
-
|
|
2379
|
-
1. **Module tests** (for modules in `src/server/modules/`):
|
|
2380
|
-
```
|
|
2381
|
-
tests/modules/<module-name>.e2e-spec.ts
|
|
2382
|
-
```
|
|
2383
|
-
- Each module gets its own test file directly in `tests/modules/`
|
|
2384
|
-
- Examples: `tests/modules/user.e2e-spec.ts`, `tests/modules/book.e2e-spec.ts`
|
|
2385
|
-
|
|
2386
|
-
2. **Common tests** (for common functionality in `src/server/common/`):
|
|
2387
|
-
```
|
|
2388
|
-
tests/common.e2e-spec.ts
|
|
2389
|
-
```
|
|
2390
|
-
- All common functionality (enums, objects, helpers) tested here
|
|
2391
|
-
- Single file for all common-related tests
|
|
2392
|
-
|
|
2393
|
-
3. **Project tests** (for everything else - root level, config, etc.):
|
|
2394
|
-
```
|
|
2395
|
-
tests/project.e2e-spec.ts
|
|
2396
|
-
```
|
|
2397
|
-
- General project-level tests
|
|
2398
|
-
- Configuration tests
|
|
2399
|
-
- Integration tests
|
|
2400
|
-
|
|
2401
|
-
**Determine correct test location**:
|
|
2402
|
-
|
|
2403
|
-
```bash
|
|
2404
|
-
# BEFORE creating a test, ask yourself:
|
|
2405
|
-
# - Is this a module in src/server/modules/? → tests/modules/<name>.e2e-spec.ts
|
|
2406
|
-
# - Is this common functionality? → Add to tests/common.e2e-spec.ts
|
|
2407
|
-
# - Is this project-level? → Add to tests/project.e2e-spec.ts
|
|
2408
|
-
|
|
2409
|
-
# Check existing test structure to confirm:
|
|
2410
|
-
ls -la tests/
|
|
2411
|
-
ls tests/modules/
|
|
2412
|
-
```
|
|
2413
|
-
|
|
2414
|
-
**Create new test files** for modules following the patterns you identified:
|
|
2415
|
-
|
|
2416
|
-
```bash
|
|
2417
|
-
# For a new module (e.g., Book)
|
|
2418
|
-
tests/modules/book.e2e-spec.ts
|
|
2419
|
-
```
|
|
2420
|
-
|
|
2421
|
-
**IMPORTANT**: Your new test file MUST:
|
|
2422
|
-
1. **Match the exact structure** of existing test files
|
|
2423
|
-
2. **Use the same imports** as existing tests
|
|
2424
|
-
3. **Follow the same setup/cleanup pattern** (beforeAll, afterAll)
|
|
2425
|
-
4. **Use the same test helpers/utilities** you observed
|
|
2426
|
-
5. **Follow the same authentication pattern**
|
|
2427
|
-
6. **Use the same assertion style**
|
|
2428
|
-
7. **Follow the same naming conventions** for describe/it blocks
|
|
2429
|
-
|
|
2430
|
-
**Each new test file must include**:
|
|
2431
|
-
1. All CRUD operations (create, find all, find by ID, update, delete)
|
|
2432
|
-
2. Authorization tests (unauthorized should fail, authorized should succeed)
|
|
2433
|
-
3. Required field validation (missing required fields should fail)
|
|
2434
|
-
4. Proper test data setup and cleanup (beforeAll, afterAll)
|
|
2435
|
-
5. Tests for any custom methods or relationships
|
|
2436
|
-
|
|
2437
|
-
**Ensure all prerequisites are met by analyzing existing tests**:
|
|
2438
|
-
|
|
2439
|
-
Before writing test code, identify ALL prerequisites from existing test files:
|
|
2440
|
-
|
|
2441
|
-
```bash
|
|
2442
|
-
# Read an existing module test to understand prerequisites
|
|
2443
|
-
cat tests/modules/user.e2e-spec.ts
|
|
2444
|
-
```
|
|
2445
|
-
|
|
2446
|
-
**Common prerequisites to check**:
|
|
2447
|
-
1. **Test data dependencies**:
|
|
2448
|
-
- Does the module reference other modules? (e.g., Book → User for borrowedBy)
|
|
2449
|
-
- Do you need to create related test data first?
|
|
2450
|
-
- Example: To test Book with borrowedBy: User, create test User first
|
|
2451
|
-
|
|
2452
|
-
2. **Authentication requirements**:
|
|
2453
|
-
- What roles/permissions are needed?
|
|
2454
|
-
- Do test users need to be created with specific roles?
|
|
2455
|
-
- Example: Admin user for create operations, regular user for read operations
|
|
2456
|
-
|
|
2457
|
-
3. **Database setup**:
|
|
2458
|
-
- Are there database constraints or required collections?
|
|
2459
|
-
- Do embedded objects or enums need to exist?
|
|
2460
|
-
|
|
2461
|
-
4. **Configuration**:
|
|
2462
|
-
- Are environment variables or config values needed?
|
|
2463
|
-
- Example: JWT secrets, database connections
|
|
2464
|
-
|
|
2465
|
-
**Pattern from existing tests**:
|
|
2466
|
-
```typescript
|
|
2467
|
-
// Example structure you should follow:
|
|
2468
|
-
beforeAll(async () => {
|
|
2469
|
-
// 1. Initialize test app/module
|
|
2470
|
-
// 2. Set up database connection
|
|
2471
|
-
// 3. Create prerequisite test data (users, roles, etc.)
|
|
2472
|
-
// 4. Authenticate and get tokens
|
|
2473
|
-
});
|
|
2474
|
-
|
|
2475
|
-
describe('Module Tests', () => {
|
|
2476
|
-
// Tests here
|
|
2477
|
-
});
|
|
2478
|
-
|
|
2479
|
-
afterAll(async () => {
|
|
2480
|
-
// 1. Delete created test data (in reverse order)
|
|
2481
|
-
// 2. Clean up connections
|
|
2482
|
-
// 3. Close app
|
|
2483
|
-
});
|
|
2484
|
-
```
|
|
2485
|
-
|
|
2486
|
-
**CRITICAL**: Look at how existing tests handle prerequisites and replicate the exact same approach.
|
|
2487
|
-
|
|
2488
|
-
#### Step 2.3: For Modified Existing Modules
|
|
2489
|
-
|
|
2490
|
-
**Update existing test files** when you modify modules:
|
|
2491
|
-
|
|
2492
|
-
1. **FIRST: Read the existing test file completely**:
|
|
2493
|
-
```bash
|
|
2494
|
-
# Find and read the test file for the module you modified
|
|
2495
|
-
find tests -name "*<module-name>*.e2e-spec.ts"
|
|
2496
|
-
cat tests/modules/<module-name>.e2e-spec.ts
|
|
2497
|
-
```
|
|
2498
|
-
|
|
2499
|
-
2. **Understand what the existing tests cover**:
|
|
2500
|
-
- Which operations are tested?
|
|
2501
|
-
- Which properties are validated?
|
|
2502
|
-
- What edge cases are covered?
|
|
2503
|
-
- How is test data structured?
|
|
2504
|
-
|
|
2505
|
-
3. **Run existing tests to ensure they pass BEFORE your changes**:
|
|
2506
|
-
```bash
|
|
2507
|
-
npm run test:e2e
|
|
2508
|
-
```
|
|
2509
|
-
|
|
2510
|
-
4. **Review and update tests**:
|
|
2511
|
-
- **Added properties**: Add tests verifying new properties work correctly
|
|
2512
|
-
- **Changed validation**: Update tests to reflect new validation rules
|
|
2513
|
-
- **Added relationships**: Add tests for new references/embedded objects
|
|
2514
|
-
- **Changed required fields**: Update CreateInput tests accordingly
|
|
2515
|
-
- **Removed properties**: Remove related test assertions
|
|
2516
|
-
|
|
2517
|
-
5. **Verify test coverage**:
|
|
2518
|
-
- All new properties are tested
|
|
2519
|
-
- Changed behavior is verified
|
|
2520
|
-
- Edge cases are covered
|
|
2521
|
-
- Authorization still works correctly
|
|
2522
|
-
|
|
2523
|
-
6. **Run tests again to ensure your changes don't break anything**:
|
|
2524
|
-
```bash
|
|
2525
|
-
npm run test:e2e
|
|
2526
|
-
```
|
|
2527
|
-
|
|
2528
|
-
### Step 3: Compare with Existing Code
|
|
2529
|
-
|
|
2530
|
-
**Compare generated code with existing project code**:
|
|
2531
|
-
|
|
2532
|
-
1. **Read existing similar modules** to understand project patterns:
|
|
2533
|
-
```bash
|
|
2534
|
-
# Example: If you created a User module, check existing modules
|
|
2535
|
-
ls src/server/modules/
|
|
2536
|
-
```
|
|
2537
|
-
|
|
2538
|
-
2. **Check for consistency**:
|
|
2539
|
-
- Code style (indentation, spacing, formatting)
|
|
2540
|
-
- Import ordering and organization
|
|
2541
|
-
- Naming conventions (camelCase, PascalCase, kebab-case)
|
|
2542
|
-
- File structure and directory organization
|
|
2543
|
-
- Comment style and documentation
|
|
2544
|
-
- Decorator usage (@Field, @Prop, etc.)
|
|
2545
|
-
- Error handling patterns
|
|
2546
|
-
- Validation patterns
|
|
2547
|
-
|
|
2548
|
-
3. **Review property ordering**:
|
|
2549
|
-
- Verify alphabetical order in models
|
|
2550
|
-
- Verify alphabetical order in inputs
|
|
2551
|
-
- Verify alphabetical order in outputs
|
|
2552
|
-
- Check decorator consistency
|
|
2553
|
-
|
|
2554
|
-
### Step 4: Critical Analysis
|
|
2555
|
-
|
|
2556
|
-
**Analyze each file critically**:
|
|
2557
|
-
|
|
2558
|
-
1. **Style consistency**:
|
|
2559
|
-
- Does the code match the project's existing style?
|
|
2560
|
-
- Are imports grouped and ordered correctly?
|
|
2561
|
-
- Is indentation consistent with the project?
|
|
2562
|
-
- Are naming conventions followed?
|
|
2563
|
-
|
|
2564
|
-
2. **Structural consistency**:
|
|
2565
|
-
- Are decorators in the same order as existing code?
|
|
2566
|
-
- Is the file structure identical to existing modules?
|
|
2567
|
-
- Are descriptions formatted the same way?
|
|
2568
|
-
- Are relationships implemented consistently?
|
|
2569
|
-
|
|
2570
|
-
3. **Code quality**:
|
|
2571
|
-
- Are there any redundant imports?
|
|
2572
|
-
- Are there any missing imports?
|
|
2573
|
-
- Are descriptions meaningful and complete?
|
|
2574
|
-
- Are TypeScript types correctly used?
|
|
2575
|
-
|
|
2576
|
-
4. **Best practices**:
|
|
2577
|
-
- Are required fields properly marked?
|
|
2578
|
-
- Are nullable fields correctly configured?
|
|
2579
|
-
- Are references properly typed?
|
|
2580
|
-
- Are arrays correctly configured?
|
|
2581
|
-
|
|
2582
|
-
### Step 5: Automated Optimizations
|
|
2583
|
-
|
|
2584
|
-
**Apply automatic improvements**:
|
|
2585
|
-
|
|
2586
|
-
1. **Fix import ordering**:
|
|
2587
|
-
- External imports first (alphabetically)
|
|
2588
|
-
- @lenne.tech/nest-server imports next
|
|
2589
|
-
- Local imports last (alphabetically by path depth)
|
|
2590
|
-
|
|
2591
|
-
2. **Fix property ordering**:
|
|
2592
|
-
- Reorder all properties alphabetically in models
|
|
2593
|
-
- Reorder all properties alphabetically in inputs
|
|
2594
|
-
- Reorder all properties alphabetically in outputs
|
|
2595
|
-
|
|
2596
|
-
3. **Fix formatting**:
|
|
2597
|
-
- Ensure consistent indentation
|
|
2598
|
-
- Remove extra blank lines
|
|
2599
|
-
- Add missing blank lines between sections
|
|
2600
|
-
|
|
2601
|
-
4. **Fix descriptions**:
|
|
2602
|
-
- Ensure all follow "ENGLISH (DEUTSCH)" format
|
|
2603
|
-
- Add missing descriptions
|
|
2604
|
-
- Improve unclear descriptions
|
|
2605
|
-
|
|
2606
|
-
5. **Fix common patterns**:
|
|
2607
|
-
- Standardize decorator usage
|
|
2608
|
-
- Standardize validation patterns
|
|
2609
|
-
- Standardize error handling
|
|
2610
|
-
|
|
2611
|
-
### Step 6: Pre-Report Testing
|
|
2612
|
-
|
|
2613
|
-
**MANDATORY**: Run all tests before reporting:
|
|
2614
|
-
|
|
2615
|
-
```bash
|
|
2616
|
-
# Run TypeScript compilation
|
|
2617
|
-
npm run build
|
|
2618
|
-
|
|
2619
|
-
# Run linting
|
|
2620
|
-
npm run lint
|
|
2621
|
-
|
|
2622
|
-
# Run all tests
|
|
2623
|
-
npm run test:e2e
|
|
2624
|
-
|
|
2625
|
-
# If any fail, fix issues and repeat
|
|
2626
|
-
```
|
|
2627
|
-
|
|
2628
|
-
**If tests fail**:
|
|
2629
|
-
1. Analyze the error
|
|
2630
|
-
2. Fix the issue
|
|
2631
|
-
3. Re-run tests
|
|
2632
|
-
4. Repeat until all tests pass
|
|
2633
|
-
|
|
2634
|
-
#### Debugging Failed Tests - Important Guidelines
|
|
2635
|
-
|
|
2636
|
-
**When tests fail, use systematic debugging with console.log statements:**
|
|
2637
|
-
|
|
2638
|
-
1. **Add debug messages in Controllers/Resolvers**:
|
|
2639
|
-
```typescript
|
|
2640
|
-
// In controller/resolver - BEFORE service call
|
|
2641
|
-
console.log('🔵 [Controller] createProduct - Input:', input);
|
|
2642
|
-
console.log('🔵 [Controller] createProduct - User:', serviceOptions?.user);
|
|
2643
|
-
|
|
2644
|
-
const result = await this.productService.create(input, serviceOptions);
|
|
2645
|
-
|
|
2646
|
-
// AFTER service call
|
|
2647
|
-
console.log('🔵 [Controller] createProduct - Result:', result);
|
|
2648
|
-
```
|
|
2649
|
-
|
|
2650
|
-
2. **Add debug messages in Services**:
|
|
2651
|
-
```typescript
|
|
2652
|
-
// In service method
|
|
2653
|
-
console.log('🟢 [Service] create - Input:', input);
|
|
2654
|
-
console.log('🟢 [Service] create - ServiceOptions:', serviceOptions);
|
|
2655
|
-
|
|
2656
|
-
const created = await super.create(input, serviceOptions);
|
|
2657
|
-
|
|
2658
|
-
console.log('🟢 [Service] create - Created:', created);
|
|
2659
|
-
```
|
|
2660
|
-
|
|
2661
|
-
3. **Understand the permissions system**:
|
|
2662
|
-
- **Controllers/Resolvers**: `@Roles()` decorator controls WHO can call the endpoint
|
|
2663
|
-
- **Services**: `serviceOptions.roles` controls what the service checks during processing
|
|
2664
|
-
- **Models**: `securityCheck()` method determines what data is returned to the user
|
|
2665
|
-
|
|
2666
|
-
4. **Default permission behavior**:
|
|
2667
|
-
- Only **Admin users** (user.roles contains 'admin') OR the **creator** (user.id === object.createdBy) of an element can access it
|
|
2668
|
-
- This is enforced in the `securityCheck()` method in models:
|
|
2669
|
-
```typescript
|
|
2670
|
-
securityCheck(user: User, force?: boolean) {
|
|
2671
|
-
// Admin: user.roles contains 'admin'
|
|
2672
|
-
if (force || user?.hasRole(RoleEnum.ADMIN)) {
|
|
2673
|
-
return this; // Admin sees everything
|
|
2674
|
-
}
|
|
2675
|
-
// Creator: user.id === this.createdBy
|
|
2676
|
-
if (!equalIds(user, this.createdBy)) {
|
|
2677
|
-
return undefined; // Non-creator (user.id !== this.createdBy) gets nothing
|
|
2678
|
-
}
|
|
2679
|
-
return this; // Creator sees their own data
|
|
2680
|
-
}
|
|
2681
|
-
```
|
|
2682
|
-
|
|
2683
|
-
5. **Debugging strategy for permission issues**:
|
|
2684
|
-
|
|
2685
|
-
**Step 1**: Run failing test with Admin user first
|
|
2686
|
-
```typescript
|
|
2687
|
-
// In test setup
|
|
2688
|
-
const adminToken = await testHelper.signIn('admin@test.com', 'admin-password');
|
|
2689
|
-
|
|
2690
|
-
// Use admin token in test
|
|
2691
|
-
const result = await testHelper.graphQl({...}, { token: adminToken });
|
|
2692
|
-
```
|
|
2693
|
-
|
|
2694
|
-
**Step 2**: Analyze results
|
|
2695
|
-
- ✅ **Works with Admin, fails with normal user** → Permission issue (check Roles, securityCheck)
|
|
2696
|
-
- ❌ **Fails with Admin too** → Different issue (check logic, data, validation)
|
|
2697
|
-
|
|
2698
|
-
6. **Common permission issues and solutions**:
|
|
2699
|
-
|
|
2700
|
-
| Problem | Cause | Solution |
|
|
2701
|
-
|---------|-------|----------|
|
|
2702
|
-
| 401/403 on endpoint | `@Roles()` too restrictive | Adjust decorator in controller/resolver |
|
|
2703
|
-
| Empty result despite data existing | `securityCheck()` returns undefined | Modify securityCheck logic or use Admin |
|
|
2704
|
-
| Service throws permission error | `serviceOptions.roles` check fails | Pass correct roles in serviceOptions |
|
|
2705
|
-
|
|
2706
|
-
7. **Remove debug messages after fixing**:
|
|
2707
|
-
```bash
|
|
2708
|
-
# After tests pass, remove all console.log statements
|
|
2709
|
-
# Search for debug patterns
|
|
2710
|
-
grep -r "console.log" src/server/modules/your-module/
|
|
2711
|
-
|
|
2712
|
-
# Remove them manually or with sed
|
|
2713
|
-
# Then verify tests still pass
|
|
2714
|
-
npm run test:e2e
|
|
2715
|
-
```
|
|
2716
|
-
|
|
2717
|
-
**Debugging workflow example**:
|
|
2718
|
-
```typescript
|
|
2719
|
-
// 1. Test fails - add debugging
|
|
2720
|
-
@Mutation(() => Product)
|
|
2721
|
-
async createProduct(@Args('input') input: ProductCreateInput, @GraphQLServiceOptions() opts) {
|
|
2722
|
-
console.log('🔵 START createProduct', { input, user: opts?.user?.email });
|
|
2723
|
-
|
|
2724
|
-
const result = await this.productService.create(input, opts);
|
|
2725
|
-
|
|
2726
|
-
console.log('🔵 END createProduct', { result: result?.id });
|
|
2727
|
-
return result;
|
|
2728
|
-
}
|
|
2729
|
-
|
|
2730
|
-
// In service
|
|
2731
|
-
async create(input: ProductCreateInput, serviceOptions?: ServiceOptions) {
|
|
2732
|
-
console.log('🟢 Service create', { input, user: serviceOptions?.user?.email });
|
|
2733
|
-
|
|
2734
|
-
const created = await super.create(input, serviceOptions);
|
|
2735
|
-
|
|
2736
|
-
console.log('🟢 Service created', { id: created?.id, createdBy: created?.createdBy });
|
|
2737
|
-
return created;
|
|
2738
|
-
}
|
|
2739
|
-
|
|
2740
|
-
// 2. Run test - observe output:
|
|
2741
|
-
// 🔵 START createProduct { input: {...}, user: 'test@test.com' }
|
|
2742
|
-
// 🟢 Service create { input: {...}, user: 'test@test.com' }
|
|
2743
|
-
// 🟢 Service created { id: '123', createdBy: '456' }
|
|
2744
|
-
// 🔵 END createProduct { result: undefined } ← AHA! Result is undefined!
|
|
2745
|
-
|
|
2746
|
-
// 3. Check model securityCheck() - likely returns undefined for non-creator (user.id !== object.createdBy)
|
|
2747
|
-
// 4. Fix: Either use Admin user (user.roles contains 'admin') or adjust securityCheck logic
|
|
2748
|
-
// 5. Test passes → Remove console.log statements
|
|
2749
|
-
// 6. Verify tests still pass
|
|
2750
|
-
```
|
|
2751
|
-
|
|
2752
|
-
**Do not proceed to final report if**:
|
|
2753
|
-
- TypeScript compilation fails
|
|
2754
|
-
- Linting fails
|
|
2755
|
-
- Any tests fail
|
|
2756
|
-
- Console shows errors or warnings
|
|
2757
|
-
|
|
2758
|
-
### Step 7: Final Verification
|
|
2759
|
-
|
|
2760
|
-
Before reporting, verify:
|
|
2761
|
-
|
|
2762
|
-
- [ ] All files compared with existing code
|
|
2763
|
-
- [ ] Code style matches project patterns
|
|
2764
|
-
- [ ] All imports properly ordered
|
|
2765
|
-
- [ ] All properties in alphabetical order
|
|
2766
|
-
- [ ] All descriptions follow format
|
|
2767
|
-
- [ ] **TestHelper source code read and understood**
|
|
2768
|
-
- [ ] **TestHelper methods and configuration understood**
|
|
2769
|
-
- [ ] **Existing tests analyzed BEFORE creating/modifying tests**
|
|
2770
|
-
- [ ] **Existing tests passed BEFORE making changes**
|
|
2771
|
-
- [ ] **Tests in correct location (tests/modules/<name>.e2e-spec.ts, tests/common.e2e-spec.ts, or tests/project.e2e-spec.ts)**
|
|
2772
|
-
- [ ] **New test files created for all new modules**
|
|
2773
|
-
- [ ] **Existing test files updated for all modified modules**
|
|
2774
|
-
- [ ] **All prerequisites identified and handled (test data dependencies, auth, etc.)**
|
|
2775
|
-
- [ ] **All new/modified tests follow exact patterns from existing tests**
|
|
2776
|
-
- [ ] TypeScript compiles without errors
|
|
2777
|
-
- [ ] Linter passes without warnings
|
|
2778
|
-
- [ ] **All tests pass AFTER changes**
|
|
2779
|
-
- [ ] No console errors or warnings
|
|
1808
|
+
**CRITICAL**: Before creating the final report, you MUST perform a comprehensive quality review.
|
|
1809
|
+
|
|
1810
|
+
**📖 For the complete quality review process with all steps, checklists, and examples, see: `quality-review.md`**
|
|
1811
|
+
|
|
1812
|
+
**Quick overview - 7 steps:**
|
|
1813
|
+
|
|
1814
|
+
1. **Identify All Changes**: Use git to identify all created/modified files
|
|
1815
|
+
2. **Test Management**:
|
|
1816
|
+
- Analyze existing tests FIRST (understand TestHelper, patterns, permissions)
|
|
1817
|
+
- Create new test files for newly created modules (in `tests/modules/`)
|
|
1818
|
+
- Update existing test files for modified modules
|
|
1819
|
+
- Follow exact patterns from existing tests
|
|
1820
|
+
3. **Compare with Existing Code**: Check consistency (style, structure, naming)
|
|
1821
|
+
4. **Critical Analysis**: Verify style, structure, code quality, best practices
|
|
1822
|
+
5. **Automated Optimizations**: Fix import ordering, property ordering, formatting, descriptions
|
|
1823
|
+
6. **Pre-Report Testing**: Run build, lint, and all tests (must all pass!)
|
|
1824
|
+
7. **Final Verification**: Complete checklist before proceeding to Final Report
|
|
1825
|
+
|
|
1826
|
+
**Critical reminders:**
|
|
1827
|
+
- [ ] **TestHelper thoroughly understood** (read source code, understand graphQl() and rest() methods)
|
|
1828
|
+
- [ ] **Existing tests analyzed** BEFORE creating new tests
|
|
1829
|
+
- [ ] **Permission system understood** (3 layers: Controller @Roles, Service options, Model securityCheck)
|
|
1830
|
+
- [ ] **Tests in correct location** (tests/modules/, tests/common.e2e-spec.ts, or tests/project.e2e-spec.ts)
|
|
1831
|
+
- [ ] **All tests pass** before reporting
|
|
1832
|
+
|
|
1833
|
+
**Permission testing approach:**
|
|
1834
|
+
- Admin users: `user.roles.includes('admin')`
|
|
1835
|
+
- Creators: `user.id === object.createdBy`
|
|
1836
|
+
- Always test with appropriate user (creator for update/delete, admin for everything)
|
|
1837
|
+
- Test security failures (403 responses)
|
|
2780
1838
|
|
|
2781
1839
|
**Only after ALL checks pass, proceed to Final Report.**
|
|
2782
1840
|
|