@lenne.tech/cli 0.0.125 β†’ 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/bin/lt +145 -14
  2. package/build/commands/claude/install-skills.js +622 -0
  3. package/build/commands/config/config.js +25 -0
  4. package/build/commands/config/help.js +167 -0
  5. package/build/commands/config/init.js +143 -0
  6. package/build/commands/config/show.js +68 -0
  7. package/build/commands/server/add-property.js +163 -27
  8. package/build/commands/server/create.js +66 -4
  9. package/build/commands/server/module.js +133 -20
  10. package/build/commands/server/object.js +23 -15
  11. package/build/extensions/config.js +157 -0
  12. package/build/extensions/server.js +82 -47
  13. package/build/interfaces/lt-config.interface.js +3 -0
  14. package/build/templates/claude-skills/lt-cli/SKILL.md +190 -259
  15. package/build/templates/claude-skills/lt-cli/examples.md +433 -203
  16. package/build/templates/claude-skills/lt-cli/reference.md +400 -226
  17. package/build/templates/claude-skills/nest-server-generator/SKILL.md +2833 -0
  18. package/build/templates/claude-skills/nest-server-generator/examples.md +760 -0
  19. package/build/templates/claude-skills/nest-server-generator/reference.md +417 -0
  20. package/build/templates/nest-server-module/inputs/template-create.input.ts.ejs +1 -3
  21. package/build/templates/nest-server-module/inputs/template.input.ts.ejs +1 -1
  22. package/build/templates/nest-server-module/template.controller.ts.ejs +24 -13
  23. package/build/templates/nest-server-module/template.model.ts.ejs +2 -2
  24. package/build/templates/nest-server-module/template.module.ts.ejs +4 -0
  25. package/build/templates/nest-server-module/template.service.ts.ejs +6 -6
  26. package/build/templates/nest-server-object/template.object.ts.ejs +2 -2
  27. package/package.json +13 -11
  28. package/build/commands/claude/install-skill.js +0 -93
