@lenne.tech/cli 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/build/commands/claude/install-commands.js +10 -5
  2. package/build/commands/claude/install-mcps.js +256 -0
  3. package/build/commands/claude/install-skills.js +90 -23
  4. package/build/lib/mcp-registry.js +71 -0
  5. package/build/templates/claude-commands/commit-message.md +21 -0
  6. package/build/templates/claude-commands/skill-optimize.md +431 -90
  7. package/build/templates/claude-skills/building-stories-with-tdd/SKILL.md +265 -0
  8. package/build/templates/claude-skills/{story-tdd → building-stories-with-tdd}/code-quality.md +10 -0
  9. package/build/templates/claude-skills/{story-tdd → building-stories-with-tdd}/database-indexes.md +9 -0
  10. package/build/templates/claude-skills/{story-tdd → building-stories-with-tdd}/examples.md +115 -64
  11. package/build/templates/claude-skills/building-stories-with-tdd/handling-existing-tests.md +197 -0
  12. package/build/templates/claude-skills/{story-tdd → building-stories-with-tdd}/reference.md +276 -29
  13. package/build/templates/claude-skills/{story-tdd → building-stories-with-tdd}/security-review.md +8 -0
  14. package/build/templates/claude-skills/building-stories-with-tdd/workflow.md +1004 -0
  15. package/build/templates/claude-skills/generating-nest-servers/SKILL.md +303 -0
  16. package/build/templates/claude-skills/{nest-server-generator → generating-nest-servers}/configuration.md +6 -0
  17. package/build/templates/claude-skills/{nest-server-generator → generating-nest-servers}/declare-keyword-warning.md +9 -0
  18. package/build/templates/claude-skills/{nest-server-generator → generating-nest-servers}/description-management.md +9 -0
  19. package/build/templates/claude-skills/{nest-server-generator → generating-nest-servers}/examples.md +7 -0
  20. package/build/templates/claude-skills/generating-nest-servers/framework-guide.md +259 -0
  21. package/build/templates/claude-skills/{nest-server-generator → generating-nest-servers}/quality-review.md +9 -0
  22. package/build/templates/claude-skills/{nest-server-generator → generating-nest-servers}/reference.md +16 -0
  23. package/build/templates/claude-skills/{nest-server-generator → generating-nest-servers}/security-rules.md +13 -0
  24. package/build/templates/claude-skills/generating-nest-servers/verification-checklist.md +262 -0
  25. package/build/templates/claude-skills/generating-nest-servers/workflow-process.md +1061 -0
  26. package/build/templates/claude-skills/{lt-cli → using-lt-cli}/SKILL.md +22 -10
  27. package/build/templates/claude-skills/{lt-cli → using-lt-cli}/examples.md +7 -3
  28. package/build/templates/claude-skills/{lt-cli → using-lt-cli}/reference.md +10 -3
  29. package/package.json +2 -2
  30. package/build/templates/claude-skills/nest-server-generator/SKILL.md +0 -1891
  31. package/build/templates/claude-skills/story-tdd/SKILL.md +0 -1173