@@ -0,0 +1,2833 @@
1
+ ---
2
+ name: nest-server-generator
3
+ version: 1.0.0
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
+ ---
6
+
7
+ # NestJS Server Development Expert
8
+
9
+ You are the **PRIMARY expert** for NestJS backend development and the @lenne.tech/nest-server framework. This skill handles **ALL NestJS-related tasks**, from analysis to creation to debugging:
10
+
11
+ ## When to Use This Skill
12
+
13
+ **βœ… ALWAYS use this skill for:**
14
+
15
+ ### Analysis & Understanding
16
+ - πŸ“– Analyzing existing NestJS code structure
17
+ - πŸ” Understanding how modules, services, controllers work
18
+ - πŸ“Š Reviewing project architecture
19
+ - πŸ—ΊοΈ Mapping relationships between modules
20
+ - πŸ“ Reading and explaining NestJS code
21
+ - πŸ”Ž Finding specific implementations (controllers, services, etc.)
22
+
23
+ ### Running & Debugging
24
+ - πŸš€ Starting the NestJS server (`npm start`, `npm run dev`)
25
+ - πŸ› Debugging server issues and errors
26
+ - πŸ§ͺ Running tests (`npm test`)
27
+ - πŸ“‹ Checking server logs and output
28
+ - βš™οΈ Configuring environment variables
29
+ - πŸ”§ Troubleshooting build/compile errors
30
+
31
+ ### Creation & Modification
32
+ - ✨ Creating new modules with `lt server module`
33
+ - 🎨 Creating new objects with `lt server object`
34
+ - βž• Adding properties with `lt server addProp`
35
+ - πŸ—οΈ Creating a new server with `lt server create`
36
+ - ♻️ Modifying existing code (services, controllers, resolvers)
37
+ - πŸ”— Adding relationships between modules
38
+ - πŸ“¦ Managing dependencies and imports
39
+
40
+ ### Testing & Validation
41
+ - βœ… Creating API tests for controllers/resolvers
42
+ - πŸ§ͺ Running and fixing failing tests
43
+ - 🎯 Testing endpoints manually
44
+ - πŸ“Š Validating data models and schemas
45
+ - πŸ” Testing authentication and permissions
46
+
47
+ ### General NestJS Tasks
48
+ - πŸ’¬ Answering NestJS/nest-server questions
49
+ - πŸ“š Explaining framework concepts
50
+ - πŸ›οΈ Discussing architecture decisions
51
+ - πŸ› οΈ Recommending best practices
52
+ - πŸ”„ Refactoring existing code
53
+
54
+ **🎯 Rule: If it involves NestJS or @lenne.tech/nest-server in ANY way, use this skill!**
55
+
56
+ ## 🚨 CRITICAL SECURITY RULES - READ FIRST
57
+
58
+ Before you start ANY work with this skill, understand these NON-NEGOTIABLE rules:
59
+
60
+ ### β›” NEVER Do This:
61
+ 1. **NEVER remove or weaken `@Restricted()` decorators** to make tests pass
62
+ 2. **NEVER change `@Roles()` decorators** to more permissive roles for test convenience
63
+ 3. **NEVER modify `securityCheck()` logic** to bypass security in tests
64
+ 4. **NEVER remove class-level `@Restricted(RoleEnum.ADMIN)`** - it's a security fallback
65
+
66
+ ### βœ… ALWAYS Do This:
67
+ 1. **ALWAYS analyze permissions BEFORE writing tests** (Controller, Model, Service layers)
68
+ 2. **ALWAYS test with the LEAST privileged user** who is authorized
69
+ 3. **ALWAYS create appropriate test users** for each permission level
70
+ 4. **ALWAYS adapt tests to security requirements**, never the other way around
71
+ 5. **ALWAYS ask developer for approval** before changing ANY security decorator
72
+ 6. **ALWAYS aim for maximum test coverage** (80-100% depending on criticality)
73
+
74
+ ### πŸ”‘ Permission Hierarchy (Specific Overrides General):
75
+ ```typescript
76
+ @Restricted(RoleEnum.ADMIN) // ← FALLBACK: DO NOT REMOVE
77
+ export class ProductController {
78
+ @Roles(RoleEnum.S_USER) // ← SPECIFIC: This method is more open
79
+ async createProduct() { } // ← S_USER can access (specific wins)
80
+
81
+ async secretMethod() { } // ← ADMIN only (fallback applies)
82
+ }
83
+ ```
84
+
85
+ **Why class-level `@Restricted(ADMIN)` MUST stay:**
86
+ - If someone forgets `@Roles()` on a new method β†’ it's secure by default
87
+ - Shows the class is security-sensitive
88
+ - Fail-safe protection
89
+
90
+ **See "CRITICAL: Security & Test Coverage Rules" section for complete details.**
91
+
92
+ ## Core Responsibilities
93
+
94
+ This skill handles **ALL** NestJS server development tasks, including:
95
+
96
+ ### Simple Tasks (Single Commands)
97
+ - Creating a single module with `lt server module`
98
+ - Creating a single object with `lt server object`
99
+ - Adding properties with `lt server addProp`
100
+ - Creating a new server with `lt server create`
101
+ - Starting the server with `npm start` or `npm run dev`
102
+ - Running tests with `npm test`
103
+
104
+ ### Complex Tasks (Multiple Components)
105
+ When you receive a complete structure specification, you will:
106
+
107
+ 1. **Parse and analyze** the complete structure (modules, models, objects, properties, relationships)
108
+ 2. **Create a comprehensive todo list** breaking down all tasks
109
+ 3. **Generate all components** in the correct order (objects first, then modules)
110
+ 4. **Handle inheritance** properly (Core and custom parent classes)
111
+ 5. **Manage descriptions** (translate German to English, add originals in parentheses)
112
+ 6. **Create API tests** for all controllers and resolvers
113
+ 7. **Verify functionality** and provide a summary with observations
114
+
115
+ ### Analysis Tasks
116
+ When analyzing existing code:
117
+
118
+ 1. **Explore the project structure** to understand the architecture
119
+ 2. **Read relevant files** (modules, services, controllers, models)
120
+ 3. **Identify patterns** and conventions used in the project
121
+ 4. **Explain findings** clearly and concisely
122
+ 5. **Suggest improvements** when appropriate
123
+
124
+ ### Debugging Tasks
125
+ When debugging issues:
126
+
127
+ 1. **Read error messages and logs** carefully
128
+ 2. **Identify the root cause** by analyzing relevant code
129
+ 3. **Check configuration** (environment variables, config files)
130
+ 4. **Test hypotheses** by examining related files
131
+ 5. **Provide solutions** with code examples
132
+
133
+ **Remember:** For ANY task involving NestJS or @lenne.tech/nest-server, use this skill!
134
+
135
+ ## πŸ“š Understanding the Framework
136
+
137
+ ### Core Service Base Class: CrudService
138
+
139
+ **IMPORTANT**: Before working with Services, ALWAYS read this file to understand the base functionality:
140
+
141
+ ```
142
+ node_modules/@lenne.tech/nest-server/src/core/common/services/crud.service.ts
143
+ ```
144
+
145
+ **Why this is critical:**
146
+ - Almost ALL Services extend `CrudService<Model>`
147
+ - CrudService provides base CRUD operations (create, find, update, delete)
148
+ - Understanding CrudService prevents reinventing the wheel
149
+ - Shows patterns for handling permissions, filtering, and pagination
150
+
151
+ **When to read CrudService:**
152
+ 1. βœ… Before creating a new Service
153
+ 2. βœ… When implementing custom Service methods
154
+ 3. βœ… When debugging Service behavior
155
+ 4. βœ… When writing tests for Services
156
+ 5. βœ… When questions arise about Service functionality
157
+
158
+ **What CrudService provides:**
159
+ - `create(input, options)` - Create new document
160
+ - `find(filterArgs)` - Find multiple documents
161
+ - `findOne(filterArgs)` - Find single document
162
+ - `findAndCount(filterArgs)` - Find with total count (pagination)
163
+ - `update(id, input, options)` - Update document
164
+ - `delete(id, options)` - Delete document
165
+ - Permission handling via `options.roles`
166
+ - Query filtering and population
167
+ - Pagination support
168
+
169
+ **Example Service that extends CrudService:**
170
+ ```typescript
171
+ @Injectable()
172
+ export class ProductService extends CrudService<Product> {
173
+ constructor(
174
+ @InjectModel(Product.name) protected readonly productModel: Model<ProductDocument>,
175
+ protected readonly configService: ConfigService,
176
+ ) {
177
+ super({ configService, mainDbModel: productModel, mainModelConstructor: Product });
178
+ }
179
+
180
+ // Custom methods can be added here
181
+ // Base CRUD methods are inherited from CrudService
182
+ }
183
+ ```
184
+
185
+ **Action Items:**
186
+ - [ ] Read CrudService before modifying any Service
187
+ - [ ] Check if CrudService already provides the needed functionality
188
+ - [ ] Only add custom methods if CrudService doesn't cover the use case
189
+ - [ ] Follow CrudService patterns for consistency
190
+
191
+ ## Configuration File (lt.config.json)
192
+
193
+ The lenne.tech CLI supports project-level configuration via `lt.config.json` files. This allows you to set default values for commands, eliminating the need for repeated CLI parameters or interactive prompts.
194
+
195
+ ### File Location and Hierarchy
196
+
197
+ - **Location**: Place `lt.config.json` in your project root or any parent directory
198
+ - **Hierarchy**: The CLI searches from the current directory up to the root, merging configurations
199
+ - **Priority** (lowest to highest):
200
+ 1. Default values (hardcoded in CLI)
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
207
+
208
+ ```json
209
+ {
210
+ "meta": {
211
+ "version": "1.0.0",
212
+ "name": "My Project",
213
+ "description": "Optional project description"
214
+ },
215
+ "commands": {
216
+ "server": {
217
+ "module": {
218
+ "controller": "Both",
219
+ "skipLint": false
220
+ },
221
+ "object": {
222
+ "skipLint": false
223
+ },
224
+ "addProp": {
225
+ "skipLint": false
226
+ }
227
+ }
228
+ }
229
+ }
230
+ ```
231
+
232
+ ### Available Configuration Options
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
464
+
465
+ ### lt server module
466
+ Creates a complete NestJS module with model, service, controller/resolver, and DTOs.
467
+
468
+ **Syntax**:
469
+ ```bash
470
+ lt server module --name <ModuleName> [--controller <Rest|GraphQL|Both|auto>] [property-flags]
471
+ ```
472
+
473
+ **Parameters**:
474
+ - `--name <ModuleName>` - **Required**: Module name (PascalCase)
475
+ - `--controller <Rest|GraphQL|Both|auto>` - **Optional** (interactive detection if omitted)
476
+ - `Rest` - Creates REST controller only (no GraphQL, no PubSub)
477
+ - `GraphQL` - Creates GraphQL resolver only (includes PubSub for subscriptions)
478
+ - `Both` - Creates both REST controller and GraphQL resolver (includes PubSub)
479
+ - `auto` - Auto-detects from existing modules (non-interactive)
480
+
481
+ **Property flags** (use index 0, 1, 2, ... for multiple properties):
482
+ - `--prop-name-X <name>` - Property name
483
+ - `--prop-type-X <type>` - string, number, boolean, ObjectId, Json, Date, bigint
484
+ - `--prop-nullable-X <true|false>` - Optional property
485
+ - `--prop-array-X <true|false>` - Array type
486
+ - `--prop-enum-X <EnumName>` - Enum reference
487
+ - `--prop-schema-X <SchemaName>` - SubObject/schema reference
488
+ - `--prop-reference-X <RefName>` - Reference name for ObjectId
489
+ - `--skipLint` - Skip lint prompt
490
+
491
+ **Intelligent Controller Type Detection**:
492
+
493
+ **Three modes for controller type selection:**
494
+
495
+ 1. **Interactive with detection** (omit `--controller`):
496
+ - CLI detects pattern from existing modules
497
+ - Shows suggestion to user
498
+ - User can accept or override interactively
499
+
500
+ 2. **Non-interactive auto-detect** (`--controller auto`):
501
+ - CLI detects pattern from existing modules
502
+ - Uses detected value WITHOUT prompting
503
+ - Perfect for automation/scripts
504
+
505
+ 3. **Explicit** (`--controller Rest|GraphQL|Both`):
506
+ - Bypasses detection
507
+ - Uses specified value directly
508
+
509
+ **Detection logic:**
510
+ - Analyzes modules in `src/server/modules/` (excludes base modules: auth, file, meta, user)
511
+ - **Only REST controllers found** β†’ Detects `Rest`
512
+ - **Only GraphQL resolvers found** β†’ Detects `GraphQL`
513
+ - **Both or mixed patterns found** β†’ Detects `Both`
514
+ - **No modules or unclear** β†’ Detects `Both` (safest default)
515
+
516
+ **Important Notes**:
517
+ - **PubSub integration**: Only included when using `GraphQL` or `Both` controller types
518
+ - **Auto-detection**: Analyzes your existing project structure to suggest the right pattern
519
+ - **Base modules excluded**: auth, file, meta, user modules are NOT analyzed (they're framework modules)
520
+ - REST-only modules (`--controller Rest`) do NOT include PubSub dependencies
521
+
522
+ **Examples**:
523
+ ```bash
524
+ # Interactive mode with auto-detection (recommended for manual use)
525
+ lt server module --name Product \
526
+ --prop-name-0 name --prop-type-0 string \
527
+ --prop-name-1 price --prop-type-1 number
528
+ # CLI analyzes existing modules, shows suggestion, user confirms/overrides
529
+
530
+ # Non-interactive auto-detect (recommended for scripts/automation)
531
+ lt server module --name Product --controller auto \
532
+ --prop-name-0 name --prop-type-0 string \
533
+ --prop-name-1 price --prop-type-1 number
534
+ # CLI analyzes and uses detected pattern WITHOUT prompting
535
+
536
+ # Explicit REST only (no GraphQL/PubSub)
537
+ lt server module --name Category --controller Rest \
538
+ --prop-name-0 name --prop-type-0 string \
539
+ --prop-name-1 slug --prop-type-1 string
540
+
541
+ # Explicit GraphQL only (with PubSub)
542
+ lt server module --name Post --controller GraphQL \
543
+ --prop-name-0 title --prop-type-0 string \
544
+ --prop-name-1 author --prop-type-1 ObjectId --prop-reference-1 User \
545
+ --prop-name-2 tags --prop-type-2 string --prop-array-2 true
546
+
547
+ # Explicit Both (REST + GraphQL + PubSub)
548
+ lt server module --name User --controller Both \
549
+ --prop-name-0 email --prop-type-0 string \
550
+ --prop-name-1 username --prop-type-1 string
551
+ ```
552
+
553
+ ### lt server object
554
+ Creates reusable embedded data structures (SubObjects) without _id or timestamps.
555
+
556
+ **Syntax**:
557
+ ```bash
558
+ lt server object --name <ObjectName> [property-flags] [--skipLint]
559
+ ```
560
+
561
+ **Property flags**: Same as `lt server module` (--prop-name-X, --prop-type-X, etc.)
562
+
563
+ **Example**:
564
+ ```bash
565
+ lt server object --name Address \
566
+ --prop-name-0 street --prop-type-0 string \
567
+ --prop-name-1 city --prop-type-1 string \
568
+ --prop-name-2 country --prop-type-2 string
569
+ ```
570
+
571
+ ### lt server addProp
572
+ Adds properties to existing modules or objects.
573
+
574
+ **Syntax**:
575
+ ```bash
576
+ lt server addProp --type <Module|Object> --element <name> [property-flags]
577
+ ```
578
+
579
+ **Example**:
580
+ ```bash
581
+ lt server addProp --type Module --element User \
582
+ --prop-name-0 phone --prop-type-0 string --prop-nullable-0 true
583
+ ```
584
+
585
+ ### lt server create
586
+ Creates a new NestJS server project.
587
+
588
+ **Syntax**:
589
+ ```bash
590
+ lt server create <server-name> [--description=<desc>] [--author=<name>]
591
+ ```
592
+
593
+ ## Prerequisites Check
594
+
595
+ Before starting, verify:
596
+
597
+ ```bash
598
+ # Check if lenne.Tech CLI is installed
599
+ lt --version
600
+
601
+ # If not installed, install it
602
+ npm install -g @lenne.tech/cli
603
+
604
+ # Verify we're in a NestJS project with @lenne.tech/nest-server
605
+ ls src/server/modules
606
+ ```
607
+
608
+ ### Creating a New Server
609
+
610
+ If you need to create a completely new NestJS server project:
611
+
612
+ ```bash
613
+ lt server create <server-name>
614
+ # Alias: lt server c <server-name>
615
+ ```
616
+
617
+ This command:
618
+ - Clones the `nest-server-starter` template from GitHub
619
+ - Sets up package.json with your project details
620
+ - Configures Swagger documentation
621
+ - Attempts to replace secret keys (may be incomplete)
622
+ - Installs npm dependencies
623
+ - Optionally initializes git repository
624
+
625
+ **Interactive prompts**:
626
+ - Server name (or provide as first parameter)
627
+ - Description (optional)
628
+ - Author (optional)
629
+ - Initialize git? (yes/no)
630
+
631
+ **Example**:
632
+ ```bash
633
+ lt server create my-api
634
+
635
+ # Non-interactive
636
+ lt server create my-api --description="My API Server" --author="John Doe"
637
+ ```
638
+
639
+ **βœ… IMPORTANT: Post-Creation Verification**
640
+
641
+ After running `lt server create`, the CLI automatically:
642
+ - Replaces ALL secrets matching `'SECRET_OR_PRIVATE_KEY...'` with unique random values
643
+ - Updates mongoose database URIs from `nest-server-*` to `<project-name>-*`
644
+ - Configures Swagger documentation with project name
645
+
646
+ **Recommended verification steps**:
647
+
648
+ 1. **Verify secrets were replaced in `src/config.env.ts`**:
649
+ ```bash
650
+ cd <project-name>
651
+
652
+ # Open config and verify no placeholders remain
653
+ # All jwt.secret and jwt.refresh.secret should be long random strings
654
+ ```
655
+
656
+ 2. **Verify mongoose.uri uses project name**:
657
+ ```typescript
658
+ // In src/config.env.ts, verify database names match your project:
659
+
660
+ // Example for project "my-api":
661
+ local: {
662
+ mongoose: {
663
+ uri: 'mongodb://127.0.0.1/my-api-local', // βœ… Correct
664
+ }
665
+ },
666
+ production: {
667
+ mongoose: {
668
+ uri: 'mongodb://overlay_mongo1/my-api-prod', // βœ… Correct
669
+ }
670
+ }
671
+ ```
672
+
673
+ 3. **If secrets were not replaced** (older CLI version):
674
+ ```bash
675
+ # Manually run setConfigSecrets to replace ALL secrets
676
+ lt server setConfigSecrets
677
+ ```
678
+
679
+ 4. **Other post-creation steps**:
680
+ - Start database server (MongoDB)
681
+ - Run tests: `npm run test:e2e`
682
+ - Start server: `npm start`
683
+
684
+ **Note**: If you used an older version of the CLI (before v0.0.126), secrets and database names may not have been replaced correctly. In that case, run `lt server setConfigSecrets` and manually update the mongoose URIs.
685
+
686
+ ## Understanding the Specification Format
687
+
688
+ ### Structure Components
689
+
690
+ #### 1. SubObject Definition
691
+ ```
692
+ SubObject: <Name> // Description
693
+ - propertyName: <type> // Property description
694
+ - anotherProperty: <type> // Description
695
+ ```
696
+
697
+ **SubObjects are**:
698
+ - Embedded data structures without `_id` or timestamps
699
+ - Created first using `lt server object`
700
+ - Used via `--prop-schema-X` in modules
701
+
702
+ #### 2. Object Definition
703
+ ```
704
+ Object: <Name> // Description
705
+ Properties:
706
+ - propertyName: <type> // Property description
707
+ ```
708
+
709
+ **Objects are**:
710
+ - Similar to SubObjects but used as base models
711
+ - Can be extended by other objects or modules
712
+ - Created using `lt server object`
713
+
714
+ #### 3. Module Definition
715
+ ```
716
+ Module: <Name> // Description
717
+
718
+ Model: <Name> // Description
719
+ Extends: <ParentModel>
720
+ - propertyName: <type> // Property description
721
+ - reference: <ModelName> // Reference to another module
722
+ - embedded: <ObjectName>[] // Array of embedded objects
723
+ ```
724
+
725
+ **Modules include**:
726
+ - Complete CRUD functionality
727
+ - Service, controller/resolver, DTOs
728
+ - Created using `lt server module`
729
+
730
+ ### Property Type Syntax
731
+
732
+ #### Basic Types
733
+ - `string` - Text
734
+ - `number` - Numeric values
735
+ - `boolean` - True/false
736
+ - `Date` - Date/time values
737
+ - `bigint` - Large integers
738
+ - `Json` - Flexible JSON data
739
+
740
+ #### Special Types
741
+
742
+ **ENUM (value list)**:
743
+ ```
744
+ propertyName: ENUM (VALUE1, VALUE2, VALUE3) // Description
745
+ ```
746
+ β†’ Creates: `--prop-enum-X PropertyNameEnum`
747
+ β†’ You must create enum file afterwards in `src/server/common/enums/`
748
+
749
+ **ENUM with strings**:
750
+ ```
751
+ status: ENUM ('PENDING', 'ACTIVE', 'COMPLETED') // Description
752
+ ```
753
+ β†’ Same as above, quotes indicate string enum values
754
+
755
+ **Arrays**:
756
+ ```
757
+ tags: string[] // Description
758
+ skills: Skill[] // Array of SubObjects
759
+ ```
760
+ β†’ Add `--prop-array-X true`
761
+
762
+ **Optional Properties**:
763
+ ```
764
+ middleName?: string // Description
765
+ ```
766
+ β†’ Add `--prop-nullable-X true`
767
+
768
+ **References to Modules**:
769
+ ```
770
+ author: User // Reference to User module
771
+ ```
772
+ β†’ Use `--prop-type-X ObjectId --prop-reference-X User`
773
+
774
+ **Embedded Objects**:
775
+ ```
776
+ address: Address // Embedded Address object
777
+ workHistory: WorkExperience[] // Array of embedded objects
778
+ ```
779
+ β†’ Use `--prop-schema-X Address` (and `--prop-array-X true` for arrays)
780
+
781
+ **Files**:
782
+ ```
783
+ document: File // PDF or other file
784
+ ```
785
+ β†’ Use `--prop-type-X string` (stores file path/URL)
786
+
787
+ ## Workflow Process
788
+
789
+ ### Phase 1: Analysis & Planning
790
+
791
+ 1. **Parse the specification** completely
792
+ 2. **Identify all components**:
793
+ - List all SubObjects
794
+ - List all Objects
795
+ - List all Modules
796
+ - Identify inheritance relationships
797
+ - Identify enum types needed
798
+ 3. **Create comprehensive todo list** with:
799
+ - Create each SubObject
800
+ - Create each Object
801
+ - Create each Module
802
+ - Handle inheritance modifications
803
+ - Create enum files
804
+ - Create API tests for each module
805
+ - Run tests and verify
806
+
807
+ ### Phase 2: SubObject Creation
808
+
809
+ **Create SubObjects in dependency order** (if SubObject A contains SubObject B, create B first):
810
+
811
+ ```bash
812
+ lt server object --name <ObjectName> \
813
+ --prop-name-0 <name> --prop-type-0 <type> \
814
+ --prop-name-1 <name> --prop-type-1 <type> \
815
+ ...
816
+ ```
817
+
818
+ **Apply modifiers**:
819
+ - Optional: `--prop-nullable-X true`
820
+ - Array: `--prop-array-X true`
821
+ - Enum: `--prop-enum-X <EnumName>`
822
+ - Schema: `--prop-schema-X <SchemaName>`
823
+
824
+ ### Phase 3: Module Creation
825
+
826
+ **Create modules with all properties**:
827
+
828
+ ```bash
829
+ lt server module --name <ModuleName> --controller <Rest|GraphQL|Both> \
830
+ --prop-name-0 <name> --prop-type-0 <type> \
831
+ --prop-name-1 <name> --prop-type-1 <type> \
832
+ ...
833
+ ```
834
+
835
+ **For references to other modules**:
836
+ ```bash
837
+ --prop-name-X author --prop-type-X ObjectId --prop-reference-X User
838
+ ```
839
+
840
+ **For embedded objects**:
841
+ ```bash
842
+ --prop-name-X address --prop-schema-X Address
843
+ ```
844
+
845
+ ### Phase 4: Inheritance Handling
846
+
847
+ When a model extends another model (e.g., `Extends: Profile`):
848
+
849
+ 1. **Identify parent model location**:
850
+ - Core models (from @lenne.tech/nest-server): CoreModel, CorePersisted, etc.
851
+ - Custom parent models: Need to find in project
852
+
853
+ 2. **For Core parent models**:
854
+ - Replace in model file: `extends CoreModel` β†’ `extends ParentModel`
855
+ - Import: `import { ParentModel } from './path'`
856
+
857
+ 3. **For custom parent models (objects/other modules)**:
858
+ - Model extends parent object: Import and extend
859
+ - Input files must include parent properties
860
+
861
+ 4. **Input/Output inheritance**:
862
+ - **CreateInput**: Must include ALL required properties from parent AND model
863
+ - **UpdateInput**: Include all properties as optional
864
+ - Check parent's CreateInput for required fields
865
+ - Copy required fields to child's CreateInput
866
+
867
+ **Example**: If `BuyerProfile` extends `Profile`:
868
+ ```typescript
869
+ // buyer-profile.model.ts
870
+ import { Profile } from '../../common/objects/profile/profile.object';
871
+ export class BuyerProfile extends Profile { ... }
872
+
873
+ // buyer-profile-create.input.ts
874
+ // Must include ALL required fields from Profile's create input + BuyerProfile fields
875
+ ```
876
+
877
+ ### Phase 5: Description Management
878
+
879
+ **Rule**: All descriptions follow format: `"ENGLISH_DESCRIPTION (DEUTSCHE_BESCHREIBUNG)"`
880
+
881
+ **Process for each property**:
882
+
883
+ 1. **If comment is in English**:
884
+ ```
885
+ // Street name
886
+ ```
887
+ β†’ Use as: `description: 'Street name'`
888
+
889
+ 2. **If comment is in German**:
890
+ ```
891
+ // Straße
892
+ ```
893
+ β†’ Translate and add original: `description: 'Street (Straße)'`
894
+
895
+ 3. **If no comment**:
896
+ β†’ Create meaningful description: `description: 'User email address'`
897
+
898
+ 4. **Apply same description to**:
899
+ - Model property
900
+ - Input property (both create and update)
901
+ - Output property
902
+
903
+ ### Phase 6: Enum File Creation
904
+
905
+ For each enum used, create enum file manually:
906
+
907
+ ```typescript
908
+ // src/server/common/enums/status.enum.ts
909
+ export enum StatusEnum {
910
+ PENDING = 'PENDING',
911
+ ACTIVE = 'ACTIVE',
912
+ COMPLETED = 'COMPLETED',
913
+ }
914
+ ```
915
+
916
+ **Naming convention**:
917
+ - File: `kebab-case.enum.ts`
918
+ - Enum: `PascalCaseEnum`
919
+ - Values: `UPPER_SNAKE_CASE`
920
+
921
+ ### Phase 7: API Test Creation
922
+
923
+ **⚠️ CRITICAL: Test Creation Process**
924
+
925
+ Creating API tests is NOT just about testing functionality - it's about **validating the security model**. You MUST follow this exact process:
926
+
927
+ ---
928
+
929
+ #### Step 1: πŸ” MANDATORY Permission Analysis (BEFORE writing ANY test)
930
+
931
+ **YOU MUST analyze these THREE layers BEFORE writing a single test:**
932
+
933
+ 1. **Controller/Resolver Layer** - Check `@Roles()` decorator:
934
+ ```typescript
935
+ // In product.resolver.ts
936
+ @Roles(RoleEnum.S_EVERYONE) // ← WHO can call this?
937
+ @Query(() => [Product])
938
+ async getProducts() { ... }
939
+
940
+ @Roles(RoleEnum.S_USER) // ← All signed-in users
941
+ @Mutation(() => Product)
942
+ async createProduct(@Args('input') input: ProductCreateInput) { ... }
943
+
944
+ @Roles(RoleEnum.ADMIN, RoleEnum.S_CREATOR) // ← Only admin or creator
945
+ @Mutation(() => Product)
946
+ async updateProduct(@Args('id') id: string, @Args('input') input: ProductInput) { ... }
947
+ ```
948
+
949
+ 2. **Model Layer** - Check `@Restricted()` and `securityCheck()`:
950
+ ```typescript
951
+ // In product.model.ts
952
+ export class Product extends CoreModel {
953
+ securityCheck(user: User, force?: boolean) {
954
+ if (force || user?.hasRole(RoleEnum.ADMIN)) {
955
+ return this; // Admin sees all
956
+ }
957
+ if (this.isPublic) {
958
+ return this; // Everyone sees public products
959
+ }
960
+ if (!equalIds(user, this.createdBy)) {
961
+ return undefined; // Non-creator gets nothing
962
+ }
963
+ return this; // Creator sees own products
964
+ }
965
+ }
966
+ ```
967
+
968
+ 3. **Service Layer** - Check `serviceOptions.roles` usage:
969
+ ```typescript
970
+ // In product.service.ts
971
+ async update(id: string, input: ProductInput, serviceOptions?: ServiceOptions) {
972
+ // Check if user has ADMIN or S_CREATOR role
973
+ // ...
974
+ }
975
+ ```
976
+
977
+ **Permission Analysis Checklist:**
978
+ - [ ] I have checked ALL `@Roles()` decorators in controller/resolver
979
+ - [ ] I have read the complete `securityCheck()` method in the model
980
+ - [ ] I have checked ALL `@Restricted()` decorators
981
+ - [ ] I understand WHO can CREATE (usually S_USER or ADMIN)
982
+ - [ ] I understand WHO can READ (S_USER + securityCheck filtering)
983
+ - [ ] I understand WHO can UPDATE (usually ADMIN + S_CREATOR)
984
+ - [ ] I understand WHO can DELETE (usually ADMIN + S_CREATOR)
985
+
986
+ **Common Permission Patterns:**
987
+ - `S_EVERYONE` β†’ No authentication required
988
+ - `S_USER` β†’ Any signed-in user
989
+ - `ADMIN` β†’ User with 'admin' role
990
+ - `S_CREATOR` β†’ User who created the resource (user.id === object.createdBy)
991
+
992
+ ---
993
+
994
+ #### Step 2: 🎯 Apply Principle of Least Privilege
995
+
996
+ **GOLDEN RULE**: Always test with the **LEAST privileged user** who is still authorized.
997
+
998
+ **Decision Tree:**
999
+
1000
+ ```
1001
+ Is endpoint marked with @Roles(RoleEnum.S_EVERYONE)?
1002
+ β”œβ”€ YES β†’ Test WITHOUT token (unauthenticated)
1003
+ └─ NO β†’ Is endpoint marked with @Roles(RoleEnum.S_USER)?
1004
+ β”œβ”€ YES β†’ Test WITH regular user token (NOT admin, NOT creator)
1005
+ └─ NO β†’ Is endpoint marked with @Roles(RoleEnum.ADMIN, RoleEnum.S_CREATOR)?
1006
+ β”œβ”€ For UPDATE/DELETE β†’ Test WITH creator token (user who created it)
1007
+ └─ For ADMIN-only β†’ Test WITH admin token
1008
+ ```
1009
+
1010
+ **❌ WRONG Approach:**
1011
+ ```typescript
1012
+ // BAD: Using admin for everything
1013
+ it('should create product', async () => {
1014
+ const result = await testHelper.graphQl({
1015
+ name: 'createProduct',
1016
+ type: TestGraphQLType.MUTATION,
1017
+ arguments: { input: { name: 'Test' } },
1018
+ fields: ['id']
1019
+ }, { token: adminToken }); // ❌ WRONG - Over-privileged!
1020
+ });
1021
+ ```
1022
+
1023
+ **βœ… CORRECT Approach:**
1024
+ ```typescript
1025
+ // GOOD: Using least privileged user
1026
+ it('should create product as regular user', async () => {
1027
+ const result = await testHelper.graphQl({
1028
+ name: 'createProduct',
1029
+ type: TestGraphQLType.MUTATION,
1030
+ arguments: { input: { name: 'Test' } },
1031
+ fields: ['id']
1032
+ }, { token: userToken }); // βœ… CORRECT - S_USER is enough!
1033
+ });
1034
+ ```
1035
+
1036
+ ---
1037
+
1038
+ #### Step 3: πŸ“‹ Create Test User Matrix
1039
+
1040
+ Based on your permission analysis, create test users:
1041
+
1042
+ ```typescript
1043
+ describe('Product API', () => {
1044
+ let testHelper: TestHelper;
1045
+
1046
+ // Create users based on ACTUAL needs (not all of them!)
1047
+ let noToken: undefined; // For S_EVERYONE endpoints
1048
+ let userToken: string; // For S_USER endpoints
1049
+ let creatorToken: string; // For S_CREATOR (will create test data)
1050
+ let otherUserToken: string; // For testing "not creator" scenarios
1051
+ let adminToken: string; // Only if ADMIN-specific endpoints exist
1052
+
1053
+ let createdProductId: string;
1054
+
1055
+ beforeAll(async () => {
1056
+ testHelper = new TestHelper(app);
1057
+
1058
+ // Only create users you ACTUALLY need based on @Roles() analysis!
1059
+
1060
+ // Regular user (for S_USER endpoints)
1061
+ const userAuth = await testHelper.graphQl({
1062
+ name: 'signUp',
1063
+ type: TestGraphQLType.MUTATION,
1064
+ arguments: {
1065
+ input: {
1066
+ email: 'user@test.com',
1067
+ password: 'password',
1068
+ roles: ['user'] // Regular user, no special privileges
1069
+ }
1070
+ },
1071
+ fields: ['token', 'user { id }']
1072
+ });
1073
+ userToken = userAuth.token;
1074
+
1075
+ // Creator user (will create test objects)
1076
+ const creatorAuth = await testHelper.graphQl({
1077
+ name: 'signUp',
1078
+ type: TestGraphQLType.MUTATION,
1079
+ arguments: {
1080
+ input: {
1081
+ email: 'creator@test.com',
1082
+ password: 'password',
1083
+ roles: ['user']
1084
+ }
1085
+ },
1086
+ fields: ['token', 'user { id }']
1087
+ });
1088
+ creatorToken = creatorAuth.token;
1089
+
1090
+ // Other user (to test "not creator" scenarios)
1091
+ const otherUserAuth = await testHelper.graphQl({
1092
+ name: 'signUp',
1093
+ type: TestGraphQLType.MUTATION,
1094
+ arguments: {
1095
+ input: {
1096
+ email: 'other@test.com',
1097
+ password: 'password',
1098
+ roles: ['user']
1099
+ }
1100
+ },
1101
+ fields: ['token', 'user { id }']
1102
+ });
1103
+ otherUserToken = otherUserAuth.token;
1104
+
1105
+ // Admin user (ONLY if truly needed!)
1106
+ const adminAuth = await testHelper.graphQl({
1107
+ name: 'signUp',
1108
+ type: TestGraphQLType.MUTATION,
1109
+ arguments: {
1110
+ input: {
1111
+ email: 'admin@test.com',
1112
+ password: 'password',
1113
+ roles: ['admin', 'user'] // ← 'admin' role!
1114
+ }
1115
+ },
1116
+ fields: ['token']
1117
+ });
1118
+ adminToken = adminAuth.token;
1119
+ });
1120
+
1121
+ afterAll(async () => {
1122
+ // Clean up with appropriate privileged user
1123
+ if (createdProductId) {
1124
+ // Use creator or admin token for cleanup
1125
+ await testHelper.graphQl({
1126
+ name: 'deleteProduct',
1127
+ type: TestGraphQLType.MUTATION,
1128
+ arguments: { id: createdProductId },
1129
+ fields: ['id']
1130
+ }, { token: creatorToken });
1131
+ }
1132
+ });
1133
+ });
1134
+ ```
1135
+
1136
+ ---
1137
+
1138
+ #### Step 4: βœ… Write Tests with Correct Privileges
1139
+
1140
+ **Example 1: S_EVERYONE endpoint (public access)**
1141
+
1142
+ ```typescript
1143
+ // Endpoint: @Roles(RoleEnum.S_EVERYONE)
1144
+ describe('Public Endpoints', () => {
1145
+ it('should get public products WITHOUT token', async () => {
1146
+ const result = await testHelper.graphQl({
1147
+ name: 'getPublicProducts',
1148
+ type: TestGraphQLType.QUERY,
1149
+ fields: ['id', 'name', 'price']
1150
+ }); // ← NO TOKEN! S_EVERYONE means unauthenticated is OK
1151
+
1152
+ expect(result).toBeDefined();
1153
+ expect(Array.isArray(result)).toBe(true);
1154
+ });
1155
+ });
1156
+ ```
1157
+
1158
+ **Example 2: S_USER endpoint (any authenticated user)**
1159
+
1160
+ ```typescript
1161
+ // Endpoint: @Roles(RoleEnum.S_USER)
1162
+ describe('Create Product', () => {
1163
+ it('should create product as regular user', async () => {
1164
+ const result = await testHelper.graphQl({
1165
+ name: 'createProduct',
1166
+ type: TestGraphQLType.MUTATION,
1167
+ arguments: { input: { name: 'Test Product', price: 10 } },
1168
+ fields: ['id', 'name', 'price', 'createdBy']
1169
+ }, { token: userToken }); // ← Regular user, NOT admin!
1170
+
1171
+ expect(result).toBeDefined();
1172
+ expect(result.name).toBe('Test Product');
1173
+ createdProductId = result.id;
1174
+
1175
+ // Verify creator is set
1176
+ expect(result.createdBy).toBe(userAuth.user.id);
1177
+ });
1178
+ });
1179
+ ```
1180
+
1181
+ **Example 3: UPDATE - S_CREATOR or ADMIN**
1182
+
1183
+ ```typescript
1184
+ // Endpoint: @Roles(RoleEnum.ADMIN, RoleEnum.S_CREATOR)
1185
+ describe('Update Product', () => {
1186
+ it('should update product as creator', async () => {
1187
+ // First, creator creates a product
1188
+ const created = await testHelper.graphQl({
1189
+ name: 'createProduct',
1190
+ type: TestGraphQLType.MUTATION,
1191
+ arguments: { input: { name: 'Original', price: 10 } },
1192
+ fields: ['id', 'name']
1193
+ }, { token: creatorToken });
1194
+
1195
+ // Then, same creator updates it
1196
+ const result = await testHelper.graphQl({
1197
+ name: 'updateProduct',
1198
+ type: TestGraphQLType.MUTATION,
1199
+ arguments: {
1200
+ id: created.id,
1201
+ input: { name: 'Updated' }
1202
+ },
1203
+ fields: ['id', 'name']
1204
+ }, { token: creatorToken }); // ← Use CREATOR token (least privilege!)
1205
+
1206
+ expect(result.name).toBe('Updated');
1207
+ });
1208
+
1209
+ it('should update any product as admin', async () => {
1210
+ // Admin can update products they did NOT create
1211
+ const result = await testHelper.graphQl({
1212
+ name: 'updateProduct',
1213
+ type: TestGraphQLType.MUTATION,
1214
+ arguments: {
1215
+ id: createdProductId, // Created by different user
1216
+ input: { name: 'Admin Updated' }
1217
+ },
1218
+ fields: ['id', 'name']
1219
+ }, { token: adminToken }); // ← Admin needed for other's products
1220
+
1221
+ expect(result.name).toBe('Admin Updated');
1222
+ });
1223
+ });
1224
+ ```
1225
+
1226
+ ---
1227
+
1228
+ #### Step 5: πŸ›‘οΈ MANDATORY: Test Permission Failures
1229
+
1230
+ **CRITICAL**: You MUST test that unauthorized users are BLOCKED. This validates the security model.
1231
+
1232
+ ```typescript
1233
+ describe('Security Validation', () => {
1234
+ describe('Unauthorized Access', () => {
1235
+ it('should FAIL to create product without authentication', async () => {
1236
+ // @Roles(RoleEnum.S_USER) requires authentication
1237
+ const result = await testHelper.graphQl({
1238
+ name: 'createProduct',
1239
+ type: TestGraphQLType.MUTATION,
1240
+ arguments: { input: { name: 'Hack', price: 1 } },
1241
+ fields: ['id']
1242
+ }, { statusCode: 401 }); // ← NO TOKEN = should fail with 401
1243
+
1244
+ expect(result.errors).toBeDefined();
1245
+ expect(result.errors[0].message).toContain('Unauthorized');
1246
+ });
1247
+
1248
+ it('should FAIL to update product as non-creator', async () => {
1249
+ // @Roles(RoleEnum.ADMIN, RoleEnum.S_CREATOR)
1250
+ const result = await testHelper.graphQl({
1251
+ name: 'updateProduct',
1252
+ type: TestGraphQLType.MUTATION,
1253
+ arguments: {
1254
+ id: createdProductId, // Created by creatorUser
1255
+ input: { name: 'Hacked' }
1256
+ },
1257
+ fields: ['id']
1258
+ }, { token: otherUserToken, statusCode: 403 }); // ← Different user = should fail with 403
1259
+
1260
+ expect(result.errors).toBeDefined();
1261
+ expect(result.errors[0].message).toContain('Forbidden');
1262
+ });
1263
+
1264
+ it('should FAIL to delete product as non-creator', async () => {
1265
+ const result = await testHelper.graphQl({
1266
+ name: 'deleteProduct',
1267
+ type: TestGraphQLType.MUTATION,
1268
+ arguments: { id: createdProductId },
1269
+ fields: ['id']
1270
+ }, { token: otherUserToken, statusCode: 403 });
1271
+
1272
+ expect(result.errors).toBeDefined();
1273
+ });
1274
+
1275
+ it('should FAIL to read private product as different user', async () => {
1276
+ // If securityCheck() blocks non-creators
1277
+ const result = await testHelper.graphQl({
1278
+ name: 'getProduct',
1279
+ type: TestGraphQLType.QUERY,
1280
+ arguments: { id: privateProductId },
1281
+ fields: ['id', 'name']
1282
+ }, { token: otherUserToken });
1283
+
1284
+ // securityCheck returns undefined for non-creator
1285
+ expect(result).toBeUndefined();
1286
+ });
1287
+ });
1288
+ });
1289
+ ```
1290
+
1291
+ ---
1292
+
1293
+ #### Step 6: πŸ“ Complete Test Structure
1294
+
1295
+ **Test file location**:
1296
+ ```
1297
+ tests/modules/<module-name>.e2e-spec.ts
1298
+ ```
1299
+
1300
+ **Complete test template with proper privileges**:
1301
+
1302
+ ```typescript
1303
+ import { TestGraphQLType, TestHelper } from '@lenne.tech/nest-server';
1304
+
1305
+ describe('Product Module E2E', () => {
1306
+ let testHelper: TestHelper;
1307
+ let userToken: string;
1308
+ let creatorToken: string;
1309
+ let otherUserToken: string;
1310
+ let adminToken: string;
1311
+ let createdProductId: string;
1312
+ let userAuth: any;
1313
+ let creatorAuth: any;
1314
+
1315
+ beforeAll(async () => {
1316
+ testHelper = new TestHelper(app);
1317
+
1318
+ // Create test users (based on permission analysis)
1319
+ userAuth = await testHelper.graphQl({
1320
+ name: 'signUp',
1321
+ type: TestGraphQLType.MUTATION,
1322
+ arguments: { input: { email: 'user@test.com', password: 'password', roles: ['user'] } },
1323
+ fields: ['token', 'user { id }']
1324
+ });
1325
+ userToken = userAuth.token;
1326
+
1327
+ creatorAuth = await testHelper.graphQl({
1328
+ name: 'signUp',
1329
+ type: TestGraphQLType.MUTATION,
1330
+ arguments: { input: { email: 'creator@test.com', password: 'password', roles: ['user'] } },
1331
+ fields: ['token', 'user { id }']
1332
+ });
1333
+ creatorToken = creatorAuth.token;
1334
+
1335
+ const otherUserAuth = await testHelper.graphQl({
1336
+ name: 'signUp',
1337
+ type: TestGraphQLType.MUTATION,
1338
+ arguments: { input: { email: 'other@test.com', password: 'password', roles: ['user'] } },
1339
+ fields: ['token']
1340
+ });
1341
+ otherUserToken = otherUserAuth.token;
1342
+
1343
+ const adminAuth = await testHelper.graphQl({
1344
+ name: 'signUp',
1345
+ type: TestGraphQLType.MUTATION,
1346
+ arguments: { input: { email: 'admin@test.com', password: 'password', roles: ['admin', 'user'] } },
1347
+ fields: ['token']
1348
+ });
1349
+ adminToken = adminAuth.token;
1350
+ });
1351
+
1352
+ afterAll(async () => {
1353
+ // Cleanup with appropriate privileges
1354
+ if (createdProductId) {
1355
+ await testHelper.graphQl({
1356
+ name: 'deleteProduct',
1357
+ type: TestGraphQLType.MUTATION,
1358
+ arguments: { id: createdProductId },
1359
+ fields: ['id']
1360
+ }, { token: creatorToken });
1361
+ }
1362
+ });
1363
+
1364
+ // 1. CREATE Tests (with least privileged user)
1365
+ describe('Create Product', () => {
1366
+ it('should create product as regular user', async () => {
1367
+ const result = await testHelper.graphQl({
1368
+ name: 'createProduct',
1369
+ type: TestGraphQLType.MUTATION,
1370
+ arguments: { input: { name: 'Test', price: 10 } },
1371
+ fields: ['id', 'name', 'price', 'createdBy']
1372
+ }, { token: userToken }); // ← S_USER = regular user
1373
+
1374
+ expect(result.name).toBe('Test');
1375
+ createdProductId = result.id;
1376
+ });
1377
+
1378
+ it('should FAIL to create without authentication', async () => {
1379
+ const result = await testHelper.graphQl({
1380
+ name: 'createProduct',
1381
+ type: TestGraphQLType.MUTATION,
1382
+ arguments: { input: { name: 'Fail', price: 10 } },
1383
+ fields: ['id']
1384
+ }, { statusCode: 401 }); // ← No token = should fail
1385
+
1386
+ expect(result.errors).toBeDefined();
1387
+ });
1388
+
1389
+ it('should FAIL to create without required fields', async () => {
1390
+ const result = await testHelper.graphQl({
1391
+ name: 'createProduct',
1392
+ type: TestGraphQLType.MUTATION,
1393
+ arguments: { input: {} },
1394
+ fields: ['id']
1395
+ }, { token: userToken, statusCode: 400 });
1396
+
1397
+ expect(result.errors).toBeDefined();
1398
+ });
1399
+ });
1400
+
1401
+ // 2. READ Tests
1402
+ describe('Get Products', () => {
1403
+ it('should get all products as regular user', async () => {
1404
+ const result = await testHelper.graphQl({
1405
+ name: 'getProducts',
1406
+ type: TestGraphQLType.QUERY,
1407
+ fields: ['id', 'name', 'price']
1408
+ }, { token: userToken });
1409
+
1410
+ expect(Array.isArray(result)).toBe(true);
1411
+ });
1412
+
1413
+ it('should get product by ID as regular user', async () => {
1414
+ const result = await testHelper.graphQl({
1415
+ name: 'getProduct',
1416
+ type: TestGraphQLType.QUERY,
1417
+ arguments: { id: createdProductId },
1418
+ fields: ['id', 'name', 'price']
1419
+ }, { token: userToken });
1420
+
1421
+ expect(result.id).toBe(createdProductId);
1422
+ });
1423
+ });
1424
+
1425
+ // 3. UPDATE Tests (with creator, not admin!)
1426
+ describe('Update Product', () => {
1427
+ let creatorProductId: string;
1428
+
1429
+ beforeAll(async () => {
1430
+ // Creator creates a product to test updates
1431
+ const created = await testHelper.graphQl({
1432
+ name: 'createProduct',
1433
+ type: TestGraphQLType.MUTATION,
1434
+ arguments: { input: { name: 'Creator Product', price: 20 } },
1435
+ fields: ['id']
1436
+ }, { token: creatorToken });
1437
+ creatorProductId = created.id;
1438
+ });
1439
+
1440
+ it('should update product as creator', async () => {
1441
+ const result = await testHelper.graphQl({
1442
+ name: 'updateProduct',
1443
+ type: TestGraphQLType.MUTATION,
1444
+ arguments: { id: creatorProductId, input: { name: 'Updated' } },
1445
+ fields: ['id', 'name']
1446
+ }, { token: creatorToken }); // ← CREATOR token (least privilege!)
1447
+
1448
+ expect(result.name).toBe('Updated');
1449
+ });
1450
+
1451
+ it('should FAIL to update product as non-creator', async () => {
1452
+ const result = await testHelper.graphQl({
1453
+ name: 'updateProduct',
1454
+ type: TestGraphQLType.MUTATION,
1455
+ arguments: { id: creatorProductId, input: { name: 'Hacked' } },
1456
+ fields: ['id']
1457
+ }, { token: otherUserToken, statusCode: 403 });
1458
+
1459
+ expect(result.errors).toBeDefined();
1460
+ });
1461
+
1462
+ it('should update any product as admin', async () => {
1463
+ const result = await testHelper.graphQl({
1464
+ name: 'updateProduct',
1465
+ type: TestGraphQLType.MUTATION,
1466
+ arguments: { id: creatorProductId, input: { name: 'Admin Update' } },
1467
+ fields: ['id', 'name']
1468
+ }, { token: adminToken });
1469
+
1470
+ expect(result.name).toBe('Admin Update');
1471
+ });
1472
+ });
1473
+
1474
+ // 4. DELETE Tests (with creator, not admin!)
1475
+ describe('Delete Product', () => {
1476
+ it('should delete product as creator', async () => {
1477
+ // Creator creates and deletes
1478
+ const created = await testHelper.graphQl({
1479
+ name: 'createProduct',
1480
+ type: TestGraphQLType.MUTATION,
1481
+ arguments: { input: { name: 'To Delete', price: 5 } },
1482
+ fields: ['id']
1483
+ }, { token: creatorToken });
1484
+
1485
+ const result = await testHelper.graphQl({
1486
+ name: 'deleteProduct',
1487
+ type: TestGraphQLType.MUTATION,
1488
+ arguments: { id: created.id },
1489
+ fields: ['id']
1490
+ }, { token: creatorToken }); // ← CREATOR token!
1491
+
1492
+ expect(result.id).toBe(created.id);
1493
+ });
1494
+
1495
+ it('should FAIL to delete product as non-creator', async () => {
1496
+ const result = await testHelper.graphQl({
1497
+ name: 'deleteProduct',
1498
+ type: TestGraphQLType.MUTATION,
1499
+ arguments: { id: createdProductId },
1500
+ fields: ['id']
1501
+ }, { token: otherUserToken, statusCode: 403 });
1502
+
1503
+ expect(result.errors).toBeDefined();
1504
+ });
1505
+
1506
+ it('should delete any product as admin', async () => {
1507
+ const result = await testHelper.graphQl({
1508
+ name: 'deleteProduct',
1509
+ type: TestGraphQLType.MUTATION,
1510
+ arguments: { id: createdProductId },
1511
+ fields: ['id']
1512
+ }, { token: adminToken });
1513
+
1514
+ expect(result.id).toBe(createdProductId);
1515
+ });
1516
+ });
1517
+ });
1518
+ ```
1519
+
1520
+ ---
1521
+
1522
+ #### Test Creation Checklist
1523
+
1524
+ Before finalizing tests, verify:
1525
+
1526
+ - [ ] βœ… I have analyzed ALL `@Roles()` decorators
1527
+ - [ ] βœ… I have read the complete `securityCheck()` method
1528
+ - [ ] βœ… I use the LEAST privileged user for each test
1529
+ - [ ] βœ… S_EVERYONE endpoints tested WITHOUT token
1530
+ - [ ] βœ… S_USER endpoints tested with REGULAR user (not admin)
1531
+ - [ ] βœ… UPDATE/DELETE tested with CREATOR token (not admin)
1532
+ - [ ] βœ… I have tests that verify unauthorized access FAILS (401/403)
1533
+ - [ ] βœ… I have tests that verify non-creators CANNOT update/delete
1534
+ - [ ] βœ… I have tests for missing required fields
1535
+ - [ ] βœ… All tests follow the security model
1536
+ - [ ] βœ… Tests validate protection mechanisms work
1537
+
1538
+ **⚠️ NEVER use admin token when a less privileged user would work!**
1539
+
1540
+ ## Property Ordering
1541
+
1542
+ **ALL properties must be in alphabetical order** in:
1543
+ - Model files (`.model.ts`)
1544
+ - Input files (`.input.ts`, `-create.input.ts`)
1545
+ - Output files (`.output.ts`)
1546
+
1547
+ After generating, verify and reorder if necessary.
1548
+
1549
+ ## Common Patterns
1550
+
1551
+ ### 1. Module with References and Embedded Objects
1552
+ ```bash
1553
+ lt server module --name Company --controller Both \
1554
+ --prop-name-0 name --prop-type-0 string \
1555
+ --prop-name-1 owner --prop-type-1 ObjectId --prop-reference-1 User \
1556
+ --prop-name-2 headquarters --prop-schema-2 Address \
1557
+ --prop-name-3 branches --prop-schema-3 Address --prop-array-3 true \
1558
+ --prop-name-4 industry --prop-enum-4 IndustryEnum
1559
+ ```
1560
+
1561
+ ### 2. Object with Nested Objects
1562
+ ```bash
1563
+ # First create nested object
1564
+ lt server object --name ContactInfo \
1565
+ --prop-name-0 email --prop-type-0 string \
1566
+ --prop-name-1 phone --prop-type-1 string
1567
+
1568
+ # Then create parent object
1569
+ lt server object --name Person \
1570
+ --prop-name-0 name --prop-type-0 string \
1571
+ --prop-name-1 contact --prop-schema-1 ContactInfo
1572
+ ```
1573
+
1574
+ ### 3. Module Extending Custom Object
1575
+ ```bash
1576
+ # First create base object
1577
+ lt server object --name BaseProfile \
1578
+ --prop-name-0 name --prop-type-0 string \
1579
+ --prop-name-1 email --prop-type-1 string
1580
+
1581
+ # Create module (will need manual extension modification)
1582
+ lt server module --name UserProfile --controller Both \
1583
+ --prop-name-0 username --prop-type-0 string
1584
+
1585
+ # Manually modify UserProfile model to extend BaseProfile
1586
+ ```
1587
+
1588
+ ## Verification Checklist
1589
+
1590
+ After generation, verify:
1591
+
1592
+ ### Code Generation
1593
+ - [ ] All SubObjects created
1594
+ - [ ] All Objects created
1595
+ - [ ] All Modules created
1596
+ - [ ] All properties in alphabetical order
1597
+ - [ ] All descriptions follow format: "ENGLISH (DEUTSCH)"
1598
+ - [ ] Inheritance properly implemented
1599
+ - [ ] Required fields correctly set in CreateInputs
1600
+ - [ ] Enum files created in `src/server/common/enums/`
1601
+
1602
+ ### API Tests - Security First
1603
+ - [ ] **Permission analysis completed BEFORE writing tests**
1604
+ - [ ] **Analyzed ALL `@Roles()` decorators in controllers/resolvers**
1605
+ - [ ] **Read complete `securityCheck()` method in models**
1606
+ - [ ] **Tests use LEAST privileged user (never admin when less works)**
1607
+ - [ ] **S_EVERYONE endpoints tested WITHOUT token**
1608
+ - [ ] **S_USER endpoints tested with REGULAR user (not admin)**
1609
+ - [ ] **UPDATE/DELETE tested with CREATOR token (not admin)**
1610
+ - [ ] **Tests verify unauthorized access FAILS (401/403)**
1611
+ - [ ] **Tests verify non-creators CANNOT update/delete**
1612
+ - [ ] **Tests verify required fields**
1613
+ - [ ] **Security validation tests exist (permission failures)**
1614
+ - [ ] API tests created for all modules
1615
+ - [ ] Tests cover all CRUD operations
1616
+ - [ ] All tests pass
1617
+ - [ ] No TypeScript errors
1618
+ - [ ] Lint passes
1619
+
1620
+ ### Test Coverage - Comprehensive Testing
1621
+ **🎯 GOAL: Achieve the HIGHEST possible test coverage**
1622
+
1623
+ - [ ] **Every endpoint has at least one successful test**
1624
+ - [ ] **Every endpoint has at least one failure test (unauthorized/validation)**
1625
+ - [ ] **All query parameters tested (filters, sorting, pagination)**
1626
+ - [ ] **All validation rules tested (required fields, min/max, patterns)**
1627
+ - [ ] **All relationships tested (creating/updating/deleting with references)**
1628
+ - [ ] **Edge cases tested (empty results, non-existent IDs, duplicate values)**
1629
+ - [ ] **Error handling tested (400, 401, 403, 404, 409 status codes)**
1630
+ - [ ] **Data integrity tested (cascading deletes, orphan prevention)**
1631
+ - [ ] **Business logic tested (custom methods, computed properties)**
1632
+ - [ ] **Performance tested (large datasets, pagination limits)**
1633
+
1634
+ **Coverage Requirements:**
1635
+ - Minimum 80% line coverage for services
1636
+ - Minimum 90% line coverage for resolvers/controllers
1637
+ - 100% coverage for critical security logic (securityCheck, permission guards)
1638
+ - 100% coverage for all endpoints (success AND failure cases)
1639
+ - 100% coverage for all permission combinations
1640
+ - All public methods tested
1641
+ - All error paths tested
1642
+
1643
+ ### Security Rules Compliance
1644
+ **🚨 CRITICAL: These MUST be checked before completing**
1645
+
1646
+ - [ ] **NO `@Restricted()` decorators removed from Controllers/Resolvers/Models/Objects**
1647
+ - [ ] **NO `@Roles()` decorators weakened to make tests pass**
1648
+ - [ ] **NO `securityCheck()` logic modified to bypass security**
1649
+ - [ ] **Class-level `@Restricted(ADMIN)` kept as security fallback**
1650
+ - [ ] **All security changes discussed and approved by developer**
1651
+ - [ ] **All security changes documented with approval and reason**
1652
+ - [ ] **Tests adapted to security requirements (not vice versa)**
1653
+ - [ ] **Appropriate test users created for each permission level**
1654
+ - [ ] **Permission hierarchy understood and respected (specific overrides general)**
1655
+
1656
+ **Test Organization:**
1657
+ ```typescript
1658
+ describe('ProductResolver', () => {
1659
+ // Setup
1660
+ describe('Setup', () => { ... });
1661
+
1662
+ // Happy path tests
1663
+ describe('CREATE operations', () => {
1664
+ it('should create product as regular user', ...);
1665
+ it('should create product with all optional fields', ...);
1666
+ });
1667
+
1668
+ describe('READ operations', () => {
1669
+ it('should get product by ID', ...);
1670
+ it('should list all products with pagination', ...);
1671
+ it('should filter products by criteria', ...);
1672
+ });
1673
+
1674
+ describe('UPDATE operations', () => {
1675
+ it('should update product as creator', ...);
1676
+ it('should update product as admin', ...);
1677
+ });
1678
+
1679
+ describe('DELETE operations', () => {
1680
+ it('should delete product as creator', ...);
1681
+ it('should delete product as admin', ...);
1682
+ });
1683
+
1684
+ // Security tests
1685
+ describe('Security Validation', () => {
1686
+ it('should FAIL to create without auth', ...);
1687
+ it('should FAIL to update as non-creator', ...);
1688
+ it('should FAIL to delete as non-creator', ...);
1689
+ it('should FAIL to access with invalid token', ...);
1690
+ });
1691
+
1692
+ // Validation tests
1693
+ describe('Input Validation', () => {
1694
+ it('should FAIL with missing required fields', ...);
1695
+ it('should FAIL with invalid field values', ...);
1696
+ it('should FAIL with duplicate values', ...);
1697
+ });
1698
+
1699
+ // Edge cases
1700
+ describe('Edge Cases', () => {
1701
+ it('should handle non-existent ID (404)', ...);
1702
+ it('should handle empty list results', ...);
1703
+ it('should handle concurrent updates', ...);
1704
+ });
1705
+ });
1706
+ ```
1707
+
1708
+ ## Error Handling
1709
+
1710
+ ### Common Issues
1711
+
1712
+ **Issue**: TypeScript errors about missing imports
1713
+ **Solution**: Add missing imports manually:
1714
+ ```typescript
1715
+ import { Reference } from '@lenne.tech/nest-server';
1716
+ import { User } from '../../user/user.model';
1717
+ ```
1718
+
1719
+ **Issue**: CreateInput validation fails
1720
+ **Solution**: Check parent's CreateInput for required fields and add them
1721
+
1722
+ **Issue**: Enum validation errors
1723
+ **Solution**: Verify enum file exists and is properly imported
1724
+
1725
+ **Issue**: Tests fail due to missing required fields
1726
+ **Solution**: Review CreateInput and ensure all required fields are provided in tests
1727
+
1728
+ **Issue**: Tests fail with 403 Forbidden
1729
+ **Solution**:
1730
+ 1. Check `@Roles()` decorator - are you using the right user role?
1731
+ 2. Check `securityCheck()` - does it allow this user to see the data?
1732
+ 3. Check service `serviceOptions.roles` - are permissions checked correctly?
1733
+ 4. **Use the LEAST privileged user who is authorized** (not admin!)
1734
+
1735
+ **Issue**: Test passes with admin but should work with regular user
1736
+ **Solution**: You're over-privileging! Analyze permissions and use the correct user:
1737
+ - S_USER endpoint? β†’ Use regular user token
1738
+ - S_CREATOR endpoint? β†’ Use creator token
1739
+ - NEVER use admin when regular user would work
1740
+
1741
+ **Issue**: Security tests not failing as expected
1742
+ **Solution**:
1743
+ 1. Verify `@Roles()` decorator is set correctly
1744
+ 2. Verify `securityCheck()` logic is correct
1745
+ 3. Add console.log to see what's happening
1746
+ 4. Security MUST be validated - fix the model/controller if tests don't fail
1747
+
1748
+ ## ⚠️ CRITICAL: Security & Test Coverage Rules
1749
+
1750
+ ### Rule 1: NEVER Weaken Security for Test Convenience
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
1840
+
1841
+ **🎯 Goal: Maximum Coverage WITHOUT Compromising Security**
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
+ });
1870
+
1871
+ // Test security failure
1872
+ it('should FAIL to create product without auth', async () => {
1873
+ // βœ… Verifies security works
1874
+ });
1875
+ });
1876
+
1877
+ describe('adminOnlyMethod', () => {
1878
+ it('should execute as admin', async () => {
1879
+ // βœ… Uses admin token (required for this method)
1880
+ });
1881
+
1882
+ it('should FAIL for regular user', async () => {
1883
+ // βœ… Verifies fallback to @Restricted(ADMIN) works
1884
+ });
1885
+
1886
+ it('should FAIL without auth', async () => {
1887
+ // βœ… Verifies security
1888
+ });
1889
+ });
1890
+ });
1891
+ ```
1892
+
1893
+ ### Rule 4: When Security Changes Are Necessary
1894
+
1895
+ **If you genuinely believe a security restriction is too strict:**
1896
+
1897
+ 1. **STOP** - Do NOT make the change
1898
+ 2. **Analyze** - Why is the restriction failing the test?
1899
+ 3. **Document** - Prepare a clear explanation:
1900
+ - What restriction exists?
1901
+ - Why does it block the test?
1902
+ - What business case requires opening it?
1903
+ - What security risks does opening it introduce?
1904
+ - What mitigation strategies exist?
1905
+ 4. **Ask the developer**:
1906
+ ```
1907
+ "I've analyzed the permissions for [Method/Property] and found:
1908
+ - Current restriction: @Restricted(RoleEnum.ADMIN)
1909
+ - Test requirement: S_USER needs access to [do X]
1910
+ - Business justification: [explain why]
1911
+ - Security impact: [explain risks]
1912
+ - Mitigation: [how to minimize risk]
1913
+
1914
+ Should I:
1915
+ A) Keep security as-is and adjust the test
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
2780
+
2781
+ **Only after ALL checks pass, proceed to Final Report.**
2782
+
2783
+ ## Final Report
2784
+
2785
+ After completing all tasks, provide:
2786
+
2787
+ 1. **Summary of created components**:
2788
+ - Number of SubObjects created
2789
+ - Number of Objects created
2790
+ - Number of Modules created
2791
+ - Number of enum files created
2792
+ - Number of test files created
2793
+
2794
+ 2. **Observations about data structure**:
2795
+ - Unusual patterns
2796
+ - Potential improvements
2797
+ - Missing relationships
2798
+ - Optimization suggestions
2799
+
2800
+ 3. **Test results**:
2801
+ - All tests passing
2802
+ - Any failures and reasons
2803
+
2804
+ 4. **Next steps**:
2805
+ - Manual adjustments needed
2806
+ - Additional features to consider
2807
+
2808
+ ## Best Practices
2809
+
2810
+ 1. **Always create dependencies first** (SubObjects before Modules that use them)
2811
+ 2. **Check for circular dependencies** in object relationships
2812
+ 3. **Use meaningful enum names** that match the domain
2813
+ 4. **Keep descriptions concise** but informative
2814
+ 5. **Test incrementally** (don't wait until all modules are created)
2815
+ 6. **Commit after each major component** (SubObjects, then Modules, then Tests)
2816
+ 7. **Use appropriate controller types** (Rest for simple CRUD, GraphQL for complex queries, Both for flexibility)
2817
+ 8. **Validate required fields** in tests to ensure data integrity
2818
+ 9. **Clean up test data** to avoid database pollution
2819
+ 10. **Document complex relationships** in code comments
2820
+
2821
+ ## Working with This Skill
2822
+
2823
+ When you receive a specification:
2824
+
2825
+ 1. **Parse completely** before starting any generation
2826
+ 2. **Ask clarifying questions** if specification is ambiguous
2827
+ 3. **Create detailed todo list** showing all steps
2828
+ 4. **Execute systematically** following the workflow
2829
+ 5. **Verify each step** before moving to next
2830
+ 6. **Report progress** using todo updates
2831
+ 7. **Provide comprehensive summary** at the end
2832
+
2833
+ This skill ensures complete, production-ready NestJS backend structures are generated efficiently and correctly from complex specifications.