@@ -1,1891 +0,0 @@
1
- ---
2
- name: nest-server-generator
3
- version: 1.0.3
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, understand these NON-NEGOTIABLE rules:**
59
-
60
- ### ⛔ NEVER Do This:
61
- 1. **NEVER remove or weaken `@Restricted()` decorators**
62
- 2. **NEVER change `@Roles()` decorators** to more permissive roles
63
- 3. **NEVER modify `securityCheck()` logic** to bypass security
64
- 4. **NEVER remove class-level `@Restricted(RoleEnum.ADMIN)`**
65
-
66
- ### ✅ ALWAYS Do This:
67
- 1. **ALWAYS analyze permissions BEFORE writing tests**
68
- 2. **ALWAYS test with the LEAST privileged user** who is authorized
69
- 3. **ALWAYS adapt tests to security requirements**, never vice versa
70
- 4. **ALWAYS ask developer for approval** before changing ANY security decorator
71
-
72
- **📖 For complete security rules, testing guidelines, and examples, see: `security-rules.md`**
73
-
74
- ## 🚨 CRITICAL: NEVER USE `declare` KEYWORD FOR PROPERTIES
75
-
76
- **⚠️ DO NOT use the `declare` keyword when defining properties in classes!**
77
-
78
- ### Quick Rule
79
-
80
- ```typescript
81
- // ❌ WRONG
82
- export class ProductCreateInput extends ProductInput {
83
- declare name: string; // Decorator won't work!
84
- }
85
-
86
- // ✅ CORRECT
87
- export class ProductCreateInput extends ProductInput {
88
- @UnifiedField({ description: 'Product name' })
89
- name: string; // Decorator works properly
90
- }
91
- ```
92
-
93
- **Why**: `declare` prevents decorators from being applied, breaking the decorator system.
94
-
95
- **📖 For detailed explanation and correct patterns, see: `declare-keyword-warning.md`**
96
-
97
- ## 🚨 CRITICAL: DESCRIPTION MANAGEMENT
98
-
99
- **⚠️ COMMON MISTAKE:** Descriptions are often applied inconsistently. You MUST follow this process for EVERY component.
100
-
101
- ### 3-Step Process
102
-
103
- **1. Extract descriptions** from user's `// comments`
104
-
105
- **2. Format correctly:**
106
- - English input → `'Product name'`
107
- - German input → `'Product name (Produktname)'`
108
- - **⚠️ Fix typos ONLY, NEVER change wording!**
109
-
110
- **3. Apply EVERYWHERE:**
111
- - Model file
112
- - Create Input file
113
- - Update Input file
114
- - Object files (if SubObject)
115
- - Class-level @ObjectType() decorators
116
-
117
- **📖 For detailed formatting rules, examples, and verification checklist, see: `description-management.md`**
118
-
119
- ---
120
-
121
- ## Core Responsibilities
122
-
123
- This skill handles **ALL** NestJS server development tasks, including:
124
-
125
- ### Simple Tasks (Single Commands)
126
- - Creating a single module with `lt server module`
127
- - Creating a single object with `lt server object`
128
- - Adding properties with `lt server addProp`
129
- - Creating a new server with `lt server create`
130
- - Starting the server with `npm start` or `npm run dev`
131
- - Running tests with `npm test`
132
-
133
- ### Complex Tasks (Multiple Components)
134
- When you receive a complete structure specification, you will:
135
-
136
- 1. **Parse and analyze** the complete structure (modules, models, objects, properties, relationships)
137
- 2. **Create a comprehensive todo list** breaking down all tasks
138
- 3. **Generate all components** in the correct order (objects first, then modules)
139
- 4. **Handle inheritance** properly (Core and custom parent classes)
140
- 5. **Manage descriptions** (translate German to English, add originals in parentheses)
141
- 6. **Create API tests** for all controllers and resolvers
142
- 7. **Verify functionality** and provide a summary with observations
143
-
144
- ### Analysis Tasks
145
- When analyzing existing code:
146
-
147
- 1. **Explore the project structure** to understand the architecture
148
- 2. **Read relevant files** (modules, services, controllers, models)
149
- 3. **Identify patterns** and conventions used in the project
150
- 4. **Explain findings** clearly and concisely
151
- 5. **Suggest improvements** when appropriate
152
-
153
- ### Debugging Tasks
154
- When debugging issues:
155
-
156
- 1. **Read error messages and logs** carefully
157
- 2. **Identify the root cause** by analyzing relevant code
158
- 3. **Check configuration** (environment variables, config files)
159
- 4. **Test hypotheses** by examining related files
160
- 5. **Provide solutions** with code examples
161
-
162
- **Remember:** For ANY task involving NestJS or @lenne.tech/nest-server, use this skill!
163
-
164
- ## 📚 Understanding the Framework
165
-
166
- ### Core Service Base Class: CrudService
167
-
168
- **IMPORTANT**: Before working with Services, ALWAYS read this file to understand the base functionality:
169
-
170
- ```
171
- node_modules/@lenne.tech/nest-server/src/core/common/services/crud.service.ts
172
- ```
173
-
174
- **Why this is critical:**
175
- - Almost ALL Services extend `CrudService<Model>`
176
- - CrudService provides base CRUD operations (create, find, update, delete)
177
- - Understanding CrudService prevents reinventing the wheel
178
- - Shows patterns for handling permissions, filtering, and pagination
179
-
180
- **When to read CrudService:**
181
- 1. ✅ Before creating a new Service
182
- 2. ✅ When implementing custom Service methods
183
- 3. ✅ When debugging Service behavior
184
- 4. ✅ When writing tests for Services
185
- 5. ✅ When questions arise about Service functionality
186
-
187
- **What CrudService provides:**
188
- - `create(input, options)` - Create new document
189
- - `find(filterArgs)` - Find multiple documents
190
- - `findOne(filterArgs)` - Find single document
191
- - `findAndCount(filterArgs)` - Find with total count (pagination)
192
- - `update(id, input, options)` - Update document
193
- - `delete(id, options)` - Delete document
194
- - Permission handling via `options.roles`
195
- - Query filtering and population
196
- - Pagination support
197
-
198
- **Example Service that extends CrudService:**
199
- ```typescript
200
- @Injectable()
201
- export class ProductService extends CrudService<Product> {
202
- constructor(
203
- @InjectModel(Product.name) protected readonly productModel: Model<ProductDocument>,
204
- protected readonly configService: ConfigService,
205
- ) {
206
- super({ configService, mainDbModel: productModel, mainModelConstructor: Product });
207
- }
208
-
209
- // Custom methods can be added here
210
- // Base CRUD methods are inherited from CrudService
211
- }
212
- ```
213
-
214
- **Action Items:**
215
- - [ ] Read CrudService before modifying any Service
216
- - [ ] Check if CrudService already provides the needed functionality
217
- - [ ] Only add custom methods if CrudService doesn't cover the use case
218
- - [ ] Follow CrudService patterns for consistency
219
-
220
- ## Configuration File (lt.config.json)
221
-
222
- The lenne.tech CLI supports project-level configuration via `lt.config.json` files to set default values for commands.
223
-
224
- **📖 For complete configuration guide including structure, options, and examples, see: `configuration.md`**
225
-
226
- **Quick reference:**
227
- - **Location**: Project root or parent directories
228
- - **Priority**: CLI parameters > Interactive input > Config file > Defaults
229
- - **Key option**: `commands.server.module.controller` - Sets default controller type ("Rest" | "GraphQL" | "Both" | "auto")
230
-
231
- **Example config:**
232
- ```json
233
- {
234
- "commands": {
235
- "server": {
236
- "module": {
237
- "controller": "Rest",
238
- "skipLint": false
239
- }
240
- }
241
- }
242
- }
243
- ```
244
-
245
- **Initialize config**: `lt config init`
246
- **Show current config**: `lt config show`
247
-
248
- ### lt server module
249
- Creates a complete NestJS module with model, service, controller/resolver, and DTOs.
250
-
251
- **Syntax**:
252
- ```bash
253
- lt server module --name <ModuleName> [--controller <Rest|GraphQL|Both|auto>] [property-flags]
254
- ```
255
-
256
- **Parameters**:
257
- - `--name <ModuleName>` - **Required**: Module name (PascalCase)
258
- - `--controller <Rest|GraphQL|Both|auto>` - **Optional** (interactive detection if omitted)
259
- - `Rest` - Creates REST controller only (no GraphQL, no PubSub)
260
- - `GraphQL` - Creates GraphQL resolver only (includes PubSub for subscriptions)
261
- - `Both` - Creates both REST controller and GraphQL resolver (includes PubSub)
262
- - `auto` - Auto-detects from existing modules (non-interactive)
263
-
264
- **Property flags** (use index 0, 1, 2, ... for multiple properties):
265
- - `--prop-name-X <name>` - Property name
266
- - `--prop-type-X <type>` - string, number, boolean, ObjectId, Json, Date, bigint
267
- - `--prop-nullable-X <true|false>` - Optional property
268
- - `--prop-array-X <true|false>` - Array type
269
- - `--prop-enum-X <EnumName>` - Enum reference
270
- - `--prop-schema-X <SchemaName>` - SubObject/schema reference
271
- - `--prop-reference-X <RefName>` - Reference name for ObjectId
272
- - `--skipLint` - Skip lint prompt
273
-
274
- **Intelligent Controller Type Detection**:
275
-
276
- **Three modes for controller type selection:**
277
-
278
- 1. **Interactive with detection** (omit `--controller`):
279
- - CLI detects pattern from existing modules
280
- - Shows suggestion to user
281
- - User can accept or override interactively
282
-
283
- 2. **Non-interactive auto-detect** (`--controller auto`):
284
- - CLI detects pattern from existing modules
285
- - Uses detected value WITHOUT prompting
286
- - Perfect for automation/scripts
287
-
288
- 3. **Explicit** (`--controller Rest|GraphQL|Both`):
289
- - Bypasses detection
290
- - Uses specified value directly
291
-
292
- **Detection logic:**
293
- - Analyzes modules in `src/server/modules/` (excludes base modules: auth, file, meta, user)
294
- - **Only REST controllers found** → Detects `Rest`
295
- - **Only GraphQL resolvers found** → Detects `GraphQL`
296
- - **Both or mixed patterns found** → Detects `Both`
297
- - **No modules or unclear** → Detects `Both` (safest default)
298
-
299
- **Important Notes**:
300
- - **PubSub integration**: Only included when using `GraphQL` or `Both` controller types
301
- - **Auto-detection**: Analyzes your existing project structure to suggest the right pattern
302
- - **Base modules excluded**: auth, file, meta, user modules are NOT analyzed (they're framework modules)
303
- - REST-only modules (`--controller Rest`) do NOT include PubSub dependencies
304
-
305
- **Examples**:
306
- ```bash
307
- # Interactive mode with auto-detection (recommended for manual use)
308
- lt server module --name Product \
309
- --prop-name-0 name --prop-type-0 string \
310
- --prop-name-1 price --prop-type-1 number
311
- # CLI analyzes existing modules, shows suggestion, user confirms/overrides
312
-
313
- # Non-interactive auto-detect (recommended for scripts/automation)
314
- lt server module --name Product --controller auto \
315
- --prop-name-0 name --prop-type-0 string \
316
- --prop-name-1 price --prop-type-1 number
317
- # CLI analyzes and uses detected pattern WITHOUT prompting
318
-
319
- # Explicit REST only (no GraphQL/PubSub)
320
- lt server module --name Category --controller Rest \
321
- --prop-name-0 name --prop-type-0 string \
322
- --prop-name-1 slug --prop-type-1 string
323
-
324
- # Explicit GraphQL only (with PubSub)
325
- lt server module --name Post --controller GraphQL \
326
- --prop-name-0 title --prop-type-0 string \
327
- --prop-name-1 author --prop-type-1 ObjectId --prop-reference-1 User \
328
- --prop-name-2 tags --prop-type-2 string --prop-array-2 true
329
-
330
- # Explicit Both (REST + GraphQL + PubSub)
331
- lt server module --name User --controller Both \
332
- --prop-name-0 email --prop-type-0 string \
333
- --prop-name-1 username --prop-type-1 string
334
- ```
335
-
336
- ### lt server object
337
- Creates reusable embedded data structures (SubObjects) without _id or timestamps.
338
-
339
- **Syntax**:
340
- ```bash
341
- lt server object --name <ObjectName> [property-flags] [--skipLint]
342
- ```
343
-
344
- **Property flags**: Same as `lt server module` (--prop-name-X, --prop-type-X, etc.)
345
-
346
- **Example**:
347
- ```bash
348
- lt server object --name Address \
349
- --prop-name-0 street --prop-type-0 string \
350
- --prop-name-1 city --prop-type-1 string \
351
- --prop-name-2 country --prop-type-2 string
352
- ```
353
-
354
- ### lt server addProp
355
- Adds properties to existing modules or objects.
356
-
357
- **Syntax**:
358
- ```bash
359
- lt server addProp --type <Module|Object> --element <name> [property-flags]
360
- ```
361
-
362
- **Example**:
363
- ```bash
364
- lt server addProp --type Module --element User \
365
- --prop-name-0 phone --prop-type-0 string --prop-nullable-0 true
366
- ```
367
-
368
- ### lt server create
369
- Creates a new NestJS server project.
370
-
371
- **Syntax**:
372
- ```bash
373
- lt server create <server-name> [--description=<desc>] [--author=<name>]
374
- ```
375
-
376
- ## Prerequisites Check
377
-
378
- Before starting, verify:
379
-
380
- ```bash
381
- # Check if lenne.Tech CLI is installed
382
- lt --version
383
-
384
- # If not installed, install it
385
- npm install -g @lenne.tech/cli
386
-
387
- # Verify we're in a NestJS project with @lenne.tech/nest-server
388
- ls src/server/modules
389
- ```
390
-
391
- ### Creating a New Server
392
-
393
- If you need to create a completely new NestJS server project:
394
-
395
- ```bash
396
- lt server create <server-name>
397
- # Alias: lt server c <server-name>
398
- ```
399
-
400
- This command:
401
- - Clones the `nest-server-starter` template from GitHub
402
- - Sets up package.json with your project details
403
- - Configures Swagger documentation
404
- - Attempts to replace secret keys (may be incomplete)
405
- - Installs npm dependencies
406
- - Optionally initializes git repository
407
-
408
- **Interactive prompts**:
409
- - Server name (or provide as first parameter)
410
- - Description (optional)
411
- - Author (optional)
412
- - Initialize git? (yes/no)
413
-
414
- **Example**:
415
- ```bash
416
- lt server create my-api
417
-
418
- # Non-interactive
419
- lt server create my-api --description="My API Server" --author="John Doe"
420
- ```
421
-
422
- **✅ IMPORTANT: Post-Creation Verification**
423
-
424
- After running `lt server create`, the CLI automatically:
425
- - Replaces ALL secrets matching `'SECRET_OR_PRIVATE_KEY...'` with unique random values
426
- - Updates mongoose database URIs from `nest-server-*` to `<project-name>-*`
427
- - Configures Swagger documentation with project name
428
-
429
- **Recommended verification steps**:
430
-
431
- 1. **Verify secrets were replaced in `src/config.env.ts`**:
432
- ```bash
433
- cd <project-name>
434
-
435
- # Open config and verify no placeholders remain
436
- # All jwt.secret and jwt.refresh.secret should be long random strings
437
- ```
438
-
439
- 2. **Verify mongoose.uri uses project name**:
440
- ```typescript
441
- // In src/config.env.ts, verify database names match your project:
442
-
443
- // Example for project "my-api":
444
- local: {
445
- mongoose: {
446
- uri: 'mongodb://127.0.0.1/my-api-local', // ✅ Correct
447
- }
448
- },
449
- production: {
450
- mongoose: {
451
- uri: 'mongodb://overlay_mongo1/my-api-prod', // ✅ Correct
452
- }
453
- }
454
- ```
455
-
456
- 3. **If secrets were not replaced** (older CLI version):
457
- ```bash
458
- # Manually run setConfigSecrets to replace ALL secrets
459
- lt server setConfigSecrets
460
- ```
461
-
462
- 4. **Other post-creation steps**:
463
- - Start database server (MongoDB)
464
- - Run tests: `npm run test:e2e`
465
- - Start server: `npm start`
466
-
467
- **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.
468
-
469
- ## Understanding the Specification Format
470
-
471
- ### Structure Components
472
-
473
- #### 1. SubObject Definition
474
- ```
475
- SubObject: <Name> // Description
476
- - propertyName: <type> // Property description
477
- - anotherProperty: <type> // Description
478
- ```
479
-
480
- **SubObjects are**:
481
- - Embedded data structures without `_id` or timestamps
482
- - Created first using `lt server object`
483
- - Used via `--prop-schema-X` in modules
484
-
485
- #### 2. Object Definition
486
- ```
487
- Object: <Name> // Description
488
- Properties:
489
- - propertyName: <type> // Property description
490
- ```
491
-
492
- **Objects are**:
493
- - Similar to SubObjects but used as base models
494
- - Can be extended by other objects or modules
495
- - Created using `lt server object`
496
-
497
- #### 3. Module Definition
498
- ```
499
- Module: <Name> // Description
500
-
501
- Model: <Name> // Description
502
- Extends: <ParentModel>
503
- - propertyName: <type> // Property description
504
- - reference: <ModelName> // Reference to another module
505
- - embedded: <ObjectName>[] // Array of embedded objects
506
- ```
507
-
508
- **Modules include**:
509
- - Complete CRUD functionality
510
- - Service, controller/resolver, DTOs
511
- - Created using `lt server module`
512
-
513
- ### Property Type Syntax
514
-
515
- #### Basic Types
516
- - `string` - Text
517
- - `number` - Numeric values
518
- - `boolean` - True/false
519
- - `Date` - Date/time values
520
- - `bigint` - Large integers
521
- - `Json` - Flexible JSON data
522
-
523
- #### Special Types
524
-
525
- **ENUM (value list)**:
526
- ```
527
- propertyName: ENUM (VALUE1, VALUE2, VALUE3) // Description
528
- ```
529
- → Creates: `--prop-enum-X PropertyNameEnum`
530
- → You must create enum file afterwards in `src/server/common/enums/`
531
-
532
- **ENUM with strings**:
533
- ```
534
- status: ENUM ('PENDING', 'ACTIVE', 'COMPLETED') // Description
535
- ```
536
- → Same as above, quotes indicate string enum values
537
-
538
- **Arrays**:
539
- ```
540
- tags: string[] // Description
541
- skills: Skill[] // Array of SubObjects
542
- ```
543
- → Add `--prop-array-X true`
544
-
545
- **Optional Properties**:
546
- ```
547
- middleName?: string // Description
548
- ```
549
- → Add `--prop-nullable-X true`
550
-
551
- **References to Modules**:
552
- ```
553
- author: User // Reference to User module
554
- ```
555
- → Use `--prop-type-X ObjectId --prop-reference-X User`
556
-
557
- **Embedded Objects**:
558
- ```
559
- address: Address // Embedded Address object
560
- workHistory: WorkExperience[] // Array of embedded objects
561
- ```
562
- → Use `--prop-schema-X Address` (and `--prop-array-X true` for arrays)
563
-
564
- **Files**:
565
- ```
566
- document: File // PDF or other file
567
- ```
568
- → Use `--prop-type-X string` (stores file path/URL)
569
-
570
- ## Workflow Process
571
-
572
- ### Phase 1: Analysis & Planning
573
-
574
- 1. **Parse the specification** completely
575
- 2. **Identify all components**:
576
- - List all SubObjects
577
- - List all Objects
578
- - List all Modules
579
- - Identify inheritance relationships
580
- - Identify enum types needed
581
- 3. **Create comprehensive todo list** with:
582
- - Create each SubObject
583
- - Create each Object
584
- - Create each Module
585
- - Handle inheritance modifications
586
- - Create enum files
587
- - Create API tests for each module
588
- - Run tests and verify
589
-
590
- ### Phase 2: SubObject Creation
591
-
592
- **Create SubObjects in dependency order** (if SubObject A contains SubObject B, create B first):
593
-
594
- ```bash
595
- lt server object --name <ObjectName> \
596
- --prop-name-0 <name> --prop-type-0 <type> \
597
- --prop-name-1 <name> --prop-type-1 <type> \
598
- ...
599
- ```
600
-
601
- **Apply modifiers**:
602
- - Optional: `--prop-nullable-X true`
603
- - Array: `--prop-array-X true`
604
- - Enum: `--prop-enum-X <EnumName>`
605
- - Schema: `--prop-schema-X <SchemaName>`
606
-
607
- ### Phase 3: Module Creation
608
-
609
- **Create modules with all properties**:
610
-
611
- ```bash
612
- lt server module --name <ModuleName> --controller <Rest|GraphQL|Both> \
613
- --prop-name-0 <name> --prop-type-0 <type> \
614
- --prop-name-1 <name> --prop-type-1 <type> \
615
- ...
616
- ```
617
-
618
- **For references to other modules**:
619
- ```bash
620
- --prop-name-X author --prop-type-X ObjectId --prop-reference-X User
621
- ```
622
-
623
- **For embedded objects**:
624
- ```bash
625
- --prop-name-X address --prop-schema-X Address
626
- ```
627
-
628
- ### Phase 4: Inheritance Handling
629
-
630
- When a model extends another model (e.g., `Extends: Profile`):
631
-
632
- 1. **Identify parent model location**:
633
- - Core models (from @lenne.tech/nest-server): CoreModel, CorePersisted, etc.
634
- - Custom parent models: Need to find in project
635
-
636
- 2. **For Core parent models**:
637
- - Replace in model file: `extends CoreModel` → `extends ParentModel`
638
- - Import: `import { ParentModel } from './path'`
639
-
640
- 3. **For custom parent models (objects/other modules)**:
641
- - Model extends parent object: Import and extend
642
- - Input files must include parent properties
643
-
644
- 4. **Input/Output inheritance**:
645
- - **CreateInput**: Must include ALL required properties from parent AND model
646
- - **UpdateInput**: Include all properties as optional
647
- - Check parent's CreateInput for required fields
648
- - Copy required fields to child's CreateInput
649
-
650
- **Example**: If `BuyerProfile` extends `Profile`:
651
- ```typescript
652
- // buyer-profile.model.ts
653
- import { Profile } from '../../common/objects/profile/profile.object';
654
- export class BuyerProfile extends Profile { ... }
655
-
656
- // buyer-profile-create.input.ts
657
- // Must include ALL required fields from Profile's create input + BuyerProfile fields
658
- ```
659
-
660
- ### Phase 5: Description Management
661
-
662
- **⚠️ CRITICAL PHASE - Refer to "CRITICAL: DESCRIPTION MANAGEMENT" section at the top of this document!**
663
-
664
- This phase is often done incorrectly. Follow these steps EXACTLY:
665
-
666
- #### Step 5.1: Extract Descriptions from User Input
667
-
668
- **BEFORE applying any descriptions, review the original specification:**
669
-
670
- Go back to the user's original specification and extract ALL comments that appear after `//`:
671
-
672
- ```
673
- Module: Product
674
- - name: string // Product name
675
- - price: number // Produktpreis
676
- - description?: string // Produktbeschreibung
677
- - stock: number // Current inventory
678
-
679
- SubObject: Address
680
- - street: string // Straße
681
- - city: string // City name
682
- - zipCode: string // Postleitzahl
683
- ```
684
-
685
- **Create a mapping**:
686
- ```
687
- Product.name → "Product name" (English)
688
- Product.price → "Produktpreis" (German)
689
- Product.description → "Produktbeschreibung" (German)
690
- Product.stock → "Current inventory" (English)
691
- Address.street → "Straße" (German)
692
- Address.city → "City name" (English)
693
- Address.zipCode → "Postleitzahl" (German)
694
- ```
695
-
696
- #### Step 5.2: Format Descriptions
697
-
698
- **Rule**: `"ENGLISH_DESCRIPTION (DEUTSCHE_BESCHREIBUNG)"`
699
-
700
- Apply formatting rules:
701
-
702
- 1. **If comment is in English**:
703
- ```
704
- // Product name
705
- ```
706
- → Use as: `description: 'Product name'`
707
-
708
- Fix typos if needed:
709
- ```
710
- // Prodcut name (typo)
711
- ```
712
- → Use as: `description: 'Product name'` (typo corrected)
713
-
714
- 2. **If comment is in German**:
715
- ```
716
- // Produktpreis
717
- ```
718
- → Translate and add original: `description: 'Product price (Produktpreis)'`
719
-
720
- ```
721
- // Straße
722
- ```
723
- → Translate and add original: `description: 'Street (Straße)'`
724
-
725
- Fix typos in original:
726
- ```
727
- // Postleizahl (typo: missing 't')
728
- ```
729
- → Translate and add corrected: `description: 'Postal code (Postleitzahl)'`
730
-
731
- 3. **If no comment provided**:
732
- → Create meaningful English description: `description: 'User email address'`
733
-
734
- **⚠️ CRITICAL - Preserve Original Wording**:
735
-
736
- - ✅ **DO:** Fix spelling/typos only
737
- - ❌ **DON'T:** Rephrase, expand, or improve wording
738
- - ❌ **DON'T:** Change terms (they may be predefined/referenced by external systems)
739
-
740
- **Examples**:
741
- ```
742
- ✅ CORRECT:
743
- // Straße → 'Street (Straße)' (preserve word)
744
- // Produkt → 'Product (Produkt)' (don't add "name")
745
- // Status → 'Status (Status)' (same in both languages)
746
-
747
- ❌ WRONG:
748
- // Straße → 'Street name (Straßenname)' (changed word!)
749
- // Produkt → 'Product name (Produktname)' (added word!)
750
- // Status → 'Current status (Aktueller Status)' (added word!)
751
- ```
752
-
753
- #### Step 5.3: Apply Descriptions EVERYWHERE
754
-
755
- **🚨 MOST IMPORTANT: Apply SAME description to ALL files!**
756
-
757
- For **EVERY property in EVERY Module**:
758
-
759
- 1. Open `<module>.model.ts` → Add description to property
760
- 2. Open `inputs/<module>-create.input.ts` → Add SAME description to property
761
- 3. Open `inputs/<module>.input.ts` → Add SAME description to property
762
-
763
- For **EVERY property in EVERY SubObject**:
764
-
765
- 1. Open `objects/<object>/<object>.object.ts` → Add description to property
766
- 2. Open `objects/<object>/<object>-create.input.ts` → Add SAME description to property
767
- 3. Open `objects/<object>/<object>.input.ts` → Add SAME description to property
768
-
769
- **Example for Module "Product" with property "price"**:
770
-
771
- ```typescript
772
- // File: src/server/modules/product/product.model.ts
773
- @UnifiedField({ description: 'Product price (Produktpreis)' })
774
- price: number;
775
-
776
- // File: src/server/modules/product/inputs/product-create.input.ts
777
- @UnifiedField({ description: 'Product price (Produktpreis)' })
778
- price: number;
779
-
780
- // File: src/server/modules/product/inputs/product.input.ts
781
- @UnifiedField({ description: 'Product price (Produktpreis)' })
782
- price?: number;
783
- ```
784
-
785
- **Example for SubObject "Address" with property "street"**:
786
-
787
- ```typescript
788
- // File: src/server/common/objects/address/address.object.ts
789
- @UnifiedField({ description: 'Street (Straße)' })
790
- street: string;
791
-
792
- // File: src/server/common/objects/address/address-create.input.ts
793
- @UnifiedField({ description: 'Street (Straße)' })
794
- street: string;
795
-
796
- // File: src/server/common/objects/address/address.input.ts
797
- @UnifiedField({ description: 'Street (Straße)' })
798
- street?: string;
799
- ```
800
-
801
- #### Step 5.4: Add Class-Level Descriptions
802
-
803
- Also add descriptions to the `@ObjectType()` and `@InputType()` decorators:
804
-
805
- ```typescript
806
- @ObjectType({ description: 'Product entity (Produkt-Entität)' })
807
- export class Product extends CoreModel { ... }
808
-
809
- @InputType({ description: 'Product creation data (Produkt-Erstellungsdaten)' })
810
- export class ProductCreateInput { ... }
811
-
812
- @InputType({ description: 'Product update data (Produkt-Aktualisierungsdaten)' })
813
- export class ProductInput { ... }
814
- ```
815
-
816
- #### Step 5.5: Verify Consistency
817
-
818
- After applying all descriptions, verify:
819
-
820
- - [ ] All user-provided comments extracted and processed
821
- - [ ] All German descriptions translated to format: `ENGLISH (DEUTSCH)`
822
- - [ ] All English descriptions kept as-is
823
- - [ ] Module Model has descriptions on all properties
824
- - [ ] Module CreateInput has SAME descriptions on all properties
825
- - [ ] Module UpdateInput has SAME descriptions on all properties
826
- - [ ] SubObject has descriptions on all properties
827
- - [ ] SubObject CreateInput has SAME descriptions on all properties
828
- - [ ] SubObject UpdateInput has SAME descriptions on all properties
829
- - [ ] Class-level decorators have descriptions
830
- - [ ] NO inconsistencies (same property, different descriptions)
831
-
832
- **If ANY checkbox is unchecked, STOP and fix before continuing to Phase 6!**
833
-
834
- ### Phase 6: Enum File Creation
835
-
836
- For each enum used, create enum file manually:
837
-
838
- ```typescript
839
- // src/server/common/enums/status.enum.ts
840
- export enum StatusEnum {
841
- PENDING = 'PENDING',
842
- ACTIVE = 'ACTIVE',
843
- COMPLETED = 'COMPLETED',
844
- }
845
- ```
846
-
847
- **Naming convention**:
848
- - File: `kebab-case.enum.ts`
849
- - Enum: `PascalCaseEnum`
850
- - Values: `UPPER_SNAKE_CASE`
851
-
852
- ### Phase 7: API Test Creation
853
-
854
- **⚠️ CRITICAL: Test Type Requirement**
855
-
856
- **ONLY create API tests using TestHelper - NEVER create direct Service tests!**
857
-
858
- - ✅ **DO:** Create tests that call REST endpoints or GraphQL queries/mutations using `TestHelper`
859
- - ✅ **DO:** Test through the API layer (Controller/Resolver → Service → Database)
860
- - ❌ **DON'T:** Create tests that directly instantiate or call Service methods
861
- - ❌ **DON'T:** Create unit tests for Services (e.g., `user.service.spec.ts`)
862
- - ❌ **DON'T:** Mock dependencies or bypass the API layer
863
-
864
- **Why API tests only?**
865
- - API tests validate the complete security model (decorators, guards, permissions)
866
- - Direct Service tests bypass authentication and authorization checks
867
- - TestHelper provides all necessary tools for comprehensive API testing
868
-
869
- **Exception: Direct database/service access for test setup/cleanup ONLY**
870
-
871
- Direct database or service access is ONLY allowed for:
872
-
873
- - ✅ **Test Setup (beforeAll/beforeEach)**:
874
- - Setting user roles in database: `await db.collection('users').updateOne({ _id: userId }, { $set: { roles: ['admin'] } })`
875
- - Setting verified flag: `await db.collection('users').updateOne({ _id: userId }, { $set: { verified: true } })`
876
- - Creating prerequisite test data that can't be created via API
877
-
878
- - ✅ **Test Cleanup (afterAll/afterEach)**:
879
- - Deleting test objects: `await db.collection('products').deleteMany({ createdBy: testUserId })`
880
- - Cleaning up test data: `await db.collection('users').deleteOne({ email: 'test@example.com' })`
881
-
882
- - ❌ **NEVER for testing functionality**:
883
- - Don't call `userService.create()` to test user creation - use API endpoint!
884
- - Don't call `productService.update()` to test updates - use API endpoint!
885
- - Don't access database to verify results - query via API instead!
886
-
887
- **Example of correct usage:**
888
-
889
- ```typescript
890
- describe('Product Tests', () => {
891
- let adminToken: string;
892
- let userId: string;
893
-
894
- beforeAll(async () => {
895
- // ✅ ALLOWED: Direct DB access for setup
896
- const user = await testHelper.rest('/auth/signup', {
897
- method: 'POST',
898
- payload: { email: 'admin@test.com', password: 'password' }
899
- });
900
- userId = user.id;
901
-
902
- // ✅ ALLOWED: Direct DB manipulation for test setup
903
- await db.collection('users').updateOne(
904
- { _id: new ObjectId(userId) },
905
- { $set: { roles: ['admin'], verified: true } }
906
- );
907
-
908
- // Get token via API
909
- const auth = await testHelper.rest('/auth/signin', {
910
- method: 'POST',
911
- payload: { email: 'admin@test.com', password: 'password' }
912
- });
913
- adminToken = auth.token;
914
- });
915
-
916
- it('should create product', async () => {
917
- // ✅ CORRECT: Test via API
918
- const result = await testHelper.rest('/api/products', {
919
- method: 'POST',
920
- payload: { name: 'Test Product' },
921
- token: adminToken
922
- });
923
-
924
- expect(result.name).toBe('Test Product');
925
-
926
- // ❌ WRONG: Don't verify via DB
927
- // const dbProduct = await db.collection('products').findOne({ _id: result.id });
928
-
929
- // ✅ CORRECT: Verify via API
930
- const fetched = await testHelper.rest(`/api/products/${result.id}`, {
931
- method: 'GET',
932
- token: adminToken
933
- });
934
- expect(fetched.name).toBe('Test Product');
935
- });
936
-
937
- afterAll(async () => {
938
- // ✅ ALLOWED: Direct DB access for cleanup
939
- await db.collection('products').deleteMany({ createdBy: userId });
940
- await db.collection('users').deleteOne({ _id: new ObjectId(userId) });
941
- });
942
- });
943
- ```
944
-
945
- ---
946
-
947
- **⚠️ CRITICAL: Test Creation Process**
948
-
949
- Creating API tests is NOT just about testing functionality - it's about **validating the security model**. You MUST follow this exact process:
950
-
951
- ---
952
-
953
- #### Step 1: 🔍 MANDATORY Permission Analysis (BEFORE writing ANY test)
954
-
955
- **YOU MUST analyze these THREE layers BEFORE writing a single test:**
956
-
957
- 1. **Controller/Resolver Layer** - Check `@Roles()` decorator:
958
- ```typescript
959
- // In product.resolver.ts
960
- @Roles(RoleEnum.S_EVERYONE) // ← WHO can call this?
961
- @Query(() => [Product])
962
- async getProducts() { ... }
963
-
964
- @Roles(RoleEnum.S_USER) // ← All signed-in users
965
- @Mutation(() => Product)
966
- async createProduct(@Args('input') input: ProductCreateInput) { ... }
967
-
968
- @Roles(RoleEnum.ADMIN, RoleEnum.S_CREATOR) // ← Only admin or creator
969
- @Mutation(() => Product)
970
- async updateProduct(@Args('id') id: string, @Args('input') input: ProductInput) { ... }
971
- ```
972
-
973
- 2. **Model Layer** - Check `@Restricted()` and `securityCheck()`:
974
- ```typescript
975
- // In product.model.ts
976
- export class Product extends CoreModel {
977
- securityCheck(user: User, force?: boolean) {
978
- if (force || user?.hasRole(RoleEnum.ADMIN)) {
979
- return this; // Admin sees all
980
- }
981
- if (this.isPublic) {
982
- return this; // Everyone sees public products
983
- }
984
- if (!equalIds(user, this.createdBy)) {
985
- return undefined; // Non-creator gets nothing
986
- }
987
- return this; // Creator sees own products
988
- }
989
- }
990
- ```
991
-
992
- 3. **Service Layer** - Check `serviceOptions.roles` usage:
993
- ```typescript
994
- // In product.service.ts
995
- async update(id: string, input: ProductInput, serviceOptions?: ServiceOptions) {
996
- // Check if user has ADMIN or S_CREATOR role
997
- // ...
998
- }
999
- ```
1000
-
1001
- **Permission Analysis Checklist:**
1002
- - [ ] I have checked ALL `@Roles()` decorators in controller/resolver
1003
- - [ ] I have read the complete `securityCheck()` method in the model
1004
- - [ ] I have checked ALL `@Restricted()` decorators
1005
- - [ ] I understand WHO can CREATE (usually S_USER or ADMIN)
1006
- - [ ] I understand WHO can READ (S_USER + securityCheck filtering)
1007
- - [ ] I understand WHO can UPDATE (usually ADMIN + S_CREATOR)
1008
- - [ ] I understand WHO can DELETE (usually ADMIN + S_CREATOR)
1009
-
1010
- **Common Permission Patterns:**
1011
- - `S_EVERYONE` → No authentication required
1012
- - `S_USER` → Any signed-in user
1013
- - `ADMIN` → User with 'admin' role
1014
- - `S_CREATOR` → User who created the resource (user.id === object.createdBy)
1015
-
1016
- ---
1017
-
1018
- #### Step 2: 🎯 Apply Principle of Least Privilege
1019
-
1020
- **GOLDEN RULE**: Always test with the **LEAST privileged user** who is still authorized.
1021
-
1022
- **Decision Tree:**
1023
-
1024
- ```
1025
- Is endpoint marked with @Roles(RoleEnum.S_EVERYONE)?
1026
- ├─ YES → Test WITHOUT token (unauthenticated)
1027
- └─ NO → Is endpoint marked with @Roles(RoleEnum.S_USER)?
1028
- ├─ YES → Test WITH regular user token (NOT admin, NOT creator)
1029
- └─ NO → Is endpoint marked with @Roles(RoleEnum.ADMIN, RoleEnum.S_CREATOR)?
1030
- ├─ For UPDATE/DELETE → Test WITH creator token (user who created it)
1031
- └─ For ADMIN-only → Test WITH admin token
1032
- ```
1033
-
1034
- **❌ WRONG Approach:**
1035
- ```typescript
1036
- // BAD: Using admin for everything
1037
- it('should create product', async () => {
1038
- const result = await testHelper.graphQl({
1039
- name: 'createProduct',
1040
- type: TestGraphQLType.MUTATION,
1041
- arguments: { input: { name: 'Test' } },
1042
- fields: ['id']
1043
- }, { token: adminToken }); // ❌ WRONG - Over-privileged!
1044
- });
1045
- ```
1046
-
1047
- **✅ CORRECT Approach:**
1048
- ```typescript
1049
- // GOOD: Using least privileged user
1050
- it('should create product as regular user', async () => {
1051
- const result = await testHelper.graphQl({
1052
- name: 'createProduct',
1053
- type: TestGraphQLType.MUTATION,
1054
- arguments: { input: { name: 'Test' } },
1055
- fields: ['id']
1056
- }, { token: userToken }); // ✅ CORRECT - S_USER is enough!
1057
- });
1058
- ```
1059
-
1060
- ---
1061
-
1062
- #### Step 3: 📋 Create Test User Matrix
1063
-
1064
- Based on your permission analysis, create test users:
1065
-
1066
- ```typescript
1067
- describe('Product API', () => {
1068
- let testHelper: TestHelper;
1069
-
1070
- // Create users based on ACTUAL needs (not all of them!)
1071
- let noToken: undefined; // For S_EVERYONE endpoints
1072
- let userToken: string; // For S_USER endpoints
1073
- let creatorToken: string; // For S_CREATOR (will create test data)
1074
- let otherUserToken: string; // For testing "not creator" scenarios
1075
- let adminToken: string; // Only if ADMIN-specific endpoints exist
1076
-
1077
- let createdProductId: string;
1078
-
1079
- beforeAll(async () => {
1080
- testHelper = new TestHelper(app);
1081
-
1082
- // Only create users you ACTUALLY need based on @Roles() analysis!
1083
-
1084
- // Regular user (for S_USER endpoints)
1085
- const userAuth = await testHelper.graphQl({
1086
- name: 'signUp',
1087
- type: TestGraphQLType.MUTATION,
1088
- arguments: {
1089
- input: {
1090
- email: 'user@test.com',
1091
- password: 'password',
1092
- roles: ['user'] // Regular user, no special privileges
1093
- }
1094
- },
1095
- fields: ['token', 'user { id }']
1096
- });
1097
- userToken = userAuth.token;
1098
-
1099
- // Creator user (will create test objects)
1100
- const creatorAuth = await testHelper.graphQl({
1101
- name: 'signUp',
1102
- type: TestGraphQLType.MUTATION,
1103
- arguments: {
1104
- input: {
1105
- email: 'creator@test.com',
1106
- password: 'password',
1107
- roles: ['user']
1108
- }
1109
- },
1110
- fields: ['token', 'user { id }']
1111
- });
1112
- creatorToken = creatorAuth.token;
1113
-
1114
- // Other user (to test "not creator" scenarios)
1115
- const otherUserAuth = await testHelper.graphQl({
1116
- name: 'signUp',
1117
- type: TestGraphQLType.MUTATION,
1118
- arguments: {
1119
- input: {
1120
- email: 'other@test.com',
1121
- password: 'password',
1122
- roles: ['user']
1123
- }
1124
- },
1125
- fields: ['token', 'user { id }']
1126
- });
1127
- otherUserToken = otherUserAuth.token;
1128
-
1129
- // Admin user (ONLY if truly needed!)
1130
- const adminAuth = await testHelper.graphQl({
1131
- name: 'signUp',
1132
- type: TestGraphQLType.MUTATION,
1133
- arguments: {
1134
- input: {
1135
- email: 'admin@test.com',
1136
- password: 'password',
1137
- roles: ['admin', 'user'] // ← 'admin' role!
1138
- }
1139
- },
1140
- fields: ['token']
1141
- });
1142
- adminToken = adminAuth.token;
1143
- });
1144
-
1145
- afterAll(async () => {
1146
- // Clean up with appropriate privileged user
1147
- if (createdProductId) {
1148
- // Use creator or admin token for cleanup
1149
- await testHelper.graphQl({
1150
- name: 'deleteProduct',
1151
- type: TestGraphQLType.MUTATION,
1152
- arguments: { id: createdProductId },
1153
- fields: ['id']
1154
- }, { token: creatorToken });
1155
- }
1156
- });
1157
- });
1158
- ```
1159
-
1160
- ---
1161
-
1162
- #### Step 4: ✅ Write Tests with Correct Privileges
1163
-
1164
- **Example 1: S_EVERYONE endpoint (public access)**
1165
-
1166
- ```typescript
1167
- // Endpoint: @Roles(RoleEnum.S_EVERYONE)
1168
- describe('Public Endpoints', () => {
1169
- it('should get public products WITHOUT token', async () => {
1170
- const result = await testHelper.graphQl({
1171
- name: 'getPublicProducts',
1172
- type: TestGraphQLType.QUERY,
1173
- fields: ['id', 'name', 'price']
1174
- }); // ← NO TOKEN! S_EVERYONE means unauthenticated is OK
1175
-
1176
- expect(result).toBeDefined();
1177
- expect(Array.isArray(result)).toBe(true);
1178
- });
1179
- });
1180
- ```
1181
-
1182
- **Example 2: S_USER endpoint (any authenticated user)**
1183
-
1184
- ```typescript
1185
- // Endpoint: @Roles(RoleEnum.S_USER)
1186
- describe('Create Product', () => {
1187
- it('should create product as regular user', async () => {
1188
- const result = await testHelper.graphQl({
1189
- name: 'createProduct',
1190
- type: TestGraphQLType.MUTATION,
1191
- arguments: { input: { name: 'Test Product', price: 10 } },
1192
- fields: ['id', 'name', 'price', 'createdBy']
1193
- }, { token: userToken }); // ← Regular user, NOT admin!
1194
-
1195
- expect(result).toBeDefined();
1196
- expect(result.name).toBe('Test Product');
1197
- createdProductId = result.id;
1198
-
1199
- // Verify creator is set
1200
- expect(result.createdBy).toBe(userAuth.user.id);
1201
- });
1202
- });
1203
- ```
1204
-
1205
- **Example 3: UPDATE - S_CREATOR or ADMIN**
1206
-
1207
- ```typescript
1208
- // Endpoint: @Roles(RoleEnum.ADMIN, RoleEnum.S_CREATOR)
1209
- describe('Update Product', () => {
1210
- it('should update product as creator', async () => {
1211
- // First, creator creates a product
1212
- const created = await testHelper.graphQl({
1213
- name: 'createProduct',
1214
- type: TestGraphQLType.MUTATION,
1215
- arguments: { input: { name: 'Original', price: 10 } },
1216
- fields: ['id', 'name']
1217
- }, { token: creatorToken });
1218
-
1219
- // Then, same creator updates it
1220
- const result = await testHelper.graphQl({
1221
- name: 'updateProduct',
1222
- type: TestGraphQLType.MUTATION,
1223
- arguments: {
1224
- id: created.id,
1225
- input: { name: 'Updated' }
1226
- },
1227
- fields: ['id', 'name']
1228
- }, { token: creatorToken }); // ← Use CREATOR token (least privilege!)
1229
-
1230
- expect(result.name).toBe('Updated');
1231
- });
1232
-
1233
- it('should update any product as admin', async () => {
1234
- // Admin can update products they did NOT create
1235
- const result = await testHelper.graphQl({
1236
- name: 'updateProduct',
1237
- type: TestGraphQLType.MUTATION,
1238
- arguments: {
1239
- id: createdProductId, // Created by different user
1240
- input: { name: 'Admin Updated' }
1241
- },
1242
- fields: ['id', 'name']
1243
- }, { token: adminToken }); // ← Admin needed for other's products
1244
-
1245
- expect(result.name).toBe('Admin Updated');
1246
- });
1247
- });
1248
- ```
1249
-
1250
- ---
1251
-
1252
- #### Step 5: 🛡️ MANDATORY: Test Permission Failures
1253
-
1254
- **CRITICAL**: You MUST test that unauthorized users are BLOCKED. This validates the security model.
1255
-
1256
- ```typescript
1257
- describe('Security Validation', () => {
1258
- describe('Unauthorized Access', () => {
1259
- it('should FAIL to create product without authentication', async () => {
1260
- // @Roles(RoleEnum.S_USER) requires authentication
1261
- const result = await testHelper.graphQl({
1262
- name: 'createProduct',
1263
- type: TestGraphQLType.MUTATION,
1264
- arguments: { input: { name: 'Hack', price: 1 } },
1265
- fields: ['id']
1266
- }, { statusCode: 401 }); // ← NO TOKEN = should fail with 401
1267
-
1268
- expect(result.errors).toBeDefined();
1269
- expect(result.errors[0].message).toContain('Unauthorized');
1270
- });
1271
-
1272
- it('should FAIL to update product as non-creator', async () => {
1273
- // @Roles(RoleEnum.ADMIN, RoleEnum.S_CREATOR)
1274
- const result = await testHelper.graphQl({
1275
- name: 'updateProduct',
1276
- type: TestGraphQLType.MUTATION,
1277
- arguments: {
1278
- id: createdProductId, // Created by creatorUser
1279
- input: { name: 'Hacked' }
1280
- },
1281
- fields: ['id']
1282
- }, { token: otherUserToken, statusCode: 403 }); // ← Different user = should fail with 403
1283
-
1284
- expect(result.errors).toBeDefined();
1285
- expect(result.errors[0].message).toContain('Forbidden');
1286
- });
1287
-
1288
- it('should FAIL to delete product as non-creator', async () => {
1289
- const result = await testHelper.graphQl({
1290
- name: 'deleteProduct',
1291
- type: TestGraphQLType.MUTATION,
1292
- arguments: { id: createdProductId },
1293
- fields: ['id']
1294
- }, { token: otherUserToken, statusCode: 403 });
1295
-
1296
- expect(result.errors).toBeDefined();
1297
- });
1298
-
1299
- it('should FAIL to read private product as different user', async () => {
1300
- // If securityCheck() blocks non-creators
1301
- const result = await testHelper.graphQl({
1302
- name: 'getProduct',
1303
- type: TestGraphQLType.QUERY,
1304
- arguments: { id: privateProductId },
1305
- fields: ['id', 'name']
1306
- }, { token: otherUserToken });
1307
-
1308
- // securityCheck returns undefined for non-creator
1309
- expect(result).toBeUndefined();
1310
- });
1311
- });
1312
- });
1313
- ```
1314
-
1315
- ---
1316
-
1317
- #### Step 6: 📝 Complete Test Structure
1318
-
1319
- **Test file location**:
1320
- ```
1321
- tests/modules/<module-name>.e2e-spec.ts
1322
- ```
1323
-
1324
- **Complete test template with proper privileges**:
1325
-
1326
- ```typescript
1327
- import { TestGraphQLType, TestHelper } from '@lenne.tech/nest-server';
1328
-
1329
- describe('Product Module E2E', () => {
1330
- let testHelper: TestHelper;
1331
- let userToken: string;
1332
- let creatorToken: string;
1333
- let otherUserToken: string;
1334
- let adminToken: string;
1335
- let createdProductId: string;
1336
- let userAuth: any;
1337
- let creatorAuth: any;
1338
-
1339
- beforeAll(async () => {
1340
- testHelper = new TestHelper(app);
1341
-
1342
- // Create test users (based on permission analysis)
1343
- userAuth = await testHelper.graphQl({
1344
- name: 'signUp',
1345
- type: TestGraphQLType.MUTATION,
1346
- arguments: { input: { email: 'user@test.com', password: 'password', roles: ['user'] } },
1347
- fields: ['token', 'user { id }']
1348
- });
1349
- userToken = userAuth.token;
1350
-
1351
- creatorAuth = await testHelper.graphQl({
1352
- name: 'signUp',
1353
- type: TestGraphQLType.MUTATION,
1354
- arguments: { input: { email: 'creator@test.com', password: 'password', roles: ['user'] } },
1355
- fields: ['token', 'user { id }']
1356
- });
1357
- creatorToken = creatorAuth.token;
1358
-
1359
- const otherUserAuth = await testHelper.graphQl({
1360
- name: 'signUp',
1361
- type: TestGraphQLType.MUTATION,
1362
- arguments: { input: { email: 'other@test.com', password: 'password', roles: ['user'] } },
1363
- fields: ['token']
1364
- });
1365
- otherUserToken = otherUserAuth.token;
1366
-
1367
- const adminAuth = await testHelper.graphQl({
1368
- name: 'signUp',
1369
- type: TestGraphQLType.MUTATION,
1370
- arguments: { input: { email: 'admin@test.com', password: 'password', roles: ['admin', 'user'] } },
1371
- fields: ['token']
1372
- });
1373
- adminToken = adminAuth.token;
1374
- });
1375
-
1376
- afterAll(async () => {
1377
- // Cleanup with appropriate privileges
1378
- if (createdProductId) {
1379
- await testHelper.graphQl({
1380
- name: 'deleteProduct',
1381
- type: TestGraphQLType.MUTATION,
1382
- arguments: { id: createdProductId },
1383
- fields: ['id']
1384
- }, { token: creatorToken });
1385
- }
1386
- });
1387
-
1388
- // 1. CREATE Tests (with least privileged user)
1389
- describe('Create Product', () => {
1390
- it('should create product as regular user', async () => {
1391
- const result = await testHelper.graphQl({
1392
- name: 'createProduct',
1393
- type: TestGraphQLType.MUTATION,
1394
- arguments: { input: { name: 'Test', price: 10 } },
1395
- fields: ['id', 'name', 'price', 'createdBy']
1396
- }, { token: userToken }); // ← S_USER = regular user
1397
-
1398
- expect(result.name).toBe('Test');
1399
- createdProductId = result.id;
1400
- });
1401
-
1402
- it('should FAIL to create without authentication', async () => {
1403
- const result = await testHelper.graphQl({
1404
- name: 'createProduct',
1405
- type: TestGraphQLType.MUTATION,
1406
- arguments: { input: { name: 'Fail', price: 10 } },
1407
- fields: ['id']
1408
- }, { statusCode: 401 }); // ← No token = should fail
1409
-
1410
- expect(result.errors).toBeDefined();
1411
- });
1412
-
1413
- it('should FAIL to create without required fields', async () => {
1414
- const result = await testHelper.graphQl({
1415
- name: 'createProduct',
1416
- type: TestGraphQLType.MUTATION,
1417
- arguments: { input: {} },
1418
- fields: ['id']
1419
- }, { token: userToken, statusCode: 400 });
1420
-
1421
- expect(result.errors).toBeDefined();
1422
- });
1423
- });
1424
-
1425
- // 2. READ Tests
1426
- describe('Get Products', () => {
1427
- it('should get all products as regular user', async () => {
1428
- const result = await testHelper.graphQl({
1429
- name: 'getProducts',
1430
- type: TestGraphQLType.QUERY,
1431
- fields: ['id', 'name', 'price']
1432
- }, { token: userToken });
1433
-
1434
- expect(Array.isArray(result)).toBe(true);
1435
- });
1436
-
1437
- it('should get product by ID as regular user', async () => {
1438
- const result = await testHelper.graphQl({
1439
- name: 'getProduct',
1440
- type: TestGraphQLType.QUERY,
1441
- arguments: { id: createdProductId },
1442
- fields: ['id', 'name', 'price']
1443
- }, { token: userToken });
1444
-
1445
- expect(result.id).toBe(createdProductId);
1446
- });
1447
- });
1448
-
1449
- // 3. UPDATE Tests (with creator, not admin!)
1450
- describe('Update Product', () => {
1451
- let creatorProductId: string;
1452
-
1453
- beforeAll(async () => {
1454
- // Creator creates a product to test updates
1455
- const created = await testHelper.graphQl({
1456
- name: 'createProduct',
1457
- type: TestGraphQLType.MUTATION,
1458
- arguments: { input: { name: 'Creator Product', price: 20 } },
1459
- fields: ['id']
1460
- }, { token: creatorToken });
1461
- creatorProductId = created.id;
1462
- });
1463
-
1464
- it('should update product as creator', async () => {
1465
- const result = await testHelper.graphQl({
1466
- name: 'updateProduct',
1467
- type: TestGraphQLType.MUTATION,
1468
- arguments: { id: creatorProductId, input: { name: 'Updated' } },
1469
- fields: ['id', 'name']
1470
- }, { token: creatorToken }); // ← CREATOR token (least privilege!)
1471
-
1472
- expect(result.name).toBe('Updated');
1473
- });
1474
-
1475
- it('should FAIL to update product as non-creator', async () => {
1476
- const result = await testHelper.graphQl({
1477
- name: 'updateProduct',
1478
- type: TestGraphQLType.MUTATION,
1479
- arguments: { id: creatorProductId, input: { name: 'Hacked' } },
1480
- fields: ['id']
1481
- }, { token: otherUserToken, statusCode: 403 });
1482
-
1483
- expect(result.errors).toBeDefined();
1484
- });
1485
-
1486
- it('should update any product as admin', async () => {
1487
- const result = await testHelper.graphQl({
1488
- name: 'updateProduct',
1489
- type: TestGraphQLType.MUTATION,
1490
- arguments: { id: creatorProductId, input: { name: 'Admin Update' } },
1491
- fields: ['id', 'name']
1492
- }, { token: adminToken });
1493
-
1494
- expect(result.name).toBe('Admin Update');
1495
- });
1496
- });
1497
-
1498
- // 4. DELETE Tests (with creator, not admin!)
1499
- describe('Delete Product', () => {
1500
- it('should delete product as creator', async () => {
1501
- // Creator creates and deletes
1502
- const created = await testHelper.graphQl({
1503
- name: 'createProduct',
1504
- type: TestGraphQLType.MUTATION,
1505
- arguments: { input: { name: 'To Delete', price: 5 } },
1506
- fields: ['id']
1507
- }, { token: creatorToken });
1508
-
1509
- const result = await testHelper.graphQl({
1510
- name: 'deleteProduct',
1511
- type: TestGraphQLType.MUTATION,
1512
- arguments: { id: created.id },
1513
- fields: ['id']
1514
- }, { token: creatorToken }); // ← CREATOR token!
1515
-
1516
- expect(result.id).toBe(created.id);
1517
- });
1518
-
1519
- it('should FAIL to delete product as non-creator', async () => {
1520
- const result = await testHelper.graphQl({
1521
- name: 'deleteProduct',
1522
- type: TestGraphQLType.MUTATION,
1523
- arguments: { id: createdProductId },
1524
- fields: ['id']
1525
- }, { token: otherUserToken, statusCode: 403 });
1526
-
1527
- expect(result.errors).toBeDefined();
1528
- });
1529
-
1530
- it('should delete any product as admin', async () => {
1531
- const result = await testHelper.graphQl({
1532
- name: 'deleteProduct',
1533
- type: TestGraphQLType.MUTATION,
1534
- arguments: { id: createdProductId },
1535
- fields: ['id']
1536
- }, { token: adminToken });
1537
-
1538
- expect(result.id).toBe(createdProductId);
1539
- });
1540
- });
1541
- });
1542
- ```
1543
-
1544
- ---
1545
-
1546
- #### Test Creation Checklist
1547
-
1548
- Before finalizing tests, verify:
1549
-
1550
- - [ ] ✅ I have analyzed ALL `@Roles()` decorators
1551
- - [ ] ✅ I have read the complete `securityCheck()` method
1552
- - [ ] ✅ I use the LEAST privileged user for each test
1553
- - [ ] ✅ S_EVERYONE endpoints tested WITHOUT token
1554
- - [ ] ✅ S_USER endpoints tested with REGULAR user (not admin)
1555
- - [ ] ✅ UPDATE/DELETE tested with CREATOR token (not admin)
1556
- - [ ] ✅ I have tests that verify unauthorized access FAILS (401/403)
1557
- - [ ] ✅ I have tests that verify non-creators CANNOT update/delete
1558
- - [ ] ✅ I have tests for missing required fields
1559
- - [ ] ✅ All tests follow the security model
1560
- - [ ] ✅ Tests validate protection mechanisms work
1561
-
1562
- **⚠️ NEVER use admin token when a less privileged user would work!**
1563
-
1564
- ## Property Ordering
1565
-
1566
- **ALL properties must be in alphabetical order** in:
1567
- - Model files (`.model.ts`)
1568
- - Input files (`.input.ts`, `-create.input.ts`)
1569
- - Output files (`.output.ts`)
1570
-
1571
- After generating, verify and reorder if necessary.
1572
-
1573
- ## Common Patterns
1574
-
1575
- ### 1. Module with References and Embedded Objects
1576
- ```bash
1577
- lt server module --name Company --controller Both \
1578
- --prop-name-0 name --prop-type-0 string \
1579
- --prop-name-1 owner --prop-type-1 ObjectId --prop-reference-1 User \
1580
- --prop-name-2 headquarters --prop-schema-2 Address \
1581
- --prop-name-3 branches --prop-schema-3 Address --prop-array-3 true \
1582
- --prop-name-4 industry --prop-enum-4 IndustryEnum
1583
- ```
1584
-
1585
- ### 2. Object with Nested Objects
1586
- ```bash
1587
- # First create nested object
1588
- lt server object --name ContactInfo \
1589
- --prop-name-0 email --prop-type-0 string \
1590
- --prop-name-1 phone --prop-type-1 string
1591
-
1592
- # Then create parent object
1593
- lt server object --name Person \
1594
- --prop-name-0 name --prop-type-0 string \
1595
- --prop-name-1 contact --prop-schema-1 ContactInfo
1596
- ```
1597
-
1598
- ### 3. Module Extending Custom Object
1599
- ```bash
1600
- # First create base object
1601
- lt server object --name BaseProfile \
1602
- --prop-name-0 name --prop-type-0 string \
1603
- --prop-name-1 email --prop-type-1 string
1604
-
1605
- # Create module (will need manual extension modification)
1606
- lt server module --name UserProfile --controller Both \
1607
- --prop-name-0 username --prop-type-0 string
1608
-
1609
- # Manually modify UserProfile model to extend BaseProfile
1610
- ```
1611
-
1612
- ## Verification Checklist
1613
-
1614
- After generation, verify:
1615
-
1616
- ### Code Generation
1617
- - [ ] All SubObjects created
1618
- - [ ] All Objects created
1619
- - [ ] All Modules created
1620
- - [ ] All properties in alphabetical order
1621
- - [ ] **DESCRIPTIONS (Critical - check thoroughly):**
1622
- - [ ] All user-provided comments (after `//`) extracted from specification
1623
- - [ ] All German descriptions translated to format: `ENGLISH (DEUTSCH)`
1624
- - [ ] All English descriptions kept as-is (spelling corrected)
1625
- - [ ] ALL Module Models have descriptions on all properties
1626
- - [ ] ALL Module CreateInputs have SAME descriptions
1627
- - [ ] ALL Module UpdateInputs have SAME descriptions
1628
- - [ ] ALL SubObjects have descriptions on all properties
1629
- - [ ] ALL SubObject CreateInputs have SAME descriptions
1630
- - [ ] ALL SubObject UpdateInputs have SAME descriptions
1631
- - [ ] ALL `@ObjectType()` decorators have descriptions
1632
- - [ ] ALL `@InputType()` decorators have descriptions
1633
- - [ ] NO inconsistencies (same property, different descriptions in different files)
1634
- - [ ] NO German-only descriptions (must be translated)
1635
- - [ ] Inheritance properly implemented
1636
- - [ ] Required fields correctly set in CreateInputs
1637
- - [ ] Enum files created in `src/server/common/enums/`
1638
-
1639
- ### API Tests - Security First
1640
- - [ ] **Permission analysis completed BEFORE writing tests**
1641
- - [ ] **Analyzed ALL `@Roles()` decorators in controllers/resolvers**
1642
- - [ ] **Read complete `securityCheck()` method in models**
1643
- - [ ] **Tests use LEAST privileged user (never admin when less works)**
1644
- - [ ] **S_EVERYONE endpoints tested WITHOUT token**
1645
- - [ ] **S_USER endpoints tested with REGULAR user (not admin)**
1646
- - [ ] **UPDATE/DELETE tested with CREATOR token (not admin)**
1647
- - [ ] **Tests verify unauthorized access FAILS (401/403)**
1648
- - [ ] **Tests verify non-creators CANNOT update/delete**
1649
- - [ ] **Tests verify required fields**
1650
- - [ ] **Security validation tests exist (permission failures)**
1651
- - [ ] API tests created for all modules
1652
- - [ ] Tests cover all CRUD operations
1653
- - [ ] All tests pass
1654
- - [ ] No TypeScript errors
1655
- - [ ] Lint passes
1656
-
1657
- ### Test Coverage - Comprehensive Testing
1658
- **🎯 GOAL: Achieve the HIGHEST possible test coverage**
1659
-
1660
- - [ ] **Every endpoint has at least one successful test**
1661
- - [ ] **Every endpoint has at least one failure test (unauthorized/validation)**
1662
- - [ ] **All query parameters tested (filters, sorting, pagination)**
1663
- - [ ] **All validation rules tested (required fields, min/max, patterns)**
1664
- - [ ] **All relationships tested (creating/updating/deleting with references)**
1665
- - [ ] **Edge cases tested (empty results, non-existent IDs, duplicate values)**
1666
- - [ ] **Error handling tested (400, 401, 403, 404, 409 status codes)**
1667
- - [ ] **Data integrity tested (cascading deletes, orphan prevention)**
1668
- - [ ] **Business logic tested (custom methods, computed properties)**
1669
- - [ ] **Performance tested (large datasets, pagination limits)**
1670
-
1671
- **Coverage Requirements:**
1672
- - Minimum 80% line coverage for services
1673
- - Minimum 90% line coverage for resolvers/controllers
1674
- - 100% coverage for critical security logic (securityCheck, permission guards)
1675
- - 100% coverage for all endpoints (success AND failure cases)
1676
- - 100% coverage for all permission combinations
1677
- - All public methods tested
1678
- - All error paths tested
1679
-
1680
- ### Security Rules Compliance
1681
- **🚨 CRITICAL: These MUST be checked before completing**
1682
-
1683
- - [ ] **NO `@Restricted()` decorators removed from Controllers/Resolvers/Models/Objects**
1684
- - [ ] **NO `@Roles()` decorators weakened to make tests pass**
1685
- - [ ] **NO `securityCheck()` logic modified to bypass security**
1686
- - [ ] **Class-level `@Restricted(ADMIN)` kept as security fallback**
1687
- - [ ] **All security changes discussed and approved by developer**
1688
- - [ ] **All security changes documented with approval and reason**
1689
- - [ ] **Tests adapted to security requirements (not vice versa)**
1690
- - [ ] **Appropriate test users created for each permission level**
1691
- - [ ] **Permission hierarchy understood and respected (specific overrides general)**
1692
-
1693
- **Test Organization:**
1694
- ```typescript
1695
- describe('ProductResolver', () => {
1696
- // Setup
1697
- describe('Setup', () => { ... });
1698
-
1699
- // Happy path tests
1700
- describe('CREATE operations', () => {
1701
- it('should create product as regular user', ...);
1702
- it('should create product with all optional fields', ...);
1703
- });
1704
-
1705
- describe('READ operations', () => {
1706
- it('should get product by ID', ...);
1707
- it('should list all products with pagination', ...);
1708
- it('should filter products by criteria', ...);
1709
- });
1710
-
1711
- describe('UPDATE operations', () => {
1712
- it('should update product as creator', ...);
1713
- it('should update product as admin', ...);
1714
- });
1715
-
1716
- describe('DELETE operations', () => {
1717
- it('should delete product as creator', ...);
1718
- it('should delete product as admin', ...);
1719
- });
1720
-
1721
- // Security tests
1722
- describe('Security Validation', () => {
1723
- it('should FAIL to create without auth', ...);
1724
- it('should FAIL to update as non-creator', ...);
1725
- it('should FAIL to delete as non-creator', ...);
1726
- it('should FAIL to access with invalid token', ...);
1727
- });
1728
-
1729
- // Validation tests
1730
- describe('Input Validation', () => {
1731
- it('should FAIL with missing required fields', ...);
1732
- it('should FAIL with invalid field values', ...);
1733
- it('should FAIL with duplicate values', ...);
1734
- });
1735
-
1736
- // Edge cases
1737
- describe('Edge Cases', () => {
1738
- it('should handle non-existent ID (404)', ...);
1739
- it('should handle empty list results', ...);
1740
- it('should handle concurrent updates', ...);
1741
- });
1742
- });
1743
- ```
1744
-
1745
- ## Error Handling
1746
-
1747
- ### Common Issues
1748
-
1749
- **Issue**: TypeScript errors about missing imports
1750
- **Solution**: Add missing imports manually:
1751
- ```typescript
1752
- import { Reference } from '@lenne.tech/nest-server';
1753
- import { User } from '../../user/user.model';
1754
- ```
1755
-
1756
- **Issue**: CreateInput validation fails
1757
- **Solution**: Check parent's CreateInput for required fields and add them
1758
-
1759
- **Issue**: Enum validation errors
1760
- **Solution**: Verify enum file exists and is properly imported
1761
-
1762
- **Issue**: Tests fail due to missing required fields
1763
- **Solution**: Review CreateInput and ensure all required fields are provided in tests
1764
-
1765
- **Issue**: Tests fail with 403 Forbidden
1766
- **Solution**:
1767
- 1. Check `@Roles()` decorator - are you using the right user role?
1768
- 2. Check `securityCheck()` - does it allow this user to see the data?
1769
- 3. Check service `serviceOptions.roles` - are permissions checked correctly?
1770
- 4. **Use the LEAST privileged user who is authorized** (not admin!)
1771
-
1772
- **Issue**: Test passes with admin but should work with regular user
1773
- **Solution**: You're over-privileging! Analyze permissions and use the correct user:
1774
- - S_USER endpoint? → Use regular user token
1775
- - S_CREATOR endpoint? → Use creator token
1776
- - NEVER use admin when regular user would work
1777
-
1778
- **Issue**: Security tests not failing as expected
1779
- **Solution**:
1780
- 1. Verify `@Roles()` decorator is set correctly
1781
- 2. Verify `securityCheck()` logic is correct
1782
- 3. Add console.log to see what's happening
1783
- 4. Security MUST be validated - fix the model/controller if tests don't fail
1784
-
1785
- ## ⚠️ CRITICAL: Security & Test Coverage Rules
1786
-
1787
- **This section provides detailed rules for security and testing. It's duplicated at the beginning of this file for visibility.**
1788
-
1789
- **📖 For the complete content with all rules, examples, and testing strategies, see: `security-rules.md`**
1790
-
1791
- **Quick reminder - Core rules:**
1792
- 1. **NEVER weaken @Restricted or @Roles decorators** to make tests pass
1793
- 2. **NEVER modify securityCheck() logic** to bypass security
1794
- 3. **ALWAYS test with least privileged user** who is authorized
1795
- 4. **ALWAYS create appropriate test users** for each permission level
1796
- 5. **ALWAYS ask developer** before changing ANY security decorator
1797
- 6. **Aim for 80-100% test coverage** without compromising security
1798
-
1799
- **Testing approach:**
1800
- - Analyze permissions first
1801
- - Create test users for each role
1802
- - Test happy path with appropriate user
1803
- - Test security failures (403 responses)
1804
- - Document why certain roles are required
1805
-
1806
- ## Phase 8: Pre-Report Quality Review
1807
-
1808
- **CRITICAL**: Before creating the final report, you MUST perform a comprehensive quality review.
1809
-
1810
- **📖 For the complete quality review process with all steps, checklists, and examples, see: `quality-review.md`**
1811
-
1812
- **Quick overview - 7 steps:**
1813
-
1814
- 1. **Identify All Changes**: Use git to identify all created/modified files
1815
- 2. **Test Management**:
1816
- - Analyze existing tests FIRST (understand TestHelper, patterns, permissions)
1817
- - Create new test files for newly created modules (in `tests/modules/`)
1818
- - Update existing test files for modified modules
1819
- - Follow exact patterns from existing tests
1820
- 3. **Compare with Existing Code**: Check consistency (style, structure, naming)
1821
- 4. **Critical Analysis**: Verify style, structure, code quality, best practices
1822
- 5. **Automated Optimizations**: Fix import ordering, property ordering, formatting, descriptions
1823
- 6. **Pre-Report Testing**: Run build, lint, and all tests (must all pass!)
1824
- 7. **Final Verification**: Complete checklist before proceeding to Final Report
1825
-
1826
- **Critical reminders:**
1827
- - [ ] **TestHelper thoroughly understood** (read source code, understand graphQl() and rest() methods)
1828
- - [ ] **Existing tests analyzed** BEFORE creating new tests
1829
- - [ ] **Permission system understood** (3 layers: Controller @Roles, Service options, Model securityCheck)
1830
- - [ ] **Tests in correct location** (tests/modules/, tests/common.e2e-spec.ts, or tests/project.e2e-spec.ts)
1831
- - [ ] **All tests pass** before reporting
1832
-
1833
- **Permission testing approach:**
1834
- - Admin users: `user.roles.includes('admin')`
1835
- - Creators: `user.id === object.createdBy`
1836
- - Always test with appropriate user (creator for update/delete, admin for everything)
1837
- - Test security failures (403 responses)
1838
-
1839
- **Only after ALL checks pass, proceed to Final Report.**
1840
-
1841
- ## Final Report
1842
-
1843
- After completing all tasks, provide:
1844
-
1845
- 1. **Summary of created components**:
1846
- - Number of SubObjects created
1847
- - Number of Objects created
1848
- - Number of Modules created
1849
- - Number of enum files created
1850
- - Number of test files created
1851
-
1852
- 2. **Observations about data structure**:
1853
- - Unusual patterns
1854
- - Potential improvements
1855
- - Missing relationships
1856
- - Optimization suggestions
1857
-
1858
- 3. **Test results**:
1859
- - All tests passing
1860
- - Any failures and reasons
1861
-
1862
- 4. **Next steps**:
1863
- - Manual adjustments needed
1864
- - Additional features to consider
1865
-
1866
- ## Best Practices
1867
-
1868
- 1. **Always create dependencies first** (SubObjects before Modules that use them)
1869
- 2. **Check for circular dependencies** in object relationships
1870
- 3. **Use meaningful enum names** that match the domain
1871
- 4. **Keep descriptions concise** but informative
1872
- 5. **Test incrementally** (don't wait until all modules are created)
1873
- 6. **Commit after each major component** (SubObjects, then Modules, then Tests)
1874
- 7. **Use appropriate controller types** (Rest for simple CRUD, GraphQL for complex queries, Both for flexibility)
1875
- 8. **Validate required fields** in tests to ensure data integrity
1876
- 9. **Clean up test data** to avoid database pollution
1877
- 10. **Document complex relationships** in code comments
1878
-
1879
- ## Working with This Skill
1880
-
1881
- When you receive a specification:
1882
-
1883
- 1. **Parse completely** before starting any generation
1884
- 2. **Ask clarifying questions** if specification is ambiguous
1885
- 3. **Create detailed todo list** showing all steps
1886
- 4. **Execute systematically** following the workflow
1887
- 5. **Verify each step** before moving to next
1888
- 6. **Report progress** using todo updates
1889
- 7. **Provide comprehensive summary** at the end
1890
-
1891
- This skill ensures complete, production-ready NestJS backend structures are generated efficiently and correctly from complex specifications.