Unit Testing Skill Unit testing validates individual functions, methods, and classes in isolation by mocking all external dependencies. --- Workflows For guided, step-by-step execution of unit testing tasks, use the appropriate workflow: | Workflow | Purpose | When to Use | |----------|---------|-------------| | Setup | Initialize test infrastructure | New project or missing test setup | | Writing | Write new unit tests | Creating tests for components | | Reviewing | Review existing tests | Code review, quality audit | | Running | Execute tests | Running tests, analyzing results | | Debugging…

: ['ts-jest', { isolatedModules: true }],\n },\n\n // Skip unnecessary transformations\n transformIgnorePatterns: [\n 'node_modules/(?!(@your-org)/)',\n ],\n\n // Filter test discovery\n testMatch: ['**/*.spec.ts'],\n testPathIgnorePatterns: ['/node_modules/', '/dist/', '/build/'],\n\n // Module resolution\n moduleNameMapper: {\n '^src/(.*)

Unit Testing Skill Unit testing validates individual functions, methods, and classes in isolation by mocking all external dependencies. --- Workflows For guided, step-by-step execution of unit testing tasks, use the appropriate workflow: | Workflow | Purpose | When to Use | |----------|---------|-------------| | Setup | Initialize test infrastructure | New project or missing test setup | | Writing | Write new unit tests | Creating tests for components | | Reviewing | Review existing tests | Code review, quality audit | | Running | Execute tests | Running tests, analyzing results | | Debugging…

: '\u003crootDir>/src/$1',\n },\n\n // Jest 30: Enable globals cleanup\n testEnvironmentOptions: {\n globalsCleanup: 'on',\n },\n};\n\nexport default config;\n```\n\n## Barrel File Optimization\n\nBarrel files (`index.ts` that re-export modules) cause Jest to load entire dependency trees for each test.\n\n### Problem\n\n```typescript\n// src/index.ts (barrel file)\nexport * from './user';\nexport * from './order';\nexport * from './payment';\n// ... 50 more exports\n\n// test file imports one thing, loads everything\nimport { UserService } from 'src';\n```\n\n### Solutions\n\n**Option 1: Direct imports**\n\n```typescript\n// Instead of\nimport { UserService } from 'src';\n\n// Use direct import\nimport { UserService } from 'src/user/user.service';\n```\n\n**Option 2: Babel plugins**\n\n```bash\nnpm install --save-dev babel-plugin-transform-barrels\n# or\nnpm install --save-dev babel-jest-boost\n```\n\n```javascript\n// babel.config.js\nmodule.exports = {\n plugins: ['babel-plugin-transform-barrels'],\n};\n```\n\n**Option 3: ESLint rule**\n\n```bash\nnpm install --save-dev eslint-plugin-no-barrel-files\n```\n\n**Potential improvement:** Up to **100x faster** test execution.\n\n## Test Isolation Optimization\n\n### Share Expensive Setup\n\n```typescript\n// SLOW: Compiles module for every test\ndescribe('Service', () => {\n beforeEach(async () => {\n const module = await Test.createTestingModule({\n providers: [Service, ...manyProviders],\n }).compile();\n });\n});\n\n// FAST: Compiles module once\ndescribe('Service', () => {\n let module: TestingModule;\n\n beforeAll(async () => {\n module = await Test.createTestingModule({\n providers: [Service, ...manyProviders],\n }).compile();\n });\n\n beforeEach(() => {\n jest.clearAllMocks();\n });\n\n afterAll(async () => {\n await module.close();\n });\n});\n```\n\n### Minimize Module Imports\n\n```typescript\n// SLOW: Imports entire AppModule\nconst module = await Test.createTestingModule({\n imports: [AppModule],\n}).compile();\n\n// FAST: Only import what's needed\nconst module = await Test.createTestingModule({\n providers: [\n TargetService,\n { provide: DependencyA, useValue: mockA },\n { provide: DependencyB, useValue: mockB },\n ],\n}).compile();\n```\n\n## Timer Optimization\n\n### Use Fake Timers\n\n```typescript\n// SLOW: Waits real time\nit('retries after delay', async () => {\n const result = await target.retryWithDelay(); // waits 1 second\n}, 5000);\n\n// FAST: Mocks time\nit('retries after delay', async () => {\n jest.useFakeTimers();\n\n const promise = target.retryWithDelay();\n jest.advanceTimersByTime(1000);\n const result = await promise;\n\n jest.useRealTimers();\n});\n```\n\n## Transform Optimization\n\n### Enable ts-jest Isolated Modules\n\n```typescript\n// jest.config.ts\n{\n transform: {\n '^.+\\\\.(t|j)s

Unit Testing Skill Unit testing validates individual functions, methods, and classes in isolation by mocking all external dependencies. --- Workflows For guided, step-by-step execution of unit testing tasks, use the appropriate workflow: | Workflow | Purpose | When to Use | |----------|---------|-------------| | Setup | Initialize test infrastructure | New project or missing test setup | | Writing | Write new unit tests | Creating tests for components | | Reviewing | Review existing tests | Code review, quality audit | | Running | Execute tests | Running tests, analyzing results | | Debugging…

: ['ts-jest', {\n isolatedModules: true, // Faster compilation\n }],\n },\n}\n```\n\n### Use SWC for Faster Transforms\n\n```bash\nnpm install --save-dev @swc/core @swc/jest\n```\n\n```typescript\n// jest.config.ts\n{\n transform: {\n '^.+\\\\.(t|j)s

Unit Testing Skill Unit testing validates individual functions, methods, and classes in isolation by mocking all external dependencies. --- Workflows For guided, step-by-step execution of unit testing tasks, use the appropriate workflow: | Workflow | Purpose | When to Use | |----------|---------|-------------| | Setup | Initialize test infrastructure | New project or missing test setup | | Writing | Write new unit tests | Creating tests for components | | Reviewing | Review existing tests | Code review, quality audit | | Running | Execute tests | Running tests, analyzing results | | Debugging…

: '@swc/jest',\n },\n}\n```\n\n## CI Optimization\n\n### Test Sharding\n\nSplit tests across multiple CI runners:\n\n```yaml\n# GitHub Actions example\njobs:\n test:\n strategy:\n matrix:\n shard: [1, 2, 3, 4]\n steps:\n - run: npm test -- --shard=${{ matrix.shard }}/4\n```\n\n```bash\n# Manual sharding\njest --shard=1/4 # Run first quarter\njest --shard=2/4 # Run second quarter\njest --shard=3/4 # Run third quarter\njest --shard=4/4 # Run fourth quarter\n```\n\n**Best practice:** Match shards to available parallel runners.\n\n### CI-Specific Configuration\n\n```typescript\n// jest.config.ts\nconst isCI = process.env.CI === 'true';\n\nexport default {\n maxWorkers: isCI ? 1 : '50%',\n // --runInBand equivalent for CI\n};\n```\n\n## Memory Optimization\n\n### Jest 30 Globals Cleanup\n\n```typescript\n// jest.config.ts\n{\n testEnvironmentOptions: {\n globalsCleanup: 'on', // Clean globals between test files\n },\n}\n```\n\nReal-world results with globals cleanup:\n- **37% faster** test runs\n- **77% less memory** usage\n\n### Identify Memory Leaks\n\n```bash\n# Run with memory debugging\nnode --expose-gc node_modules/.bin/jest --runInBand --logHeapUsage\n```\n\n## Identifying Slow Tests\n\n### Use Slow Test Reporter\n\n```bash\nnpm install --save-dev jest-slow-test-reporter\n```\n\n```typescript\n// jest.config.ts\n{\n reporters: [\n 'default',\n ['jest-slow-test-reporter', { numTests: 10, warnOnSlowerThan: 300 }],\n ],\n}\n```\n\n### Built-in Timing\n\n```bash\n# Verbose output includes timing (no console output)\nnpm test -- --verbose > /tmp/ut-${UT_SESSION}-timing.log 2>&1\ntail -50 /tmp/ut-${UT_SESSION}-timing.log\n\n# Find slowest tests\nsort -t'(' -k2 -rn /tmp/ut-${UT_SESSION}-timing.log | head -20\n```\n\n## Performance Checklist\n\n**Configuration:**\n- [ ] `maxWorkers` set appropriately for environment\n- [ ] Caching enabled\n- [ ] `testEnvironment: 'node'` (unless testing DOM)\n- [ ] `isolatedModules: true` for ts-jest\n- [ ] `testPathIgnorePatterns` excludes unnecessary paths\n\n**Code:**\n- [ ] No barrel file imports in tests\n- [ ] Expensive setup in `beforeAll`, not `beforeEach`\n- [ ] Minimal module imports (mock dependencies)\n- [ ] Fake timers instead of real delays\n- [ ] No unnecessary async/await\n\n**CI:**\n- [ ] Consider `--runInBand` for CI\n- [ ] Test sharding for large suites\n- [ ] Appropriate timeout configuration\n\n**Memory:**\n- [ ] Globals cleanup enabled (Jest 30)\n- [ ] Resources cleaned up in `afterAll`\n- [ ] No open handles (tests exit cleanly)\n\n## Quick Diagnosis\n\n**Redirect to temp files only (no console output).** Use unique session ID.\n\n```bash\n# Initialize session (once at start)\nexport UT_SESSION=$(date +%s)-$\n\n# Check overall timing (no console output)\nnpm test -- --verbose > /tmp/ut-${UT_SESSION}-perf.log 2>&1\ntail -50 /tmp/ut-${UT_SESSION}-perf.log\n\n# Check for slow tests\ngrep -E \"^\\s+✓.*\\(\\d+.*ms\\)\" /tmp/ut-${UT_SESSION}-perf.log | sort -t'(' -k2 -rn | head -10\n\n# Check for open handles (no console output)\nnpm test -- --detectOpenHandles > /tmp/ut-${UT_SESSION}-handles.log 2>&1\ntail -100 /tmp/ut-${UT_SESSION}-handles.log\n\n# Check memory usage (no console output)\nnode --expose-gc node_modules/.bin/jest --runInBand --logHeapUsage > /tmp/ut-${UT_SESSION}-memory.log 2>&1\ntail -50 /tmp/ut-${UT_SESSION}-memory.log\n\n# Cleanup\nrm -f /tmp/ut-${UT_SESSION}-*.log\n```\n\n## References\n\n- [Jest 30 Release Blog](https://jestjs.io/blog/2025/06/04/jest-30)\n- [Jest CLI Options](https://jestjs.io/docs/cli)\n- [Make Jest 20% Faster](https://ivantanev.com/make-jest-faster/)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":8359,"content_sha256":"a71d733b5b615c6b558c6353736ee2458f67e76daa57761e7885540ded570e8c"},{"filename":"references/common/rules.md","content":"# Unit Testing Rules\n\n## Context Efficiency Rule\n\n**ALWAYS redirect unit test output to temp files**. Test output can be verbose and bloats agent context.\n\n**IMPORTANT**: Use unique session ID in filenames to prevent conflicts when multiple agents run.\n\n```bash\n# Initialize session (once at start of testing session)\nexport UT_SESSION=$(date +%s)-$\n\n# Standard pattern - redirect to temp file only (no console output)\nnpm test > /tmp/ut-${UT_SESSION}-output.log 2>&1\n\n# Read summary only (last 50 lines)\ntail -50 /tmp/ut-${UT_SESSION}-output.log\n\n# Get failure details\ngrep -B 2 -A 15 \"FAIL\\|✕\" /tmp/ut-${UT_SESSION}-output.log\n\n# Cleanup when done\nrm -f /tmp/ut-${UT_SESSION}-*.log /tmp/ut-${UT_SESSION}-*.md\n```\n\n**Temp File Locations** (with `${UT_SESSION}` unique per agent):\n- `/tmp/ut-${UT_SESSION}-output.log` - Full test output\n- `/tmp/ut-${UT_SESSION}-failures.md` - Tracking file for one-by-one fixing\n- `/tmp/ut-${UT_SESSION}-debug.log` - Debug runs\n- `/tmp/ut-${UT_SESSION}-verify.log` - Verification runs\n- `/tmp/ut-${UT_SESSION}-coverage.log` - Coverage output\n\n---\n\n## One-by-One Fixing Rule\n\n**CRITICAL: When tests fail, NEVER keep running the full suite.**\n\n```\n❌ WRONG: Run full suite → See 5 failures → Run full suite again → Still 5 failures → ...\n✅ RIGHT: Run full suite → See 5 failures → Fix test 1 only → Verify → Fix test 2 only → ... → Run full suite ONCE\n```\n\n**WHY**: Full suite runs waste time and pollute output. Each failing test adds noise, making debugging harder.\n\n### Protocol:\n1. Run full suite ONCE to identify all failures\n2. Create tracking file `/tmp/ut-${UT_SESSION}-failures.md` listing all failing tests\n3. Fix ONE test at a time using `-t \"test name\"`\n4. Verify each fix with 3-5 runs of that specific test\n5. Mark as FIXED in tracking file\n6. Move to next failing test\n7. Run full suite ONLY ONCE after ALL individual tests pass\n\n---\n\n## Core Rules\n\n| Rule | Requirement |\n|------|-------------|\n| Variable for SUT | Use `target` (MANDATORY) |\n| Mock prefix | `mockServiceName` |\n| Mock type | `DeepMocked\u003cT>` |\n| Pattern | Arrange-Act-Assert with comments |\n| Coverage | 80%+ for new code |\n| Logger | `.setLogger(new MockLoggerService())` |\n\n## What NOT to Unit Test\n\nDo NOT create unit tests for:\n\n| Type | Reason |\n|------|--------|\n| **Interfaces** | Type definitions only, no runtime behavior |\n| **Enums** | Static value mappings, no logic to test |\n| **Constants** | Static values, no behavior |\n| **Type aliases** | Type definitions only |\n| **DTOs** (plain) | Data structures without logic |\n\nOnly test files containing **executable logic** (classes with methods, functions with behavior).\n\n## AAA Pattern (Mandatory)\n\nALL unit tests MUST follow Arrange-Act-Assert:\n\n```typescript\nit('should return user when found', async () => {\n // Arrange\n const userId = 'user-123';\n mockRepository.findById.mockResolvedValue({\n id: userId,\n email: '[email protected]',\n });\n\n // Act\n const result = await target.getUser(userId);\n\n // Assert\n expect(result).toEqual({ id: userId, email: '[email protected]' });\n expect(mockRepository.findById).toHaveBeenCalledWith(userId);\n});\n```\n\n### Formatting Guidelines\n\n| Test Length | Format |\n|-------------|--------|\n| \u003c= 3 lines | No blank lines needed |\n| > 3 lines | Blank line between phases |\n| Complex phases | Comment labels for each phase |\n\n## Naming Conventions\n\n### Test Files\n- Pattern: `*.spec.ts`\n- Location: Co-located with source file\n\n### Test Structure\n```typescript\ndescribe('ClassName', () => {\n describe('methodName', () => {\n it('should [expected behavior] when [condition]', () => {});\n });\n});\n```\n\n### Test Naming Pattern\n`should [verb] [expected] when [condition]`\n\n```typescript\ndescribe('UserService', () => {\n describe('findById', () => {\n it('should return user when user exists', async () => {...});\n it('should return null when user not found', async () => {...});\n it('should throw NotFoundException when id is invalid', async () => {...});\n });\n});\n```\n\n## Variable Naming\n\n| Variable | Convention | Example |\n|----------|------------|---------|\n| SUT | `target` | `let target: UserService;` |\n| Mocks | `mock` prefix | `let mockRepository: DeepMocked\u003cUserRepository>;` |\n| Mock Type | `DeepMocked\u003cT>` | `DeepMocked\u003cUserRepository>` |\n| Test Module | `module` | `let module: TestingModule;` |\n\n## Setup Requirements\n\n```typescript\nbeforeEach(async () => {\n // 1. Create mocks\n mockRepository = createMock\u003cUserRepository>();\n\n // 2. Create testing module\n const module: TestingModule = await Test.createTestingModule({\n providers: [\n UserService,\n { provide: UserRepository, useValue: mockRepository },\n ],\n })\n .setLogger(new MockLoggerService()) // REQUIRED\n .compile();\n\n // 3. Get target\n target = module.get\u003cUserService>(UserService);\n});\n\nafterEach(() => {\n jest.clearAllMocks(); // REQUIRED\n});\n```\n\n## Test Isolation Rules\n\n1. **Fresh mocks per test**: Create mocks in `beforeEach`\n2. **Clear mocks after**: Use `jest.clearAllMocks()` in `afterEach`\n3. **No shared state**: Never share mutable state between tests\n4. **Deterministic results**: Tests must produce same results on every run\n\n## Coverage Requirements\n\n| Category | Priority | Must Test |\n|----------|----------|-----------|\n| Happy path | MANDATORY | All public methods |\n| Edge cases | MANDATORY | Empty, null, boundaries |\n| Error cases | MANDATORY | Not found, validation |\n| Exceptions | MANDATORY | Type, code, message |\n| Business rules | MANDATORY | Domain logic |\n\n**Coverage Target:** 80%+ for new code\n\n## Mock Rules\n\n### Always verify mock calls:\n```typescript\nexpect(mockRepository.save).toHaveBeenCalledWith({\n email: '[email protected]',\n name: 'John',\n});\nexpect(mockRepository.save).toHaveBeenCalledTimes(1);\n```\n\n### Never mock internal implementation:\n```typescript\n// BAD - Mocking internal mapper\nexpect(mockMapper.toEntity).toHaveBeenCalled();\n\n// GOOD - Verify behavior\nexpect(result).toEqual(expectedUser);\nexpect(mockRepository.save).toHaveBeenCalledWith(\n expect.objectContaining({ email: input.email })\n);\n```\n\n## Checklist\n\n**Setup:**\n- [ ] Use `target` for system under test\n- [ ] Use `mock` prefix for all mocks\n- [ ] Use `DeepMocked\u003cT>` type\n- [ ] Include `.setLogger(new MockLoggerService())`\n- [ ] Follow AAA pattern with comments\n- [ ] Direct mock methods, not `jest.spyOn()`\n- [ ] Reset mocks in `afterEach`\n\n**Test Coverage (MANDATORY):**\n- [ ] Happy path tests for all public methods\n- [ ] Edge case tests (empty inputs, null values, boundaries)\n- [ ] Error case tests (not found, validation failures)\n- [ ] Exception behavior tests (correct type, error code, message)\n- [ ] Business rule tests (domain logic, calculations)\n\n**Assertions:**\n- [ ] Assert specific values, not just types or existence\n- [ ] Validate ALL result properties with expected values\n- [ ] Verify mock functions called with correct parameters\n- [ ] Check mock call counts (`.toHaveBeenCalledTimes()`)\n- [ ] No conditional assertions (no `if` statements)\n- [ ] Tests fail when any field differs from expectations\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":7103,"content_sha256":"d2316c455ade6acf0016706bac60c750874de366cb65b84548a5814f5d71593a"},{"filename":"references/kafka/kafka.md","content":"# NestJS Kafka Unit Testing Patterns\n\n## Overview\n\nUnit testing Kafka producers and consumers using `@nestjs/microservices` ClientKafka/ClientProxy by mocking the Kafka client without real connections.\n\n## When to Use\n\n| Test Type | Approach |\n|-----------|----------|\n| **Service unit tests** | Mock ClientKafka/ClientProxy |\n| **Message handler tests** | Test @MessagePattern/@EventPattern handlers directly |\n| **Producer tests** | Mock emit() and send() methods |\n| **E2E tests** | Real Kafka via Docker |\n\n## Key Libraries\n\n```json\n{\n \"@golevelup/ts-jest\": \"^0.4.0\",\n \"@nestjs/microservices\": \"^11.0.0\",\n \"@nestjs/testing\": \"^11.0.12\",\n \"jest\": \"^29.7.0\"\n}\n```\n\n---\n\n## Standard Producer Service Test Template\n\n```typescript\nimport { Test, TestingModule } from '@nestjs/testing';\nimport { createMock, DeepMocked } from '@golevelup/ts-jest';\nimport { ClientKafka } from '@nestjs/microservices';\nimport { MockLoggerService } from 'src/shared/logger/services/mock-logger.service';\nimport { of } from 'rxjs';\n\ndescribe('EventProducerService', () => {\n let target: EventProducerService;\n let mockKafkaClient: DeepMocked\u003cClientKafka>;\n\n beforeEach(async () => {\n mockKafkaClient = createMock\u003cClientKafka>();\n\n const module: TestingModule = await Test.createTestingModule({\n providers: [\n EventProducerService,\n { provide: 'KAFKA_CLIENT', useValue: mockKafkaClient },\n ],\n })\n .setLogger(new MockLoggerService())\n .compile();\n\n target = module.get\u003cEventProducerService>(EventProducerService);\n });\n\n afterEach(() => {\n jest.clearAllMocks();\n });\n});\n```\n\n---\n\n## Testing Producer emit() (Fire-and-Forget)\n\n### Basic Event Emission\n\n```typescript\ndescribe('emit', () => {\n it('should emit event to Kafka topic', async () => {\n // Arrange\n const topic = 'user.created';\n const payload = {\n userId: 'user-123',\n email: '[email protected]',\n timestamp: new Date().toISOString(),\n };\n mockKafkaClient.emit.mockReturnValue(of(undefined));\n\n // Act\n await target.emitUserCreated(payload);\n\n // Assert\n expect(mockKafkaClient.emit).toHaveBeenCalledWith(topic, payload);\n expect(mockKafkaClient.emit).toHaveBeenCalledTimes(1);\n });\n\n it('should emit event with message key', async () => {\n // Arrange\n const topic = 'order.updated';\n const key = 'order-456';\n const payload = { orderId: key, status: 'shipped' };\n mockKafkaClient.emit.mockReturnValue(of(undefined));\n\n // Act\n await target.emitOrderUpdate(key, payload);\n\n // Assert\n expect(mockKafkaClient.emit).toHaveBeenCalledWith(topic, {\n key,\n value: payload,\n });\n });\n\n it('should handle emit error gracefully', async () => {\n // Arrange\n const error = new Error('Kafka connection failed');\n mockKafkaClient.emit.mockReturnValue({\n subscribe: ({ error: errorFn }) => {\n errorFn(error);\n return { unsubscribe: jest.fn() };\n },\n } as any);\n\n // Act & Assert\n await expect(target.emitUserCreated({ userId: '123' }))\n .resolves.not.toThrow();\n });\n});\n```\n\n### Testing Observable-Based emit()\n\n```typescript\ndescribe('emit with Observable', () => {\n it('should complete Observable on successful emit', (done) => {\n // Arrange\n const payload = { userId: 'user-123' };\n mockKafkaClient.emit.mockReturnValue(of(undefined));\n\n // Act\n const result$ = target.emitAsync(payload);\n\n // Assert\n result$.subscribe({\n complete: () => {\n expect(mockKafkaClient.emit).toHaveBeenCalled();\n done();\n },\n });\n });\n\n it('should convert Observable to Promise with lastValueFrom', async () => {\n // Arrange\n const payload = { orderId: 'order-123' };\n mockKafkaClient.emit.mockReturnValue(of({ success: true }));\n\n // Act\n const result = await target.emitAndWait(payload);\n\n // Assert\n expect(result).toEqual({ success: true });\n });\n});\n```\n\n---\n\n## Testing Producer send() (Request-Response)\n\n```typescript\ndescribe('send', () => {\n it('should send message and receive response', async () => {\n // Arrange\n const pattern = 'get.user.profile';\n const payload = { userId: 'user-123' };\n const expectedResponse = {\n id: 'user-123',\n name: 'John Doe',\n email: '[email protected]',\n };\n mockKafkaClient.send.mockReturnValue(of(expectedResponse));\n\n // Act\n const result = await target.getUserProfile(payload.userId);\n\n // Assert\n expect(result).toEqual(expectedResponse);\n expect(mockKafkaClient.send).toHaveBeenCalledWith(pattern, payload);\n });\n\n it('should handle timeout on send', async () => {\n // Arrange\n mockKafkaClient.send.mockReturnValue(\n new Observable((subscriber) => {\n // Never completes - simulates timeout\n })\n );\n\n // Act & Assert\n await expect(target.getUserProfileWithTimeout('user-123'))\n .rejects.toThrow('Request timeout');\n });\n});\n```\n\n---\n\n## Testing @MessagePattern Handlers\n\n```typescript\nimport { Test, TestingModule } from '@nestjs/testing';\nimport { createMock, DeepMocked } from '@golevelup/ts-jest';\nimport { MockLoggerService } from 'src/shared/logger/services/mock-logger.service';\n\ndescribe('UserMessageHandler', () => {\n let target: UserMessageHandler;\n let mockUserService: DeepMocked\u003cUserService>;\n\n beforeEach(async () => {\n mockUserService = createMock\u003cUserService>();\n\n const module: TestingModule = await Test.createTestingModule({\n providers: [\n UserMessageHandler,\n { provide: UserService, useValue: mockUserService },\n ],\n })\n .setLogger(new MockLoggerService())\n .compile();\n\n target = module.get\u003cUserMessageHandler>(UserMessageHandler);\n });\n\n describe('handleGetUser (@MessagePattern)', () => {\n it('should return user for valid request', async () => {\n // Arrange\n const payload = { userId: 'user-123' };\n const expectedUser = {\n id: 'user-123',\n name: 'John Doe',\n email: '[email protected]',\n };\n mockUserService.findById.mockResolvedValue(expectedUser);\n\n // Act\n const result = await target.handleGetUser(payload);\n\n // Assert\n expect(result).toEqual(expectedUser);\n expect(mockUserService.findById).toHaveBeenCalledWith('user-123');\n });\n\n it('should return null when user not found', async () => {\n // Arrange\n mockUserService.findById.mockResolvedValue(null);\n\n // Act\n const result = await target.handleGetUser({ userId: 'non-existent' });\n\n // Assert\n expect(result).toBeNull();\n });\n\n it('should handle service error', async () => {\n // Arrange\n mockUserService.findById.mockRejectedValue(new Error('Database error'));\n\n // Act & Assert\n await expect(target.handleGetUser({ userId: 'user-123' }))\n .rejects.toThrow('Database error');\n });\n });\n});\n```\n\n---\n\n## Testing @EventPattern Handlers\n\n```typescript\ndescribe('EventHandler', () => {\n let target: OrderEventHandler;\n let mockOrderService: DeepMocked\u003cOrderService>;\n let mockNotificationService: DeepMocked\u003cNotificationService>;\n\n beforeEach(async () => {\n mockOrderService = createMock\u003cOrderService>();\n mockNotificationService = createMock\u003cNotificationService>();\n\n const module: TestingModule = await Test.createTestingModule({\n providers: [\n OrderEventHandler,\n { provide: OrderService, useValue: mockOrderService },\n { provide: NotificationService, useValue: mockNotificationService },\n ],\n })\n .setLogger(new MockLoggerService())\n .compile();\n\n target = module.get\u003cOrderEventHandler>(OrderEventHandler);\n });\n\n describe('handleOrderCreated (@EventPattern)', () => {\n it('should process order created event', async () => {\n // Arrange\n const event = {\n orderId: 'order-123',\n userId: 'user-456',\n items: [{ productId: 'prod-1', quantity: 2 }],\n total: 99.99,\n };\n mockOrderService.processNewOrder.mockResolvedValue(undefined);\n mockNotificationService.sendOrderConfirmation.mockResolvedValue(undefined);\n\n // Act\n await target.handleOrderCreated(event);\n\n // Assert\n expect(mockOrderService.processNewOrder).toHaveBeenCalledWith(event);\n expect(mockNotificationService.sendOrderConfirmation).toHaveBeenCalledWith(\n event.userId,\n event.orderId\n );\n });\n\n it('should handle duplicate event idempotently', async () => {\n // Arrange\n const event = { orderId: 'order-123', userId: 'user-456' };\n mockOrderService.isProcessed.mockResolvedValue(true);\n\n // Act\n await target.handleOrderCreated(event);\n\n // Assert\n expect(mockOrderService.processNewOrder).not.toHaveBeenCalled();\n });\n\n it('should not throw on handler error (fire-and-forget)', async () => {\n // Arrange\n const event = { orderId: 'order-123' };\n mockOrderService.processNewOrder.mockRejectedValue(\n new Error('Processing failed')\n );\n\n // Act & Assert\n await expect(target.handleOrderCreated(event))\n .resolves.not.toThrow();\n });\n });\n});\n```\n\n---\n\n## Testing with @Payload and @Ctx Decorators\n\n```typescript\nimport { KafkaContext } from '@nestjs/microservices';\n\ndescribe('Handler with Context', () => {\n it('should extract payload and context correctly', async () => {\n // Arrange\n const payload = { userId: 'user-123', action: 'login' };\n const mockContext = createMock\u003cKafkaContext>({\n getTopic: jest.fn().mockReturnValue('user.events'),\n getPartition: jest.fn().mockReturnValue(0),\n getMessage: jest.fn().mockReturnValue({\n key: Buffer.from('user-123'),\n value: Buffer.from(JSON.stringify(payload)),\n offset: '100',\n timestamp: Date.now().toString(),\n }),\n });\n\n mockAuditService.log.mockResolvedValue(undefined);\n\n // Act\n await target.handleUserEvent(payload, mockContext);\n\n // Assert\n expect(mockAuditService.log).toHaveBeenCalledWith({\n topic: 'user.events',\n partition: 0,\n payload,\n });\n });\n\n it('should handle message offset for commit tracking', async () => {\n // Arrange\n const mockContext = createMock\u003cKafkaContext>();\n mockContext.getMessage.mockReturnValue({\n offset: '150',\n key: null,\n value: Buffer.from('{}'),\n } as any);\n\n // Act\n await target.handleWithOffset({}, mockContext);\n\n // Assert\n expect(mockOffsetTracker.markProcessed).toHaveBeenCalledWith('150');\n });\n});\n```\n\n---\n\n## Testing ClientProxy (Custom Kafka Client)\n\nFor services extending ClientProxy:\n\n```typescript\ndescribe('CustomKafkaProducer', () => {\n let target: CustomKafkaProducer;\n let mockClientProxy: DeepMocked\u003cClientProxy>;\n\n beforeEach(async () => {\n mockClientProxy = createMock\u003cClientProxy>();\n\n // If testing a service that wraps ClientProxy\n const module: TestingModule = await Test.createTestingModule({\n providers: [\n CustomKafkaProducer,\n { provide: 'KAFKA_SERVICE', useValue: mockClientProxy },\n ],\n }).compile();\n\n target = module.get\u003cCustomKafkaProducer>(CustomKafkaProducer);\n });\n\n it('should connect on module init', async () => {\n // Arrange\n mockClientProxy.connect.mockResolvedValue(undefined);\n\n // Act\n await target.onModuleInit();\n\n // Assert\n expect(mockClientProxy.connect).toHaveBeenCalledTimes(1);\n });\n\n it('should close on module destroy', async () => {\n // Arrange\n mockClientProxy.close.mockResolvedValue(undefined);\n\n // Act\n await target.onModuleDestroy();\n\n // Assert\n expect(mockClientProxy.close).toHaveBeenCalledTimes(1);\n });\n});\n```\n\n---\n\n## Testing Batch Operations\n\n```typescript\ndescribe('Batch emit', () => {\n it('should emit multiple messages in batch', async () => {\n // Arrange\n const events = [\n { userId: 'user-1', action: 'login' },\n { userId: 'user-2', action: 'logout' },\n { userId: 'user-3', action: 'login' },\n ];\n mockKafkaClient.emit.mockReturnValue(of(undefined));\n\n // Act\n await target.emitBatch('user.events', events);\n\n // Assert\n expect(mockKafkaClient.emit).toHaveBeenCalledTimes(events.length);\n events.forEach((event, index) => {\n expect(mockKafkaClient.emit).toHaveBeenNthCalledWith(\n index + 1,\n 'user.events',\n event\n );\n });\n });\n\n it('should continue batch on individual message failure', async () => {\n // Arrange\n const events = [\n { id: '1' },\n { id: '2' },\n { id: '3' },\n ];\n mockKafkaClient.emit\n .mockReturnValueOnce(of(undefined))\n .mockReturnValueOnce(throwError(() => new Error('Failed')))\n .mockReturnValueOnce(of(undefined));\n\n // Act\n const result = await target.emitBatchWithContinue('topic', events);\n\n // Assert\n expect(result.succeeded).toBe(2);\n expect(result.failed).toBe(1);\n expect(mockKafkaClient.emit).toHaveBeenCalledTimes(3);\n });\n});\n```\n\n---\n\n## Testing Serialization\n\n```typescript\ndescribe('Message Serialization', () => {\n it('should serialize message payload correctly', async () => {\n // Arrange\n const payload = {\n id: 'msg-123',\n timestamp: new Date('2024-01-15T10:00:00Z'),\n data: { nested: { value: 'test' } },\n };\n mockKafkaClient.emit.mockReturnValue(of(undefined));\n\n // Act\n await target.emitWithSerialization('topic', payload);\n\n // Assert\n expect(mockKafkaClient.emit).toHaveBeenCalledWith('topic', {\n value: JSON.stringify(payload),\n headers: {\n 'content-type': 'application/json',\n },\n });\n });\n\n it('should handle deserialization in handler', async () => {\n // Arrange - payload comes pre-deserialized by NestJS\n const rawPayload = { userId: 'user-123', action: 'test' };\n mockUserService.process.mockResolvedValue(undefined);\n\n // Act\n await target.handleMessage(rawPayload);\n\n // Assert\n expect(mockUserService.process).toHaveBeenCalledWith(rawPayload);\n });\n});\n```\n\n---\n\n## Testing Error Handling\n\n```typescript\ndescribe('Error Handling', () => {\n it('should retry on transient error', async () => {\n // Arrange\n const payload = { id: 'msg-123' };\n mockKafkaClient.emit\n .mockReturnValueOnce(throwError(() => new Error('Connection lost')))\n .mockReturnValueOnce(throwError(() => new Error('Connection lost')))\n .mockReturnValueOnce(of(undefined));\n\n // Act\n await target.emitWithRetry('topic', payload, { maxRetries: 3 });\n\n // Assert\n expect(mockKafkaClient.emit).toHaveBeenCalledTimes(3);\n });\n\n it('should throw after max retries exceeded', async () => {\n // Arrange\n mockKafkaClient.emit.mockReturnValue(\n throwError(() => new Error('Kafka unavailable'))\n );\n\n // Act & Assert\n await expect(\n target.emitWithRetry('topic', {}, { maxRetries: 3 })\n ).rejects.toThrow('Max retries exceeded');\n expect(mockKafkaClient.emit).toHaveBeenCalledTimes(3);\n });\n\n it('should send to dead letter queue on permanent failure', async () => {\n // Arrange\n const payload = { orderId: 'order-123' };\n const error = new Error('Invalid message format');\n mockOrderService.process.mockRejectedValue(error);\n mockDeadLetterService.send.mockResolvedValue(undefined);\n\n // Act\n await target.handleWithDLQ(payload);\n\n // Assert\n expect(mockDeadLetterService.send).toHaveBeenCalledWith(\n 'orders.dlq',\n { payload, error: error.message }\n );\n });\n});\n```\n\n---\n\n## Testing Module Registration\n\n```typescript\ndescribe('KafkaModule', () => {\n it('should register Kafka client with correct config', async () => {\n // Arrange & Act\n const module: TestingModule = await Test.createTestingModule({\n imports: [\n ClientsModule.register([\n {\n name: 'KAFKA_SERVICE',\n transport: Transport.KAFKA,\n options: {\n client: {\n clientId: 'test-client',\n brokers: ['localhost:9092'],\n },\n consumer: {\n groupId: 'test-group',\n },\n },\n },\n ]),\n ],\n }).compile();\n\n // Assert\n const kafkaClient = module.get('KAFKA_SERVICE');\n expect(kafkaClient).toBeDefined();\n });\n});\n```\n\n---\n\n## Best Practices\n\n1. **Mock ClientKafka/ClientProxy**: Never connect to real Kafka in unit tests\n2. **Test handlers directly**: Call @MessagePattern/@EventPattern handlers as regular methods\n3. **Use Observable testing**: Handle emit() returning Observable properly\n4. **Test idempotency**: Verify handlers handle duplicate messages correctly\n5. **Mock KafkaContext**: Use createMock for context when testing @Ctx decorators\n6. **Test serialization separately**: Verify message format matches expected schema\n7. **Test error handling**: Verify retry logic and dead letter queue behavior\n8. **Test lifecycle hooks**: Verify connect/close in onModuleInit/onModuleDestroy\n\n---\n\n## Common Patterns Quick Reference\n\n| Scenario | Mock Setup |\n|----------|-----------|\n| emit() success | `mockClient.emit.mockReturnValue(of(undefined))` |\n| emit() error | `mockClient.emit.mockReturnValue(throwError(() => new Error()))` |\n| send() response | `mockClient.send.mockReturnValue(of(responseData))` |\n| send() timeout | Return Observable that never completes |\n| KafkaContext | `createMock\u003cKafkaContext>()` with method mocks |\n| Batch emit | Multiple `mockReturnValueOnce` calls |\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":17497,"content_sha256":"068987928819b9f2322ddea3543df83640969a04ba2a542119e22af7af7d2971"},{"filename":"references/mocking/deep-mocked.md","content":"# @golevelup/ts-jest Deep Mocking Patterns\n\n## Overview\n\n`@golevelup/ts-jest` provides `createMock\u003cT>()` for generating deeply nested mock objects with full TypeScript support.\n\n## Installation\n\n```bash\nnpm install -D @golevelup/ts-jest\n```\n\n## Basic Usage\n\n```typescript\nimport { createMock, DeepMocked } from '@golevelup/ts-jest';\n\nlet mockService: DeepMocked\u003cUserService>;\n\nbeforeEach(() => {\n mockService = createMock\u003cUserService>();\n});\n```\n\n## Key Features\n\n| Feature | Description |\n|---------|-------------|\n| `createMock\u003cT>()` | Creates mock with all properties as `jest.fn()` |\n| `DeepMocked\u003cT>` | Type for full TypeScript support on mocks |\n| Strict Mode | `createMock\u003cT>({}, { strict: true })` throws on unstubbed methods |\n| Auto Sub-properties | All nested properties automatically mocked |\n\n## Mock Configuration Patterns\n\n### Simple Mock Return Value\n\n```typescript\nmockRepository.findById.mockResolvedValue({\n id: 'user-123',\n email: '[email protected]',\n});\n```\n\n### Sequential Return Values\n\n```typescript\nmockService.process\n .mockResolvedValueOnce({ success: true })\n .mockResolvedValueOnce({ success: false })\n .mockRejectedValueOnce(new Error('Third call fails'));\n```\n\n### Mock Implementation\n\n```typescript\nmockService.calculate.mockImplementation((a, b) => a + b);\n```\n\n### Async Mock Implementation\n\n```typescript\nmockService.fetch.mockImplementation(async (id) => {\n if (id === 'invalid') return null;\n return { id, name: 'Test' };\n});\n```\n\n## NestJS Integration\n\n### Standard Provider Mocking\n\n```typescript\nimport { Test, TestingModule } from '@nestjs/testing';\nimport { createMock, DeepMocked } from '@golevelup/ts-jest';\n\ndescribe('UserService', () => {\n let target: UserService;\n let mockRepository: DeepMocked\u003cUserRepository>;\n\n beforeEach(async () => {\n mockRepository = createMock\u003cUserRepository>();\n\n const module: TestingModule = await Test.createTestingModule({\n providers: [\n UserService,\n { provide: UserRepository, useValue: mockRepository },\n ],\n }).compile();\n\n target = module.get\u003cUserService>(UserService);\n });\n});\n```\n\n### Auto-Mocking with useMocker (NestJS V8+)\n\n```typescript\nconst module = await Test.createTestingModule({\n providers: [MyService],\n})\n.useMocker(createMock) // Auto-mock all missing dependencies\n.compile();\n```\n\n## Mocking Complex Types\n\n### ExecutionContext (Guards, Interceptors)\n\n```typescript\nconst mockContext = createMock\u003cExecutionContext>();\n\n// Configure HTTP context\nmockContext.switchToHttp().getRequest.mockReturnValue({\n headers: { authorization: 'Bearer token' },\n method: 'GET',\n url: '/users',\n});\n\nmockContext.switchToHttp().getResponse.mockReturnValue({\n status: jest.fn().mockReturnThis(),\n json: jest.fn(),\n});\n```\n\n### CallHandler (Interceptors)\n\n```typescript\nimport { of } from 'rxjs';\n\nconst mockCallHandler = createMock\u003cCallHandler>({\n handle: () => of({ data: 'response' }),\n});\n```\n\n### ArgumentsHost (Filters)\n\n```typescript\nconst mockHost = createMock\u003cArgumentsHost>();\n\nmockHost.switchToHttp().getRequest.mockReturnValue({\n url: '/api/users',\n method: 'POST',\n});\n\nmockHost.switchToHttp().getResponse.mockReturnValue({\n status: jest.fn().mockReturnThis(),\n json: jest.fn(),\n});\n```\n\n### ClientGrpc\n\n```typescript\nconst mockGrpcClient = createMock\u003cClientGrpc>();\nconst mockAuthClient = createMock\u003cAuthServiceClient>();\n\nmockGrpcClient.getService.mockReturnValue(mockAuthClient);\n\nmockAuthClient.validateToken.mockReturnValue(\n of({ valid: true, userId: 'user-123' })\n);\n```\n\n## Strict Mode\n\nThrows error when unstubbed method is called:\n\n```typescript\nconst strictMock = createMock\u003cUserService>({}, { strict: true });\n\n// This will throw because findById wasn't stubbed\nawait strictMock.findById('123');\n// Error: Unmocked method findById was called\n```\n\nUse strict mode when you want to ensure all called methods are explicitly mocked.\n\n## Partial Mocking\n\nProvide partial implementation while auto-mocking the rest:\n\n```typescript\nconst mockService = createMock\u003cUserService>({\n findById: jest.fn().mockResolvedValue({ id: '123', name: 'John' }),\n // Other methods are auto-mocked\n});\n```\n\n## Nested Object Mocking\n\nDeep properties are automatically mocked:\n\n```typescript\nconst mockService = createMock\u003cComplexService>();\n\n// Nested properties work automatically\nmockService.config.database.connection.pool.size = 10;\nmockService.config.database.connect.mockResolvedValue(true);\n```\n\n## Verifying Mock Calls\n\n```typescript\n// Verify call happened\nexpect(mockService.findById).toHaveBeenCalled();\n\n// Verify specific arguments\nexpect(mockService.findById).toHaveBeenCalledWith('user-123');\n\n// Verify call count\nexpect(mockService.findById).toHaveBeenCalledTimes(1);\n\n// Verify with partial matching\nexpect(mockService.create).toHaveBeenCalledWith(\n expect.objectContaining({ email: '[email protected]' })\n);\n\n// Verify order of calls\nexpect(mockService.save).toHaveBeenCalledBefore(mockService.notify);\n```\n\n## Resetting Mocks\n\n```typescript\nafterEach(() => {\n jest.clearAllMocks(); // Clears call history, keeps implementation\n});\n\n// Or reset to jest.fn()\nafterEach(() => {\n jest.resetAllMocks(); // Clears history + resets implementation\n});\n```\n\n## Best Practices\n\n1. **Always use DeepMocked\u003cT>** for type safety\n2. **Create mocks in beforeEach** for fresh state\n3. **Clear mocks in afterEach** to prevent test pollution\n4. **Use meaningful variable names** with `mock` prefix\n5. **Prefer createMock over manual mocking** for complex interfaces\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":5514,"content_sha256":"d290faeeecfb6cd68aa523f6f0f5b89caad3913498102025dc363b8966faf37e"},{"filename":"references/mocking/factories.md","content":"# Test Data Factory Patterns\n\n## Why Use Factories?\n\n- **DRY**: Avoid repeating test data creation\n- **Type Safety**: Full TypeScript support with overrides\n- **Maintainability**: Single place to update when model changes\n- **Clarity**: Tests focus on what's different, not boilerplate\n\n## Pattern 1: Simple Factory Functions\n\n### Basic Factory\n\n```typescript\n// test/factories/user.factory.ts\nimport { User } from '../../src/domain/entities/user.entity';\n\nexport function createUserFixture(overrides?: Partial\u003cUser>): User {\n return {\n id: 'user-123',\n email: '[email protected]',\n name: 'Test User',\n role: 'user',\n isActive: true,\n createdAt: new Date('2025-01-01'),\n updatedAt: new Date('2025-01-01'),\n ...overrides,\n };\n}\n```\n\n### Usage in Tests\n\n```typescript\nimport { createUserFixture } from 'test/factories/user.factory';\n\ndescribe('UserService', () => {\n it('should return admin user', async () => {\n // Arrange\n const adminUser = createUserFixture({ role: 'admin' });\n mockRepository.findById.mockResolvedValue(adminUser);\n\n // Act\n const result = await target.findById('user-123');\n\n // Assert\n expect(result.role).toBe('admin');\n });\n\n it('should handle inactive user', async () => {\n // Arrange\n const inactiveUser = createUserFixture({ isActive: false });\n mockRepository.findById.mockResolvedValue(inactiveUser);\n\n // Act & Assert\n await expect(target.findById('user-123')).rejects.toThrow('User is inactive');\n });\n});\n```\n\n## Pattern 2: Factory with Counter\n\nEnsures unique IDs across tests:\n\n```typescript\n// test/factories/user.factory.ts\nlet counter = 0;\n\nexport function createUserFixture(overrides?: Partial\u003cUser>): User {\n counter++;\n return {\n id: `user-${counter}`,\n email: `user${counter}@example.com`,\n name: `Test User ${counter}`,\n role: 'user',\n isActive: true,\n createdAt: new Date(),\n updatedAt: new Date(),\n ...overrides,\n };\n}\n\nexport function resetUserCounter(): void {\n counter = 0;\n}\n```\n\n### Usage with Counter Reset\n\n```typescript\nimport { createUserFixture, resetUserCounter } from 'test/factories/user.factory';\n\nbeforeEach(() => {\n resetUserCounter();\n});\n\nit('should create unique users', () => {\n const user1 = createUserFixture();\n const user2 = createUserFixture();\n\n expect(user1.id).toBe('user-1');\n expect(user2.id).toBe('user-2');\n});\n```\n\n## Pattern 3: Factory with Builder Pattern\n\nFor complex objects with many optional fields:\n\n```typescript\n// test/factories/order.factory.ts\nexport class OrderFixtureBuilder {\n private order: Order = {\n id: 'order-123',\n userId: 'user-123',\n items: [],\n status: 'pending',\n total: 0,\n createdAt: new Date(),\n };\n\n withId(id: string): OrderFixtureBuilder {\n this.order.id = id;\n return this;\n }\n\n withItems(items: OrderItem[]): OrderFixtureBuilder {\n this.order.items = items;\n this.order.total = items.reduce((sum, item) => sum + item.price * item.quantity, 0);\n return this;\n }\n\n withStatus(status: OrderStatus): OrderFixtureBuilder {\n this.order.status = status;\n return this;\n }\n\n paid(): OrderFixtureBuilder {\n this.order.status = 'paid';\n this.order.paidAt = new Date();\n return this;\n }\n\n shipped(): OrderFixtureBuilder {\n this.order.status = 'shipped';\n this.order.shippedAt = new Date();\n return this;\n }\n\n build(): Order {\n return { ...this.order };\n }\n}\n\nexport function orderBuilder(): OrderFixtureBuilder {\n return new OrderFixtureBuilder();\n}\n```\n\n### Usage\n\n```typescript\nimport { orderBuilder } from 'test/factories/order.factory';\n\nit('should process paid order', async () => {\n // Arrange\n const order = orderBuilder()\n .withItems([\n { productId: 'prod-1', quantity: 2, price: 100 },\n ])\n .paid()\n .build();\n\n mockOrderRepository.findById.mockResolvedValue(order);\n\n // Act\n const result = await target.processOrder('order-123');\n\n // Assert\n expect(result.status).toBe('processing');\n});\n```\n\n## Pattern 4: Related Entity Factories\n\nFor entities with relationships:\n\n```typescript\n// test/factories/index.ts\nexport function createUserWithOrders(\n userOverrides?: Partial\u003cUser>,\n orderCount: number = 1\n): { user: User; orders: Order[] } {\n const user = createUserFixture(userOverrides);\n\n const orders = Array.from({ length: orderCount }, (_, i) =>\n createOrderFixture({\n id: `order-${i + 1}`,\n userId: user.id,\n })\n );\n\n return { user, orders };\n}\n```\n\n### Usage\n\n```typescript\nit('should calculate total spend for user', async () => {\n // Arrange\n const { user, orders } = createUserWithOrders(\n { id: 'user-123' },\n 3 // Create 3 orders\n );\n\n mockUserRepository.findById.mockResolvedValue(user);\n mockOrderRepository.findByUserId.mockResolvedValue(orders);\n\n // Act\n const result = await target.getTotalSpend('user-123');\n\n // Assert\n expect(result).toBe(orders.reduce((sum, o) => sum + o.total, 0));\n});\n```\n\n## Pattern 5: DTO Factories\n\nFor request/response objects:\n\n```typescript\n// test/factories/dto.factory.ts\nexport function createUserDtoFixture(overrides?: Partial\u003cCreateUserDto>): CreateUserDto {\n return {\n email: '[email protected]',\n name: 'Test User',\n password: 'SecurePass123!',\n ...overrides,\n };\n}\n\nexport function createUpdateUserDtoFixture(overrides?: Partial\u003cUpdateUserDto>): UpdateUserDto {\n return {\n name: 'Updated Name',\n ...overrides,\n };\n}\n\nexport function createPaginationQueryFixture(overrides?: Partial\u003cPaginationQueryDto>): PaginationQueryDto {\n return {\n page: 1,\n limit: 10,\n sortBy: 'createdAt',\n order: 'desc',\n ...overrides,\n };\n}\n```\n\n## Pattern 6: Mock Response Factories\n\nFor external service responses:\n\n```typescript\n// test/factories/external.factory.ts\nexport function createAuthResponseFixture(overrides?: Partial\u003cAuthResponse>): AuthResponse {\n return {\n accessToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.test',\n refreshToken: 'refresh-token-123',\n expiresIn: 3600,\n tokenType: 'Bearer',\n ...overrides,\n };\n}\n\nexport function createPaymentResponseFixture(overrides?: Partial\u003cPaymentResponse>): PaymentResponse {\n return {\n transactionId: 'tx-123456',\n status: 'success',\n amount: 100,\n currency: 'USD',\n processedAt: new Date(),\n ...overrides,\n };\n}\n```\n\n## Factory Organization\n\n### Directory Structure\n\n```\ntest/\n├── factories/\n│ ├── index.ts # Barrel export\n│ ├── user.factory.ts\n│ ├── order.factory.ts\n│ ├── dto.factory.ts\n│ └── external.factory.ts\n└── helpers/\n```\n\n### Barrel Export\n\n```typescript\n// test/factories/index.ts\nexport * from './user.factory';\nexport * from './order.factory';\nexport * from './dto.factory';\nexport * from './external.factory';\n```\n\n### Import in Tests\n\n```typescript\nimport {\n createUserFixture,\n createOrderFixture,\n createUserDtoFixture,\n} from 'test/factories';\n```\n\n## Best Practices\n\n1. **Default to Valid State**: Factories should create valid objects by default\n2. **Use Sensible Defaults**: Defaults should pass validation\n3. **Allow Full Override**: Accept `Partial\u003cT>` for flexibility\n4. **Type Safety**: Return typed objects, not `any`\n5. **Isolation**: Don't share state between factories (or reset it)\n6. **Naming Convention**: Use `create*Fixture` or `*Builder` suffix\n7. **Co-locate**: Keep factories close to where they're used\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":7422,"content_sha256":"2f5e4bfbd272aec419aa2899a4397683d84505be5194a9e378ca13dd13d840b6"},{"filename":"references/mocking/jest-native.md","content":"# Jest Native Mocking Patterns\n\n## Mock vs Spy Decision Matrix\n\n| Scenario | Recommended | Rationale |\n|----------|-------------|-----------|\n| Mock entire module | `jest.mock()` | Replaces all exports; prevents side effects |\n| Track real method calls | `jest.spyOn()` | Preserves original implementation |\n| Override specific method | `jest.spyOn().mockImplementation()` | Partial mocking with easy restoration |\n| Mock callback parameter | `jest.fn()` | Creates new function with no behavior |\n| Mock class method | `jest.spyOn(instance, 'method')` | Tracks calls on specific instance |\n| Mock getters/setters | `jest.spyOn(obj, 'prop', 'get')` | Special accessor type required |\n\n## jest.fn() - Function Mocking\n\n### Basic Usage\n\n```typescript\nconst mockCallback = jest.fn();\n\n// Call the mock\nmockCallback('arg1', 'arg2');\n\n// Verify\nexpect(mockCallback).toHaveBeenCalled();\nexpect(mockCallback).toHaveBeenCalledWith('arg1', 'arg2');\n```\n\n### With Return Value\n\n```typescript\nconst mockFn = jest.fn().mockReturnValue('mocked result');\nconst result = mockFn();\nexpect(result).toBe('mocked result');\n```\n\n### With Async Return\n\n```typescript\nconst mockAsyncFn = jest.fn().mockResolvedValue({ data: 'result' });\nconst result = await mockAsyncFn();\nexpect(result).toEqual({ data: 'result' });\n```\n\n### With Implementation\n\n```typescript\nconst mockFn = jest.fn((a: number, b: number) => a + b);\nexpect(mockFn(2, 3)).toBe(5);\n```\n\n### Sequential Returns\n\n```typescript\nconst mockFn = jest.fn()\n .mockReturnValueOnce('first')\n .mockReturnValueOnce('second')\n .mockReturnValue('default');\n\nexpect(mockFn()).toBe('first');\nexpect(mockFn()).toBe('second');\nexpect(mockFn()).toBe('default');\nexpect(mockFn()).toBe('default');\n```\n\n## jest.mock() - Module Mocking\n\n### Basic Module Mock\n\n```typescript\njest.mock('../services/email.service');\n\nimport { EmailService } from '../services/email.service';\n\n// All exports are auto-mocked\nconst mockEmailService = EmailService as jest.Mocked\u003ctypeof EmailService>;\n```\n\n### With Factory Function\n\n```typescript\njest.mock('../services/email.service', () => ({\n EmailService: jest.fn().mockImplementation(() => ({\n send: jest.fn().mockResolvedValue(true),\n validate: jest.fn().mockReturnValue(true),\n })),\n}));\n```\n\n### Partial Mock\n\n```typescript\njest.mock('../utils/helpers', () => ({\n ...jest.requireActual('../utils/helpers'),\n formatDate: jest.fn().mockReturnValue('2025-01-01'),\n}));\n```\n\n### Mock Third-Party Library\n\n```typescript\njest.mock('axios');\nimport axios from 'axios';\n\nconst mockedAxios = axios as jest.Mocked\u003ctypeof axios>;\n\nmockedAxios.get.mockResolvedValue({ data: { users: [] } });\n```\n\n## jest.spyOn() - Method Spying\n\n### Basic Spy\n\n```typescript\nconst user = {\n getName: () => 'John',\n};\n\nconst spy = jest.spyOn(user, 'getName');\n\nuser.getName();\n\nexpect(spy).toHaveBeenCalled();\nexpect(spy).toHaveReturnedWith('John');\n```\n\n### Spy with Mock Implementation\n\n```typescript\nconst spy = jest.spyOn(user, 'getName').mockReturnValue('Jane');\n\nexpect(user.getName()).toBe('Jane');\n```\n\n### Spy on Class Method\n\n```typescript\nconst service = new UserService();\nconst spy = jest.spyOn(service, 'findById').mockResolvedValue({ id: '123' });\n\nawait service.findById('123');\n\nexpect(spy).toHaveBeenCalledWith('123');\n```\n\n### Spy on Getter/Setter\n\n```typescript\nconst obj = {\n get value() { return 'original'; },\n set value(v) { /* setter */ },\n};\n\nconst getter = jest.spyOn(obj, 'value', 'get').mockReturnValue('mocked');\nconst setter = jest.spyOn(obj, 'value', 'set');\n\nexpect(obj.value).toBe('mocked');\n\nobj.value = 'new';\nexpect(setter).toHaveBeenCalledWith('new');\n```\n\n### Spy on Private Method (via bracket notation)\n\n```typescript\nconst spy = jest.spyOn(service as any, 'privateMethod');\n\n// Call through public interface that uses private method\nawait service.publicMethod();\n\nexpect(spy).toHaveBeenCalled();\n```\n\n## Mock Reset Methods\n\n| Method | Effect |\n|--------|--------|\n| `jest.clearAllMocks()` | Clears `.mock.calls` and `.mock.instances` |\n| `jest.resetAllMocks()` | Clear + resets return values and implementations |\n| `jest.restoreAllMocks()` | Restores original implementation (spyOn only) |\n\n### Usage\n\n```typescript\nafterEach(() => {\n jest.clearAllMocks(); // Recommended for most cases\n});\n\nafterAll(() => {\n jest.restoreAllMocks(); // Restore original after all tests\n});\n```\n\n## Fake Timers\n\n### Basic Usage\n\n```typescript\nbeforeEach(() => {\n jest.useFakeTimers();\n});\n\nafterEach(() => {\n jest.useRealTimers();\n});\n\nit('should delay execution', async () => {\n const callback = jest.fn();\n\n setTimeout(callback, 1000);\n\n expect(callback).not.toHaveBeenCalled();\n\n jest.advanceTimersByTime(1000);\n\n expect(callback).toHaveBeenCalled();\n});\n```\n\n### Run All Timers\n\n```typescript\nit('should execute all pending timers', () => {\n const callback = jest.fn();\n\n setTimeout(callback, 5000);\n setInterval(() => callback(), 1000);\n\n jest.runAllTimers();\n\n expect(callback).toHaveBeenCalled();\n});\n```\n\n### Advance to Next Timer\n\n```typescript\njest.advanceTimersToNextTimer();\n```\n\n## Mock Console\n\n```typescript\nbeforeEach(() => {\n jest.spyOn(console, 'log').mockImplementation(() => {});\n jest.spyOn(console, 'error').mockImplementation(() => {});\n});\n\nafterEach(() => {\n jest.restoreAllMocks();\n});\n\nit('should log error', () => {\n target.doSomethingThatLogs();\n\n expect(console.error).toHaveBeenCalledWith('Error message');\n});\n```\n\n## Manual Mocks (__mocks__ Directory)\n\n### Structure\n\n```\nsrc/\n├── services/\n│ └── email.service.ts\n└── __mocks__/\n └── services/\n └── email.service.ts\n```\n\n### Manual Mock Implementation\n\n```typescript\n// __mocks__/services/email.service.ts\nexport const EmailService = jest.fn().mockImplementation(() => ({\n send: jest.fn().mockResolvedValue(true),\n}));\n```\n\n### Using Manual Mock\n\n```typescript\njest.mock('../services/email.service');\n// Will automatically use __mocks__/services/email.service.ts\n```\n\n## Common Patterns\n\n### Mock HTTP Client\n\n```typescript\njest.mock('axios');\nimport axios from 'axios';\n\nconst mockedAxios = axios as jest.Mocked\u003ctypeof axios>;\n\nbeforeEach(() => {\n mockedAxios.get.mockReset();\n});\n\nit('should fetch data', async () => {\n mockedAxios.get.mockResolvedValue({ data: { id: 1, name: 'John' } });\n\n const result = await service.fetchUser(1);\n\n expect(result).toEqual({ id: 1, name: 'John' });\n expect(mockedAxios.get).toHaveBeenCalledWith('/users/1');\n});\n```\n\n### Mock Date\n\n```typescript\nbeforeEach(() => {\n jest.useFakeTimers();\n jest.setSystemTime(new Date('2025-01-01T00:00:00Z'));\n});\n\nafterEach(() => {\n jest.useRealTimers();\n});\n\nit('should use mocked date', () => {\n expect(new Date().toISOString()).toBe('2025-01-01T00:00:00.000Z');\n});\n```\n\n### Mock Environment Variables\n\n```typescript\nconst originalEnv = process.env;\n\nbeforeEach(() => {\n process.env = { ...originalEnv, NODE_ENV: 'test' };\n});\n\nafterEach(() => {\n process.env = originalEnv;\n});\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":6989,"content_sha256":"9471641695ca4f695ec804529000445d5ebbbc1f0c62188533967b1df0aeb5a6"},{"filename":"references/nestjs/controllers.md","content":"# Controller Testing Patterns\n\n## Standard Controller Test Template\n\n```typescript\nimport { Test, TestingModule } from '@nestjs/testing';\nimport { createMock, DeepMocked } from '@golevelup/ts-jest';\nimport { MockLoggerService } from 'src/shared/logger/services/mock-logger.service';\n\ndescribe('UserController', () => {\n let target: UserController;\n let mockUserService: DeepMocked\u003cUserService>;\n\n beforeEach(async () => {\n mockUserService = createMock\u003cUserService>();\n\n const module: TestingModule = await Test.createTestingModule({\n controllers: [UserController],\n providers: [\n { provide: UserService, useValue: mockUserService },\n ],\n })\n .setLogger(new MockLoggerService())\n .compile();\n\n target = module.get\u003cUserController>(UserController);\n });\n\n afterEach(() => {\n jest.clearAllMocks();\n });\n});\n```\n\n## Testing Controller Methods\n\n### GET Endpoint\n\n```typescript\ndescribe('findById', () => {\n it('should return user when exists', async () => {\n // Arrange\n const expected: User = {\n id: 'user-123',\n email: '[email protected]',\n name: 'John Doe',\n };\n mockUserService.findById.mockResolvedValue(expected);\n\n // Act\n const result = await target.findById('user-123');\n\n // Assert\n expect(result).toEqual(expected);\n expect(mockUserService.findById).toHaveBeenCalledWith('user-123');\n });\n\n it('should propagate NotFoundException from service', async () => {\n // Arrange\n mockUserService.findById.mockRejectedValue(new NotFoundException());\n\n // Act & Assert\n await expect(target.findById('invalid')).rejects.toThrow(NotFoundException);\n });\n});\n```\n\n### POST Endpoint\n\n```typescript\ndescribe('create', () => {\n it('should create user and return 201', async () => {\n // Arrange\n const input: CreateUserDto = {\n email: '[email protected]',\n name: 'John Doe',\n };\n const expected: User = {\n id: 'user-123',\n ...input,\n createdAt: new Date(),\n };\n mockUserService.create.mockResolvedValue(expected);\n\n // Act\n const result = await target.create(input);\n\n // Assert\n expect(result).toEqual(expected);\n expect(mockUserService.create).toHaveBeenCalledWith(input);\n });\n\n it('should propagate ValidateException for invalid input', async () => {\n // Arrange\n const input: CreateUserDto = { email: 'invalid', name: '' };\n mockUserService.create.mockRejectedValue(new ValidateException('Invalid input'));\n\n // Act & Assert\n await expect(target.create(input)).rejects.toThrow(ValidateException);\n });\n});\n```\n\n### PUT/PATCH Endpoint\n\n```typescript\ndescribe('update', () => {\n it('should update user and return updated entity', async () => {\n // Arrange\n const id = 'user-123';\n const input: UpdateUserDto = { name: 'New Name' };\n const expected: User = {\n id,\n email: '[email protected]',\n name: 'New Name',\n };\n mockUserService.update.mockResolvedValue(expected);\n\n // Act\n const result = await target.update(id, input);\n\n // Assert\n expect(result.name).toBe('New Name');\n expect(mockUserService.update).toHaveBeenCalledWith(id, input);\n });\n});\n```\n\n### DELETE Endpoint\n\n```typescript\ndescribe('delete', () => {\n it('should delete user and return void', async () => {\n // Arrange\n const id = 'user-123';\n mockUserService.delete.mockResolvedValue(undefined);\n\n // Act\n await target.delete(id);\n\n // Assert\n expect(mockUserService.delete).toHaveBeenCalledWith(id);\n });\n});\n```\n\n## Testing with Request Context\n\n```typescript\ndescribe('Controller with Request Context', () => {\n it('should pass user from request to service', async () => {\n // Arrange\n const requestUser = { id: 'user-123', role: 'admin' };\n mockUserService.getProfile.mockResolvedValue({ id: 'user-123', name: 'John' });\n\n // Act\n const result = await target.getProfile(requestUser);\n\n // Assert\n expect(mockUserService.getProfile).toHaveBeenCalledWith('user-123');\n });\n});\n```\n\n## Testing Response Transformation\n\n```typescript\ndescribe('Response Transformation', () => {\n it('should transform service response to DTO', async () => {\n // Arrange\n const serviceResponse: User = {\n id: 'user-123',\n email: '[email protected]',\n name: 'John',\n passwordHash: 'secret', // Should be excluded\n };\n mockUserService.findById.mockResolvedValue(serviceResponse);\n\n // Act\n const result = await target.findById('user-123');\n\n // Assert - Verify passwordHash is excluded\n expect(result).toEqual({\n id: 'user-123',\n email: '[email protected]',\n name: 'John',\n });\n expect(result).not.toHaveProperty('passwordHash');\n });\n});\n```\n\n## Testing Pagination\n\n```typescript\ndescribe('Pagination', () => {\n it('should pass pagination params to service', async () => {\n // Arrange\n const query: PaginationQueryDto = { page: 2, limit: 20 };\n const expected = {\n items: [{ id: 'user-1' }],\n total: 100,\n page: 2,\n limit: 20,\n };\n mockUserService.findAll.mockResolvedValue(expected);\n\n // Act\n const result = await target.findAll(query);\n\n // Assert\n expect(result).toEqual(expected);\n expect(mockUserService.findAll).toHaveBeenCalledWith({ page: 2, limit: 20 });\n });\n});\n```\n\n## Testing File Upload\n\n```typescript\ndescribe('File Upload', () => {\n it('should upload file and return URL', async () => {\n // Arrange\n const file: Express.Multer.File = {\n fieldname: 'avatar',\n originalname: 'photo.jpg',\n buffer: Buffer.from('fake-image'),\n mimetype: 'image/jpeg',\n size: 1024,\n } as Express.Multer.File;\n\n mockFileService.upload.mockResolvedValue({\n url: 'https://storage.example.com/photo.jpg',\n });\n\n // Act\n const result = await target.uploadAvatar('user-123', file);\n\n // Assert\n expect(result.url).toBe('https://storage.example.com/photo.jpg');\n expect(mockFileService.upload).toHaveBeenCalledWith(file);\n });\n});\n```\n\n## Testing with Query Decorators\n\n```typescript\ndescribe('Query Parameters', () => {\n it('should parse query parameters correctly', async () => {\n // Arrange\n const query = {\n search: 'john',\n status: 'active',\n sortBy: 'createdAt',\n order: 'desc',\n };\n mockUserService.search.mockResolvedValue({ items: [], total: 0 });\n\n // Act\n await target.search(query);\n\n // Assert\n expect(mockUserService.search).toHaveBeenCalledWith(query);\n });\n});\n```\n\n## Testing Error Responses\n\n```typescript\ndescribe('Error Handling', () => {\n it('should propagate NotFoundException with correct status', async () => {\n // Arrange\n mockUserService.findById.mockRejectedValue(\n new NotFoundException('User not found')\n );\n\n // Act & Assert\n await expect(target.findById('invalid'))\n .rejects.toMatchObject({\n status: 404,\n message: 'User not found',\n });\n });\n\n it('should propagate ValidateException with correct status', async () => {\n // Arrange\n mockUserService.create.mockRejectedValue(\n new ValidateException('Invalid email format')\n );\n\n // Act & Assert\n await expect(target.create({ email: 'invalid', name: 'John' }))\n .rejects.toMatchObject({\n status: 400,\n message: 'Invalid email format',\n });\n });\n});\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":7357,"content_sha256":"783df434e9e502438beb306714cf8577f87b3c1b61bdcde4d3e007a75dc40116"},{"filename":"references/nestjs/guards.md","content":"# Guard Testing Patterns\n\n## Standard Guard Test Template\n\n```typescript\nimport { createMock, DeepMocked } from '@golevelup/ts-jest';\nimport { ExecutionContext, UnauthorizedException } from '@nestjs/common';\n\ndescribe('AuthGuard', () => {\n let target: AuthGuard;\n let mockAuthService: DeepMocked\u003cAuthService>;\n\n beforeEach(() => {\n mockAuthService = createMock\u003cAuthService>();\n target = new AuthGuard(mockAuthService);\n });\n\n afterEach(() => {\n jest.clearAllMocks();\n });\n});\n```\n\n## Testing Basic Authentication Guard\n\n```typescript\ndescribe('BasicAuthGuard', () => {\n let target: BasicAuthGuard;\n let mockAuthService: DeepMocked\u003cAuthService>;\n\n beforeEach(() => {\n mockAuthService = createMock\u003cAuthService>();\n target = new BasicAuthGuard(mockAuthService);\n });\n\n it('should allow access with valid credentials', async () => {\n // Arrange\n const mockContext = createMock\u003cExecutionContext>();\n mockContext.switchToHttp().getRequest.mockReturnValue({\n headers: { authorization: 'Basic dGVzdDpwYXNzd29yZA==' }, // test:password\n });\n mockAuthService.validateCredentials.mockResolvedValue({\n id: 'user-123',\n username: 'test',\n });\n\n // Act\n const result = await target.canActivate(mockContext);\n\n // Assert\n expect(result).toBe(true);\n expect(mockAuthService.validateCredentials).toHaveBeenCalledWith('test', 'password');\n });\n\n it('should deny access with invalid credentials', async () => {\n // Arrange\n const mockContext = createMock\u003cExecutionContext>();\n mockContext.switchToHttp().getRequest.mockReturnValue({\n headers: { authorization: 'Basic aW52YWxpZDppbnZhbGlk' },\n });\n mockAuthService.validateCredentials.mockResolvedValue(null);\n\n // Act & Assert\n await expect(target.canActivate(mockContext)).rejects.toThrow(UnauthorizedException);\n });\n\n it('should deny access when no authorization header', async () => {\n // Arrange\n const mockContext = createMock\u003cExecutionContext>();\n mockContext.switchToHttp().getRequest.mockReturnValue({\n headers: {},\n });\n\n // Act & Assert\n await expect(target.canActivate(mockContext)).rejects.toThrow(UnauthorizedException);\n });\n});\n```\n\n## Testing JWT Guard\n\n```typescript\ndescribe('JwtAuthGuard', () => {\n let target: JwtAuthGuard;\n let mockJwtService: DeepMocked\u003cJwtService>;\n\n beforeEach(() => {\n mockJwtService = createMock\u003cJwtService>();\n target = new JwtAuthGuard(mockJwtService);\n });\n\n it('should allow access with valid JWT token', async () => {\n // Arrange\n const mockContext = createMock\u003cExecutionContext>();\n const validToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.test';\n mockContext.switchToHttp().getRequest.mockReturnValue({\n headers: { authorization: `Bearer ${validToken}` },\n });\n mockJwtService.verify.mockReturnValue({\n sub: 'user-123',\n email: '[email protected]',\n });\n\n // Act\n const result = await target.canActivate(mockContext);\n\n // Assert\n expect(result).toBe(true);\n expect(mockJwtService.verify).toHaveBeenCalledWith(validToken);\n });\n\n it('should deny access with expired token', async () => {\n // Arrange\n const mockContext = createMock\u003cExecutionContext>();\n mockContext.switchToHttp().getRequest.mockReturnValue({\n headers: { authorization: 'Bearer expired-token' },\n });\n mockJwtService.verify.mockImplementation(() => {\n throw new Error('Token expired');\n });\n\n // Act & Assert\n await expect(target.canActivate(mockContext)).rejects.toThrow(UnauthorizedException);\n });\n\n it('should deny access with malformed token', async () => {\n // Arrange\n const mockContext = createMock\u003cExecutionContext>();\n mockContext.switchToHttp().getRequest.mockReturnValue({\n headers: { authorization: 'Bearer malformed' },\n });\n mockJwtService.verify.mockImplementation(() => {\n throw new Error('Malformed token');\n });\n\n // Act & Assert\n await expect(target.canActivate(mockContext)).rejects.toThrow(UnauthorizedException);\n });\n});\n```\n\n## Testing Role-Based Guard\n\n```typescript\ndescribe('RolesGuard', () => {\n let target: RolesGuard;\n let mockReflector: DeepMocked\u003cReflector>;\n\n beforeEach(() => {\n mockReflector = createMock\u003cReflector>();\n target = new RolesGuard(mockReflector);\n });\n\n it('should allow access when user has required role', async () => {\n // Arrange\n const mockContext = createMock\u003cExecutionContext>();\n mockContext.switchToHttp().getRequest.mockReturnValue({\n user: { id: 'user-123', roles: ['admin'] },\n });\n mockReflector.get.mockReturnValue(['admin']);\n\n // Act\n const result = await target.canActivate(mockContext);\n\n // Assert\n expect(result).toBe(true);\n });\n\n it('should deny access when user lacks required role', async () => {\n // Arrange\n const mockContext = createMock\u003cExecutionContext>();\n mockContext.switchToHttp().getRequest.mockReturnValue({\n user: { id: 'user-123', roles: ['user'] },\n });\n mockReflector.get.mockReturnValue(['admin']);\n\n // Act & Assert\n await expect(target.canActivate(mockContext)).rejects.toThrow(ForbiddenException);\n });\n\n it('should allow access when no roles required', async () => {\n // Arrange\n const mockContext = createMock\u003cExecutionContext>();\n mockContext.switchToHttp().getRequest.mockReturnValue({\n user: { id: 'user-123', roles: ['user'] },\n });\n mockReflector.get.mockReturnValue(undefined);\n\n // Act\n const result = await target.canActivate(mockContext);\n\n // Assert\n expect(result).toBe(true);\n });\n});\n```\n\n## Testing API Key Guard\n\n```typescript\ndescribe('ApiKeyGuard', () => {\n let target: ApiKeyGuard;\n let mockApiKeyService: DeepMocked\u003cApiKeyService>;\n\n beforeEach(() => {\n mockApiKeyService = createMock\u003cApiKeyService>();\n target = new ApiKeyGuard(mockApiKeyService);\n });\n\n it('should allow access with valid API key in header', async () => {\n // Arrange\n const mockContext = createMock\u003cExecutionContext>();\n mockContext.switchToHttp().getRequest.mockReturnValue({\n headers: { 'x-api-key': 'valid-api-key' },\n });\n mockApiKeyService.validate.mockResolvedValue({\n clientId: 'client-123',\n permissions: ['read', 'write'],\n });\n\n // Act\n const result = await target.canActivate(mockContext);\n\n // Assert\n expect(result).toBe(true);\n expect(mockApiKeyService.validate).toHaveBeenCalledWith('valid-api-key');\n });\n\n it('should deny access with invalid API key', async () => {\n // Arrange\n const mockContext = createMock\u003cExecutionContext>();\n mockContext.switchToHttp().getRequest.mockReturnValue({\n headers: { 'x-api-key': 'invalid-key' },\n });\n mockApiKeyService.validate.mockResolvedValue(null);\n\n // Act & Assert\n await expect(target.canActivate(mockContext)).rejects.toThrow(UnauthorizedException);\n });\n});\n```\n\n## Testing GraphQL Context\n\n```typescript\ndescribe('GraphQL Guard', () => {\n it('should extract context from GraphQL request', async () => {\n // Arrange\n const mockContext = createMock\u003cExecutionContext>();\n mockContext.getType.mockReturnValue('graphql');\n\n // Mock GqlExecutionContext\n const gqlContext = {\n getContext: () => ({\n req: { headers: { authorization: 'Bearer token' } },\n }),\n };\n jest.spyOn(GqlExecutionContext, 'create').mockReturnValue(gqlContext as any);\n\n mockAuthService.validateToken.mockResolvedValue({ id: 'user-123' });\n\n // Act\n const result = await target.canActivate(mockContext);\n\n // Assert\n expect(result).toBe(true);\n });\n});\n```\n\n## Testing Throttle Guard\n\n```typescript\ndescribe('ThrottleGuard', () => {\n let target: ThrottleGuard;\n let mockThrottlerStorage: DeepMocked\u003cThrottlerStorage>;\n\n beforeEach(() => {\n mockThrottlerStorage = createMock\u003cThrottlerStorage>();\n target = new ThrottleGuard(mockThrottlerStorage, { ttl: 60, limit: 10 });\n });\n\n it('should allow request within rate limit', async () => {\n // Arrange\n const mockContext = createMock\u003cExecutionContext>();\n mockContext.switchToHttp().getRequest.mockReturnValue({\n ip: '127.0.0.1',\n });\n mockThrottlerStorage.getRecord.mockResolvedValue({ count: 5 });\n\n // Act\n const result = await target.canActivate(mockContext);\n\n // Assert\n expect(result).toBe(true);\n });\n\n it('should block request exceeding rate limit', async () => {\n // Arrange\n const mockContext = createMock\u003cExecutionContext>();\n mockContext.switchToHttp().getRequest.mockReturnValue({\n ip: '127.0.0.1',\n });\n mockThrottlerStorage.getRecord.mockResolvedValue({ count: 10 });\n\n // Act & Assert\n await expect(target.canActivate(mockContext)).rejects.toThrow(ThrottlerException);\n });\n});\n```\n\n## Testing Guard with Decorator Metadata\n\n```typescript\ndescribe('Guard with Metadata', () => {\n it('should read metadata from handler', async () => {\n // Arrange\n const mockContext = createMock\u003cExecutionContext>();\n mockContext.getHandler.mockReturnValue(jest.fn());\n mockContext.getClass.mockReturnValue(jest.fn());\n\n mockReflector.getAllAndOverride.mockReturnValue(['admin']);\n\n mockContext.switchToHttp().getRequest.mockReturnValue({\n user: { roles: ['admin'] },\n });\n\n // Act\n const result = await target.canActivate(mockContext);\n\n // Assert\n expect(mockReflector.getAllAndOverride).toHaveBeenCalledWith(\n 'roles',\n [mockContext.getHandler(), mockContext.getClass()]\n );\n expect(result).toBe(true);\n });\n});\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":9561,"content_sha256":"755c4da7a8ff36303d9c88e25ba7531e0916a949cbc9bebf1416872c06f128a5"},{"filename":"references/nestjs/interceptors.md","content":"# Interceptor Testing Patterns\n\n## Standard Interceptor Test Template\n\n```typescript\nimport { createMock, DeepMocked } from '@golevelup/ts-jest';\nimport { CallHandler, ExecutionContext } from '@nestjs/common';\nimport { of, throwError } from 'rxjs';\n\ndescribe('LoggingInterceptor', () => {\n let target: LoggingInterceptor;\n let mockLogger: DeepMocked\u003cLoggerService>;\n\n beforeEach(() => {\n mockLogger = createMock\u003cLoggerService>();\n target = new LoggingInterceptor(mockLogger);\n });\n\n afterEach(() => {\n jest.clearAllMocks();\n });\n});\n```\n\n## Testing Response Transformation\n\n```typescript\ndescribe('TransformInterceptor', () => {\n let target: TransformInterceptor;\n\n beforeEach(() => {\n target = new TransformInterceptor();\n });\n\n it('should wrap response in standard format', async () => {\n // Arrange\n const mockContext = createMock\u003cExecutionContext>();\n const mockCallHandler = createMock\u003cCallHandler>({\n handle: () => of({ id: 'user-123', name: 'John' }),\n });\n\n // Act\n const result$ = target.intercept(mockContext, mockCallHandler);\n const result = await result$.toPromise();\n\n // Assert\n expect(result).toEqual({\n success: true,\n data: { id: 'user-123', name: 'John' },\n });\n });\n\n it('should handle null response', async () => {\n // Arrange\n const mockContext = createMock\u003cExecutionContext>();\n const mockCallHandler = createMock\u003cCallHandler>({\n handle: () => of(null),\n });\n\n // Act\n const result$ = target.intercept(mockContext, mockCallHandler);\n const result = await result$.toPromise();\n\n // Assert\n expect(result).toEqual({\n success: true,\n data: null,\n });\n });\n\n it('should handle array response', async () => {\n // Arrange\n const mockContext = createMock\u003cExecutionContext>();\n const items = [{ id: '1' }, { id: '2' }];\n const mockCallHandler = createMock\u003cCallHandler>({\n handle: () => of(items),\n });\n\n // Act\n const result$ = target.intercept(mockContext, mockCallHandler);\n const result = await result$.toPromise();\n\n // Assert\n expect(result).toEqual({\n success: true,\n data: items,\n });\n });\n});\n```\n\n## Testing Logging Interceptor\n\n```typescript\ndescribe('LoggingInterceptor', () => {\n let target: LoggingInterceptor;\n let mockLogger: DeepMocked\u003cLoggerService>;\n\n beforeEach(() => {\n mockLogger = createMock\u003cLoggerService>();\n target = new LoggingInterceptor(mockLogger);\n });\n\n it('should log request and response', async () => {\n // Arrange\n const mockContext = createMock\u003cExecutionContext>();\n mockContext.switchToHttp().getRequest.mockReturnValue({\n method: 'GET',\n url: '/users/123',\n });\n\n const mockCallHandler = createMock\u003cCallHandler>({\n handle: () => of({ id: 'user-123' }),\n });\n\n // Act\n const result$ = target.intercept(mockContext, mockCallHandler);\n await result$.toPromise();\n\n // Assert\n expect(mockLogger.log).toHaveBeenCalledWith(\n expect.stringContaining('GET /users/123')\n );\n });\n\n it('should log execution time', async () => {\n // Arrange\n const mockContext = createMock\u003cExecutionContext>();\n mockContext.switchToHttp().getRequest.mockReturnValue({\n method: 'GET',\n url: '/users',\n });\n\n const mockCallHandler = createMock\u003cCallHandler>({\n handle: () => of([]),\n });\n\n // Act\n const result$ = target.intercept(mockContext, mockCallHandler);\n await result$.toPromise();\n\n // Assert\n expect(mockLogger.log).toHaveBeenCalledWith(\n expect.stringMatching(/\\d+ms/)\n );\n });\n});\n```\n\n## Testing Error Handling Interceptor\n\n```typescript\ndescribe('ErrorInterceptor', () => {\n let target: ErrorInterceptor;\n\n beforeEach(() => {\n target = new ErrorInterceptor();\n });\n\n it('should pass through successful response', async () => {\n // Arrange\n const mockContext = createMock\u003cExecutionContext>();\n const mockCallHandler = createMock\u003cCallHandler>({\n handle: () => of({ success: true }),\n });\n\n // Act\n const result$ = target.intercept(mockContext, mockCallHandler);\n const result = await result$.toPromise();\n\n // Assert\n expect(result).toEqual({ success: true });\n });\n\n it('should transform error to standard format', (done) => {\n // Arrange\n const mockContext = createMock\u003cExecutionContext>();\n const mockCallHandler = createMock\u003cCallHandler>({\n handle: () => throwError(() => new Error('Something went wrong')),\n });\n\n // Act\n const result$ = target.intercept(mockContext, mockCallHandler);\n\n // Assert\n result$.subscribe({\n error: (error) => {\n expect(error).toBeInstanceOf(InternalServerErrorException);\n done();\n },\n });\n });\n\n it('should preserve HttpException type', (done) => {\n // Arrange\n const mockContext = createMock\u003cExecutionContext>();\n const mockCallHandler = createMock\u003cCallHandler>({\n handle: () => throwError(() => new NotFoundException('User not found')),\n });\n\n // Act\n const result$ = target.intercept(mockContext, mockCallHandler);\n\n // Assert\n result$.subscribe({\n error: (error) => {\n expect(error).toBeInstanceOf(NotFoundException);\n expect(error.message).toBe('User not found');\n done();\n },\n });\n });\n});\n```\n\n## Testing Timeout Interceptor\n\n```typescript\ndescribe('TimeoutInterceptor', () => {\n let target: TimeoutInterceptor;\n\n beforeEach(() => {\n jest.useFakeTimers();\n target = new TimeoutInterceptor(5000); // 5 second timeout\n });\n\n afterEach(() => {\n jest.useRealTimers();\n });\n\n it('should complete before timeout', async () => {\n // Arrange\n const mockContext = createMock\u003cExecutionContext>();\n const mockCallHandler = createMock\u003cCallHandler>({\n handle: () => of({ success: true }),\n });\n\n // Act\n const result$ = target.intercept(mockContext, mockCallHandler);\n const result = await result$.toPromise();\n\n // Assert\n expect(result).toEqual({ success: true });\n });\n\n it('should throw timeout error after threshold', (done) => {\n // Arrange\n const mockContext = createMock\u003cExecutionContext>();\n const mockCallHandler = createMock\u003cCallHandler>({\n handle: () => new Observable((subscriber) => {\n // Never completes\n }),\n });\n\n // Act\n const result$ = target.intercept(mockContext, mockCallHandler);\n\n result$.subscribe({\n error: (error) => {\n expect(error).toBeInstanceOf(RequestTimeoutException);\n done();\n },\n });\n\n // Fast-forward time\n jest.advanceTimersByTime(6000);\n });\n});\n```\n\n## Testing Cache Interceptor\n\n```typescript\ndescribe('CacheInterceptor', () => {\n let target: CacheInterceptor;\n let mockCacheManager: DeepMocked\u003cCache>;\n\n beforeEach(() => {\n mockCacheManager = createMock\u003cCache>();\n target = new CacheInterceptor(mockCacheManager);\n });\n\n it('should return cached response if exists', async () => {\n // Arrange\n const mockContext = createMock\u003cExecutionContext>();\n mockContext.switchToHttp().getRequest.mockReturnValue({\n url: '/users/123',\n method: 'GET',\n });\n\n const cachedData = { id: 'user-123', name: 'John' };\n mockCacheManager.get.mockResolvedValue(cachedData);\n\n const mockCallHandler = createMock\u003cCallHandler>();\n\n // Act\n const result$ = target.intercept(mockContext, mockCallHandler);\n const result = await result$.toPromise();\n\n // Assert\n expect(result).toEqual(cachedData);\n expect(mockCallHandler.handle).not.toHaveBeenCalled();\n });\n\n it('should fetch and cache on cache miss', async () => {\n // Arrange\n const mockContext = createMock\u003cExecutionContext>();\n mockContext.switchToHttp().getRequest.mockReturnValue({\n url: '/users/123',\n method: 'GET',\n });\n\n mockCacheManager.get.mockResolvedValue(null);\n mockCacheManager.set.mockResolvedValue(undefined);\n\n const freshData = { id: 'user-123', name: 'John' };\n const mockCallHandler = createMock\u003cCallHandler>({\n handle: () => of(freshData),\n });\n\n // Act\n const result$ = target.intercept(mockContext, mockCallHandler);\n const result = await result$.toPromise();\n\n // Assert\n expect(result).toEqual(freshData);\n expect(mockCacheManager.set).toHaveBeenCalledWith(\n expect.any(String),\n freshData,\n expect.any(Number)\n );\n });\n\n it('should skip cache for non-GET requests', async () => {\n // Arrange\n const mockContext = createMock\u003cExecutionContext>();\n mockContext.switchToHttp().getRequest.mockReturnValue({\n url: '/users',\n method: 'POST',\n });\n\n const mockCallHandler = createMock\u003cCallHandler>({\n handle: () => of({ id: 'new-user' }),\n });\n\n // Act\n const result$ = target.intercept(mockContext, mockCallHandler);\n await result$.toPromise();\n\n // Assert\n expect(mockCacheManager.get).not.toHaveBeenCalled();\n });\n});\n```\n\n## Testing Request Context Interceptor\n\n```typescript\ndescribe('RequestContextInterceptor', () => {\n let target: RequestContextInterceptor;\n let mockClsService: DeepMocked\u003cClsService>;\n\n beforeEach(() => {\n mockClsService = createMock\u003cClsService>();\n target = new RequestContextInterceptor(mockClsService);\n });\n\n it('should set correlation ID in context', async () => {\n // Arrange\n const mockContext = createMock\u003cExecutionContext>();\n mockContext.switchToHttp().getRequest.mockReturnValue({\n headers: { 'x-correlation-id': 'corr-123' },\n });\n\n const mockCallHandler = createMock\u003cCallHandler>({\n handle: () => of({ success: true }),\n });\n\n // Act\n await target.intercept(mockContext, mockCallHandler).toPromise();\n\n // Assert\n expect(mockClsService.set).toHaveBeenCalledWith('correlationId', 'corr-123');\n });\n\n it('should generate correlation ID if not provided', async () => {\n // Arrange\n const mockContext = createMock\u003cExecutionContext>();\n mockContext.switchToHttp().getRequest.mockReturnValue({\n headers: {},\n });\n\n const mockCallHandler = createMock\u003cCallHandler>({\n handle: () => of({ success: true }),\n });\n\n // Act\n await target.intercept(mockContext, mockCallHandler).toPromise();\n\n // Assert\n expect(mockClsService.set).toHaveBeenCalledWith(\n 'correlationId',\n expect.any(String)\n );\n });\n});\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":10359,"content_sha256":"708ad952954f7fa9fdb2a3edb5d45a7ccafd6d32c526603fd3253a8fa8b5afc1"},{"filename":"references/nestjs/pipes-filters.md","content":"# Pipe and Filter Testing Patterns\n\n## Pipe Testing\n\n### Standard Pipe Test Template\n\n```typescript\nimport { BadRequestException } from '@nestjs/common';\n\ndescribe('ValidationPipe', () => {\n let target: CustomValidationPipe;\n\n beforeEach(() => {\n target = new CustomValidationPipe();\n });\n\n afterEach(() => {\n jest.clearAllMocks();\n });\n});\n```\n\n### Testing Validation Pipe\n\n```typescript\ndescribe('CustomValidationPipe', () => {\n let target: CustomValidationPipe;\n\n beforeEach(() => {\n target = new CustomValidationPipe();\n });\n\n it('should pass valid data through', () => {\n // Arrange\n const validData = { email: '[email protected]', name: 'John' };\n const metadata: ArgumentMetadata = { type: 'body', metatype: CreateUserDto };\n\n // Act\n const result = target.transform(validData, metadata);\n\n // Assert\n expect(result).toEqual(validData);\n });\n\n it('should throw BadRequestException for invalid email', () => {\n // Arrange\n const invalidData = { email: 'invalid-email', name: 'John' };\n const metadata: ArgumentMetadata = { type: 'body', metatype: CreateUserDto };\n\n // Act & Assert\n expect(() => target.transform(invalidData, metadata))\n .toThrow(BadRequestException);\n });\n\n it('should throw BadRequestException with field details', () => {\n // Arrange\n const invalidData = { email: 'invalid', name: '' };\n const metadata: ArgumentMetadata = { type: 'body', metatype: CreateUserDto };\n\n // Act & Assert\n try {\n target.transform(invalidData, metadata);\n fail('Expected BadRequestException');\n } catch (error) {\n expect(error).toBeInstanceOf(BadRequestException);\n expect(error.getResponse()).toMatchObject({\n statusCode: 400,\n message: expect.arrayContaining([\n expect.stringContaining('email'),\n ]),\n });\n }\n });\n});\n```\n\n### Testing Parse Pipes\n\n```typescript\ndescribe('ParseIntPipe', () => {\n let target: ParseIntPipe;\n\n beforeEach(() => {\n target = new ParseIntPipe();\n });\n\n it('should parse valid integer string', () => {\n // Arrange\n const value = '123';\n const metadata: ArgumentMetadata = { type: 'param', data: 'id' };\n\n // Act\n const result = target.transform(value, metadata);\n\n // Assert\n expect(result).toBe(123);\n });\n\n it('should throw BadRequestException for non-numeric string', () => {\n // Arrange\n const value = 'abc';\n const metadata: ArgumentMetadata = { type: 'param', data: 'id' };\n\n // Act & Assert\n expect(() => target.transform(value, metadata))\n .toThrow(BadRequestException);\n });\n\n it('should throw BadRequestException for floating point', () => {\n // Arrange\n const value = '12.5';\n const metadata: ArgumentMetadata = { type: 'param', data: 'id' };\n\n // Act & Assert\n expect(() => target.transform(value, metadata))\n .toThrow(BadRequestException);\n });\n});\n\ndescribe('ParseUUIDPipe', () => {\n let target: ParseUUIDPipe;\n\n beforeEach(() => {\n target = new ParseUUIDPipe();\n });\n\n it('should pass valid UUID', () => {\n // Arrange\n const value = '123e4567-e89b-12d3-a456-426614174000';\n const metadata: ArgumentMetadata = { type: 'param', data: 'id' };\n\n // Act\n const result = target.transform(value, metadata);\n\n // Assert\n expect(result).toBe(value);\n });\n\n it('should throw for invalid UUID format', () => {\n // Arrange\n const value = 'not-a-uuid';\n const metadata: ArgumentMetadata = { type: 'param', data: 'id' };\n\n // Act & Assert\n expect(() => target.transform(value, metadata))\n .toThrow(BadRequestException);\n });\n});\n```\n\n### Testing Transformation Pipe\n\n```typescript\ndescribe('TrimPipe', () => {\n let target: TrimPipe;\n\n beforeEach(() => {\n target = new TrimPipe();\n });\n\n it('should trim whitespace from string', () => {\n // Arrange\n const value = ' hello world ';\n const metadata: ArgumentMetadata = { type: 'body' };\n\n // Act\n const result = target.transform(value, metadata);\n\n // Assert\n expect(result).toBe('hello world');\n });\n\n it('should trim all string properties in object', () => {\n // Arrange\n const value = {\n name: ' John ',\n email: ' [email protected] ',\n age: 25,\n };\n const metadata: ArgumentMetadata = { type: 'body' };\n\n // Act\n const result = target.transform(value, metadata);\n\n // Assert\n expect(result).toEqual({\n name: 'John',\n email: '[email protected]',\n age: 25,\n });\n });\n});\n\ndescribe('DefaultValuePipe', () => {\n it('should return value when present', () => {\n // Arrange\n const target = new DefaultValuePipe('default');\n const metadata: ArgumentMetadata = { type: 'query', data: 'page' };\n\n // Act\n const result = target.transform('5', metadata);\n\n // Assert\n expect(result).toBe('5');\n });\n\n it('should return default when value is undefined', () => {\n // Arrange\n const target = new DefaultValuePipe('default');\n const metadata: ArgumentMetadata = { type: 'query', data: 'page' };\n\n // Act\n const result = target.transform(undefined, metadata);\n\n // Assert\n expect(result).toBe('default');\n });\n});\n```\n\n---\n\n## Exception Filter Testing\n\n### Standard Filter Test Template\n\n```typescript\nimport { createMock, DeepMocked } from '@golevelup/ts-jest';\nimport { ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common';\n\ndescribe('HttpExceptionFilter', () => {\n let target: HttpExceptionFilter;\n\n beforeEach(() => {\n target = new HttpExceptionFilter();\n });\n\n afterEach(() => {\n jest.clearAllMocks();\n });\n});\n```\n\n### Testing HTTP Exception Filter\n\n```typescript\ndescribe('HttpExceptionFilter', () => {\n let target: HttpExceptionFilter;\n\n beforeEach(() => {\n target = new HttpExceptionFilter();\n });\n\n it('should catch and format HttpException', () => {\n // Arrange\n const mockHost = createMock\u003cArgumentsHost>();\n const mockResponse = {\n status: jest.fn().mockReturnThis(),\n json: jest.fn(),\n };\n const mockRequest = {\n url: '/users/123',\n method: 'GET',\n };\n\n mockHost.switchToHttp().getResponse.mockReturnValue(mockResponse);\n mockHost.switchToHttp().getRequest.mockReturnValue(mockRequest);\n\n const exception = new HttpException('User not found', HttpStatus.NOT_FOUND);\n\n // Act\n target.catch(exception, mockHost);\n\n // Assert\n expect(mockResponse.status).toHaveBeenCalledWith(404);\n expect(mockResponse.json).toHaveBeenCalledWith(\n expect.objectContaining({\n statusCode: 404,\n message: 'User not found',\n path: '/users/123',\n })\n );\n });\n\n it('should handle BadRequestException with validation errors', () => {\n // Arrange\n const mockHost = createMock\u003cArgumentsHost>();\n const mockResponse = {\n status: jest.fn().mockReturnThis(),\n json: jest.fn(),\n };\n mockHost.switchToHttp().getResponse.mockReturnValue(mockResponse);\n mockHost.switchToHttp().getRequest.mockReturnValue({ url: '/users', method: 'POST' });\n\n const exception = new BadRequestException({\n statusCode: 400,\n message: ['email must be a valid email', 'name should not be empty'],\n error: 'Bad Request',\n });\n\n // Act\n target.catch(exception, mockHost);\n\n // Assert\n expect(mockResponse.status).toHaveBeenCalledWith(400);\n expect(mockResponse.json).toHaveBeenCalledWith(\n expect.objectContaining({\n statusCode: 400,\n message: expect.arrayContaining([\n 'email must be a valid email',\n 'name should not be empty',\n ]),\n })\n );\n });\n});\n```\n\n### Testing Custom Exception Filter\n\n```typescript\ndescribe('ValidateExceptionFilter', () => {\n let target: ValidateExceptionFilter;\n let mockLogger: DeepMocked\u003cLoggerService>;\n\n beforeEach(() => {\n mockLogger = createMock\u003cLoggerService>();\n target = new ValidateExceptionFilter(mockLogger);\n });\n\n it('should format ValidateException response', () => {\n // Arrange\n const mockHost = createMock\u003cArgumentsHost>();\n const mockResponse = {\n status: jest.fn().mockReturnThis(),\n json: jest.fn(),\n };\n mockHost.switchToHttp().getResponse.mockReturnValue(mockResponse);\n mockHost.switchToHttp().getRequest.mockReturnValue({\n url: '/users',\n method: 'POST',\n headers: { 'x-correlation-id': 'corr-123' },\n });\n\n const exception = new ValidateException('Validation failed', {\n errorCode: ErrorCode.VALIDATION_ERROR,\n details: [{ field: 'email', message: 'Invalid email' }],\n });\n\n // Act\n target.catch(exception, mockHost);\n\n // Assert\n expect(mockResponse.status).toHaveBeenCalledWith(400);\n expect(mockResponse.json).toHaveBeenCalledWith({\n code: ErrorCode.VALIDATION_ERROR,\n message: 'Validation failed',\n details: [{ field: 'email', message: 'Invalid email' }],\n });\n });\n\n it('should log exception details', () => {\n // Arrange\n const mockHost = createMock\u003cArgumentsHost>();\n const mockResponse = {\n status: jest.fn().mockReturnThis(),\n json: jest.fn(),\n };\n mockHost.switchToHttp().getResponse.mockReturnValue(mockResponse);\n mockHost.switchToHttp().getRequest.mockReturnValue({ url: '/users', method: 'POST' });\n\n const exception = new ValidateException('Validation failed');\n\n // Act\n target.catch(exception, mockHost);\n\n // Assert\n expect(mockLogger.warn).toHaveBeenCalledWith(\n expect.stringContaining('Validation failed')\n );\n });\n});\n```\n\n### Testing All Exceptions Filter\n\n```typescript\ndescribe('AllExceptionsFilter', () => {\n let target: AllExceptionsFilter;\n let mockLogger: DeepMocked\u003cLoggerService>;\n\n beforeEach(() => {\n mockLogger = createMock\u003cLoggerService>();\n target = new AllExceptionsFilter(mockLogger);\n });\n\n it('should handle unknown errors', () => {\n // Arrange\n const mockHost = createMock\u003cArgumentsHost>();\n const mockResponse = {\n status: jest.fn().mockReturnThis(),\n json: jest.fn(),\n };\n mockHost.switchToHttp().getResponse.mockReturnValue(mockResponse);\n mockHost.switchToHttp().getRequest.mockReturnValue({ url: '/api', method: 'GET' });\n\n const exception = new Error('Unexpected error');\n\n // Act\n target.catch(exception, mockHost);\n\n // Assert\n expect(mockResponse.status).toHaveBeenCalledWith(500);\n expect(mockResponse.json).toHaveBeenCalledWith(\n expect.objectContaining({\n statusCode: 500,\n message: 'Internal server error',\n })\n );\n expect(mockLogger.error).toHaveBeenCalled();\n });\n\n it('should not expose internal error details in production', () => {\n // Arrange\n process.env.NODE_ENV = 'production';\n const mockHost = createMock\u003cArgumentsHost>();\n const mockResponse = {\n status: jest.fn().mockReturnThis(),\n json: jest.fn(),\n };\n mockHost.switchToHttp().getResponse.mockReturnValue(mockResponse);\n mockHost.switchToHttp().getRequest.mockReturnValue({ url: '/api', method: 'GET' });\n\n const exception = new Error('Database connection failed: password123');\n\n // Act\n target.catch(exception, mockHost);\n\n // Assert\n expect(mockResponse.json).toHaveBeenCalledWith(\n expect.objectContaining({\n message: 'Internal server error',\n })\n );\n expect(mockResponse.json).not.toHaveBeenCalledWith(\n expect.objectContaining({\n message: expect.stringContaining('password123'),\n })\n );\n\n // Cleanup\n process.env.NODE_ENV = 'test';\n });\n});\n```\n\n### Testing WebSocket Exception Filter\n\n```typescript\ndescribe('WsExceptionFilter', () => {\n let target: WsExceptionFilter;\n\n beforeEach(() => {\n target = new WsExceptionFilter();\n });\n\n it('should emit error event to client', () => {\n // Arrange\n const mockHost = createMock\u003cArgumentsHost>();\n const mockClient = {\n emit: jest.fn(),\n };\n mockHost.switchToWs().getClient.mockReturnValue(mockClient);\n\n const exception = new WsException('Connection error');\n\n // Act\n target.catch(exception, mockHost);\n\n // Assert\n expect(mockClient.emit).toHaveBeenCalledWith('error', {\n message: 'Connection error',\n });\n });\n});\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":12206,"content_sha256":"d517201d2aad498a200ffbdf273857ce10cbd5686c300fe613d48a29d2fbb749"},{"filename":"references/nestjs/services.md","content":"# Service/Usecase Testing Patterns\n\n## Standard Service Test Template\n\n```typescript\nimport { Test, TestingModule } from '@nestjs/testing';\nimport { createMock, DeepMocked } from '@golevelup/ts-jest';\nimport { MockLoggerService } from 'src/shared/logger/services/mock-logger.service';\n\ndescribe('UserService', () => {\n let target: UserService;\n let mockRepository: DeepMocked\u003cUserRepository>;\n let mockEventEmitter: DeepMocked\u003cEventEmitter2>;\n\n beforeEach(async () => {\n mockRepository = createMock\u003cUserRepository>();\n mockEventEmitter = createMock\u003cEventEmitter2>();\n\n const module: TestingModule = await Test.createTestingModule({\n providers: [\n UserService,\n { provide: UserRepository, useValue: mockRepository },\n { provide: EventEmitter2, useValue: mockEventEmitter },\n ],\n })\n .setLogger(new MockLoggerService())\n .compile();\n\n target = module.get\u003cUserService>(UserService);\n });\n\n afterEach(() => {\n jest.clearAllMocks();\n });\n});\n```\n\n## Testing CRUD Operations\n\n### Create Operation\n\n```typescript\ndescribe('create', () => {\n it('should create user and return created entity', async () => {\n // Arrange\n const input: CreateUserDto = {\n email: '[email protected]',\n name: 'John Doe',\n };\n const expected: User = {\n id: 'user-123',\n email: '[email protected]',\n name: 'John Doe',\n createdAt: new Date(),\n };\n mockRepository.create.mockResolvedValue(expected);\n\n // Act\n const result = await target.create(input);\n\n // Assert\n expect(result).toEqual(expected);\n expect(mockRepository.create).toHaveBeenCalledWith(input);\n expect(mockRepository.create).toHaveBeenCalledTimes(1);\n });\n\n it('should emit event after creation', async () => {\n // Arrange\n const input: CreateUserDto = { email: '[email protected]', name: 'John' };\n const created: User = { id: 'user-123', ...input, createdAt: new Date() };\n mockRepository.create.mockResolvedValue(created);\n\n // Act\n await target.create(input);\n\n // Assert\n expect(mockEventEmitter.emit).toHaveBeenCalledWith(\n 'user.created',\n expect.objectContaining({ userId: 'user-123' })\n );\n });\n});\n```\n\n### Read Operations\n\n```typescript\ndescribe('findById', () => {\n it('should return user when exists', async () => {\n // Arrange\n const expected: User = { id: 'user-123', email: '[email protected]', name: 'John' };\n mockRepository.findById.mockResolvedValue(expected);\n\n // Act\n const result = await target.findById('user-123');\n\n // Assert\n expect(result).toEqual(expected);\n });\n\n it('should throw NotFoundException when not exists', async () => {\n // Arrange\n mockRepository.findById.mockResolvedValue(null);\n\n // Act & Assert\n await expect(target.findById('invalid')).rejects.toThrow(NotFoundException);\n });\n});\n\ndescribe('findAll', () => {\n it('should return paginated results', async () => {\n // Arrange\n const users: User[] = [\n { id: 'user-1', email: '[email protected]', name: 'Alice' },\n { id: 'user-2', email: '[email protected]', name: 'Bob' },\n ];\n mockRepository.findAll.mockResolvedValue({ items: users, total: 100 });\n\n // Act\n const result = await target.findAll({ page: 1, limit: 10 });\n\n // Assert\n expect(result.items).toEqual(users);\n expect(result.total).toBe(100);\n });\n});\n```\n\n### Update Operation\n\n```typescript\ndescribe('update', () => {\n it('should update and return updated entity', async () => {\n // Arrange\n const existingUser: User = { id: 'user-123', email: '[email protected]', name: 'Old Name' };\n const updateInput: UpdateUserDto = { name: 'New Name' };\n const expected: User = { ...existingUser, name: 'New Name' };\n\n mockRepository.findById.mockResolvedValue(existingUser);\n mockRepository.update.mockResolvedValue(expected);\n\n // Act\n const result = await target.update('user-123', updateInput);\n\n // Assert\n expect(result.name).toBe('New Name');\n expect(mockRepository.update).toHaveBeenCalledWith('user-123', updateInput);\n });\n\n it('should throw NotFoundException when user not exists', async () => {\n // Arrange\n mockRepository.findById.mockResolvedValue(null);\n\n // Act & Assert\n await expect(target.update('invalid', { name: 'New' }))\n .rejects.toThrow(NotFoundException);\n });\n});\n```\n\n### Delete Operation\n\n```typescript\ndescribe('delete', () => {\n it('should soft delete user', async () => {\n // Arrange\n const existingUser: User = { id: 'user-123', email: '[email protected]', name: 'John' };\n mockRepository.findById.mockResolvedValue(existingUser);\n mockRepository.softDelete.mockResolvedValue({ affected: 1 });\n\n // Act\n await target.delete('user-123');\n\n // Assert\n expect(mockRepository.softDelete).toHaveBeenCalledWith('user-123');\n });\n\n it('should throw ForbiddenException when deleting admin', async () => {\n // Arrange\n const adminUser: User = { id: 'admin-1', email: '[email protected]', name: 'Admin', role: 'admin' };\n mockRepository.findById.mockResolvedValue(adminUser);\n\n // Act & Assert\n await expect(target.delete('admin-1')).rejects.toThrow(ForbiddenException);\n });\n});\n```\n\n## Testing Business Logic\n\n```typescript\ndescribe('calculateDiscount', () => {\n it('should apply 10% discount for premium users', async () => {\n // Arrange\n const user: User = { id: 'user-123', tier: 'premium' };\n const order = { subtotal: 100 };\n mockRepository.findById.mockResolvedValue(user);\n\n // Act\n const result = await target.calculateDiscount('user-123', order);\n\n // Assert\n expect(result.discount).toBe(10);\n expect(result.total).toBe(90);\n });\n\n it('should apply 0% discount for basic users', async () => {\n // Arrange\n const user: User = { id: 'user-123', tier: 'basic' };\n const order = { subtotal: 100 };\n mockRepository.findById.mockResolvedValue(user);\n\n // Act\n const result = await target.calculateDiscount('user-123', order);\n\n // Assert\n expect(result.discount).toBe(0);\n expect(result.total).toBe(100);\n });\n});\n```\n\n## Testing External Service Integration\n\n```typescript\ndescribe('sendNotification', () => {\n it('should send email notification', async () => {\n // Arrange\n const user: User = { id: 'user-123', email: '[email protected]', name: 'John' };\n mockRepository.findById.mockResolvedValue(user);\n mockEmailService.send.mockResolvedValue({ messageId: 'msg-456' });\n\n // Act\n await target.sendNotification('user-123', 'Welcome!');\n\n // Assert\n expect(mockEmailService.send).toHaveBeenCalledWith({\n to: '[email protected]',\n subject: 'Notification',\n body: 'Welcome!',\n });\n });\n\n it('should handle email service failure gracefully', async () => {\n // Arrange\n const user: User = { id: 'user-123', email: '[email protected]', name: 'John' };\n mockRepository.findById.mockResolvedValue(user);\n mockEmailService.send.mockRejectedValue(new Error('SMTP error'));\n\n // Act & Assert\n await expect(target.sendNotification('user-123', 'Welcome!'))\n .rejects.toThrow(InternalException);\n });\n});\n```\n\n## Testing Transaction Rollback Logic\n\n```typescript\ndescribe('transferFunds', () => {\n it('should rollback on failure', async () => {\n // Arrange\n const fromAccount = { id: 'acc-1', balance: 100 };\n const toAccount = { id: 'acc-2', balance: 50 };\n\n mockRepository.findById\n .mockResolvedValueOnce(fromAccount)\n .mockResolvedValueOnce(toAccount);\n mockRepository.updateBalance.mockResolvedValueOnce({ ...fromAccount, balance: 50 });\n mockRepository.updateBalance.mockRejectedValueOnce(new Error('DB error'));\n\n // Act & Assert\n await expect(target.transferFunds('acc-1', 'acc-2', 50))\n .rejects.toThrow(InternalException);\n\n // Verify rollback was attempted\n expect(mockRepository.rollback).toHaveBeenCalled();\n });\n});\n```\n\n## Auto-Mocking with useMocker\n\n```typescript\ndescribe('UserService with Auto-Mocking', () => {\n let target: UserService;\n\n beforeEach(async () => {\n const module: TestingModule = await Test.createTestingModule({\n providers: [UserService],\n })\n .useMocker(createMock) // Auto-mock all dependencies\n .setLogger(new MockLoggerService())\n .compile();\n\n target = module.get\u003cUserService>(UserService);\n });\n\n it('should create user', async () => {\n // All dependencies are automatically mocked\n const result = await target.create({ email: '[email protected]', name: 'John' });\n expect(result).toBeDefined();\n });\n});\n```\n\n## Testing with Dependency Injection Tokens\n\n```typescript\ndescribe('Service with Custom Tokens', () => {\n const CONFIG_TOKEN = 'APP_CONFIG';\n\n beforeEach(async () => {\n const module: TestingModule = await Test.createTestingModule({\n providers: [\n ConfigurableService,\n {\n provide: CONFIG_TOKEN,\n useValue: { apiUrl: 'http://test.com', timeout: 5000 },\n },\n ],\n }).compile();\n\n target = module.get\u003cConfigurableService>(ConfigurableService);\n });\n\n it('should use injected config', async () => {\n const result = await target.getApiUrl();\n expect(result).toBe('http://test.com');\n });\n});\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":9257,"content_sha256":"2acb021c550db65f8ac671945786f45772531a2b14b3240070cfe612f5c67044"},{"filename":"references/redis/redis.md","content":"# Redis Unit Testing Patterns\n\n## Overview\n\nUnit testing Redis cache operations, health checks, and pub/sub by mocking the Redis client without real connections.\n\n## When to Use\n\n| Test Type | Approach |\n|-----------|----------|\n| **Service unit tests** | Mock Redis client |\n| **Cache layer tests** | Mock get/set/del operations |\n| **Health checks** | Mock PING command |\n| **E2E tests** | Real Redis via Docker |\n\n## Key Libraries\n\n```json\n{\n \"@golevelup/ts-jest\": \"^0.4.0\",\n \"jest\": \"^29.7.0\",\n \"@nestjs/testing\": \"^11.0.12\",\n \"ioredis\": \"^5.0.0\"\n}\n```\n\n---\n\n## Standard Cache Service Test Template\n\n```typescript\nimport { Test, TestingModule } from '@nestjs/testing';\nimport { createMock, DeepMocked } from '@golevelup/ts-jest';\nimport { MockLoggerService } from 'src/shared/logger/services/mock-logger.service';\nimport { RedisService } from '@ocean-network-express/om-lib-nestjs-redis';\n\ndescribe('CacheService', () => {\n let target: CacheService;\n let mockRedisService: DeepMocked\u003cRedisService>;\n let mockRedisClient: DeepMocked\u003cany>;\n\n beforeEach(async () => {\n // Mock the low-level Redis client\n mockRedisClient = {\n get: jest.fn(),\n set: jest.fn(),\n setex: jest.fn(),\n del: jest.fn(),\n ping: jest.fn(),\n expire: jest.fn(),\n };\n\n // Mock the RedisService wrapper\n mockRedisService = createMock\u003cRedisService>({\n getOrThrow: jest.fn().mockReturnValue(mockRedisClient),\n });\n\n const module: TestingModule = await Test.createTestingModule({\n providers: [\n CacheService,\n { provide: RedisService, useValue: mockRedisService },\n ],\n })\n .setLogger(new MockLoggerService())\n .compile();\n\n target = module.get\u003cCacheService>(CacheService);\n });\n\n afterEach(() => {\n jest.clearAllMocks();\n });\n});\n```\n\n---\n\n## Testing Cache Operations\n\n### Cache Hit (GET)\n\n```typescript\ndescribe('get', () => {\n it('should return cached data on cache hit', async () => {\n // Arrange\n const key = 'user:123:profile';\n const cachedData = {\n id: '123',\n name: 'John Doe',\n email: '[email protected]',\n };\n mockRedisClient.get.mockResolvedValue(JSON.stringify(cachedData));\n\n // Act\n const result = await target.get(key);\n\n // Assert\n expect(result).toEqual(cachedData);\n expect(mockRedisClient.get).toHaveBeenCalledWith(key);\n expect(mockRedisClient.get).toHaveBeenCalledTimes(1);\n });\n\n it('should return null on cache miss', async () => {\n // Arrange\n mockRedisClient.get.mockResolvedValue(null);\n\n // Act\n const result = await target.get('non-existent-key');\n\n // Assert\n expect(result).toBeNull();\n });\n\n it('should handle JSON parse error gracefully', async () => {\n // Arrange\n mockRedisClient.get.mockResolvedValue('invalid-json{');\n\n // Act\n const result = await target.get('key');\n\n // Assert\n expect(result).toBeNull();\n });\n});\n```\n\n### Cache Set with TTL\n\n```typescript\ndescribe('set', () => {\n it('should store data with TTL', async () => {\n // Arrange\n const key = 'user:123:profile';\n const data = { id: '123', name: 'John' };\n const ttl = 300; // 5 minutes\n mockRedisClient.setex.mockResolvedValue('OK');\n\n // Act\n await target.set(key, data, ttl);\n\n // Assert\n expect(mockRedisClient.setex).toHaveBeenCalledWith(\n key,\n ttl,\n JSON.stringify(data)\n );\n });\n\n it('should store data with TTL and jitter', async () => {\n // Arrange\n const key = 'user:123:data';\n const data = { userId: '123' };\n const baseTtl = 300;\n const jitterRange = 30;\n\n // Act\n await target.setWithJitter(key, data, baseTtl, jitterRange);\n\n // Assert\n const [, actualTtl] = mockRedisClient.setex.mock.calls[0];\n expect(actualTtl).toBeGreaterThanOrEqual(baseTtl - jitterRange);\n expect(actualTtl).toBeLessThanOrEqual(baseTtl + jitterRange);\n });\n\n it('should use set without TTL when TTL is 0', async () => {\n // Arrange\n const key = 'permanent-key';\n const data = { value: 'permanent' };\n mockRedisClient.set.mockResolvedValue('OK');\n\n // Act\n await target.set(key, data, 0);\n\n // Assert\n expect(mockRedisClient.set).toHaveBeenCalledWith(key, JSON.stringify(data));\n expect(mockRedisClient.setex).not.toHaveBeenCalled();\n });\n});\n```\n\n### Cache Delete\n\n```typescript\ndescribe('delete', () => {\n it('should delete key from cache', async () => {\n // Arrange\n const key = 'user:123:profile';\n mockRedisClient.del.mockResolvedValue(1);\n\n // Act\n const result = await target.delete(key);\n\n // Assert\n expect(result).toBe(true);\n expect(mockRedisClient.del).toHaveBeenCalledWith(key);\n });\n\n it('should return false when key does not exist', async () => {\n // Arrange\n mockRedisClient.del.mockResolvedValue(0);\n\n // Act\n const result = await target.delete('non-existent');\n\n // Assert\n expect(result).toBe(false);\n });\n\n it('should delete multiple keys', async () => {\n // Arrange\n const keys = ['key1', 'key2', 'key3'];\n mockRedisClient.del.mockResolvedValue(3);\n\n // Act\n const result = await target.deleteMany(keys);\n\n // Assert\n expect(result).toBe(3);\n expect(mockRedisClient.del).toHaveBeenCalledWith(...keys);\n });\n});\n```\n\n---\n\n## Testing Graceful Degradation\n\n```typescript\ndescribe('Graceful Degradation', () => {\n it('should return null on Redis GET error', async () => {\n // Arrange\n mockRedisClient.get.mockRejectedValue(new Error('Redis connection failed'));\n\n // Act\n const result = await target.get('key');\n\n // Assert\n expect(result).toBeNull();\n });\n\n it('should not throw on Redis SET error', async () => {\n // Arrange\n mockRedisClient.setex.mockRejectedValue(new Error('Redis connection failed'));\n\n // Act & Assert\n await expect(target.set('key', { data: 'test' }, 300))\n .resolves.not.toThrow();\n });\n\n it('should log error on Redis failure', async () => {\n // Arrange\n const error = new Error('Connection timeout');\n mockRedisClient.get.mockRejectedValue(error);\n const loggerSpy = jest.spyOn(target['logger'], 'error');\n\n // Act\n await target.get('key');\n\n // Assert\n expect(loggerSpy).toHaveBeenCalledWith(\n expect.stringContaining('Redis error'),\n expect.any(Object)\n );\n });\n});\n```\n\n---\n\n## Testing Health Checks\n\n```typescript\ndescribe('RedisHealthIndicator', () => {\n let target: RedisHealthIndicator;\n let mockRedisClient: DeepMocked\u003cRedis>;\n let mockHealthIndicatorService: DeepMocked\u003cHealthIndicatorService>;\n\n beforeEach(async () => {\n mockRedisClient = createMock\u003cRedis>();\n mockRedisService = createMock\u003cRedisService>();\n mockHealthIndicatorService = createMock\u003cHealthIndicatorService>();\n mockRedisService.getOrThrow.mockReturnValue(mockRedisClient as any);\n\n const module: TestingModule = await Test.createTestingModule({\n providers: [\n RedisHealthIndicator,\n { provide: RedisService, useValue: mockRedisService },\n { provide: HealthIndicatorService, useValue: mockHealthIndicatorService },\n ],\n }).compile();\n\n target = module.get\u003cRedisHealthIndicator>(RedisHealthIndicator);\n });\n\n it('should return UP when PING returns PONG', async () => {\n // Arrange\n const mockIndicator = {\n up: jest.fn().mockReturnValue({ redis: { status: 'up' } }),\n down: jest.fn(),\n };\n mockHealthIndicatorService.check.mockReturnValue(mockIndicator as any);\n mockRedisClient.ping.mockResolvedValue('PONG');\n\n // Act\n const result = await target.isHealthy('redis');\n\n // Assert\n expect(result.redis.status).toBe('up');\n expect(mockRedisClient.ping).toHaveBeenCalledTimes(1);\n });\n\n it('should return DOWN when PING fails', async () => {\n // Arrange\n const error = new Error('Connection refused');\n const mockIndicator = {\n up: jest.fn(),\n down: jest.fn().mockReturnValue({\n redis: { status: 'down', message: 'Connection refused' },\n }),\n };\n mockHealthIndicatorService.check.mockReturnValue(mockIndicator as any);\n mockRedisClient.ping.mockRejectedValue(error);\n\n // Act\n const result = await target.isHealthy('redis');\n\n // Assert\n expect(result.redis.status).toBe('down');\n expect(mockIndicator.down).toHaveBeenCalled();\n });\n\n it('should handle getOrThrow throwing error', async () => {\n // Arrange\n const error = new Error('No redis connection');\n mockRedisService.getOrThrow.mockImplementation(() => {\n throw error;\n });\n const mockIndicator = {\n up: jest.fn(),\n down: jest.fn().mockReturnValue({ redis: { status: 'down' } }),\n };\n mockHealthIndicatorService.check.mockReturnValue(mockIndicator as any);\n\n // Act\n const result = await target.isHealthy('redis');\n\n // Assert\n expect(result.redis.status).toBe('down');\n });\n});\n```\n\n---\n\n## Testing Cache Key Patterns\n\n```typescript\ndescribe('Cache Key Generation', () => {\n it('should generate user-specific cache key', () => {\n // Arrange\n const userId = 'user-123';\n\n // Act\n const key = target.getUserCacheKey(userId, 'profile');\n\n // Assert\n expect(key).toBe('user:user-123:profile');\n });\n\n it('should generate session cache key', () => {\n // Arrange\n const sessionId = 'session-abc';\n\n // Act\n const key = target.getSessionCacheKey(sessionId);\n\n // Assert\n expect(key).toBe('session:session-abc');\n });\n\n it('should handle special characters in key', () => {\n // Arrange\n const userId = '[email protected]';\n\n // Act\n const key = target.getUserCacheKey(userId, 'data');\n\n // Assert\n expect(key).toBe('user:[email protected]:data');\n });\n});\n```\n\n---\n\n## Testing Cache-Aside Pattern\n\n```typescript\ndescribe('Cache-Aside Pattern', () => {\n it('should return cached value on hit', async () => {\n // Arrange\n const cachedUser = { id: '123', name: 'Cached User' };\n mockRedisClient.get.mockResolvedValue(JSON.stringify(cachedUser));\n\n // Act\n const result = await target.getOrFetch('user:123', async () => {\n return { id: '123', name: 'Fresh User' };\n });\n\n // Assert\n expect(result).toEqual(cachedUser);\n expect(mockRedisClient.get).toHaveBeenCalledTimes(1);\n });\n\n it('should fetch and cache on miss', async () => {\n // Arrange\n const freshUser = { id: '123', name: 'Fresh User' };\n mockRedisClient.get.mockResolvedValue(null);\n mockRedisClient.setex.mockResolvedValue('OK');\n\n // Act\n const result = await target.getOrFetch(\n 'user:123',\n async () => freshUser,\n 300\n );\n\n // Assert\n expect(result).toEqual(freshUser);\n expect(mockRedisClient.setex).toHaveBeenCalledWith(\n 'user:123',\n 300,\n JSON.stringify(freshUser)\n );\n });\n\n it('should return fresh data when cache fails', async () => {\n // Arrange\n const freshUser = { id: '123', name: 'Fresh User' };\n mockRedisClient.get.mockRejectedValue(new Error('Redis error'));\n\n // Act\n const result = await target.getOrFetch(\n 'user:123',\n async () => freshUser\n );\n\n // Assert\n expect(result).toEqual(freshUser);\n });\n});\n```\n\n---\n\n## Testing TTL with Jitter\n\n```typescript\ndescribe('TTL with Jitter', () => {\n it('should apply jitter to prevent cache stampede', async () => {\n // Arrange\n const baseTtl = 300;\n const jitterPercent = 0.1; // 10%\n const calls: number[] = [];\n\n mockRedisClient.setex.mockImplementation((key, ttl) => {\n calls.push(ttl);\n return Promise.resolve('OK');\n });\n\n // Act - Set multiple items\n for (let i = 0; i \u003c 10; i++) {\n await target.setWithJitter(`key-${i}`, { data: i }, baseTtl, jitterPercent);\n }\n\n // Assert - TTLs should vary\n const uniqueTtls = new Set(calls);\n expect(uniqueTtls.size).toBeGreaterThan(1);\n\n // All TTLs should be within jitter range\n const minTtl = baseTtl * (1 - jitterPercent);\n const maxTtl = baseTtl * (1 + jitterPercent);\n calls.forEach((ttl) => {\n expect(ttl).toBeGreaterThanOrEqual(minTtl);\n expect(ttl).toBeLessThanOrEqual(maxTtl);\n });\n });\n});\n```\n\n---\n\n## Testing Pub/Sub (if applicable)\n\n```typescript\ndescribe('Pub/Sub', () => {\n it('should publish message to channel', async () => {\n // Arrange\n const channel = 'notifications';\n const message = { type: 'user.created', userId: '123' };\n mockRedisClient.publish.mockResolvedValue(1);\n\n // Act\n const result = await target.publish(channel, message);\n\n // Assert\n expect(result).toBe(1);\n expect(mockRedisClient.publish).toHaveBeenCalledWith(\n channel,\n JSON.stringify(message)\n );\n });\n\n it('should subscribe to channel', async () => {\n // Arrange\n const channel = 'notifications';\n const handler = jest.fn();\n mockRedisClient.subscribe.mockResolvedValue(undefined);\n\n // Act\n await target.subscribe(channel, handler);\n\n // Assert\n expect(mockRedisClient.subscribe).toHaveBeenCalledWith(channel);\n });\n});\n```\n\n---\n\n## Best Practices\n\n1. **Mock both layers**: RedisService wrapper AND underlying Redis client\n2. **Test graceful degradation**: Cache failures should not crash the app\n3. **Use TTL with jitter**: Prevent cache stampede on expiration\n4. **Test health checks**: Verify PING/PONG for Kubernetes probes\n5. **Test cache-aside pattern**: Verify fetch-on-miss and cache-on-fetch\n6. **Verify JSON serialization**: Test both valid and invalid JSON handling\n7. **Test key generation**: Ensure consistent cache key patterns\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":13535,"content_sha256":"a55cdba9a2fee6058c8eb76dceb652baa61c1eff2f9658ee379a7e8ade460a51"},{"filename":"references/repository/mongodb.md","content":"# MongoDB Repository Testing with mongodb-memory-server\n\n## Overview\n\nUse `mongodb-memory-server` for repository tests to verify actual MongoDB operations with real queries, indexes, and aggregations.\n\n## When to Use\n\n| Test Type | Approach |\n|-----------|----------|\n| **Service/Usecase tests** | Mock repository interface |\n| **Repository implementation tests** | Use mongodb-memory-server |\n| **E2E tests** | Real MongoDB via Docker |\n\n## Installation\n\n```bash\nnpm install -D mongodb-memory-server\n```\n\n## Test Helper Setup\n\n```typescript\n// test/helpers/mongodb-test.helper.ts\nimport { MongoMemoryServer } from 'mongodb-memory-server';\nimport { MongooseModule, MongooseModuleOptions } from '@nestjs/mongoose';\n\nlet mongoServer: MongoMemoryServer;\n\nexport async function createMongoMemoryServer(): Promise\u003cMongoMemoryServer> {\n mongoServer = await MongoMemoryServer.create();\n return mongoServer;\n}\n\nexport function getMongooseTestModule(server: MongoMemoryServer): ReturnType\u003ctypeof MongooseModule.forRoot> {\n return MongooseModule.forRoot(server.getUri(), {\n // Mongoose options\n });\n}\n\nexport async function closeMongoMemoryServer(): Promise\u003cvoid> {\n if (mongoServer) {\n await mongoServer.stop();\n }\n}\n```\n\n## Standard Repository Test Template\n\n```typescript\nimport { Test, TestingModule } from '@nestjs/testing';\nimport { MongooseModule, getModelToken } from '@nestjs/mongoose';\nimport { Model } from 'mongoose';\nimport { MongoMemoryServer } from 'mongodb-memory-server';\nimport { createMongoMemoryServer, getMongooseTestModule } from 'test/helpers/mongodb-test.helper';\nimport { MockLoggerService } from 'src/shared/logger/services/mock-logger.service';\n\ndescribe('UserRepository', () => {\n let target: UserRepository;\n let userModel: Model\u003cUserDocument>;\n let mongoServer: MongoMemoryServer;\n let moduleRef: TestingModule;\n\n beforeAll(async () => {\n mongoServer = await createMongoMemoryServer();\n const mongooseModule = getMongooseTestModule(mongoServer);\n\n moduleRef = await Test.createTestingModule({\n imports: [\n mongooseModule,\n MongooseModule.forFeature([\n { name: UserDocument.name, schema: UserSchema },\n ]),\n ],\n providers: [UserRepository],\n })\n .setLogger(new MockLoggerService())\n .compile();\n\n target = moduleRef.get\u003cUserRepository>(UserRepository);\n userModel = moduleRef.get(getModelToken(UserDocument.name));\n\n // Create indexes (important for unique constraints)\n await userModel.createIndexes();\n }, 60000);\n\n afterAll(async () => {\n await moduleRef?.close();\n await mongoServer?.stop();\n });\n\n beforeEach(async () => {\n // Clean slate per test\n await userModel.deleteMany({});\n });\n});\n```\n\n## Testing CRUD Operations\n\n### Create\n\n```typescript\ndescribe('create', () => {\n it('should create and persist user', async () => {\n // Arrange\n const input = {\n email: '[email protected]',\n name: 'John Doe',\n };\n\n // Act\n const result = await target.create(input);\n\n // Assert\n expect(result.id).toBeDefined();\n expect(result.email).toBe('[email protected]');\n expect(result.name).toBe('John Doe');\n\n // Verify persisted in database\n const stored = await userModel.findById(result.id).exec();\n expect(stored).toBeDefined();\n expect(stored?.email).toBe('[email protected]');\n });\n\n it('should throw on duplicate email', async () => {\n // Arrange\n await target.create({ email: '[email protected]', name: 'First' });\n\n // Act & Assert\n await expect(\n target.create({ email: '[email protected]', name: 'Second' })\n ).rejects.toThrow();\n });\n});\n```\n\n### Read\n\n```typescript\ndescribe('findById', () => {\n it('should find user by ID', async () => {\n // Arrange\n const created = await target.create({\n email: '[email protected]',\n name: 'John',\n });\n\n // Act\n const result = await target.findById(created.id);\n\n // Assert\n expect(result).toBeDefined();\n expect(result?.id).toBe(created.id);\n expect(result?.email).toBe('[email protected]');\n });\n\n it('should return null for non-existent ID', async () => {\n // Arrange\n const nonExistentId = new Types.ObjectId().toString();\n\n // Act\n const result = await target.findById(nonExistentId);\n\n // Assert\n expect(result).toBeNull();\n });\n});\n\ndescribe('findAll', () => {\n it('should return paginated results', async () => {\n // Arrange\n await target.create({ email: '[email protected]', name: 'A' });\n await target.create({ email: '[email protected]', name: 'B' });\n await target.create({ email: '[email protected]', name: 'C' });\n\n // Act\n const result = await target.findAll({ page: 1, limit: 2 });\n\n // Assert\n expect(result.items).toHaveLength(2);\n expect(result.total).toBe(3);\n });\n\n it('should return empty array when no data', async () => {\n // Act\n const result = await target.findAll({ page: 1, limit: 10 });\n\n // Assert\n expect(result.items).toEqual([]);\n expect(result.total).toBe(0);\n });\n});\n```\n\n### Update\n\n```typescript\ndescribe('update', () => {\n it('should update user and return updated document', async () => {\n // Arrange\n const created = await target.create({\n email: '[email protected]',\n name: 'Old Name',\n });\n\n // Act\n const result = await target.update(created.id, { name: 'New Name' });\n\n // Assert\n expect(result?.name).toBe('New Name');\n expect(result?.email).toBe('[email protected]'); // Unchanged\n\n // Verify in database\n const stored = await userModel.findById(created.id).exec();\n expect(stored?.name).toBe('New Name');\n });\n\n it('should return null when updating non-existent document', async () => {\n // Arrange\n const nonExistentId = new Types.ObjectId().toString();\n\n // Act\n const result = await target.update(nonExistentId, { name: 'New' });\n\n // Assert\n expect(result).toBeNull();\n });\n});\n```\n\n### Delete\n\n```typescript\ndescribe('delete', () => {\n it('should soft delete user', async () => {\n // Arrange\n const created = await target.create({\n email: '[email protected]',\n name: 'John',\n });\n\n // Act\n await target.softDelete(created.id);\n\n // Assert - Should not appear in normal queries\n const found = await target.findById(created.id);\n expect(found).toBeNull();\n\n // But exists in database with deletedAt\n const stored = await userModel.findById(created.id).exec();\n expect(stored?.deletedAt).toBeDefined();\n });\n\n it('should hard delete user', async () => {\n // Arrange\n const created = await target.create({\n email: '[email protected]',\n name: 'John',\n });\n\n // Act\n await target.hardDelete(created.id);\n\n // Assert\n const stored = await userModel.findById(created.id).exec();\n expect(stored).toBeNull();\n });\n});\n```\n\n## Testing Indexes and Constraints\n\n```typescript\ndescribe('Indexes', () => {\n it('should enforce unique email constraint', async () => {\n // Arrange\n await target.create({ email: '[email protected]', name: 'First' });\n\n // Act & Assert\n await expect(\n target.create({ email: '[email protected]', name: 'Second' })\n ).rejects.toThrow(/duplicate key/);\n });\n\n it('should allow searching by indexed field', async () => {\n // Arrange\n await target.create({ email: '[email protected]', name: 'John' });\n await target.create({ email: '[email protected]', name: 'Jane' });\n\n // Act\n const result = await target.findByEmail('[email protected]');\n\n // Assert\n expect(result?.name).toBe('John');\n });\n});\n```\n\n## Testing Aggregations\n\n```typescript\ndescribe('Aggregations', () => {\n it('should aggregate user statistics', async () => {\n // Arrange\n await target.create({ email: '[email protected]', name: 'A', role: 'admin' });\n await target.create({ email: '[email protected]', name: 'B', role: 'user' });\n await target.create({ email: '[email protected]', name: 'C', role: 'user' });\n\n // Act\n const result = await target.countByRole();\n\n // Assert\n expect(result).toEqual(\n expect.arrayContaining([\n { role: 'admin', count: 1 },\n { role: 'user', count: 2 },\n ])\n );\n });\n});\n```\n\n## Testing Transactions (MongoDB 4.0+)\n\n```typescript\ndescribe('Transactions', () => {\n it('should rollback on error', async () => {\n // Arrange\n const user = await target.create({ email: '[email protected]', name: 'John' });\n\n // Act\n try {\n await target.transferCredits(user.id, 'non-existent-id', 100);\n } catch (error) {\n // Expected to fail\n }\n\n // Assert - Original user should be unchanged\n const stored = await userModel.findById(user.id).exec();\n expect(stored?.credits).toBe(0); // Not deducted\n });\n});\n```\n\n## Performance Tips\n\n1. **Use `--runInBand`** for sequential test execution to avoid port conflicts\n2. **Reuse MongoMemoryServer** across tests in the same file\n3. **Clean data in `beforeEach`**, not `afterEach` - tests should clean up before, not after\n4. **Create indexes in `beforeAll`** - indexes only need to be created once\n5. **Use v9.x if encountering resource cleanup issues** with v10+\n\n## Timeout Configuration\n\n```typescript\n// jest.config.js\nmodule.exports = {\n testTimeout: 60000, // 60 seconds for in-memory DB setup\n};\n\n// Or per-test\nbeforeAll(async () => {\n // setup\n}, 60000); // 60 second timeout\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":9359,"content_sha256":"9f5ba02f121cd133b35f54ca397fee71e52344afab93c9c4b34e86f9c66b6a24"},{"filename":"references/repository/postgres.md","content":"# PostgreSQL Repository Testing with pg-mem\n\n## Overview\n\nUse `pg-mem` for repository tests to verify actual TypeORM queries, constraints, and schema compliance without a real PostgreSQL server.\n\n## When to Use\n\n| Test Type | Approach |\n|-----------|----------|\n| **Service/Usecase tests** | Mock repository interface |\n| **Repository implementation tests** | Use pg-mem |\n| **E2E tests** | Real PostgreSQL via Docker |\n\n## Installation\n\n```bash\nnpm install -D pg-mem\n```\n\n## Test Helper Setup\n\n```typescript\n// test/helpers/pgmock.helper.ts\nimport { newDb, DataType, IMemoryDb } from 'pg-mem';\nimport { DataSource } from 'typeorm';\n\nexport async function createPgMemDataSource(\n entities: Function[]\n): Promise\u003cDataSource> {\n const db = newDb({ autoCreateForeignKeyIndices: true });\n\n // Register common PostgreSQL functions\n db.public.registerFunction({\n name: 'current_database',\n returns: DataType.text,\n implementation: () => 'test_db',\n });\n\n db.public.registerFunction({\n name: 'version',\n returns: DataType.text,\n implementation: () => 'PostgreSQL 14.0 (pg-mem)',\n });\n\n // Create TypeORM DataSource\n const dataSource = await db.adapters.createTypeormDataSource({\n type: 'postgres',\n entities,\n synchronize: true,\n });\n\n await dataSource.initialize();\n\n return dataSource;\n}\n\nexport class PgMockTestHelper {\n constructor(private dataSource: DataSource) {}\n\n async clearTable(entity: Function): Promise\u003cvoid> {\n const repository = this.dataSource.getRepository(entity);\n await repository.clear();\n }\n\n async assertRecordExists(\n entity: Function,\n where: Record\u003cstring, unknown>,\n expected: Record\u003cstring, unknown>\n ): Promise\u003cvoid> {\n const repository = this.dataSource.getRepository(entity);\n const record = await repository.findOne({ where });\n\n expect(record).toBeDefined();\n expect(record).toMatchObject(expected);\n }\n\n async assertRecordCount(\n entity: Function,\n where: Record\u003cstring, unknown>,\n count: number\n ): Promise\u003cvoid> {\n const repository = this.dataSource.getRepository(entity);\n const actualCount = await repository.count({ where });\n\n expect(actualCount).toBe(count);\n }\n\n async assertSoftDeleted(entity: Function, id: string): Promise\u003cvoid> {\n const repository = this.dataSource.getRepository(entity);\n const record = await repository.findOne({\n where: { id },\n withDeleted: true,\n });\n\n expect(record).toBeDefined();\n expect(record?.isValid).toBe(false);\n }\n}\n```\n\n## Standard Repository Test Template\n\n```typescript\nimport { Test, TestingModule } from '@nestjs/testing';\nimport { getRepositoryToken } from '@nestjs/typeorm';\nimport { DataSource } from 'typeorm';\nimport { createPgMemDataSource, PgMockTestHelper } from 'test/helpers/pgmock.helper';\nimport { MockLoggerService } from 'src/shared/logger/services/mock-logger.service';\nimport { DBConnections } from '../utils/constaint';\n\ndescribe('BlogRepositoryImpl', () => {\n let target: BlogRepositoryImpl;\n let dataSource: DataSource;\n let helper: PgMockTestHelper;\n let moduleRef: TestingModule;\n\n beforeAll(async () => {\n dataSource = await createPgMemDataSource([BlogSchema]);\n helper = new PgMockTestHelper(dataSource);\n\n const repository = dataSource.getRepository(BlogSchema);\n\n moduleRef = await Test.createTestingModule({\n providers: [\n BlogRepositoryImpl,\n {\n provide: getRepositoryToken(BlogSchema, DBConnections.INTERNAL),\n useValue: repository,\n },\n ],\n })\n .setLogger(new MockLoggerService())\n .compile();\n\n target = moduleRef.get\u003cBlogRepositoryImpl>(BlogRepositoryImpl);\n }, 60000);\n\n afterAll(async () => {\n await moduleRef?.close();\n await dataSource?.destroy();\n });\n\n beforeEach(async () => {\n await helper.clearTable(BlogSchema);\n });\n});\n```\n\n## Testing CRUD Operations\n\n### Create\n\n```typescript\ndescribe('create', () => {\n it('should create and persist blog', async () => {\n // Arrange\n const input: Partial\u003cBlog> = {\n title: 'Test Blog',\n content: 'Test Content',\n authorId: 'author-1',\n };\n\n // Act\n const result = await target.create(input);\n\n // Assert\n expect(result.id).toBeDefined();\n expect(result.title).toBe('Test Blog');\n expect(result.content).toBe('Test Content');\n expect(result.authorId).toBe('author-1');\n expect(result.isValid).toBe(true);\n\n // Verify persisted\n await helper.assertRecordExists(BlogSchema, { id: result.id }, input);\n });\n\n it('should auto-generate UUID for id', async () => {\n // Act\n const result = await target.create({\n title: 'Test',\n content: 'Content',\n authorId: 'author-1',\n });\n\n // Assert\n expect(result.id).toMatch(\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/\n );\n });\n});\n```\n\n### Read\n\n```typescript\ndescribe('findById', () => {\n it('should find blog by ID', async () => {\n // Arrange\n const created = await target.create({\n title: 'Test Blog',\n content: 'Content',\n authorId: 'author-1',\n });\n\n // Act\n const result = await target.findById(created.id);\n\n // Assert\n expect(result).toBeDefined();\n expect(result?.id).toBe(created.id);\n expect(result?.title).toBe('Test Blog');\n });\n\n it('should return null for non-existent ID', async () => {\n // Act\n const result = await target.findById('non-existent-id');\n\n // Assert\n expect(result).toBeNull();\n });\n\n it('should not return soft-deleted records', async () => {\n // Arrange\n const created = await target.create({\n title: 'Test',\n content: 'Content',\n authorId: 'author-1',\n });\n await target.softDelete(created.id);\n\n // Act\n const result = await target.findById(created.id);\n\n // Assert\n expect(result).toBeNull();\n });\n});\n\ndescribe('findAll', () => {\n it('should return paginated results', async () => {\n // Arrange\n await target.create({ title: 'Blog 1', content: 'C1', authorId: 'a1' });\n await target.create({ title: 'Blog 2', content: 'C2', authorId: 'a1' });\n await target.create({ title: 'Blog 3', content: 'C3', authorId: 'a1' });\n\n // Act\n const result = await target.findAll({ page: 1, limit: 2 });\n\n // Assert\n expect(result.items).toHaveLength(2);\n expect(result.total).toBe(3);\n });\n\n it('should exclude soft-deleted records', async () => {\n // Arrange\n const blog1 = await target.create({ title: 'Blog 1', content: 'C1', authorId: 'a1' });\n await target.create({ title: 'Blog 2', content: 'C2', authorId: 'a1' });\n await target.softDelete(blog1.id);\n\n // Act\n const result = await target.findAll({ page: 1, limit: 10 });\n\n // Assert\n expect(result.items).toHaveLength(1);\n expect(result.total).toBe(1);\n expect(result.items[0].title).toBe('Blog 2');\n });\n});\n```\n\n### Update\n\n```typescript\ndescribe('update', () => {\n it('should update and return updated entity', async () => {\n // Arrange\n const created = await target.create({\n title: 'Old Title',\n content: 'Old Content',\n authorId: 'author-1',\n });\n\n // Act\n const result = await target.update(created.id, { title: 'New Title' });\n\n // Assert\n expect(result?.title).toBe('New Title');\n expect(result?.content).toBe('Old Content'); // Unchanged\n\n // Verify in database\n await helper.assertRecordExists(\n BlogSchema,\n { id: created.id },\n { title: 'New Title' }\n );\n });\n\n it('should update updatedAt timestamp', async () => {\n // Arrange\n const created = await target.create({\n title: 'Test',\n content: 'Content',\n authorId: 'author-1',\n });\n const originalUpdatedAt = created.updatedAt;\n\n // Wait a bit to ensure different timestamp\n await new Promise(resolve => setTimeout(resolve, 10));\n\n // Act\n const result = await target.update(created.id, { title: 'Updated' });\n\n // Assert\n expect(result?.updatedAt.getTime()).toBeGreaterThan(originalUpdatedAt.getTime());\n });\n});\n```\n\n### Soft Delete\n\n```typescript\ndescribe('softDelete', () => {\n it('should soft delete by setting isValid to false', async () => {\n // Arrange\n const created = await target.create({\n title: 'Test',\n content: 'Content',\n authorId: 'author-1',\n });\n\n // Act\n await target.softDelete(created.id);\n\n // Assert\n await helper.assertSoftDeleted(BlogSchema, created.id);\n });\n\n it('should update deletedAt timestamp', async () => {\n // Arrange\n const created = await target.create({\n title: 'Test',\n content: 'Content',\n authorId: 'author-1',\n });\n\n // Act\n await target.softDelete(created.id);\n\n // Assert\n const repository = dataSource.getRepository(BlogSchema);\n const record = await repository.findOne({\n where: { id: created.id },\n withDeleted: true,\n });\n expect(record?.deletedAt).toBeDefined();\n });\n});\n```\n\n## Testing Query Filters\n\n```typescript\ndescribe('Query Filters', () => {\n it('should filter by author', async () => {\n // Arrange\n await target.create({ title: 'Blog 1', content: 'C1', authorId: 'author-1' });\n await target.create({ title: 'Blog 2', content: 'C2', authorId: 'author-2' });\n await target.create({ title: 'Blog 3', content: 'C3', authorId: 'author-1' });\n\n // Act\n const result = await target.findByAuthor('author-1', { page: 1, limit: 10 });\n\n // Assert\n expect(result.items).toHaveLength(2);\n expect(result.items.every(b => b.authorId === 'author-1')).toBe(true);\n });\n\n it('should search by title', async () => {\n // Arrange\n await target.create({ title: 'NestJS Tutorial', content: 'C1', authorId: 'a1' });\n await target.create({ title: 'React Guide', content: 'C2', authorId: 'a1' });\n await target.create({ title: 'NestJS Best Practices', content: 'C3', authorId: 'a1' });\n\n // Act\n const result = await target.search('NestJS', { page: 1, limit: 10 });\n\n // Assert\n expect(result.items).toHaveLength(2);\n });\n});\n```\n\n## Testing Sorting\n\n```typescript\ndescribe('Sorting', () => {\n it('should sort by createdAt descending', async () => {\n // Arrange\n await target.create({ title: 'First', content: 'C1', authorId: 'a1' });\n await new Promise(r => setTimeout(r, 10));\n await target.create({ title: 'Second', content: 'C2', authorId: 'a1' });\n await new Promise(r => setTimeout(r, 10));\n await target.create({ title: 'Third', content: 'C3', authorId: 'a1' });\n\n // Act\n const result = await target.findAll({\n page: 1,\n limit: 10,\n sortBy: 'createdAt',\n order: 'desc',\n });\n\n // Assert\n expect(result.items[0].title).toBe('Third');\n expect(result.items[2].title).toBe('First');\n });\n});\n```\n\n## Testing Unique Constraints\n\n```typescript\ndescribe('Unique Constraints', () => {\n it('should enforce unique slug', async () => {\n // Arrange\n await target.create({\n title: 'First Blog',\n slug: 'my-blog',\n content: 'C1',\n authorId: 'a1',\n });\n\n // Act & Assert\n await expect(\n target.create({\n title: 'Second Blog',\n slug: 'my-blog', // Duplicate\n content: 'C2',\n authorId: 'a1',\n })\n ).rejects.toThrow();\n });\n});\n```\n\n## Key Differences from Usecase Tests\n\n| Usecase Tests | Repository Tests |\n|---------------|------------------|\n| Mock repository interface | Real pg-mem database |\n| Verify business logic | Validate SQL behavior |\n| Fast (no DB setup) | Slower (DB initialization) |\n| Test service layer | Test data layer |\n\n## pg-mem Limitations\n\n1. Not all PostgreSQL functions are supported - register custom functions as needed\n2. Some advanced features (CTEs, window functions) may have limited support\n3. Performance characteristics differ from real PostgreSQL\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":11836,"content_sha256":"605d3647d552b19d0a8e75757949c4ea95b49139a5cfca3081873a98c9a12146"},{"filename":"workflows/debugging/steps/step-01-init.md","content":"---\nname: 'step-01-init'\ndescription: 'Initialize debugging session — reproduce failure and detect continuation'\nnextStepFile: './step-02-classify-failure.md'\n---\n\n# Step 1: Initialize Debugging Session\n\n## STEP GOAL\n\nSet up the debugging session: identify the failing test, reproduce the failure, set the output path, and check for an existing output to resume.\n\n## EXECUTION\n\n### 1. Ask the User\n\nAsk the user:\n- **Failing test name** (exact test description string)\n- **Test file path** (e.g., `src/modules/user/user.service.spec.ts`)\n- **Output path** for the debug resolution report (suggest default: `./debug-report-{{date}}.md`)\n- **Or provide path to an existing report** to resume a previous session\n\n### 2. Check for Existing Report\n\nIf the user provides a path to an existing report file:\n- Read the file\n- Parse the YAML frontmatter\n- If `stepsCompleted` is non-empty → **STOP and load `step-01b-continue.md`**\n\n### 3. Fresh Workflow Setup\n\nIf starting fresh:\n1. Copy the template from `templates/output-template.md`\n2. Fill in the frontmatter:\n - `failingTest`: the exact test name provided by the user\n - `testFile`: the test file path\n - `outputPath`: the chosen output path\n - `date`: current date\n3. Write the initialized report to the output path\n\n### 4. Initialize Session and Reproduce the Failure\n\nInitialize the temp file session and run the failing test in isolation:\n\n```bash\n# Initialize session (once at start of debugging)\nexport UT_SESSION=$(date +%s)-$\n\n# Run the failing test in isolation (output to temp file only, no console)\nnpm test -- -t \"[exact test name]\" > /tmp/ut-${UT_SESSION}-debug.log 2>&1\n\n# Read only summary\ntail -50 /tmp/ut-${UT_SESSION}-debug.log\n```\n\nExtract error details:\n\n```bash\ngrep -B 5 -A 20 \"FAIL\\|Error:\" /tmp/ut-${UT_SESSION}-debug.log\n```\n\n### 5. Document Failure in Tracking File\n\nCreate the tracking file to record the failure:\n\n```bash\ncat > /tmp/ut-${UT_SESSION}-failures.md \u003c\u003c 'EOF'\n# Unit Test Failures\n\n## Test 1: \"[test name]\"\n- File: [path/to/file.spec.ts:line]\n- Error: [error type]\n- Expected: [expected value]\n- Received: [received value]\n- Status: IN_PROGRESS\nEOF\n```\n\n### 6. Append Failure Reproduction to Report\n\nAppend to the output document:\n\n```markdown\n## Step 1: Failure Reproduction\n\n**Test**: {{failingTest}}\n**File**: {{testFile}}\n**Error Output**:\n{{extracted error details}}\n\n**Failure consistently reproduced**: YES/NO\n```\n\n## FRONTMATTER UPDATE\n\nUpdate the output document frontmatter:\n- Add `1` to `stepsCompleted`\n- Fill `failingTest` and `testFile`\n\n## PRESENT TO USER\n\nShow the user:\n- Confirmation of the failing test and output path\n- The reproduced failure details (error message, expected vs received)\n- Session ID for temp file tracking\n\nThen ask: **[C] Continue to Step 2: Classify the Failure Type**\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-02-classify-failure.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2888,"content_sha256":"6bee98e7d54dd2507676be9a3f1634168b11b3161c39d4c1d43b882619a94e04"},{"filename":"workflows/debugging/steps/step-01b-continue.md","content":"---\nname: 'step-01b-continue'\ndescription: 'Resume debugging session from last completed step'\n---\n\n# Step 1b: Continue Previous Debugging Session\n\n## STEP GOAL\n\nResume a previously started debugging session by reading the existing report, determining progress, and routing to the next incomplete step.\n\n## EXECUTION\n\n### 1. Read Existing Report\n\n- Read the file at the output path provided by the user\n- Parse the YAML frontmatter\n- Extract `stepsCompleted` array\n\n### 2. Show Progress Summary\n\nDisplay to the user:\n\n```\nDebugging Session Progress\n==========================\nFailing Test: {{failingTest}}\nTest File: {{testFile}}\nFailure Type: {{failureType}}\nDate Started: {{date}}\nSteps Completed: {{stepsCompleted}}\n\nStep Map:\n [1] Reproduce Failure {{done/pending}}\n [2] Classify Failure Type {{done/pending}}\n [3] Analyze Root Cause {{done/pending}}\n [4] Implement Fix {{done/pending}}\n [5] Verify Fix {{done/pending}}\n [6] Document Resolution {{done/pending}}\n```\n\n### 3. Offer Options\n\nPresent:\n- **[R] Resume** from the next incomplete step\n- **[O] Overview** - re-read the existing report content before resuming\n- **[X] Start over** - create a fresh report (confirm: this will overwrite)\n\n### 4. Route to Next Step\n\nOn **[R]** or after **[O]**:\n\nDetermine the next step from `max(stepsCompleted) + 1` and load the corresponding file:\n\n| Next Step | File |\n|-----------|------|\n| 2 | `step-02-classify-failure.md` |\n| 3 | `step-03-analyze.md` |\n| 4 | `step-04-implement-fix.md` |\n| 5 | `step-05-verify-fix.md` |\n| 6 | `step-06-document-resolution.md` |\n\nOn **[X]**: Go back to `step-01-init.md` fresh workflow setup (section 3).\n\n## NEXT STEP\n\nLoad the step file determined above.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1754,"content_sha256":"b501d1ea6b927fb11f0515e4ba9ff1e55f4c0f70df403b80593c14773a457789"},{"filename":"workflows/debugging/steps/step-02-classify-failure.md","content":"---\nname: 'step-02-classify-failure'\ndescription: 'Classify the failure type to guide analysis'\nnextStepFile: './step-03-analyze.md'\nreferenceFiles:\n - 'references/common/rules.md'\n - 'references/common/knowledge.md'\n---\n\n# Step 2: Classify the Failure Type\n\n## STEP GOAL\n\nIdentify and categorize the test failure to determine the correct analysis approach.\n\n## REFERENCE LOADING\n\nBefore starting classification, load and read:\n- `references/common/rules.md` - Understand expected test patterns and conventions\n- `references/common/knowledge.md` - Understand test lifecycle and framework behavior\n\n## EXECUTION\n\n### 1. Identify Failure Category\n\nUsing the reproduced error output from Step 1, classify the failure into one of these categories:\n\n| Type | Indicators | Common Causes |\n|------|------------|---------------|\n| Assertion Failure | \"Expected X, received Y\" | Wrong mock return, logic error |\n| Exception | \"Error: [message]\" | Thrown error not caught/expected |\n| Timeout | \"exceeded timeout\" | Unresolved promise, missing mock |\n| Mock Error | \"is not a function\" | Missing mock setup |\n| Module Error | \"Cannot find module\" | Import/path issue |\n| Type Error | \"is not defined\" | Missing variable/import |\n\n### 2. Determine Category and Likely Cause\n\nExamine the error output carefully and determine:\n\n```\n**Failure Category:** [category from table above]\n**Likely Cause:** [initial hypothesis based on error indicators]\n```\n\nConsider:\n- What is the exact error message?\n- Where in the test lifecycle does it fail (setup, act, assert)?\n- Are there any dependency-related clues?\n\n## PRESENT FINDINGS\n\nPresent to the user:\n\n```\nStep 2: Failure Classification\n==============================\n\nFailure Category: [category]\nIndicators Found: [matching indicators from error output]\nLikely Cause: [initial hypothesis]\nAnalysis Approach: Type [A/B/C/D] - [category name]\n```\n\nThen ask: **[C] Continue to Step 3: Deep Analysis**\n\n## FRONTMATTER UPDATE\n\nUpdate the output document:\n- Add `2` to `stepsCompleted`\n- Fill `failureType` with the identified category\n- Append the classification findings to the report\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-03-analyze.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2187,"content_sha256":"650c481f2b9ebff7439b922491f752e6f621efbad4afacaf4f86676be159c2de"},{"filename":"workflows/debugging/steps/step-03-analyze.md","content":"---\nname: 'step-03-analyze'\ndescription: 'Deep analysis based on failure type to identify root cause'\nnextStepFile: './step-04-implement-fix.md'\nreferenceFiles:\n - 'references/mocking/deep-mocked.md'\n - 'references/mocking/jest-native.md'\n---\n\n# Step 3: Analyze Based on Failure Type\n\n## STEP GOAL\n\nPerform deep analysis specific to the classified failure type to identify the exact root cause.\n\n## REFERENCE LOADING\n\nBefore starting analysis, load and read:\n- `references/mocking/deep-mocked.md` - DeepMocked patterns and usage\n- `references/mocking/jest-native.md` - Native Jest mock patterns\n\nThese references help identify whether mocks are correctly set up and used.\n\n## EXECUTION\n\nBased on the failure type classified in Step 2, follow the corresponding analysis path below. Apply **all checks** in the relevant section.\n\n### Type A: Assertion Failure\n\n1. Compare expected vs received in detail:\n ```\n Expected: { id: 'user-123', email: '[email protected]' }\n Received: { id: 'user-123', email: undefined }\n ```\n\n2. Check possible causes:\n - [ ] Mock not returning expected data\n - [ ] Wrong mock method being called\n - [ ] Logic error in target code\n - [ ] Test expectation incorrect\n\n3. Add debug logging to investigate:\n ```typescript\n it('failing test', async () => {\n // Arrange\n console.log('Mock setup:', mockService.method.getMockImplementation());\n\n // Act\n const result = await target.methodName(input);\n console.log('Result:', JSON.stringify(result, null, 2));\n\n // Assert\n expect(result).toEqual(expected);\n });\n ```\n\n### Type B: Unexpected Exception\n\n1. Identify where the exception is thrown:\n ```typescript\n it('failing test', async () => {\n try {\n const result = await target.methodName(input);\n console.log('Success:', result);\n } catch (error) {\n console.log('Error type:', error.constructor.name);\n console.log('Error message:', error.message);\n console.log('Stack:', error.stack);\n throw error;\n }\n });\n ```\n\n2. Check possible causes:\n - [ ] Mock throwing instead of resolving\n - [ ] Missing required mock return value\n - [ ] Validation failing in target code\n - [ ] Dependency not properly mocked\n\n### Type C: Timeout\n\n1. Check for unresolved promises:\n - Mock method called but not configured\n - Async operation waiting forever\n - Event that never fires\n\n2. Debug technique:\n ```typescript\n it('timing out test', async () => {\n console.log('Before call');\n const promise = target.methodName(input);\n console.log('After call, waiting...');\n const result = await promise;\n console.log('Resolved:', result);\n }, 30000); // Extended timeout for debugging\n ```\n\n3. Common fixes:\n - Add `mockResolvedValue` to async mocks\n - Add `mockReturnValue` to sync mocks\n - Check if mock method name matches actual call\n\n### Type D: Mock Error\n\n1. Verify mock is properly created:\n ```typescript\n beforeEach(() => {\n mockService = createMock\u003cServiceType>();\n console.log('Mock created:', Object.keys(mockService));\n });\n ```\n\n2. Verify mock is injected:\n ```typescript\n const module = await Test.createTestingModule({\n providers: [\n Target,\n { provide: ServiceType, useValue: mockService },\n ],\n }).compile();\n\n // Verify injection\n const injected = module.get(ServiceType);\n console.log('Injected same as mock:', injected === mockService);\n ```\n\n### For Module Error or Type Error\n\nApply the same systematic approach:\n- **Module Error**: Verify import paths, check for circular dependencies, confirm module exists\n- **Type Error**: Check variable declarations, verify imports, check for typos in identifiers\n\n## PRESENT FINDINGS\n\nPresent to the user:\n\n```\nStep 3: Root Cause Analysis\n===========================\n\nAnalysis Type: [A/B/C/D]\nRoot Cause: [specific root cause identified]\nEvidence: [what pointed to this cause]\nAffected Code: [file and line]\n```\n\nThen ask: **[C] Continue to Step 4: Implement Fix**\n\n## FRONTMATTER UPDATE\n\nUpdate the output document:\n- Add `3` to `stepsCompleted`\n- Append the root cause analysis findings to the report\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-04-implement-fix.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":4243,"content_sha256":"99a338c0d026baea279085504700c2c34b5643866808d54c37c9b7f661e1bb18"},{"filename":"workflows/debugging/steps/step-04-implement-fix.md","content":"---\nname: 'step-04-implement-fix'\ndescription: 'Apply targeted fix based on identified root cause'\nnextStepFile: './step-05-verify-fix.md'\n---\n\n# Step 4: Implement Fix\n\n## STEP GOAL\n\nApply a minimal, targeted fix based on the root cause identified in Step 3. Fix only what is necessary -- avoid unnecessary changes.\n\n## EXECUTION\n\n### 1. Select Fix Based on Root Cause\n\nUse the root cause from Step 3 to determine the appropriate fix:\n\n| Root Cause | Fix |\n|------------|-----|\n| Missing mock return | Add `mockResolvedValue()` / `mockReturnValue()` |\n| Wrong mock method | Fix method name or use correct mock |\n| Logic error in test | Fix test arrangement or expectations |\n| Logic error in target | Fix target code (create as separate task) |\n| Missing mock | Add mock to provider list |\n| Import error | Fix import path |\n\n### 2. Apply Minimal Fix\n\nApply the smallest possible change that addresses the root cause:\n- Change only the lines directly related to the root cause\n- Do not refactor or improve unrelated code\n- If the fix requires a target code change (not test code), flag it as a separate task\n\n### 3. Document the Fix\n\nRecord what was changed:\n\n```\n**Fix Applied:**\n- File: [path to modified file]\n- Change: [description of the specific change]\n- Reason: [why this fixes the root cause]\n```\n\n## PRESENT FINDINGS\n\nPresent to the user:\n\n```\nStep 4: Fix Applied\n===================\n\nFile: [path]\nChange: [description]\nReason: [why this addresses the root cause]\n\nLines Modified:\n [before -> after summary]\n```\n\nThen ask: **[C] Continue to Step 5: Verify Fix**\n\n## FRONTMATTER UPDATE\n\nUpdate the output document:\n- Add `4` to `stepsCompleted`\n- Append the fix details to the report\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-05-verify-fix.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1766,"content_sha256":"68953eb5d656050d30887a7ac73133641552e1027561d4a588f748b4c44a988c"},{"filename":"workflows/debugging/steps/step-05-verify-fix.md","content":"---\nname: 'step-05-verify-fix'\ndescription: 'Verify fix passes consistently and check for regressions'\nnextStepFile: './step-06-document-resolution.md'\n---\n\n# Step 5: Verify Fix\n\n## STEP GOAL\n\nVerify that the applied fix resolves the failing test consistently and does not introduce regressions in related tests.\n\n## EXECUTION\n\n### 1. Run Fixed Test Multiple Times\n\nRun the fixed test 3-5 times to confirm it passes consistently (output to temp files only, no console):\n\n```bash\nrm -f /tmp/ut-${UT_SESSION}-verify.log\nfor i in {1..5}; do\n npm test -- -t \"[test name]\" > /tmp/ut-${UT_SESSION}-run$i.log 2>&1\n if [ $? -eq 0 ]; then echo \"Run $i: PASS\"; else echo \"Run $i: FAIL\"; fi\ndone\n```\n\nIf any run fails, the fix is incomplete -- go back to Step 3 to re-analyze.\n\n### 2. Run Related Tests in Same File\n\nRun all tests in the same file to check for regressions:\n\n```bash\nnpm test -- [path/to/file.spec.ts] > /tmp/ut-${UT_SESSION}-output.log 2>&1\ntail -50 /tmp/ut-${UT_SESSION}-output.log\n```\n\nIf related tests fail, analyze whether the fix introduced a regression. If so, go back to Step 4 to adjust the fix.\n\n### 3. Run Full Suite ONLY ONCE\n\nRun the full test suite **only after** all individual tests pass. This is a final regression check:\n\n```bash\nnpm test > /tmp/ut-${UT_SESSION}-output.log 2>&1\ntail -50 /tmp/ut-${UT_SESSION}-output.log\n```\n\n### 4. Update Tracking File\n\nUpdate the tracking file with the result:\n\n```markdown\n## Test 1: \"[test name]\"\n- Status: FIXED\n- Root Cause: [description]\n- Fix: [what was changed]\n```\n\n## PRESENT FINDINGS\n\nPresent to the user:\n\n```\nStep 5: Verification Results\n============================\n\nFixed Test (5 runs): PASS / FAIL (N/5)\nRelated Tests: PASS / FAIL (details)\nFull Suite: PASS / FAIL (details)\n```\n\nThen ask: **[C] Continue to Step 6: Document Resolution**\n\n## FRONTMATTER UPDATE\n\nUpdate the output document:\n- Add `5` to `stepsCompleted`\n- Append the verification results to the report\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-06-document-resolution.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2043,"content_sha256":"d6b7f71726445968aab4a8a4293bb1b8f0bb8f3b9888762311fb5b0cb1477708"},{"filename":"workflows/debugging/steps/step-06-document-resolution.md","content":"---\nname: 'step-06-document-resolution'\ndescription: 'Document the complete debugging resolution'\n---\n\n# Step 6: Document Resolution\n\n## STEP GOAL\n\nRecord the complete debugging session with root cause, fix, lessons learned, and a reference of debug techniques for future use.\n\n## EXECUTION\n\n### 1. Compile Resolution Report\n\nAppend the full resolution report to the output document:\n\n```markdown\n## Debug Resolution Report\n\n### Original Failure\n- Test: [test name]\n- Error: [error message]\n- Date: [date]\n\n### Root Cause\n[Description of what caused the failure]\n\n### Fix Applied\n- File: [path]\n- Change: [specific change made]\n\n### Lessons Learned\n[Any insights for preventing similar issues]\n\n### Verification\n- Fixed test: PASS\n- Related tests: PASS\n- Regression check: PASS\n```\n\n### 2. Include Debug Techniques Reference\n\nAppend the following reference section to the report for future use:\n\n```markdown\n## Debug Techniques Reference\n\n### Console Logging\n\n```typescript\nit('test with logging', async () => {\n console.log('Input:', input);\n console.log('Mock calls before:', mockService.method.mock.calls);\n\n const result = await target.methodName(input);\n\n console.log('Result:', result);\n console.log('Mock calls after:', mockService.method.mock.calls);\n});\n```\n\n### Check Mock State\n\n```typescript\n// Check if mock was called\nconsole.log('Called:', mockService.method.mock.calls.length);\n\n// Check call arguments\nconsole.log('Args:', mockService.method.mock.calls[0]);\n\n// Check return value\nconsole.log('Returns:', mockService.method.mock.results);\n```\n\n### Step Through with Debugger\n\n```bash\n# Run Jest with debugger (this requires console for interactive debugging)\nnode --inspect-brk node_modules/.bin/jest --runInBand -t \"[test name]\"\n```\n\nThen attach VS Code debugger or Chrome DevTools.\n\n### Isolate Setup Issues\n\n```typescript\ndescribe('Target', () => {\n it('should create module', async () => {\n const module = await Test.createTestingModule({\n providers: [Target],\n }).compile();\n\n expect(module).toBeDefined();\n });\n\n it('should get target', async () => {\n const module = await Test.createTestingModule({\n providers: [Target],\n }).compile();\n\n const target = module.get\u003cTarget>(Target);\n expect(target).toBeDefined();\n });\n});\n```\n```\n\n### 3. Include Common Issues and Solutions\n\nAppend the quick-reference table:\n\n```markdown\n## Common Issues and Solutions\n\n| Issue | Symptom | Solution |\n|-------|---------|----------|\n| Mock not returning | `undefined` result | Add `mockResolvedValue()` |\n| Mock not called | Call count is 0 | Verify mock injection |\n| Wrong mock called | Different mock has calls | Check dependency injection |\n| Async not awaited | Promise object in result | Add `await` to call |\n| Mock reset missing | State from other tests | Add `jest.clearAllMocks()` |\n| Private method tested | Cannot access | Test via public interface |\n| Logger errors | Console noise | Add `MockLoggerService` |\n```\n\n### 4. Post-Workflow Verification Checklist\n\n```markdown\n## Post-Workflow Verification\n\n1. Re-read `references/common/rules.md` to ensure fix follows conventions\n2. Confirm all tests pass\n3. Run coverage to ensure fix did not reduce coverage\n4. Consider if similar issues exist elsewhere\n```\n\n### 5. Cleanup Temp Files\n\n```bash\nrm -f /tmp/ut-${UT_SESSION}-*.log /tmp/ut-${UT_SESSION}-*.md\n```\n\n## PRESENT FINDINGS\n\nPresent to the user the compiled resolution report including:\n- Original failure summary\n- Root cause\n- Fix applied\n- Lessons learned\n- Verification status\n\n## FRONTMATTER UPDATE\n\nUpdate the output document:\n- Add `6` to `stepsCompleted`\n- Set `status` to `'complete'`\n\n## WORKFLOW COMPLETE\n\nThe debugging workflow is finished. The full debug resolution report is saved at the output path. Use this report as a reference if similar test failures occur in the future.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3856,"content_sha256":"922f5ae54d1335284b523d6add39179e9c78914444baf9706cd18694b50a0ff0"},{"filename":"workflows/debugging/templates/output-template.md","content":"---\nstepsCompleted: []\nworkflowType: 'debugging'\nstatus: 'in-progress'\ndate: ''\nfailingTest: ''\ntestFile: ''\nfailureType: ''\noutputPath: ''\n---\n\n# Debug Resolution Report\n\n**Date**: {{date}}\n**Failing Test**: {{failingTest}}\n**Test File**: {{testFile}}\n**Failure Type**: {{failureType}}\n**Status**: {{status}}\n\n---\n\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":316,"content_sha256":"44deecde86fa1e2fcd65ea08533acd92ee78d585a850778fbe675268feb85935"},{"filename":"workflows/debugging/workflow.md","content":"---\nname: 'debugging'\ndescription: 'Systematically diagnose and fix failing unit tests'\nfirstStepFile: './steps/step-01-init.md'\ntemplateFile: './templates/output-template.md'\n---\n\n# Debugging Unit Test Workflow\n\nSystematically diagnose and fix failing unit tests using structured debugging techniques. This workflow guides you through reproducing, classifying, analyzing, fixing, verifying, and documenting test failures one at a time.\n\n## When to Use\n\n- Unit tests are failing and you need systematic diagnosis\n- You want to debug one test at a time rather than chasing multiple failures\n- Test failures are unclear and require structured root cause analysis\n- You need to document debugging resolutions for future reference\n\n## Prerequisites\n\nHave the failing test name and file path ready. The project must have a working test runner (`npm test`).\n\n## Step-File Architecture\n\nThis workflow uses a **step-file architecture** for context-safe execution:\n\n- Each step is a separate file loaded sequentially\n- Progress is tracked via `stepsCompleted` in the output document's YAML frontmatter\n- If context is compacted mid-workflow, step-01 detects existing output and resumes from the last completed step via step-01b\n\n### Steps\n\n| Step | File | Description |\n|------|------|-------------|\n| 1 | `step-01-init.md` | Initialize debugging session, reproduce failure |\n| 1b | `step-01b-continue.md` | Resume from last completed step |\n| 2 | `step-02-classify-failure.md` | Classify the failure type |\n| 3 | `step-03-analyze.md` | Deep analysis based on failure type |\n| 4 | `step-04-implement-fix.md` | Apply targeted fix for root cause |\n| 5 | `step-05-verify-fix.md` | Verify fix and check for regressions |\n| 6 | `step-06-document-resolution.md` | Document the debugging resolution |\n\n### Rules\n\n1. **Load one step at a time** - Read the step file, execute it, then load the next\n2. **Update frontmatter after each step** - Add the step number to `stepsCompleted`\n3. **Wait for user confirmation** - Present findings and wait for `[C]` before proceeding\n4. **Diagnose before fixing** - Never apply a fix without understanding the root cause first\n5. **ALWAYS fix ONE test at a time** - Run only `-t \"test name\"`, never full suite while debugging\n6. **NEVER run full suite while debugging** - Only after ALL individual tests pass\n7. **ALWAYS output to temp files** - Redirect test output to temp files to avoid context bloat\n\n### Critical: One-by-One Fixing Rule\n\n```\nWRONG: Run full suite -> See 5 failures -> Run full suite again -> Still 5 failures -> ...\nRIGHT: Run full suite -> See 5 failures -> Fix test 1 only -> Verify -> Fix test 2 only -> ... -> Run full suite ONCE\n```\n\n**WHY**: Full suite runs waste time and pollute output. Each failing test adds noise, making debugging harder.\n\n### Context Efficiency: Temp File Output\n\n**Why**: Test output can be verbose. Direct terminal output bloats agent context.\n\n**IMPORTANT**: Redirect output to temp files only (NO console output). Use unique session ID to prevent conflicts.\n\n**Temp File Locations** (with `${UT_SESSION}` unique per agent):\n- `/tmp/ut-${UT_SESSION}-debug.log` - Debug runs\n- `/tmp/ut-${UT_SESSION}-output.log` - General test output\n- `/tmp/ut-${UT_SESSION}-verify.log` - Verification runs\n- `/tmp/ut-${UT_SESSION}-failures.md` - Tracking file\n\n## Begin\n\nLoad `steps/step-01-init.md` to start.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3369,"content_sha256":"f0276dc613c3342808279dd0f6785b12434b07f353a6a855be5b7de58e2e8210"},{"filename":"workflows/optimizing/steps/step-01-init.md","content":"---\nname: 'step-01-init'\ndescription: 'Initialize optimization session and detect continuation'\nnextStepFile: './step-02-identify-opportunities.md'\n---\n\n# Step 1: Initialize Optimization Session\n\n## STEP GOAL\n\nSet up the optimization session: initialize a temp file session, identify test files to optimize, measure baseline performance with open handle detection, and check for an existing report to resume.\n\n## EXECUTION\n\n### 1. Ask the User\n\nAsk the user:\n- **What tests to optimize?** (file path, directory, or entire test suite)\n- **Output path** for the optimization report (suggest a default: `./test-optimization-report-{{date}}.md`)\n- **Or provide path to an existing report** to resume a previous optimization\n\n### 2. Check for Existing Report\n\nIf the user provides a path to an existing report file:\n- Read the file\n- Parse the YAML frontmatter\n- If `stepsCompleted` is non-empty — **STOP and load `step-01b-continue.md`**\n\n### 3. Fresh Workflow Setup\n\nIf starting fresh:\n1. Copy the template from `templates/output-template.md`\n2. Fill in the frontmatter:\n - `targetTests`: the test path(s) provided by the user\n - `outputPath`: the chosen output path\n - `date`: current date\n3. Write the initialized report to the output path\n\n### 4. Initialize Temp File Session\n\nSet up the temp file session for capturing test output without bloating agent context:\n\n```bash\n# Initialize session (once at start of optimization)\nexport UT_SESSION=$(date +%s)-$\n```\n\n**Temp File Locations** (with `${UT_SESSION}` unique per agent):\n- `/tmp/ut-${UT_SESSION}-baseline.log` — Baseline run\n- `/tmp/ut-${UT_SESSION}-optimized.log` — Optimized run\n- `/tmp/ut-${UT_SESSION}-timing.log` — Timing data\n\n### 5. Measure Baseline Performance\n\nRun tests with timing and open handle detection (output to temp file):\n\n```bash\nnpm test -- --verbose --detectOpenHandles --forceExit > /tmp/ut-${UT_SESSION}-baseline.log 2>&1\ntail -50 /tmp/ut-${UT_SESSION}-baseline.log\n```\n\nCheck for open handles specifically:\n\n```bash\ngrep -A 20 \"open handle\" /tmp/ut-${UT_SESSION}-baseline.log\n```\n\nIdentify slow tests (use Jest's built-in timing):\n\n```bash\ngrep -E \"^\\s*\\d+.*ms$\" /tmp/ut-${UT_SESSION}-baseline.log\n```\n\n### 6. Document Baseline Metrics\n\nRecord the baseline performance data:\n\n```\n**Current Test Performance:**\n\n| Metric | Value |\n|--------|-------|\n| Total test count | [X] |\n| Total execution time | [T]s |\n| Slowest test file | [file] ([T]s) |\n| Slowest individual test | [name] ([T]ms) |\n| Average test time | [T]ms |\n| Open handles detected | [count] |\n```\n\n### 7. Categorize Tests by Speed\n\n| Category | Threshold | Count |\n|----------|-----------|-------|\n| Fast | \u003c 50ms | [X] |\n| Medium | 50-200ms | [X] |\n| Slow | > 200ms | [X] |\n\n### 8. Append Findings to Report\n\nAppend the baseline metrics and speed categorization to the output document.\n\n## FRONTMATTER UPDATE\n\nUpdate the output document frontmatter:\n- Add `1` to `stepsCompleted`\n- Fill `targetTests` with the test path(s)\n- Fill `baselineTime` with total execution time\n- Fill `openHandlesCount` with number of open handles detected\n\n## PRESENT TO USER\n\nShow the user:\n- Confirmation of target tests and output path\n- Baseline performance metrics table\n- Speed categorization table\n- Number of open handles detected (highlight if > 0)\n\nThen ask: **[C] Continue to Step 2: Identify Optimization Opportunities**\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-02-identify-opportunities.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3457,"content_sha256":"30eace9ad50a582a9d6079ea2fcd4563e1aba9b88e92cce70e002f255e83fd5e"},{"filename":"workflows/optimizing/steps/step-01b-continue.md","content":"---\nname: 'step-01b-continue'\ndescription: 'Resume optimization workflow from last completed step'\n---\n\n# Step 1b: Continue Previous Optimization\n\n## STEP GOAL\n\nResume a previously started optimization workflow by reading the existing report, determining progress, and routing to the next incomplete step.\n\n## EXECUTION\n\n### 1. Read Existing Report\n\n- Read the file at the output path provided by the user\n- Parse the YAML frontmatter\n- Extract `stepsCompleted` array\n\n### 2. Show Progress Summary\n\nDisplay to the user:\n\n```\nTest Optimization Progress\n===========================\nTarget: {{targetTests}}\nBaseline Time: {{baselineTime}}\nOpen Handles: {{openHandlesCount}}\nDate Started: {{date}}\nSteps Completed: {{stepsCompleted}}\n\nStep Map:\n [1] Measure Baseline {{done/pending}}\n [2] Identify Opportunities {{done/pending}}\n [3] Optimize Setup {{done/pending}}\n [4] Fix Open Handles (CRITICAL) {{done/pending}}\n [5] Optimize Async {{done/pending}}\n [6] Optimize Data {{done/pending}}\n [7] Optimize Organization {{done/pending}}\n [8] Measure Improvement {{done/pending}}\n [9] Document Optimizations {{done/pending}}\n```\n\n### 3. Offer Options\n\nPresent:\n- **[R] Resume** from the next incomplete step\n- **[O] Overview** — re-read the existing report content before resuming\n- **[X] Start over** — create a fresh report (confirm: this will overwrite)\n\n### 4. Route to Next Step\n\nOn **[R]** or after **[O]**:\n\nDetermine the next step from `max(stepsCompleted) + 1` and load the corresponding file:\n\n| Next Step | File |\n|-----------|------|\n| 2 | `step-02-identify-opportunities.md` |\n| 3 | `step-03-optimize-setup.md` |\n| 4 | `step-04-fix-open-handles.md` |\n| 5 | `step-05-optimize-async.md` |\n| 6 | `step-06-optimize-data.md` |\n| 7 | `step-07-optimize-organization.md` |\n| 8 | `step-08-measure-improvement.md` |\n| 9 | `step-09-document-optimizations.md` |\n\nOn **[X]**: Go back to `step-01-init.md` fresh workflow setup (section 3).\n\n## NEXT STEP\n\nLoad the step file determined above.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2088,"content_sha256":"e8a57a042e233f689725efcd8d98215b98fded8b848bf09774859432541bd8ad"},{"filename":"workflows/optimizing/steps/step-02-identify-opportunities.md","content":"---\nname: 'step-02-identify-opportunities'\ndescription: 'Identify optimization opportunities and open handle sources'\nnextStepFile: './step-03-optimize-setup.md'\nreferenceFiles:\n - 'references/common/performance-optimization.md'\n - 'references/common/detect-open-handles.md'\n---\n\n# Step 2: Identify Optimization Opportunities and Open Handle Sources\n\n## STEP GOAL\n\nAnalyze slow tests from the baseline measurement to identify specific optimization opportunities. Identify all open handle sources and categorize them by priority.\n\n## REFERENCE LOADING\n\nBefore starting analysis, load and read:\n- `references/common/performance-optimization.md` — Worker config, caching, CI optimization\n- `references/common/detect-open-handles.md` — Open handle detection and cleanup\n\n## EXECUTION\n\n### 1. Analyze Slow Tests for Common Issues\n\nReview the baseline log and categorize issues by impact:\n\n| Issue | Indicator | Impact |\n|-------|-----------|--------|\n| Heavy setup | beforeEach > 100ms | HIGH |\n| Unnecessary async | await where not needed | MEDIUM |\n| Module recreation | Full module per test | HIGH |\n| Real timeouts | setTimeout in test | HIGH |\n| Large mock data | Complex test data | LOW |\n| Open handles | Jest warning message | CRITICAL |\n\nFor each slow test file identified in Step 1, check which of these issues apply.\n\n### 2. Identify Open Handle Sources\n\nCross-reference the baseline open handle output with common sources:\n\n| Handle Type | Common Source | Solution |\n|-------------|---------------|----------|\n| TCPSERVERWRAP | Unclosed HTTP server | Close in afterAll |\n| TCPWRAP | Database connections | Disconnect in afterAll |\n| HTTPINCOMINGMESSAGE | Pending HTTP requests | Abort/close connections |\n| Timeout | setTimeout not cleared | Use fake timers or clearTimeout |\n| KAFKAPRODUCER | Kafka client not closed | Disconnect producer |\n| KAFKACONSUMER | Kafka consumer running | Disconnect consumer |\n| REDISCLIENT | Redis connection open | Quit/disconnect client |\n| MONGOCLIENT | MongoDB connection open | Close connection |\n\n### 3. Check for Anti-Patterns\n\nScan test files for known anti-patterns that impact performance:\n- `beforeEach` with `Test.createTestingModule` (should be `beforeAll`)\n- Creating all mocks in `beforeEach` even when not all tests use them\n- `imports: [AppModule]` in test modules (should use minimal providers)\n- Real `setTimeout`/`setInterval` without fake timers\n- Missing `afterAll` cleanup blocks\n- Using `--forceExit` to mask open handle issues\n\n### 4. Document Findings\n\nCompile the analysis into a prioritized list:\n\n```\n**Optimization Opportunities Found:**\n\n1. [Issue]: [Description]\n - Location: [file:line or test name]\n - Estimated improvement: [X]ms\n - Priority: [HIGH/MEDIUM/LOW]\n\n**Open Handles Found:**\n\n1. [Handle Type]: [Description]\n - Location: [file:line or test name]\n - Source: [what created the handle]\n - Priority: CRITICAL\n```\n\n## PRESENT FINDINGS\n\nPresent findings to the user:\n\n```\nStep 2: Optimization Opportunities\n====================================\n\nPerformance Issues Found:\n [PRIORITY] [Issue] — [Location]\n Indicator: [what was detected]\n Estimated improvement: [X]ms\n\nOpen Handle Sources Found:\n [CRITICAL] [Handle Type] — [Location]\n Source: [what created the handle]\n Fix: [brief solution]\n\nSummary:\n Performance issues: [count] (HIGH: [n], MEDIUM: [n], LOW: [n])\n Open handles: [count] (ALL are CRITICAL priority)\n```\n\nThen ask: **[C] Continue to Step 3: Optimize Test Setup**\n\n## FRONTMATTER UPDATE\n\nUpdate the output document:\n- Add `2` to `stepsCompleted`\n- Append the findings section to the report\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-03-optimize-setup.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3697,"content_sha256":"26f83a6a7bc12962088bf786f44fe56165f1287738bc6d1022deb59dd403df4b"},{"filename":"workflows/optimizing/steps/step-03-optimize-setup.md","content":"---\nname: 'step-03-optimize-setup'\ndescription: 'Optimize test setup patterns'\nnextStepFile: './step-04-fix-open-handles.md'\nreferenceFiles:\n - 'references/common/performance-optimization.md'\n---\n\n# Step 3: Optimize Test Setup\n\n## STEP GOAL\n\nOptimize test module creation and mock setup to reduce per-test overhead. Apply shared setup, lazy mock creation, and minimal module patterns.\n\n## REFERENCE LOADING\n\nBefore starting, load and read:\n- `references/common/performance-optimization.md` — Worker config, caching, CI optimization\n\n## EXECUTION\n\n### Technique A: Share Expensive Setup\n\nMove one-time setup to `beforeAll` and keep only mock resets in `beforeEach`:\n\n```typescript\n// BEFORE (slow)\ndescribe('Service', () => {\n let target: Service;\n\n beforeEach(async () => {\n // Expensive: compiles module for every test\n const module = await Test.createTestingModule({\n providers: [Service, ...manyProviders],\n }).compile();\n target = module.get(Service);\n });\n});\n\n// AFTER (fast)\ndescribe('Service', () => {\n let module: TestingModule;\n let target: Service;\n\n beforeAll(async () => {\n // One-time: compiles module once\n module = await Test.createTestingModule({\n providers: [Service, ...manyProviders],\n }).compile();\n });\n\n beforeEach(() => {\n target = module.get(Service);\n jest.clearAllMocks(); // Reset mock state\n });\n});\n```\n\n**When to apply**: Any test file where `Test.createTestingModule` is inside `beforeEach`. This is the single highest-impact optimization for NestJS tests.\n\n### Technique B: Lazy Mock Creation\n\nOnly create mocks when the specific test needs them:\n\n```typescript\n// BEFORE\nbeforeEach(() => {\n mockServiceA = createMock\u003cServiceA>();\n mockServiceB = createMock\u003cServiceB>();\n mockServiceC = createMock\u003cServiceC>();\n // Creates all mocks even if test only uses one\n});\n\n// AFTER\nconst getMockServiceA = () => createMock\u003cServiceA>();\nconst getMockServiceB = () => createMock\u003cServiceB>();\n\nit('test using only service A', () => {\n const mockA = getMockServiceA();\n // Only creates what's needed\n});\n```\n\n**When to apply**: Test files with many mocks where individual tests only use a subset.\n\n### Technique C: Reduce Module Complexity\n\nOnly include required providers, not the full AppModule:\n\n```typescript\n// BEFORE (slow)\nconst module = await Test.createTestingModule({\n imports: [AppModule], // Imports everything\n}).compile();\n\n// AFTER (fast)\nconst module = await Test.createTestingModule({\n providers: [\n TargetService,\n { provide: DependencyA, useValue: mockA },\n { provide: DependencyB, useValue: mockB },\n ],\n}).compile();\n```\n\n**When to apply**: Any test importing `AppModule` or large feature modules. Unit tests should only include the target and its direct mocked dependencies.\n\n### Apply Optimizations\n\nFor each test file identified in Step 2:\n1. Check which techniques apply\n2. Apply changes one technique at a time\n3. Run the specific test file after each change to verify tests still pass:\n ```bash\n npm test -- [path/to/file.spec.ts] > /tmp/ut-${UT_SESSION}-setup-check.log 2>&1\n tail -20 /tmp/ut-${UT_SESSION}-setup-check.log\n ```\n\n## PRESENT FINDINGS\n\nPresent findings to the user:\n\n```\nStep 3: Setup Optimizations\n=============================\n\nOptimizations Applied:\n [file.spec.ts]\n - Technique A: Moved TestingModule to beforeAll [APPLIED/SKIPPED]\n - Technique B: Lazy mock creation [APPLIED/SKIPPED]\n - Technique C: Reduced module complexity [APPLIED/SKIPPED]\n\nVerification:\n All modified test files: [PASS/FAIL]\n```\n\nThen ask: **[C] Continue to Step 4: Fix Open Handles (CRITICAL)**\n\n## FRONTMATTER UPDATE\n\nUpdate the output document:\n- Add `3` to `stepsCompleted`\n- Append the setup optimization details to the report\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-04-fix-open-handles.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3861,"content_sha256":"0ec6f43abef0feadc24bf1db2da5ac317d3dad82eda4b24a4c87785105363dc2"},{"filename":"workflows/optimizing/steps/step-04-fix-open-handles.md","content":"---\nname: 'step-04-fix-open-handles'\ndescription: 'Fix all open handles (CRITICAL)'\nnextStepFile: './step-05-optimize-async.md'\nreferenceFiles:\n - 'references/common/detect-open-handles.md'\n---\n\n# Step 4: Fix Open Handles (CRITICAL)\n\n## STEP GOAL\n\nFix ALL open handles detected in the baseline measurement. Open handles prevent Jest from exiting cleanly and indicate resource leaks. This is CRITICAL priority — fix all open handles before other optimizations.\n\n## REFERENCE LOADING\n\nBefore starting, load and read:\n- `references/common/detect-open-handles.md` — Open handle detection and cleanup patterns\n\n## EXECUTION\n\n### Technique A: Proper Module Cleanup\n\nEnsure every test module is closed in `afterAll`:\n\n```typescript\ndescribe('Service', () => {\n let module: TestingModule;\n let target: Service;\n\n beforeAll(async () => {\n module = await Test.createTestingModule({\n providers: [Service],\n }).compile();\n\n target = module.get(Service);\n });\n\n afterAll(async () => {\n // CRITICAL: Close the module to release resources\n await module.close();\n });\n\n afterEach(() => {\n jest.clearAllMocks();\n });\n});\n```\n\n**When to apply**: Every test file that creates a `TestingModule`. This is the most common source of open handles.\n\n### Technique B: Close Database Connections\n\n```typescript\n// MongoDB\ndescribe('Repository', () => {\n let mongoServer: MongoMemoryServer;\n let connection: Connection;\n\n beforeAll(async () => {\n mongoServer = await MongoMemoryServer.create();\n connection = await mongoose.connect(mongoServer.getUri());\n });\n\n afterAll(async () => {\n // CRITICAL: Close in correct order\n await connection.close();\n await mongoServer.stop();\n });\n});\n\n// PostgreSQL (pg-mem)\ndescribe('Repository', () => {\n let db: IMemoryDb;\n\n beforeAll(() => {\n db = newDb();\n });\n\n afterAll(() => {\n // pg-mem doesn't need explicit cleanup, but real connections do\n });\n});\n```\n\n**When to apply**: Any test that creates database connections — MongoDB, PostgreSQL, or other databases.\n\n### Technique C: Close Kafka Connections\n\n```typescript\ndescribe('KafkaService', () => {\n let module: TestingModule;\n let kafkaClient: ClientKafka;\n\n beforeAll(async () => {\n module = await Test.createTestingModule({\n providers: [KafkaService],\n }).compile();\n\n kafkaClient = module.get(ClientKafka);\n await kafkaClient.connect();\n });\n\n afterAll(async () => {\n // CRITICAL: Disconnect Kafka client\n await kafkaClient.close();\n await module.close();\n });\n});\n```\n\n**When to apply**: Any test that creates or connects Kafka producers or consumers.\n\n### Technique D: Close Redis Connections\n\n```typescript\ndescribe('CacheService', () => {\n let module: TestingModule;\n let redisClient: Redis;\n\n beforeAll(async () => {\n module = await Test.createTestingModule({\n providers: [CacheService],\n }).compile();\n\n redisClient = module.get(REDIS_CLIENT);\n });\n\n afterAll(async () => {\n // CRITICAL: Quit Redis client\n await redisClient.quit();\n await module.close();\n });\n});\n```\n\n**When to apply**: Any test that creates Redis client connections.\n\n### Technique E: Clear Timers\n\n```typescript\ndescribe('Service with timers', () => {\n beforeEach(() => {\n jest.useFakeTimers();\n });\n\n afterEach(() => {\n // CRITICAL: Clear all timers and restore real timers\n jest.clearAllTimers();\n jest.useRealTimers();\n });\n});\n\n// Alternative: Track and clear specific timers\ndescribe('Service', () => {\n let timerId: NodeJS.Timeout;\n\n it('should set timer', () => {\n timerId = setTimeout(() => {}, 1000);\n });\n\n afterEach(() => {\n if (timerId) {\n clearTimeout(timerId);\n }\n });\n});\n```\n\n**When to apply**: Any test that uses `setTimeout`, `setInterval`, or code under test that creates timers.\n\n### Technique F: Close HTTP Servers\n\n```typescript\ndescribe('Controller E2E', () => {\n let app: INestApplication;\n\n beforeAll(async () => {\n const module = await Test.createTestingModule({\n imports: [AppModule],\n }).compile();\n\n app = module.createNestApplication();\n await app.init();\n });\n\n afterAll(async () => {\n // CRITICAL: Close the application\n await app.close();\n });\n});\n```\n\n**When to apply**: Any test that creates a NestJS application instance or HTTP server.\n\n### Technique G: Verify No Open Handles\n\nAfter applying all fixes above, verify each file is clean:\n\n```bash\nnpm test -- --detectOpenHandles [path/to/file.spec.ts]\n```\n\nExpected output should NOT contain:\n- \"Jest has detected the following X open handles\"\n- Any TCPWRAP, TCPSERVERWRAP, or similar warnings\n\nRun the full suite with detection:\n\n```bash\nnpm test -- --detectOpenHandles > /tmp/ut-${UT_SESSION}-handles-check.log 2>&1\ngrep -c \"open handle\" /tmp/ut-${UT_SESSION}-handles-check.log\n# Expected: 0\n```\n\n### Apply Fixes\n\nFor each open handle identified in Step 2:\n1. Determine which technique applies based on the handle type\n2. Apply the fix\n3. Verify the specific file with `--detectOpenHandles`\n4. Move to the next handle\n\n## PRESENT FINDINGS\n\nPresent findings to the user:\n\n```\nStep 4: Open Handle Fixes (CRITICAL)\n======================================\n\nFixes Applied:\n [file.spec.ts]\n - Technique [X]: [description] [FIXED]\n [file2.spec.ts]\n - Technique [X]: [description] [FIXED]\n\nVerification:\n Open handles before: [count]\n Open handles after: [count] (target: 0)\n Clean exit: [YES/NO]\n```\n\nThen ask: **[C] Continue to Step 5: Optimize Async Operations**\n\n## FRONTMATTER UPDATE\n\nUpdate the output document:\n- Add `4` to `stepsCompleted`\n- Update `openHandlesCount` to reflect remaining count (target: 0)\n- Append the open handle fix details to the report\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-05-optimize-async.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":5787,"content_sha256":"11ace626733ef977749082f63f7ae594684bb3b60cb0539e9674880072ff024a"},{"filename":"workflows/optimizing/steps/step-05-optimize-async.md","content":"---\nname: 'step-05-optimize-async'\ndescription: 'Optimize async operations in tests'\nnextStepFile: './step-06-optimize-data.md'\nreferenceFiles:\n - 'references/common/performance-optimization.md'\n---\n\n# Step 5: Optimize Async Operations\n\n## STEP GOAL\n\nOptimize async patterns in tests to reduce unnecessary overhead from awaiting synchronous operations, real timeouts, and suboptimal parallelization.\n\n## REFERENCE LOADING\n\nBefore starting, load and read:\n- `references/common/performance-optimization.md` — Worker config, caching, CI optimization\n\n## EXECUTION\n\n### Technique A: Remove Unnecessary Async\n\nDo not use `async`/`await` when the method under test is synchronous:\n\n```typescript\n// BEFORE\nit('should return value', async () => {\n const result = await target.syncMethod(); // Method is not async\n expect(result).toBe(expected);\n});\n\n// AFTER\nit('should return value', () => {\n const result = target.syncMethod();\n expect(result).toBe(expected);\n});\n```\n\n**When to apply**: Any test that uses `async`/`await` but the method under test returns a plain value, not a Promise. Check the source code to confirm whether the method is actually async.\n\n### Technique B: Mock Time Instead of Waiting\n\nReplace real timeouts with fake timers to eliminate wait time:\n\n```typescript\n// BEFORE (slow - waits 1 second)\nit('should retry after delay', async () => {\n mockService.call\n .mockRejectedValueOnce(new Error('fail'))\n .mockResolvedValue('success');\n\n const result = await target.callWithRetry();\n expect(result).toBe('success');\n}, 5000);\n\n// AFTER (fast - mocks time)\nit('should retry after delay', async () => {\n jest.useFakeTimers();\n\n mockService.call\n .mockRejectedValueOnce(new Error('fail'))\n .mockResolvedValue('success');\n\n const promise = target.callWithRetry();\n jest.advanceTimersByTime(1000);\n const result = await promise;\n\n expect(result).toBe('success');\n jest.useRealTimers();\n});\n```\n\n**When to apply**: Any test with explicit timeout values (the second argument to `it()`), or tests that call code with `setTimeout`/`setInterval`/retry logic. This can eliminate seconds of wait time per test.\n\n### Technique C: Parallel Test Execution\n\nEnsure tests are independent for parallel execution and configure optimal worker count:\n\n```typescript\n// jest.config.ts\nexport default {\n maxWorkers: '50%', // Use half of CPU cores\n // OR\n maxWorkers: 4, // Fixed number\n};\n```\n\n**When to apply**: When tests are independent (no shared mutable state between test files). Check that no test file depends on global state, shared database records, or file system state from another test file.\n\n### Apply Optimizations\n\nFor each test file:\n1. Scan for unnecessary `async`/`await` usage\n2. Identify tests with real timeouts that can use fake timers\n3. Verify test independence for parallel execution\n4. Apply changes and verify:\n ```bash\n npm test -- [path/to/file.spec.ts] > /tmp/ut-${UT_SESSION}-async-check.log 2>&1\n tail -20 /tmp/ut-${UT_SESSION}-async-check.log\n ```\n\n## PRESENT FINDINGS\n\nPresent findings to the user:\n\n```\nStep 5: Async Optimizations\n=============================\n\nOptimizations Applied:\n [file.spec.ts]\n - Technique A: Removed unnecessary async [APPLIED/SKIPPED]\n - Technique B: Mocked time for [N] tests [APPLIED/SKIPPED]\n\n Jest Config:\n - Technique C: maxWorkers set to [value] [APPLIED/SKIPPED]\n\nVerification:\n All modified test files: [PASS/FAIL]\n```\n\nThen ask: **[C] Continue to Step 6: Optimize Test Data**\n\n## FRONTMATTER UPDATE\n\nUpdate the output document:\n- Add `5` to `stepsCompleted`\n- Append the async optimization details to the report\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-06-optimize-data.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3712,"content_sha256":"0540b7660e7db91cc7fbff6718afc1948c2de9ec8a30ddf9f17c48dd24298b3e"},{"filename":"workflows/optimizing/steps/step-06-optimize-data.md","content":"---\nname: 'step-06-optimize-data'\ndescription: 'Optimize test data patterns'\nnextStepFile: './step-07-optimize-organization.md'\nreferenceFiles:\n - 'references/mocking/factories.md'\n---\n\n# Step 6: Optimize Test Data\n\n## STEP GOAL\n\nOptimize test data creation and mock data to reduce verbosity, improve maintainability, and eliminate unnecessary data setup overhead.\n\n## REFERENCE LOADING\n\nBefore starting, load and read:\n- `references/mocking/factories.md` — Factory patterns for test data creation\n\n## EXECUTION\n\n### Technique A: Use Factories\n\nReplace repeated inline object creation with reusable factory functions:\n\n```typescript\n// BEFORE (verbose, repeated)\nit('test 1', () => {\n const user = {\n id: 'user-1',\n email: '[email protected]',\n name: 'Test User',\n role: 'admin',\n createdAt: new Date(),\n };\n});\n\nit('test 2', () => {\n const user = {\n id: 'user-2',\n email: '[email protected]',\n name: 'Test User 2',\n role: 'user',\n createdAt: new Date(),\n };\n});\n\n// AFTER (concise, reusable)\nconst createUser = (overrides = {}): User => ({\n id: 'user-1',\n email: '[email protected]',\n name: 'Test User',\n role: 'user',\n createdAt: new Date(),\n ...overrides,\n});\n\nit('test 1', () => {\n const user = createUser({ role: 'admin' });\n});\n\nit('test 2', () => {\n const user = createUser({ id: 'user-2', email: '[email protected]' });\n});\n```\n\n**When to apply**: Any test file where the same object shape is created more than twice with minor variations. Factories reduce duplication and make tests more readable by highlighting only the relevant differences.\n\n### Technique B: Minimize Mock Data\n\nOnly include fields that are actually used by the code under test:\n\n```typescript\n// BEFORE (excessive data)\nmockRepository.findById.mockResolvedValue({\n id: 'user-1',\n email: '[email protected]',\n firstName: 'John',\n lastName: 'Doe',\n address: { street: '123 Main', city: 'NYC', zip: '10001' },\n preferences: { theme: 'dark', notifications: true },\n // ... 20 more fields\n});\n\n// AFTER (minimal required data)\nmockRepository.findById.mockResolvedValue({\n id: 'user-1',\n email: '[email protected]',\n // Only fields actually used by the test\n});\n```\n\n**When to apply**: Any mock return value that includes fields not referenced by the code path under test. Trace the code to determine which fields are actually accessed.\n\n### Apply Optimizations\n\nFor each test file:\n1. Identify repeated object creation patterns\n2. Extract factory functions for common entities\n3. Trim mock return values to only required fields\n4. Verify tests still pass:\n ```bash\n npm test -- [path/to/file.spec.ts] > /tmp/ut-${UT_SESSION}-data-check.log 2>&1\n tail -20 /tmp/ut-${UT_SESSION}-data-check.log\n ```\n\n## PRESENT FINDINGS\n\nPresent findings to the user:\n\n```\nStep 6: Data Optimizations\n============================\n\nOptimizations Applied:\n [file.spec.ts]\n - Technique A: Created [N] factory functions [APPLIED/SKIPPED]\n - Technique B: Minimized [N] mock data objects [APPLIED/SKIPPED]\n\nVerification:\n All modified test files: [PASS/FAIL]\n```\n\nThen ask: **[C] Continue to Step 7: Optimize Test Organization**\n\n## FRONTMATTER UPDATE\n\nUpdate the output document:\n- Add `6` to `stepsCompleted`\n- Append the data optimization details to the report\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-07-optimize-organization.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3366,"content_sha256":"18deba0bc959f469bfe292889303145adfa0a2a599fb1b6f35802d6c43b3caa2"},{"filename":"workflows/optimizing/steps/step-07-optimize-organization.md","content":"---\nname: 'step-07-optimize-organization'\ndescription: 'Optimize test organization'\nnextStepFile: './step-08-measure-improvement.md'\n---\n\n# Step 7: Optimize Test Organization\n\n## STEP GOAL\n\nImprove test organization for better performance and maintainability through logical grouping, redundancy elimination, and test categorization.\n\n## EXECUTION\n\n### Technique A: Group Related Tests\n\nOrganize tests into `describe` blocks by method for shared context and better readability:\n\n```typescript\ndescribe('UserService', () => {\n // Group by method\n describe('findById', () => {\n // All findById tests share context\n });\n\n describe('create', () => {\n // All create tests share context\n });\n});\n```\n\n**When to apply**: Test files where tests for different methods are interleaved or not logically grouped. Grouping by method allows shared setup within each group and makes it easier to run specific method tests.\n\n### Technique B: Skip Redundant Tests\n\nIdentify and skip tests that duplicate coverage already provided by other tests:\n\n```typescript\n// If testing same logic through multiple paths, skip redundant ones\nit.skip('redundant test covered by other tests', () => {});\n\n// Or use conditional skipping\nconst skipIfCI = process.env.CI ? it.skip : it;\nskipIfCI('expensive test skipped in CI', () => {});\n```\n\n**When to apply**: Use sparingly. Only skip tests that are truly redundant (testing the exact same code path with equivalent inputs). Never skip tests just because they are slow — optimize them instead. Conditional skipping is appropriate for tests that require external resources unavailable in CI.\n\n### Technique C: Use Test Tags\n\nUse Jest's path filtering to run specific categories of tests:\n\n```bash\n# Run only fast tests\nnpm test -- --testPathIgnorePatterns=\"slow\"\n\n# Run only specific category\nnpm test -- --testPathPattern=\"unit\"\n```\n\n**When to apply**: When the test suite has grown large enough to benefit from selective execution. Consider organizing test files into directories or using naming conventions (e.g., `*.unit.spec.ts`, `*.integration.spec.ts`) that enable path-based filtering.\n\n### Apply Optimizations\n\nFor each test file:\n1. Review `describe` block organization\n2. Identify any truly redundant tests\n3. Consider test categorization for the overall suite\n4. Verify tests still pass:\n ```bash\n npm test -- [path/to/file.spec.ts] > /tmp/ut-${UT_SESSION}-org-check.log 2>&1\n tail -20 /tmp/ut-${UT_SESSION}-org-check.log\n ```\n\n## PRESENT FINDINGS\n\nPresent findings to the user:\n\n```\nStep 7: Organization Optimizations\n=====================================\n\nOptimizations Applied:\n [file.spec.ts]\n - Technique A: Reorganized into [N] describe blocks [APPLIED/SKIPPED]\n - Technique B: Skipped [N] redundant tests [APPLIED/SKIPPED]\n\n Suite-wide:\n - Technique C: Test categorization [APPLIED/SKIPPED]\n\nVerification:\n All modified test files: [PASS/FAIL]\n```\n\nThen ask: **[C] Continue to Step 8: Measure Improvement**\n\n## FRONTMATTER UPDATE\n\nUpdate the output document:\n- Add `7` to `stepsCompleted`\n- Append the organization optimization details to the report\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-08-measure-improvement.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3230,"content_sha256":"f09ce7e889a521642464cbcc0a76996fcb3d50674775021f270ac3387b179c5f"},{"filename":"workflows/optimizing/steps/step-08-measure-improvement.md","content":"---\nname: 'step-08-measure-improvement'\ndescription: 'Measure performance improvement against baseline'\nnextStepFile: './step-09-document-optimizations.md'\n---\n\n# Step 8: Measure Improvement\n\n## STEP GOAL\n\nRe-run the full test suite with performance measurement and open handle detection, then compare results against the baseline metrics captured in Step 1.\n\n## EXECUTION\n\n### 1. Re-run Performance Measurement with Open Handle Detection\n\nCapture the optimized run to a temp file:\n\n```bash\nnpm test -- --verbose --detectOpenHandles > /tmp/ut-${UT_SESSION}-optimized.log 2>&1\ntail -50 /tmp/ut-${UT_SESSION}-optimized.log\n```\n\n### 2. Verify Clean Exit\n\nRun without `--forceExit` to confirm tests exit cleanly:\n\n```bash\nnpm test -- --verbose > /tmp/ut-${UT_SESSION}-output.log 2>&1\ntail -30 /tmp/ut-${UT_SESSION}-output.log\n# Should exit cleanly without hanging\n```\n\n### 3. Compare with Baseline\n\nExtract timing data from both runs:\n\n```bash\necho \"=== BASELINE ===\" && grep -E \"Time:|passed|failed\" /tmp/ut-${UT_SESSION}-baseline.log\necho \"=== OPTIMIZED ===\" && grep -E \"Time:|passed|failed\" /tmp/ut-${UT_SESSION}-optimized.log\n```\n\n### 4. Document Comparison\n\nBuild the before/after comparison table:\n\n```\n**Performance Improvement:**\n\n| Metric | Before | After | Improvement |\n|--------|--------|-------|-------------|\n| Total time | [T]s | [T]s | [X]% faster |\n| Slowest file | [T]s | [T]s | [X]% faster |\n| Slowest test | [T]ms | [T]ms | [X]% faster |\n| Average test | [T]ms | [T]ms | [X]% faster |\n| Open handles | [X] | 0 | Fixed |\n| Clean exit | No | Yes | Fixed |\n```\n\n### 5. Verify No Regressions\n\nConfirm that optimizations did not break anything:\n- **All tests still pass** — same pass count as baseline\n- **Coverage unchanged or improved** — run coverage check if available\n- **Test quality maintained** — no assertions removed, no tests deleted\n- **No open handles detected** — grep the optimized log for handle warnings\n- **Tests exit cleanly without `--forceExit`** — process exits on its own\n\n```bash\n# Verify no open handles remain\ngrep -c \"open handle\" /tmp/ut-${UT_SESSION}-optimized.log\n# Expected: 0\n\n# Verify test counts match\ngrep -E \"Tests:.*passed\" /tmp/ut-${UT_SESSION}-baseline.log\ngrep -E \"Tests:.*passed\" /tmp/ut-${UT_SESSION}-optimized.log\n```\n\n## PRESENT FINDINGS\n\nPresent findings to the user:\n\n```\nStep 8: Performance Comparison\n================================\n\n| Metric | Before | After | Improvement |\n|--------|--------|-------|-------------|\n| Total time | [T]s | [T]s | [X]% faster |\n| Slowest file | [T]s | [T]s | [X]% faster |\n| Slowest test | [T]ms | [T]ms | [X]% faster |\n| Average test | [T]ms | [T]ms | [X]% faster |\n| Open handles | [X] | 0 | Fixed |\n| Clean exit | No | Yes | Fixed |\n\nRegression Check:\n All tests pass: [YES/NO]\n Coverage unchanged: [YES/NO]\n No open handles: [YES/NO]\n Clean exit: [YES/NO]\n```\n\nThen ask: **[C] Continue to Step 9: Document Optimizations**\n\n## FRONTMATTER UPDATE\n\nUpdate the output document:\n- Add `8` to `stepsCompleted`\n- Append the comparison table and regression check to the report\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-09-document-optimizations.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3179,"content_sha256":"85a59dbe11a965a828944902a327e974da9e96caa8282ebe87e446fc4fe54007"},{"filename":"workflows/optimizing/steps/step-09-document-optimizations.md","content":"---\nname: 'step-09-document-optimizations'\ndescription: 'Document all optimizations applied'\n---\n\n# Step 9: Document Optimizations\n\n## STEP GOAL\n\nCompile a complete optimization report documenting all changes made, their impact, and reference tables for future use.\n\n## EXECUTION\n\n### 1. Compile Final Report\n\nBuild the complete optimization report with all sections:\n\n```markdown\n## Test Optimization Report\n\n### Date: [date]\n\n### Before\n- Total tests: [X]\n- Total time: [T]s\n- Slowest: [test] ([T]ms)\n\n### After\n- Total tests: [X]\n- Total time: [T]s\n- Slowest: [test] ([T]ms)\n- Open handles: 0\n- Clean exit: Yes\n\n### Optimizations Applied\n\n1. **[Technique Name]**\n - Files affected: [list]\n - Improvement: [X]ms saved\n\n### Recommendations for Future\n- [Patterns to follow]\n- [Anti-patterns to avoid]\n```\n\n### 2. Include Optimization Quick Reference\n\nAppend the quick reference table for future optimization work:\n\n| Technique | Typical Improvement | Effort |\n|-----------|--------------------:|--------|\n| Fix open handles | Clean exit | CRITICAL |\n| beforeAll for module | 30-50% | Low |\n| Remove unnecessary async | 5-10% | Low |\n| Mock timers | 50-90% per test | Medium |\n| Minimal mock data | 5-15% | Low |\n| Test factories | Maintainability | Medium |\n| Parallel execution | 30-60% | Low |\n\n### 3. Include Open Handle Quick Reference\n\nAppend the open handle reference for diagnosing future issues:\n\n| Handle Type | Source | Fix |\n|-------------|--------|-----|\n| TCPSERVERWRAP | HTTP server | `await app.close()` in afterAll |\n| TCPWRAP | DB connection | Close connection in afterAll |\n| MONGOCLIENT | MongoDB | `await connection.close()` |\n| KAFKAPRODUCER | Kafka producer | `await client.close()` |\n| KAFKACONSUMER | Kafka consumer | `await consumer.disconnect()` |\n| REDISCLIENT | Redis | `await client.quit()` |\n| Timeout | setTimeout | `jest.useFakeTimers()` or `clearTimeout()` |\n| HTTPINCOMINGMESSAGE | Pending request | Abort controller or close connection |\n\n### 4. Include Anti-Patterns to Avoid\n\nAppend the anti-pattern reference:\n\n| Don't | Why | Do Instead |\n|-------|-----|------------|\n| Use `--forceExit` | Hides resource leaks | Fix open handles properly |\n| Remove assertions for speed | Reduces test quality | Keep assertions, optimize setup |\n| Share mutable state | Causes flaky tests | Reset state in beforeEach |\n| Skip slow tests | Reduces coverage | Optimize the tests |\n| Use real timeouts | Slows tests significantly | Mock time |\n| Over-parallelize | Can cause resource contention | Find optimal worker count |\n| Skip afterAll cleanup | Causes open handles | Always close resources |\n\n### 5. Clean Up Temp Files\n\nRemove all temp files created during the optimization session:\n\n```bash\nrm -f /tmp/ut-${UT_SESSION}-*.log\n```\n\n## PRESENT FINDINGS\n\nPresent the compiled report to the user, including:\n- Before/after metrics summary\n- All optimizations applied with file lists and improvements\n- Optimization Quick Reference table\n- Open Handle Quick Reference table\n- Anti-Patterns to Avoid table\n- Recommendations for maintaining test performance\n\n## FRONTMATTER UPDATE\n\nUpdate the output document frontmatter:\n- Add `9` to `stepsCompleted`\n- Set `status` to `'complete'`\n\n## WORKFLOW COMPLETE\n\nThe test optimization workflow is complete. The full report is saved at the output path.\n\nOffer next steps:\n- **Apply patterns to other test files** — use the quick reference to optimize additional tests\n- **Set up CI guardrails** — configure `--detectOpenHandles` in CI pipeline\n- **Monitor performance** — re-run this workflow periodically to catch regressions\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3600,"content_sha256":"d14d1d69dd5750ae8bb6ded11df9e8a2da5caa4aacc669ca7a5c369ff4f95a0b"},{"filename":"workflows/optimizing/templates/output-template.md","content":"---\nstepsCompleted: []\nworkflowType: 'optimizing'\nstatus: 'in-progress'\ndate: ''\ntargetTests: ''\nbaselineTime: ''\nopenHandlesCount: 0\noutputPath: ''\n---\n\n# Test Optimization Report\n\n**Date**: {{date}}\n**Target**: {{targetTests}}\n**Baseline Time**: {{baselineTime}}\n**Open Handles**: {{openHandlesCount}}\n**Status**: {{status}}\n\n---\n\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":333,"content_sha256":"659bfd790eee85d028683283977076f05fcec3695a332dc6d7bbfc409f8bab91"},{"filename":"workflows/optimizing/workflow.md","content":"---\nname: 'optimizing'\ndescription: 'Improve unit test performance, maintainability, and reliability'\nfirstStepFile: './steps/step-01-init.md'\ntemplateFile: './templates/output-template.md'\n---\n\n# Optimizing Unit Test Workflow\n\nImprove unit test performance, maintainability, and reliability through systematic optimization techniques. Ensure tests run fast and exit cleanly without open handles.\n\n## When to Use\n\n- Tests are running slowly and need performance improvement\n- Jest reports open handles or tests hang after completion\n- Test suite maintainability needs improvement\n- Preparing test suite for CI/CD pipeline optimization\n\n## Step-File Architecture\n\nThis workflow uses a **step-file architecture** for context-safe execution:\n\n- Each step is a separate file loaded sequentially\n- Progress is tracked via `stepsCompleted` in the output document's YAML frontmatter\n- If context is compacted mid-workflow, step-01 detects existing output and resumes from the last completed step via step-01b\n\n### Steps\n\n| Step | File | Description |\n|------|------|-------------|\n| 1 | `step-01-init.md` | Initialize session, measure baseline performance, detect continuation |\n| 1b | `step-01b-continue.md` | Resume from last completed step |\n| 2 | `step-02-identify-opportunities.md` | Identify optimization opportunities and open handle sources |\n| 3 | `step-03-optimize-setup.md` | Optimize test setup patterns |\n| 4 | `step-04-fix-open-handles.md` | Fix all open handles (CRITICAL) |\n| 5 | `step-05-optimize-async.md` | Optimize async operations in tests |\n| 6 | `step-06-optimize-data.md` | Optimize test data patterns |\n| 7 | `step-07-optimize-organization.md` | Optimize test organization |\n| 8 | `step-08-measure-improvement.md` | Measure performance improvement against baseline |\n| 9 | `step-09-document-optimizations.md` | Document all optimizations applied |\n\n### Rules\n\n1. **Load one step at a time** - Read the step file, execute it, then load the next\n2. **Update frontmatter after each step** - Add the step number to `stepsCompleted`\n3. **Wait for user confirmation** - Present findings and wait for `[C]` before proceeding\n4. **Measure before optimizing** - Always capture baseline metrics before making changes\n5. **Optimize one thing at a time** - Apply changes incrementally and verify after each\n6. **Don't sacrifice quality for speed** - Never remove assertions or reduce coverage for performance\n7. **ALWAYS output to temp files** - Redirect all test output to `/tmp/ut-${UT_SESSION}-*.log` files, never to console directly\n\n## Begin\n\nLoad `steps/step-01-init.md` to start.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2593,"content_sha256":"b05cc0ce849f15b88da2b611ea1440a4b21662bc947bdcbf318c3106a06a676d"},{"filename":"workflows/reviewing/steps/step-01-init.md","content":"---\nname: 'step-01-init'\ndescription: 'Initialize review workflow and detect continuation'\nnextStepFile: './step-02-test-structure.md'\n---\n\n# Step 1: Initialize Unit Test Review\n\n## STEP GOAL\n\nSet up the review session: identify test files to review, set the output path for the review report, and check for an existing report to resume.\n\n## EXECUTION\n\n### 1. Ask the User\n\nAsk the user:\n- **What test file(s) to review?** (file path or directory, e.g., `path/to/component.spec.ts` or `path/to/module/`)\n- **Output path** for the review report (suggest a default: `./unit-test-review-{{date}}.md`)\n- **Or provide path to an existing report** to resume a previous review\n\n### 2. Check for Existing Report\n\nIf the user provides a path to an existing report file:\n- Read the file\n- Parse the YAML frontmatter\n- If `stepsCompleted` is non-empty → **STOP and load `step-01b-continue.md`**\n\n### 3. Fresh Workflow Setup\n\nIf starting fresh:\n1. Copy the template from `templates/output-template.md`\n2. Fill in the frontmatter:\n - `targetTests`: the test file path(s) provided by the user\n - `targetSource`: the corresponding source file path(s)\n - `outputPath`: the chosen output path\n - `date`: current date\n3. Write the initialized report to the output path\n\n### 4. Understand Context\n\n**Goal**: Understand what the code is supposed to do before judging how it is tested.\n\n- Read the test file(s) to understand what is currently being tested\n- Read the corresponding source file(s) to understand what **should** be tested\n- Identify the class/module under test, its public methods, and its dependencies\n\nList files to review:\n```\n**Files to Review:**\n- Test: [path/to/file.spec.ts]\n- Source: [path/to/file.ts]\n```\n\n### 5. Append Initial Context to Report\n\nAppend to the output document:\n\n```markdown\n## Step 1: Context\n\n**Test File**: {{targetTests}}\n**Source File**: {{targetSource}}\n**Class/Module Under Test**: {{className}}\n**Public Methods**: {{list of public methods}}\n**Dependencies**: {{list of dependencies}}\n```\n\n## FRONTMATTER UPDATE\n\nUpdate the output document frontmatter:\n- Add `1` to `stepsCompleted`\n- Fill `targetTests` and `targetSource`\n\n## PRESENT TO USER\n\nShow the user:\n- Files identified for review\n- Source code understanding (class, public methods, dependencies)\n- Summary of what the test file currently covers\n\nThen ask: **[C] Continue to Step 2: Test Structure**\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-02-test-structure.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2470,"content_sha256":"326ed34c024a2f7783211a0f2196950f4db58c3b35221ca95450e2becc0354e9"},{"filename":"workflows/reviewing/steps/step-01b-continue.md","content":"---\nname: 'step-01b-continue'\ndescription: 'Resume unit test review from last completed step'\n---\n\n# Step 1b: Continue Previous Unit Test Review\n\n## STEP GOAL\n\nResume a previously started unit test review by reading the existing report, determining progress, and routing to the next incomplete step.\n\n## EXECUTION\n\n### 1. Read Existing Report\n\n- Read the file at the output path provided by the user\n- Parse the YAML frontmatter\n- Extract `stepsCompleted` array\n\n### 2. Show Progress Summary\n\nDisplay to the user:\n\n```\nUnit Test Review Progress\n=========================\nTest File: {{targetTests}}\nSource File: {{targetSource}}\nDate Started: {{date}}\nSteps Completed: {{stepsCompleted}}\n\nStep Map:\n [1] Initialize & Context {{done/pending}}\n [2] Test Structure {{done/pending}}\n [3] Setup & Teardown {{done/pending}}\n [4] AAA Pattern {{done/pending}}\n [5] Assertions Quality {{done/pending}}\n [6] Test Coverage {{done/pending}}\n [7] Exception Testing {{done/pending}}\n [8] Compile Report {{done/pending}}\n```\n\n### 3. Offer Options\n\nPresent:\n- **[R] Resume** from the next incomplete step\n- **[O] Overview** — re-read the existing report content before resuming\n- **[X] Start over** — create a fresh report (confirm: this will overwrite)\n\n### 4. Route to Next Step\n\nOn **[R]** or after **[O]**:\n\nDetermine the next step from `max(stepsCompleted) + 1` and load the corresponding file:\n\n| Next Step | File |\n|-----------|------|\n| 2 | `step-02-test-structure.md` |\n| 3 | `step-03-setup-teardown.md` |\n| 4 | `step-04-aaa-pattern.md` |\n| 5 | `step-05-assertions.md` |\n| 6 | `step-06-coverage.md` |\n| 7 | `step-07-exception-testing.md` |\n| 8 | `step-08-compile-report.md` |\n\nOn **[X]**: Go back to `step-01-init.md` fresh workflow setup (section 3).\n\n## NEXT STEP\n\nLoad the step file determined above.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1908,"content_sha256":"66e0123f243f7988e90853bdd7cd92e4b396e4158e0a0d33f0ab71bd232b7ce7"},{"filename":"workflows/reviewing/steps/step-02-test-structure.md","content":"---\nname: 'step-02-test-structure'\ndescription: 'Review test file structure and naming conventions'\nnextStepFile: './step-03-setup-teardown.md'\nreferenceFiles:\n - 'references/common/rules.md'\n---\n\n# Step 2: Review Test Structure\n\n## STEP GOAL\n\nCheck test file structure against conventions — file naming, co-location, variable naming, mock patterns, and describe/it hierarchy.\n\n## REFERENCE LOADING\n\nBefore starting analysis, load and read:\n- `references/common/rules.md` — AAA pattern, naming conventions, coverage requirements\n\nCite specific rules when reporting findings.\n\n## ANALYSIS PROCESS\n\n### 1. Check Test File Structure Against Conventions\n\n| Criterion | Expected | Status |\n|-----------|----------|--------|\n| File naming | `*.spec.ts` | [ ] |\n| File location | Co-located with source | [ ] |\n| SUT variable | Named `target` | [ ] |\n| Mock variables | Prefixed with `mock` | [ ] |\n| Mock types | Using `DeepMocked\u003cT>` | [ ] |\n| Logger | Using `MockLoggerService` | [ ] |\n\n### 2. Check describe/it Structure\n\nVerify the test file follows the nested describe/it convention:\n\n```typescript\ndescribe('ClassName', () => {\n describe('methodName', () => {\n it('should [expected] when [condition]', () => {});\n });\n});\n```\n\nCheck for:\n- Top-level `describe` matches the class/module name\n- Second-level `describe` blocks for each method\n- `it` blocks follow the `should [expected] when [condition]` pattern\n- No deeply nested describes beyond method level (unless grouping edge cases)\n\n### 3. Document Structural Issues\n\nFor each issue found, classify severity:\n\n```\n**Structural Issues:**\n- [CRITICAL/MAJOR/MINOR]: [Description]\n```\n\n## PRESENT FINDINGS\n\nPresent findings to the user in this format:\n\n```\nStep 2: Test Structure\n======================\n\nConvention Checklist:\n File naming (*.spec.ts): [PASS/FAIL]\n Co-located with source: [PASS/FAIL]\n SUT named `target`: [PASS/FAIL]\n Mock prefix `mock`: [PASS/FAIL]\n DeepMocked\u003cT> types: [PASS/FAIL]\n MockLoggerService: [PASS/FAIL]\n\nDescribe/It Structure:\n Top-level matches class name: [PASS/FAIL]\n Method-level describes: [PASS/FAIL]\n It naming convention: [PASS/FAIL]\n\nIssues Found: N\n - [SEVERITY]: description\n```\n\nThen ask: **[C] Continue to Step 3: Setup & Teardown**\n\n## FRONTMATTER UPDATE\n\nUpdate the output document:\n- Add `2` to `stepsCompleted`\n- Append the findings section to the report\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-03-setup-teardown.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2533,"content_sha256":"e0a092093866bdd04a9b74a7082b63b9a6e6b0d2e1feeb4dbc97cda0f890189a"},{"filename":"workflows/reviewing/steps/step-03-setup-teardown.md","content":"---\nname: 'step-03-setup-teardown'\ndescription: 'Review setup and teardown patterns'\nnextStepFile: './step-04-aaa-pattern.md'\nreferenceFiles:\n - 'references/common/rules.md'\n---\n\n# Step 3: Review Setup and Teardown\n\n## STEP GOAL\n\nVerify beforeEach/afterEach patterns follow conventions — fresh mock creation, TestingModule setup, logger configuration, and proper cleanup.\n\n## REFERENCE LOADING\n\nBefore starting analysis, load and read:\n- `references/common/rules.md` — AAA pattern, naming conventions, coverage requirements\n\nCite specific rules when reporting findings.\n\n## ANALYSIS PROCESS\n\n### 1. Verify beforeEach Setup\n\n| Criterion | Status | Issue |\n|-----------|--------|-------|\n| Mocks created fresh | [ ] | |\n| TestingModule built | [ ] | |\n| `.setLogger()` included | [ ] | |\n| Target retrieved from module | [ ] | |\n\nCheck that:\n- All mocks are created fresh in each `beforeEach` (not shared across tests)\n- `TestingModule` is built with proper providers\n- `.setLogger(new MockLoggerService())` is called on the target\n- The SUT (`target`) is retrieved from the module via `module.get()`\n\n### 2. Verify afterEach Cleanup\n\n| Criterion | Status | Issue |\n|-----------|--------|-------|\n| `jest.clearAllMocks()` called | [ ] | |\n| No shared mutable state | [ ] | |\n\n### 3. Check for Setup Anti-Patterns\n\nWatch for and report:\n- **Shared state between tests**: Variables mutated in one test affecting another\n- **Missing mock reset**: Mocks not cleared between tests, leading to leaky state\n- **Expensive setup in beforeEach**: Operations that should be in `beforeAll` instead (e.g., reading config files, static data)\n- **Setup duplication**: Same arrangement repeated in every test instead of using `beforeEach`\n- **Over-mocking in setup**: Mocking things not needed for most tests in the suite\n\n## PRESENT FINDINGS\n\nPresent findings to the user in this format:\n\n```\nStep 3: Setup and Teardown\n===========================\n\nbeforeEach:\n Fresh mock creation: [PASS/FAIL]\n TestingModule built: [PASS/FAIL]\n .setLogger() included: [PASS/FAIL]\n Target retrieved from module: [PASS/FAIL]\n\nafterEach:\n jest.clearAllMocks(): [PASS/FAIL]\n No shared mutable state: [PASS/FAIL]\n\nAnti-Patterns Found: N\n - [SEVERITY]: description\n```\n\nThen ask: **[C] Continue to Step 4: AAA Pattern**\n\n## FRONTMATTER UPDATE\n\nUpdate the output document:\n- Add `3` to `stepsCompleted`\n- Append the findings section to the report\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-04-aaa-pattern.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2531,"content_sha256":"84264ec4d38882ebbc738649c03efbe5240c2ed640defc710e9c663131771362"},{"filename":"workflows/reviewing/steps/step-04-aaa-pattern.md","content":"---\nname: 'step-04-aaa-pattern'\ndescription: 'Review AAA pattern compliance'\nnextStepFile: './step-05-assertions.md'\nreferenceFiles:\n - 'references/common/rules.md'\n---\n\n# Step 4: Review AAA Pattern Compliance\n\n## STEP GOAL\n\nVerify every test follows the Arrange-Act-Assert pattern with proper separation, comments, and single-action discipline.\n\n## REFERENCE LOADING\n\nBefore starting analysis, load and read:\n- `references/common/rules.md` — AAA pattern, naming conventions, coverage requirements\n\nCite specific rules when reporting findings.\n\n## ANALYSIS PROCESS\n\n### 1. Verify AAA Pattern in Each Test\n\nFor each test, check all three phases:\n\n| Test | Arrange | Act | Assert | Comments |\n|------|---------|-----|--------|----------|\n| test name | [ ] | [ ] | [ ] | [ ] |\n\n- **Arrange**: Sets up test data, configures mocks\n- **Act**: Calls the method under test (single action)\n- **Assert**: Verifies the result and/or mock interactions\n\n### 2. Check for AAA Violations\n\nFor each test, look for:\n- **Missing phase comments**: Each phase should have a `// Arrange`, `// Act`, `// Assert` comment\n- **Multiple actions in single test**: Only ONE call to the method under test per `it` block\n- **Assertions in Arrange phase**: Assertions should only appear in the Assert phase\n- **Missing assertions**: Every test must have at least one assertion\n- **Blurred boundaries**: Act and Assert combined (e.g., `expect(target.method()).toEqual(...)` — acceptable for simple cases but note it)\n\n### 3. Document AAA Issues\n\n```\n**AAA Pattern Issues:**\n- [test name]: [issue description]\n```\n\n## PRESENT FINDINGS\n\nPresent findings to the user in this format:\n\n```\nStep 4: AAA Pattern Compliance\n===============================\n\nTest-by-Test Review:\n [PASS/FAIL] \"should [expected] when [condition]\"\n - Arrange: [OK / MISSING / ISSUE]\n - Act: [OK / MULTIPLE ACTIONS / MISSING]\n - Assert: [OK / MISSING / WEAK]\n - Comments: [OK / MISSING]\n\nSummary: N tests reviewed, N with AAA violations\n\nIssues:\n - [test name]: [description]\n```\n\nThen ask: **[C] Continue to Step 5: Assertions Quality**\n\n## FRONTMATTER UPDATE\n\nUpdate the output document:\n- Add `4` to `stepsCompleted`\n- Append the findings section to the report\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-05-assertions.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2294,"content_sha256":"b8280895d15828f8d3a5ec44a0b4c1c964156d7371467938ee79798cb9bedf23"},{"filename":"workflows/reviewing/steps/step-05-assertions.md","content":"---\nname: 'step-05-assertions'\ndescription: 'Review assertion quality'\nnextStepFile: './step-06-coverage.md'\nreferenceFiles:\n - 'references/common/assertions.md'\n - 'references/common/rules.md'\n---\n\n# Step 5: Review Assertion Quality\n\n## STEP GOAL\n\nCheck assertion quality — verify tests use specific value assertions, avoid existence-only checks, and properly verify mock interactions.\n\n## REFERENCE LOADING\n\nBefore starting analysis, load and read:\n- `references/common/assertions.md` — Assertion patterns and matchers\n- `references/common/rules.md` — AAA pattern, naming conventions, coverage requirements\n\nCite specific rules when reporting findings.\n\n## ANALYSIS PROCESS\n\n### 1. Check for Assertion Anti-Patterns\n\n| Anti-Pattern | Found | Location |\n|--------------|-------|----------|\n| `toBeDefined()` only | [ ] | |\n| `toBeTruthy()` only | [ ] | |\n| Missing property assertions | [ ] | |\n| Conditional assertions (if/else) | [ ] | |\n| No mock call verification | [ ] | |\n\n### 2. Verify Assertions Are Specific\n\nAssertions should verify exact values, not just existence:\n\n```typescript\n// BAD\nexpect(result).toBeDefined();\n\n// GOOD\nexpect(result).toEqual({ id: 'user-123', email: '[email protected]' });\n```\n\nFor each test, check:\n- Does it verify the **actual values** returned, not just that something was returned?\n- Does it check **all relevant properties** of the result?\n- Are assertions **deterministic** (no random data, no Date.now())?\n\n### 3. Verify Mock Interactions\n\nEvery test that arranges mocks should verify those mocks were called correctly:\n\n```typescript\nexpect(mockService.method).toHaveBeenCalledWith(expectedArgs);\nexpect(mockService.method).toHaveBeenCalledTimes(1);\n```\n\nCheck:\n- Are mock calls verified with `toHaveBeenCalledWith` (specific arguments)?\n- Are mock call counts verified with `toHaveBeenCalledTimes`?\n- Are **all** arranged mocks actually asserted against?\n\n### 4. Document Assertion Issues\n\n```\n**Assertion Issues:**\n- [CRITICAL]: [test name] - Only checks existence, not values\n- [MAJOR]: [test name] - Missing mock call verification\n- [MINOR]: [test name] - Could use more specific matcher\n```\n\n## PRESENT FINDINGS\n\nPresent findings to the user in this format:\n\n```\nStep 5: Assertion Quality\n==========================\n\nAnti-Pattern Scan:\n toBeDefined() only: [FOUND N / NONE]\n toBeTruthy() only: [FOUND N / NONE]\n Missing property assertions: [FOUND N / NONE]\n Conditional assertions: [FOUND N / NONE]\n No mock call verification: [FOUND N / NONE]\n\nTest-by-Test Assertion Review:\n [PASS/FAIL] \"test name\"\n - Value specificity: [OK / WEAK / MISSING]\n - Mock verification: [OK / INCOMPLETE / MISSING]\n\nSummary: N assertions reviewed, N issues found\n Critical: N | Major: N | Minor: N\n```\n\nThen ask: **[C] Continue to Step 6: Test Coverage**\n\n## FRONTMATTER UPDATE\n\nUpdate the output document:\n- Add `5` to `stepsCompleted`\n- Append the findings section to the report\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-06-coverage.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3044,"content_sha256":"cc333fdd9750409b49b07dc836407d02f043c270fe187bd976ee8337d65032e3"},{"filename":"workflows/reviewing/steps/step-06-coverage.md","content":"---\nname: 'step-06-coverage'\ndescription: 'Review test coverage'\nnextStepFile: './step-07-exception-testing.md'\n---\n\n# Step 6: Review Test Coverage\n\n## STEP GOAL\n\nRun coverage analysis and identify gaps — check statement, branch, function, and line coverage against the 80% threshold, and identify missing test cases by category.\n\n## EXECUTION\n\n### 1. Run Coverage Report\n\n```bash\nnpm run test:cov -- [path/to/file.spec.ts]\n```\n\nParse the output and extract coverage numbers.\n\n### 2. Check Coverage Against Requirements\n\n| Category | Required | Actual | Status |\n|----------|----------|--------|--------|\n| Statement | 80% | [ ]% | |\n| Branch | 80% | [ ]% | |\n| Function | 80% | [ ]% | |\n| Line | 80% | [ ]% | |\n\n### 3. Identify Missing Test Cases by Category\n\nCross-reference the source file with the test file to find untested logic:\n\n| Category | Coverage | Missing Tests |\n|----------|----------|---------------|\n| Happy path | [ ] | |\n| Edge cases | [ ] | |\n| Error cases | [ ] | |\n| Business rules | [ ] | |\n\nFor each category:\n- **Happy path**: Are all normal execution paths tested?\n- **Edge cases**: Empty inputs, boundary values, null/undefined handling?\n- **Error cases**: All error branches, invalid inputs, failed dependencies?\n- **Business rules**: Are domain-specific rules validated?\n\n### 4. List Uncovered Code Paths\n\n```\n**Uncovered Code:**\n- Line [X-Y]: [description of uncovered logic]\n- Line [X-Y]: [description of uncovered logic]\n```\n\nIdentify:\n- Uncovered branches (if/else, switch cases, ternary operators)\n- Uncovered methods (public methods with no tests)\n- Uncovered error paths (catch blocks, guard clauses)\n\n## PRESENT FINDINGS\n\nPresent findings to the user in this format:\n\n```\nStep 6: Test Coverage\n======================\n\nCoverage Report:\n Statement: XX% [PASS/FAIL] (required: 80%)\n Branch: XX% [PASS/FAIL] (required: 80%)\n Function: XX% [PASS/FAIL] (required: 80%)\n Line: XX% [PASS/FAIL] (required: 80%)\n\nMissing Test Cases:\n Happy path: [COMPLETE / N missing]\n Edge cases: [COMPLETE / N missing]\n Error cases: [COMPLETE / N missing]\n Business rules: [COMPLETE / N missing]\n\nUncovered Code Paths:\n - Line [X-Y]: [description]\n```\n\nThen ask: **[C] Continue to Step 7: Exception Testing**\n\n## FRONTMATTER UPDATE\n\nUpdate the output document:\n- Add `6` to `stepsCompleted`\n- Append the findings section to the report\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-07-exception-testing.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2458,"content_sha256":"e1910cea660a407c9f249a8bbd5a31eb7fc481eb9b9e1b4b139c765460d9bc4c"},{"filename":"workflows/reviewing/steps/step-07-exception-testing.md","content":"---\nname: 'step-07-exception-testing'\ndescription: 'Review exception testing completeness'\nnextStepFile: './step-08-compile-report.md'\nreferenceFiles:\n - 'references/common/rules.md'\n - 'references/common/assertions.md'\n---\n\n# Step 7: Review Exception Testing\n\n## STEP GOAL\n\nVerify every exception thrown in the source code has a corresponding test that checks the exception type, message, and error code.\n\n## REFERENCE LOADING\n\nBefore starting analysis, load and read:\n- `references/common/rules.md` — AAA pattern, naming conventions, coverage requirements\n- `references/common/assertions.md` — Assertion patterns and matchers\n\nCite specific rules when reporting findings.\n\n## ANALYSIS PROCESS\n\n### 1. Inventory All Exceptions in Source Code\n\nScan the source file for every `throw` statement and map each to its condition:\n\n| Exception | Condition | Test Exists | Verifies Type | Verifies Message | Verifies Code |\n|-----------|-----------|-------------|---------------|------------------|---------------|\n| NotFoundException | not found | [ ] | [ ] | [ ] | [ ] |\n| BadRequestException | validation | [ ] | [ ] | [ ] | [ ] |\n\nInclude:\n- Explicit `throw new XxxException(...)` statements\n- Exceptions thrown by called methods that should propagate\n- Guard clauses that throw\n\n### 2. Check Exception Test Patterns\n\nEach exception test should verify multiple aspects:\n\n```typescript\n// Should verify type\nawait expect(target.method()).rejects.toThrow(NotFoundException);\n\n// Should verify message\nawait expect(target.method()).rejects.toThrow('Expected message');\n\n// Should verify error code if applicable\nawait expect(target.method()).rejects.toMatchObject({\n errorCode: 'EXPECTED_CODE',\n});\n```\n\nFor each exception test, check:\n- Does it verify the **exception type** (class)?\n- Does it verify the **error message** (string content)?\n- Does it verify the **error code** if the exception has one?\n- Does it properly set up the condition that triggers the exception?\n- Does it use `rejects.toThrow` for async methods?\n\n### 3. Document Exception Testing Issues\n\n```\n**Exception Testing Issues:**\n- [exception type]: [missing verification]\n- [exception type]: Test exists but does not verify message\n- [exception type]: No test found for this exception\n```\n\n## PRESENT FINDINGS\n\nPresent findings to the user in this format:\n\n```\nStep 7: Exception Testing\n==========================\n\nException Inventory:\n [PASS/FAIL] ExceptionType — \"condition description\"\n - Type verified: [YES/NO]\n - Message verified: [YES/NO]\n - Code verified: [YES/NO/N/A]\n - Test location: [test name or MISSING]\n\nSummary:\n Exceptions in source: N\n Fully tested: N\n Partially tested: N\n Untested: N\n\nIssues:\n - [SEVERITY]: [description]\n```\n\nThen ask: **[C] Continue to Step 8: Compile Report**\n\n## FRONTMATTER UPDATE\n\nUpdate the output document:\n- Add `7` to `stepsCompleted`\n- Append the findings section to the report\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-08-compile-report.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3003,"content_sha256":"8698dd1b463bc75ac1e5c6d3126d1fc22845e3e760fdac6472c4469df763ed81"},{"filename":"workflows/reviewing/steps/step-08-compile-report.md","content":"---\nname: 'step-08-compile-report'\ndescription: 'Compile final review report'\n---\n\n# Step 8: Compile Review Report\n\n## STEP GOAL\n\nCompile all findings from steps 2-7 into a structured, prioritized review report with actionable recommendations.\n\n## COMPILATION PROCESS\n\n### 1. Gather All Findings\n\nRead through the output document and collect all issues found in steps 2-7.\n\n### 2. Build Summary Table\n\n```markdown\n## Unit Test Review Report\n\n### File: [path/to/file.spec.ts]\n### Source: [path/to/source.ts]\n### Date: [current date]\n\n---\n\n### Summary\n\n| Metric | Status |\n|--------|--------|\n| Overall Quality | [PASS/NEEDS WORK/FAIL] |\n| Coverage | [X]% |\n| Critical Issues | [count] |\n| Major Issues | [count] |\n| Minor Issues | [count] |\n```\n\n### 3. Categorize Issues by Severity\n\nUse these severity definitions:\n\n| Severity | Definition | Examples |\n|----------|------------|----------|\n| CRITICAL | Test provides false confidence or is fundamentally broken | Only checks `toBeDefined()`, testing wrong behavior |\n| MAJOR | Test has significant gaps or doesn't follow conventions | Missing mock verification, no exception testing |\n| MINOR | Test works but could be improved | Missing comments, inconsistent naming |\n\n### 4. Critical Issues (Must Fix)\n\n```markdown\n### Critical Issues (Must Fix)\n\n1. **[Issue Title]**\n - Location: [test name / line number]\n - Problem: [description]\n - Fix: [specific action to take]\n```\n\n### 5. Major Issues (Should Fix)\n\n```markdown\n### Major Issues (Should Fix)\n\n1. **[Issue Title]**\n - Location: [test name / line number]\n - Problem: [description]\n - Fix: [specific action to take]\n```\n\n### 6. Minor Issues (Consider Fixing)\n\n```markdown\n### Minor Issues (Consider Fixing)\n\n1. **[Issue Title]**\n - Location: [test name / line number]\n - Problem: [description]\n - Fix: [specific action to take]\n```\n\n### 7. Missing Test Cases\n\n```markdown\n### Missing Test Cases\n\n| Method | Missing Category | Priority |\n|--------|-----------------|----------|\n| [method] | [category] | [HIGH/MEDIUM/LOW] |\n```\n\n### 8. Recommendations\n\n```markdown\n### Recommendations\n\n1. [Specific recommendation]\n2. [Specific recommendation]\n```\n\nPrioritize recommendations by impact:\n1. Fix critical issues first (false confidence, broken tests)\n2. Add missing exception tests\n3. Improve assertion quality\n4. Fill coverage gaps\n5. Fix structural/naming conventions\n\n### 9. Quick Review Checklist\n\nAppend the quick review checklist to the report:\n\n```\n**Review Checklist:**\n\nStructure:\n- [ ] File named *.spec.ts\n- [ ] Co-located with source\n- [ ] Using `target` for SUT\n- [ ] Using `mock` prefix for mocks\n- [ ] Using `DeepMocked\u003cT>` type\n\nSetup:\n- [ ] Fresh mocks in beforeEach\n- [ ] `.setLogger(new MockLoggerService())`\n- [ ] `jest.clearAllMocks()` in afterEach\n\nAAA Pattern:\n- [ ] Arrange-Act-Assert structure\n- [ ] Comments for each phase\n- [ ] Single action per test\n\nAssertions:\n- [ ] Specific value assertions\n- [ ] Mock call verification\n- [ ] Mock call count verification\n- [ ] No conditional assertions\n\nCoverage:\n- [ ] Happy path tests\n- [ ] Edge case tests\n- [ ] Error case tests\n- [ ] Exception tests with type/message/code\n- [ ] 80%+ coverage achieved\n```\n\n## PRESENT FINDINGS\n\nPresent the compiled report to the user. Highlight critical issues first.\n\nOffer next steps:\n- **Help fix critical issues** — offer to start with the most impactful fixes\n- **Re-read `references/common/rules.md`** to confirm all issues were identified\n- **Prioritize fixes** with the user\n\n## FRONTMATTER UPDATE\n\nUpdate the output document frontmatter:\n- Add `8` to `stepsCompleted`\n- Set `status` to `'complete'`\n\n## WORKFLOW COMPLETE\n\nThe unit test review workflow is complete. The full report is saved at the output path.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3746,"content_sha256":"602deea67c945433f8b95394d4b36adc98f19d9c138d875cfa7c49a7a3bced35"},{"filename":"workflows/reviewing/templates/output-template.md","content":"---\nstepsCompleted: []\nworkflowType: 'reviewing'\nstatus: 'in-progress'\ndate: ''\ntargetTests: ''\ntargetSource: ''\noutputPath: ''\n---\n\n# Unit Test Review Report\n\n**Date**: {{date}}\n**Test File**: {{targetTests}}\n**Source File**: {{targetSource}}\n**Status**: {{status}}\n\n---\n\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":273,"content_sha256":"12ca7e31aaaf01c3aa29bb7374281bada0711ea8238cfa89aaad5eec138814e3"},{"filename":"workflows/reviewing/workflow.md","content":"---\nname: 'reviewing'\ndescription: 'Systematically review existing unit tests for quality, conventions, and coverage'\nfirstStepFile: './steps/step-01-init.md'\ntemplateFile: './templates/output-template.md'\n---\n\n# Reviewing Unit Test Workflow\n\nSystematically review existing unit tests to ensure they meet quality standards, follow conventions, and provide adequate coverage.\n\n## When to Use\n\n- Reviewing unit tests during code review\n- Performing a quality audit on existing test suites\n- Reviewing tests written by yourself or a colleague before merging\n\n## Prerequisites\n\nHave the test file(s) and corresponding source file(s) accessible.\n\n## Step-File Architecture\n\nThis workflow uses a **step-file architecture** for context-safe execution:\n\n- Each step is a separate file loaded sequentially\n- Progress is tracked via `stepsCompleted` in the output document's YAML frontmatter\n- If context is compacted mid-workflow, step-01 detects existing output and resumes from the last completed step via step-01b\n\n### Steps\n\n| Step | File | Description |\n|------|------|-------------|\n| 1 | `step-01-init.md` | Initialize workflow, set output path, detect continuation |\n| 1b | `step-01b-continue.md` | Resume from last completed step |\n| 2 | `step-02-test-structure.md` | Review test file structure and naming conventions |\n| 3 | `step-03-setup-teardown.md` | Review setup and teardown patterns |\n| 4 | `step-04-aaa-pattern.md` | Review AAA pattern compliance |\n| 5 | `step-05-assertions.md` | Review assertion quality |\n| 6 | `step-06-coverage.md` | Review test coverage |\n| 7 | `step-07-exception-testing.md` | Review exception testing completeness |\n| 8 | `step-08-compile-report.md` | Compile final review report |\n\n### Rules\n\n1. **Load one step at a time** - Read the step file, execute it, then load the next\n2. **Update frontmatter after each step** - Add the step number to `stepsCompleted`\n3. **Wait for user confirmation** - Present findings and wait for `[C]` before proceeding\n4. **Load reference files** - Each step specifies which reference files to load before analysis\n5. **Cite specific rules** - When reporting findings, cite the specific rule from the reference file\n\n## Begin\n\nLoad `steps/step-01-init.md` to start.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2232,"content_sha256":"bf1d65463f74c3f3a76dd0efbebf4aa58608db2aa4cb4e3e31e842bef6714610"},{"filename":"workflows/running/steps/step-01-init.md","content":"---\nname: 'step-01-init'\ndescription: 'Initialize test execution session and detect continuation'\nnextStepFile: './step-02-run-tests.md'\n---\n\n# Step 1: Initialize Test Execution\n\n## STEP GOAL\n\nSet up the test execution session: determine test scope, initialize the temp file session with a unique `UT_SESSION` ID, set the output path for the execution report, and check for an existing report to resume.\n\n## EXECUTION\n\n### 1. Ask the User\n\nAsk the user:\n- **What tests to run?** (all tests, specific file, pattern, or single test name)\n- **Output path** for the execution report (suggest a default: `./test-execution-report-{{date}}.md`)\n- **Or provide path to an existing report** to resume a previous execution\n\n### 2. Check for Existing Report\n\nIf the user provides a path to an existing report file:\n- Read the file\n- Parse the YAML frontmatter\n- If `stepsCompleted` is non-empty → **STOP and load `step-01b-continue.md`**\n\n### 3. Fresh Workflow Setup\n\nIf starting fresh:\n1. Copy the template from `templates/output-template.md`\n2. Fill in the frontmatter:\n - `testScope`: description of what tests will be run\n - `testCommand`: the exact command to execute\n - `outputPath`: the chosen output path\n - `date`: current date\n3. Write the initialized report to the output path\n\n### 4. Initialize UT_SESSION\n\nSet up the temp file session for all test output:\n\n```bash\n# Initialize session (once at start)\nexport UT_SESSION=$(date +%s)-$\n```\n\n**Temp File Locations** (with `${UT_SESSION}` unique per session):\n- `/tmp/ut-${UT_SESSION}-output.log` - Full test output\n- `/tmp/ut-${UT_SESSION}-failures.md` - Tracking file for one-by-one fixing\n- `/tmp/ut-${UT_SESSION}-debug.log` - Debug runs\n- `/tmp/ut-${UT_SESSION}-coverage.log` - Coverage output\n\n### 5. Determine Test Scope\n\nIdentify what tests to run based on user input:\n\n| Scope | Command | Use When |\n|-------|---------|----------|\n| All tests | `npm test` | Full validation |\n| Single file | `npm test -- [path]` | Focused testing |\n| Pattern match | `npm test -- --testPathPattern=[pattern]` | Module testing |\n| Single test | `npm test -- -t \"[test name]\"` | Debugging specific test |\n\n### 6. Confirm Scope with User\n\nPresent the determined scope:\n\n```\n**Test Scope:**\n- Running: [description]\n- Command: [command to execute]\n- Session: UT_SESSION=${UT_SESSION}\n```\n\n## FRONTMATTER UPDATE\n\nUpdate the output document frontmatter:\n- Add `1` to `stepsCompleted`\n- Fill in `testScope` and `testCommand`\n\n## PRESENT TO USER\n\nShow the user:\n- Confirmation of test scope and command\n- Output path for the report\n- Session ID for temp files\n\nThen ask: **[C] Continue to Step 2: Run Tests**\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-02-run-tests.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2723,"content_sha256":"f24e90286b8d6456a5bb024e45eb519f6c670d07206ce95a92ed157c61c96f40"},{"filename":"workflows/running/steps/step-01b-continue.md","content":"---\nname: 'step-01b-continue'\ndescription: 'Resume test execution from last completed step'\n---\n\n# Step 1b: Continue Previous Test Execution\n\n## STEP GOAL\n\nResume a previously started test execution workflow by reading the existing report, determining progress, and routing to the next incomplete step.\n\n## EXECUTION\n\n### 1. Read Existing Report\n\n- Read the file at the output path provided by the user\n- Parse the YAML frontmatter\n- Extract `stepsCompleted` array\n\n### 2. Re-initialize UT_SESSION\n\nRe-establish the temp file session:\n\n```bash\nexport UT_SESSION=$(date +%s)-$\n```\n\n### 3. Show Progress Summary\n\nDisplay to the user:\n\n```\nTest Execution Progress\n========================\nScope: {{testScope}}\nCommand: {{testCommand}}\nDate Started: {{date}}\nSteps Completed: {{stepsCompleted}}\n\nStep Map:\n [1] Initialize & Scope {{done/pending}}\n [2] Run Tests {{done/pending}}\n [3] Interpret Results {{done/pending}}\n [4] Coverage {{done/pending}}\n [5] Handle Scenarios {{done/pending}}\n [6] Summary Report {{done/pending}}\n```\n\n### 4. Offer Options\n\nPresent:\n- **[R] Resume** from the next incomplete step\n- **[O] Overview** - re-read the existing report content before resuming\n- **[X] Start over** - create a fresh report (confirm: this will overwrite)\n\n### 5. Route to Next Step\n\nOn **[R]** or after **[O]**:\n\nDetermine the next step from `max(stepsCompleted) + 1` and load the corresponding file:\n\n| Next Step | File |\n|-----------|------|\n| 2 | `step-02-run-tests.md` |\n| 3 | `step-03-interpret-results.md` |\n| 4 | `step-04-coverage.md` |\n| 5 | `step-05-handle-scenarios.md` |\n| 6 | `step-06-summary-report.md` |\n\nOn **[X]**: Go back to `step-01-init.md` fresh workflow setup (section 3).\n\n## NEXT STEP\n\nLoad the step file determined above.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1831,"content_sha256":"b25c184d4cb4d8833978cd2ed37c84d9d8ab309e71ed670df11bed805cf0baa1"},{"filename":"workflows/running/steps/step-02-run-tests.md","content":"---\nname: 'step-02-run-tests'\ndescription: 'Execute tests with output captured to temp files'\nnextStepFile: './step-03-interpret-results.md'\n---\n\n# Step 2: Run Tests\n\n## STEP GOAL\n\nExecute the determined test command with all output captured to temp files. Never output test results directly to console.\n\n## EXECUTION\n\n### 1. Context Efficiency: Temp File Output\n\n**Why**: Test output can be verbose. Direct terminal output bloats agent context.\n\n**IMPORTANT**: Redirect output to temp files only (NO console output). Use the unique session ID to prevent conflicts.\n\n```bash\n# Initialize session (if not already set)\nexport UT_SESSION=$(date +%s)-$\n\n# Standard pattern for all test runs - redirect to file only (no console)\nnpm test > /tmp/ut-${UT_SESSION}-output.log 2>&1\n\n# Read only summary\ntail -50 /tmp/ut-${UT_SESSION}-output.log\n\n# Get failure details\ngrep -B 2 -A 15 \"FAIL\\|✕\" /tmp/ut-${UT_SESSION}-output.log\n\n# Cleanup when done\nrm -f /tmp/ut-${UT_SESSION}-*.log /tmp/ut-${UT_SESSION}-*.md\n```\n\n### 2. Execute Test Command\n\nRun the test command determined in Step 1, redirecting all output to the temp file:\n\n```bash\n# Standard run\nnpm test -- [path/to/file.spec.ts] > /tmp/ut-${UT_SESSION}-output.log 2>&1\ntail -50 /tmp/ut-${UT_SESSION}-output.log\n\n# With verbose output\nnpm test -- [path/to/file.spec.ts] --verbose > /tmp/ut-${UT_SESSION}-output.log 2>&1\ntail -100 /tmp/ut-${UT_SESSION}-output.log\n\n# Specific test only\nnpm test -- -t \"test name\" > /tmp/ut-${UT_SESSION}-output.log 2>&1\ntail -50 /tmp/ut-${UT_SESSION}-output.log\n```\n\n### 3. Read Results from Temp File\n\nAfter execution, read only the relevant portions:\n\n```bash\n# Get summary (last 50 lines)\ntail -50 /tmp/ut-${UT_SESSION}-output.log\n\n# Get failure details\ngrep -B 2 -A 15 \"FAIL\\|✕\" /tmp/ut-${UT_SESSION}-output.log\n```\n\n## PRESENT FINDINGS\n\nConfirm to the user:\n- Tests executed with the determined command\n- Output captured to `/tmp/ut-${UT_SESSION}-output.log`\n- Quick summary of pass/fail status from the tail output\n\nThen ask: **[C] Continue to Step 3: Interpret Results**\n\n## FRONTMATTER UPDATE\n\nUpdate the output document frontmatter:\n- Add `2` to `stepsCompleted`\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-03-interpret-results.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2235,"content_sha256":"700ae6f189575f45da7b20ff8152a296c4f473ddb53bf8463f73265767935afb"},{"filename":"workflows/running/steps/step-03-interpret-results.md","content":"---\nname: 'step-03-interpret-results'\ndescription: 'Parse and categorize test results'\nnextStepFile: './step-04-coverage.md'\n---\n\n# Step 3: Interpret Results\n\n## STEP GOAL\n\nInterpret the test output captured in the temp file. Parse pass/fail/skip counts, extract details for each failure, and categorize failures by type.\n\n## EXECUTION\n\n### 1. Parse Test Output\n\nExtract the summary from the test output:\n\n```\n**Test Results:**\n\nPassed: [X] tests\nFailed: [Y] tests\nSkipped: [Z] tests\nTotal time: [T]s\n```\n\n### 2. Extract Failure Details\n\nFor each failure, extract:\n- **Test name**: the full test description\n- **Describe path**: the nested describe block path\n- **Error type**: what kind of error occurred\n- **Expected vs received**: the assertion mismatch\n- **Stack trace location**: file and line number\n\n### 3. Categorize Failures\n\nClassify each failure into one of these categories:\n\n| Failure Type | Count | Description |\n|--------------|-------|-------------|\n| Assertion failure | [X] | Expected value mismatch |\n| Exception thrown | [X] | Unexpected error |\n| Timeout | [X] | Test exceeded time limit |\n| Setup failure | [X] | beforeEach/beforeAll error |\n\n### 4. Append to Report\n\nAppend the parsed results and categorization to the output document:\n\n```markdown\n## Step 3: Test Results\n\n**Passed**: [X] | **Failed**: [Y] | **Skipped**: [Z] | **Time**: [T]s\n\n### Failure Details\n[For each failure: test name, describe path, error type, expected vs received, stack trace]\n\n### Failure Categories\n[The categorization table above filled in]\n```\n\n## PRESENT FINDINGS\n\nPresent the parsed results to the user:\n- Summary counts (passed, failed, skipped, time)\n- Each failure with extracted details\n- Categorization table\n\nThen ask: **[C] Continue to Step 4: Coverage**\n\n## FRONTMATTER UPDATE\n\nUpdate the output document frontmatter:\n- Add `3` to `stepsCompleted`\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-04-coverage.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1935,"content_sha256":"d5478ba38cb543cbf58da8dbffd10bab5ba8f735a5485f822442422f03093495"},{"filename":"workflows/running/steps/step-04-coverage.md","content":"---\nname: 'step-04-coverage'\ndescription: 'Run and analyze test coverage'\nnextStepFile: './step-05-handle-scenarios.md'\n---\n\n# Step 4: Run Coverage (If Requested)\n\n## STEP GOAL\n\nExecute the coverage report command, capture output to a temp file, parse statement/branch/function/line coverage, check against the 80% threshold, and identify uncovered lines.\n\n## EXECUTION\n\n### 1. Ask User\n\nAsk the user if they want to run coverage analysis. If not requested, skip to presenting findings with a note that coverage was not run.\n\n### 2. Execute Coverage Command\n\nRun the coverage command with output to temp file only (no console):\n\n```bash\nnpm run test:cov -- [path/to/file.spec.ts] > /tmp/ut-${UT_SESSION}-coverage.log 2>&1\ntail -50 /tmp/ut-${UT_SESSION}-coverage.log\n```\n\n### 3. Parse Coverage Output\n\nExtract coverage metrics and present:\n\n```\n**Coverage Report:**\n\n| Metric | Coverage | Threshold | Status |\n|--------|----------|-----------|--------|\n| Statements | [X]% | 80% | [PASS/FAIL] |\n| Branches | [X]% | 80% | [PASS/FAIL] |\n| Functions | [X]% | 80% | [PASS/FAIL] |\n| Lines | [X]% | 80% | [PASS/FAIL] |\n```\n\n### 4. Identify Uncovered Lines\n\nList uncovered code areas:\n\n```\n**Uncovered Code:**\n- [file:line-range]: [description]\n```\n\n### 5. Append to Report\n\nAppend coverage findings to the output document:\n\n```markdown\n## Step 4: Coverage\n\n| Metric | Coverage | Threshold | Status |\n|--------|----------|-----------|--------|\n| Statements | [X]% | 80% | [PASS/FAIL] |\n| Branches | [X]% | 80% | [PASS/FAIL] |\n| Functions | [X]% | 80% | [PASS/FAIL] |\n| Lines | [X]% | 80% | [PASS/FAIL] |\n\n### Uncovered Code\n[List of uncovered lines/ranges]\n```\n\n## PRESENT FINDINGS\n\nPresent the coverage report to the user:\n- Coverage metrics table with pass/fail against 80% threshold\n- Uncovered code areas (if any)\n- Or a note that coverage was skipped if not requested\n\nThen ask: **[C] Continue to Step 5: Handle Scenarios**\n\n## FRONTMATTER UPDATE\n\nUpdate the output document frontmatter:\n- Add `4` to `stepsCompleted`\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-05-handle-scenarios.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2093,"content_sha256":"40182e1a806d64f51f56dbde742e6a286c85d714dcca5684f9889a3b6194c7e3"},{"filename":"workflows/running/steps/step-05-handle-scenarios.md","content":"---\nname: 'step-05-handle-scenarios'\ndescription: 'Handle test result scenarios and recommend actions'\nnextStepFile: './step-06-summary-report.md'\n---\n\n# Step 5: Handle Common Scenarios\n\n## STEP GOAL\n\nBased on the test results interpreted in Step 3, identify which scenario applies and handle it accordingly. This includes the one-by-one fixing protocol for failures.\n\n## EXECUTION\n\nDetermine which scenario applies from the test results and follow the corresponding handling procedure.\n\n### Scenario A: All Tests Pass\n\n```\n**Result: ALL TESTS PASSED**\n\n- Total: [X] tests in [Y] test suites\n- Time: [T]s\n- Coverage: [X]% (if run)\n\nNo action required.\n```\n\n### Scenario B: Some Tests Fail\n\n1. Present failure summary:\n\n```\n**Result: [X] TESTS FAILED**\n\nFailed Tests:\n1. [describe path] > [test name]\n - Error: [error message]\n - Expected: [expected value]\n - Received: [received value]\n - Location: [file:line]\n\nWARNING: Do NOT run full suite again!\n```\n\n2. **Follow one-by-one fixing protocol**:\n\n**Critical: One-by-One Fixing Rule**\n\n**When tests fail, NEVER keep running the full suite.** Instead:\n1. Note all failing tests in `/tmp/ut-${UT_SESSION}-failures.md`\n2. Fix ONE test at a time using `-t \"test name\"`\n3. Verify each fix with 3-5 runs of that specific test\n4. Only run full suite ONCE after ALL individual tests pass\n\n```bash\n# Create tracking file with all failures\ncat > /tmp/ut-${UT_SESSION}-failures.md \u003c\u003c 'EOF'\n# Unit Test Failures\n\n## Test 1: \"[test name]\"\n- File: [path:line]\n- Error: [message]\n- Status: PENDING\n\n## Test 2: \"[test name]\"\n- File: [path:line]\n- Error: [message]\n- Status: PENDING\nEOF\n\n# Fix ONE test at a time (no console output)\nnpm test -- -t \"[first test name]\" > /tmp/ut-${UT_SESSION}-debug.log 2>&1\ntail -50 /tmp/ut-${UT_SESSION}-debug.log\n```\n\n3. Recommend using `debugging-unit-test.md` workflow for investigation\n\n### Scenario C: Setup Failures\n\n1. Identify setup issue:\n\n```\n**Result: SETUP FAILURE**\n\n- Phase: [beforeAll/beforeEach]\n- Error: [error message]\n- Stack: [relevant stack trace]\n```\n\n2. Common causes:\n - Missing mock setup\n - Module resolution error\n - Dependency injection failure\n\n### Scenario D: Timeout\n\n1. Identify timeout issue:\n\n```\n**Result: TIMEOUT**\n\n- Test: [test name]\n- Timeout: [default or custom]\n```\n\n2. Possible causes:\n - Unresolved promise\n - Missing `mockResolvedValue`\n - Infinite loop\n\n### Append to Report\n\nAppend the scenario handling to the output document:\n\n```markdown\n## Step 5: Scenario Handling\n\n**Scenario**: [A/B/C/D] - [description]\n\n[Scenario-specific details and actions taken]\n```\n\n## PRESENT FINDINGS\n\nPresent the scenario determination and actions to the user:\n- Which scenario was identified\n- Actions taken or recommended\n- For Scenario B: the one-by-one fixing protocol status\n\nThen ask: **[C] Continue to Step 6: Summary Report**\n\n## FRONTMATTER UPDATE\n\nUpdate the output document frontmatter:\n- Add `5` to `stepsCompleted`\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-06-summary-report.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3020,"content_sha256":"cfefc5cdb4ab8cf9eae232480b75a70ce277111b7fcd51273e874e0e49ee9096"},{"filename":"workflows/running/steps/step-06-summary-report.md","content":"---\nname: 'step-06-summary-report'\ndescription: 'Compile and present test execution summary'\n---\n\n# Step 6: Present Summary Report\n\n## STEP GOAL\n\nCompile the full test execution report with all findings from previous steps, including command executed, results summary, execution time, coverage, issues found, and recommended actions.\n\n## EXECUTION\n\n### 1. Compile Execution Report\n\nAppend the full summary to the output document:\n\n```markdown\n## Test Execution Report\n\n### Command Executed\n```bash\n[exact command run]\n```\n\n### Results Summary\n\n| Status | Count |\n|--------|-------|\n| Passed | [X] |\n| Failed | [Y] |\n| Skipped | [Z] |\n| Total | [X+Y+Z] |\n\n### Execution Time\n- Total: [T]s\n- Slowest: [test name] ([T]s)\n\n### Coverage (if run)\n- Statements: [X]%\n- Branches: [X]%\n- Functions: [X]%\n- Lines: [X]%\n\n### Issues Found\n[List any failures or concerns]\n\n### Recommended Actions\n[Based on results]\n```\n\n### 2. Common Commands Reference\n\nInclude the reference table for future use:\n\n**All commands redirect output to temp files only (no console output).**\n\n```bash\n# Initialize session (once at start)\nexport UT_SESSION=$(date +%s)-$\n```\n\n| Task | Command |\n|------|---------|\n| Run all tests | `npm test > /tmp/ut-${UT_SESSION}-output.log 2>&1 && tail -50 /tmp/ut-${UT_SESSION}-output.log` |\n| Run single file | `npm test -- path/to/file.spec.ts > /tmp/ut-${UT_SESSION}-output.log 2>&1 && tail -50 /tmp/ut-${UT_SESSION}-output.log` |\n| Run single test | `npm test -- -t \"should return user\" > /tmp/ut-${UT_SESSION}-output.log 2>&1 && tail -50 /tmp/ut-${UT_SESSION}-output.log` |\n| Get failure details | `grep -B 2 -A 15 \"FAIL\\|✕\" /tmp/ut-${UT_SESSION}-output.log` |\n| Coverage | `npm run test:cov > /tmp/ut-${UT_SESSION}-coverage.log 2>&1 && tail -50 /tmp/ut-${UT_SESSION}-coverage.log` |\n| Verbose output | `npm test -- --verbose > /tmp/ut-${UT_SESSION}-output.log 2>&1 && tail -100 /tmp/ut-${UT_SESSION}-output.log` |\n| Clear cache | `npx jest --clearCache` |\n| Cleanup | `rm -f /tmp/ut-${UT_SESSION}-*.log /tmp/ut-${UT_SESSION}-*.md` |\n\n### 3. Post-Workflow Actions\n\nBased on results, recommend next workflow:\n\n| Result | Recommended Workflow |\n|--------|---------------------|\n| All pass | None required |\n| Failures found | `debugging-unit-test.md` |\n| Low coverage | `writing-unit-test.md` |\n| Slow tests | `optimizing-unit-test.md` |\n\n### 4. Troubleshooting Quick Reference\n\n| Symptom | Likely Cause | Solution |\n|---------|--------------|----------|\n| \"Cannot find module\" | Path alias issue | Check jest.config moduleNameMapper |\n| \"is not a function\" | Mock not configured | Add mockResolvedValue/mockReturnValue |\n| Timeout | Unresolved promise | Check async/await usage |\n| \"Received: undefined\" | Missing mock return | Configure mock return value |\n| Snapshot mismatch | Output changed | Review changes, update if correct |\n\n## PRESENT FINDINGS\n\nShow the compiled report to the user:\n- Full summary with all sections\n- Post-workflow action recommendations\n- Troubleshooting reference for any issues encountered\n\n### 5. Cleanup Temp Files\n\nRemind the user to clean up session temp files:\n\n```bash\nrm -f /tmp/ut-${UT_SESSION}-*.log /tmp/ut-${UT_SESSION}-*.md\n```\n\n## FRONTMATTER UPDATE\n\nUpdate the output document frontmatter:\n- Add `6` to `stepsCompleted`\n- Set `status` to `'complete'`\n\n## WORKFLOW COMPLETE\n\nThe test execution workflow is complete. The full report is saved at the output path.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3416,"content_sha256":"f350d88004d8a5961521209e1621b3209bb610482b8b77eee78a33b753791d18"},{"filename":"workflows/running/templates/output-template.md","content":"---\nstepsCompleted: []\nworkflowType: 'running'\nstatus: 'in-progress'\ndate: ''\ntestScope: ''\ntestCommand: ''\noutputPath: ''\n---\n\n# Test Execution Report\n\n**Date**: {{date}}\n**Scope**: {{testScope}}\n**Command**: {{testCommand}}\n**Status**: {{status}}\n\n---\n\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":255,"content_sha256":"dfa28218d8ed0c24267807fcd35166d5b33e3ba12aa0ce4a8d6ce224f58558cd"},{"filename":"workflows/running/workflow.md","content":"---\nname: 'running'\ndescription: 'Execute unit tests with proper configuration, filtering, and result interpretation'\nfirstStepFile: './steps/step-01-init.md'\ntemplateFile: './templates/output-template.md'\n---\n\n# Running Unit Test Workflow\n\nExecute unit tests effectively with proper configuration, filtering, and result interpretation.\n\n## When to Use\n\n- Running tests (full suite, single file, or specific test)\n- Analyzing test results and categorizing failures\n- Checking test coverage against thresholds\n- Diagnosing setup failures and timeouts\n\n## Step-File Architecture\n\nThis workflow uses a **step-file architecture** for context-safe execution:\n\n- Each step is a separate file loaded sequentially\n- Progress is tracked via `stepsCompleted` in the output document's YAML frontmatter\n- If context is compacted mid-workflow, step-01 detects existing output and resumes from the last completed step via step-01b\n\n### Steps\n\n| Step | File | Description |\n|------|------|-------------|\n| 1 | `step-01-init.md` | Initialize session, determine test scope, detect continuation |\n| 1b | `step-01b-continue.md` | Resume from last completed step |\n| 2 | `step-02-run-tests.md` | Execute tests with output captured to temp files |\n| 3 | `step-03-interpret-results.md` | Parse and categorize test results |\n| 4 | `step-04-coverage.md` | Run and analyze test coverage |\n| 5 | `step-05-handle-scenarios.md` | Handle test result scenarios and recommend actions |\n| 6 | `step-06-summary-report.md` | Compile and present test execution summary |\n\n### Rules\n\n1. **Load one step at a time** - Read the step file, execute it, then load the next\n2. **Update frontmatter after each step** - Add the step number to `stepsCompleted`\n3. **Wait for user confirmation** - Present findings and wait for `[C]` before proceeding\n4. **NEVER run full suite repeatedly** - When debugging failures, fix one test at a time using `-t \"test name\"`\n5. **ALWAYS output to temp files** - Redirect all test output to `/tmp/ut-${UT_SESSION}-*.log` files, never to console directly\n\n## Begin\n\nLoad `steps/step-01-init.md` to start.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2096,"content_sha256":"e9f97b849c3b1dab46a6d7e12c30ed7ebcee85cda5aa85b161fa3ec6cbc4bde6"},{"filename":"workflows/setup/steps/step-01-init.md","content":"---\nname: 'step-01-init'\ndescription: 'Initialize unit test setup workflow — set output path and detect continuation'\nnextStepFile: './step-02-dependencies.md'\n---\n\n# Step 1: Initialize Unit Test Setup\n\n## STEP GOAL\n\nSet up the unit test setup session: identify the target project, set the output path for the setup report, check for an existing report to resume, and perform initial infrastructure analysis.\n\n## EXECUTION\n\n### 1. Ask the User\n\nAsk the user:\n- **Target project path** — the root directory of the NestJS/TypeScript project\n- **Output path** for the setup report (suggest a default: `./unit-test-setup-report-{{date}}.md`)\n- **Or provide path to an existing report** to resume a previous setup\n\n### 2. Check for Existing Report\n\nIf the user provides a path to an existing report file:\n- Read the file\n- Parse the YAML frontmatter\n- If `stepsCompleted` is non-empty — **STOP and load `step-01b-continue.md`**\n\n### 3. Fresh Workflow Setup\n\nIf starting fresh:\n1. Copy the template from `templates/output-template.md`\n2. Fill in the frontmatter:\n - `targetProject`: the project path provided by the user\n - `outputPath`: the chosen output path\n - `date`: current date\n3. Write the initialized report to the output path\n\n### 4. Initial Infrastructure Analysis\n\nAnalyze the existing test infrastructure in the target project:\n\n1. **Check for existing test configuration:**\n - Look for `jest.config.ts` or `jest.config.js` in project root\n - Check for existing `test/` directory structure\n - Look for `*.spec.ts` files to understand existing patterns\n\n2. **Check package.json for testing dependencies:**\n - `@nestjs/testing`\n - `jest`\n - `ts-jest`\n - `@golevelup/ts-jest`\n - `@types/jest`\n\n3. **Check for existing spec files:**\n - Count `*.spec.ts` files in the project\n - Note any existing test patterns\n\n### 5. Append Findings to Report\n\nAppend to the output document:\n\n```markdown\n## Step 1: Infrastructure Analysis\n\n**Test Infrastructure Analysis:**\n- Jest config: [Found/Not found] at [path]\n- Test directory: [Found/Not found] at [path]\n- Existing spec files: [count] files found\n- Dependencies status: [list installed and missing dependencies]\n\n**Existing Patterns:**\n- [describe any existing test patterns found, or \"No existing tests found\"]\n```\n\n## FRONTMATTER UPDATE\n\nUpdate the output document frontmatter:\n- Add `1` to `stepsCompleted`\n\n## PRESENT TO USER\n\nShow the user:\n- Confirmation of the target project and output path\n- Infrastructure analysis summary\n- List of installed vs missing dependencies\n\nThen ask: **[C] Continue to Step 2: Install Missing Dependencies**\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-02-dependencies.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2698,"content_sha256":"6bb207b9144a5797abbe10ceed55191b552ad8aaf9efc535e4ee7a248eb8bfc6"},{"filename":"workflows/setup/steps/step-01b-continue.md","content":"---\nname: 'step-01b-continue'\ndescription: 'Resume unit test setup from last completed step'\n---\n\n# Step 1b: Continue Previous Setup\n\n## STEP GOAL\n\nResume a previously started unit test setup by reading the existing report, determining progress, and routing to the next incomplete step.\n\n## EXECUTION\n\n### 1. Read Existing Report\n\n- Read the file at the output path provided by the user\n- Parse the YAML frontmatter\n- Extract `stepsCompleted` array\n\n### 2. Show Progress Summary\n\nDisplay to the user:\n\n```\nUnit Test Setup Progress\n========================\nProject: {{targetProject}}\nDate Started: {{date}}\nSteps Completed: {{stepsCompleted}}\n\nStep Map:\n [1] Infrastructure Analysis {{done/pending}}\n [2] Install Dependencies {{done/pending}}\n [3] Configure Jest {{done/pending}}\n [4] Create Test Helpers {{done/pending}}\n [5] Verify Setup {{done/pending}}\n [6] Configure npm Scripts {{done/pending}}\n```\n\n### 3. Offer Options\n\nPresent:\n- **[R] Resume** from the next incomplete step\n- **[O] Overview** — re-read the existing report content before resuming\n- **[X] Start over** — create a fresh report (confirm: this will overwrite)\n\n### 4. Route to Next Step\n\nOn **[R]** or after **[O]**:\n\nDetermine the next step from `max(stepsCompleted) + 1` and load the corresponding file:\n\n| Next Step | File |\n|-----------|------|\n| 2 | `step-02-dependencies.md` |\n| 3 | `step-03-configure-jest.md` |\n| 4 | `step-04-test-helpers.md` |\n| 5 | `step-05-verify-setup.md` |\n| 6 | `step-06-npm-scripts.md` |\n\nOn **[X]**: Go back to `step-01-init.md` fresh workflow setup (section 3).\n\n## NEXT STEP\n\nLoad the step file determined above.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1677,"content_sha256":"e9a1ff874d2f59ae3fb231cac36fc90f36ea98da962931ed220b732099db5927"},{"filename":"workflows/setup/steps/step-02-dependencies.md","content":"---\nname: 'step-02-dependencies'\ndescription: 'Install missing testing dependencies'\nnextStepFile: './step-03-configure-jest.md'\nreferenceFiles:\n - 'references/common/knowledge.md'\n - 'references/common/rules.md'\n---\n\n# Step 2: Install Missing Dependencies\n\n## STEP GOAL\n\nIdentify all missing testing dependencies and install them so the project has everything needed for NestJS unit testing.\n\n## REFERENCE LOADING\n\nBefore starting, load and read:\n- `references/common/knowledge.md` — testing philosophy and pyramid\n- `references/common/rules.md` — naming conventions and setup requirements\n\n## EXECUTION\n\n### 1. Check Required Dependencies\n\nReview the project's `package.json` and verify the following devDependencies are present:\n\n```json\n{\n \"devDependencies\": {\n \"@nestjs/testing\": \"^11.0.12\",\n \"jest\": \"^29.7.0\",\n \"ts-jest\": \"^29.2.6\",\n \"@golevelup/ts-jest\": \"^0.4.0\",\n \"@types/jest\": \"^29.5.14\"\n }\n}\n```\n\n### 2. Identify Missing Dependencies\n\nCompare installed dependencies against the required list. Note:\n- Which dependencies are already installed and at what version\n- Which dependencies are missing entirely\n- Which dependencies have incompatible versions\n\n### 3. Present Installation Command\n\nIf there are missing dependencies, present the install command:\n\n```bash\nnpm install --save-dev [missing-packages]\n```\n\nWait for user confirmation before running the installation.\n\n### 4. Verify Installation\n\nAfter installation completes:\n- Confirm all required dependencies appear in `package.json`\n- Verify no installation errors occurred\n\n## PRESENT FINDINGS\n\nPresent findings to the user:\n\n```\nStep 2: Dependencies\n====================\n\nRequired Dependencies:\n [@nestjs/testing] : INSTALLED (version) / MISSING\n [jest] : INSTALLED (version) / MISSING\n [ts-jest] : INSTALLED (version) / MISSING\n [@golevelup/ts-jest]: INSTALLED (version) / MISSING\n [@types/jest] : INSTALLED (version) / MISSING\n\nAction Taken: [Installed X missing packages / All dependencies already present]\n```\n\nThen ask: **[C] Continue to Step 3: Configure Jest**\n\n## FRONTMATTER UPDATE\n\nUpdate the output document:\n- Add `2` to `stepsCompleted`\n- Append the dependency status section to the report\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-03-configure-jest.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2306,"content_sha256":"335cbceffd4d3ba4b12ffd3fe64db946767da402d40b3f0707b38d9d22b8e635"},{"filename":"workflows/setup/steps/step-03-configure-jest.md","content":"---\nname: 'step-03-configure-jest'\ndescription: 'Configure Jest for the project'\nnextStepFile: './step-04-test-helpers.md'\n---\n\n# Step 3: Configure Jest\n\n## STEP GOAL\n\nCreate or verify the Jest configuration file (`jest.config.ts`) so the project has a correct and complete test runner setup.\n\n## EXECUTION\n\n### 1. Check for Existing Configuration\n\nLook for `jest.config.ts` or `jest.config.js` in the project root.\n\n### 2a. If No Config Exists — Create It\n\nCreate `jest.config.ts` with the following standard configuration:\n\n```typescript\nimport type { Config } from 'jest';\n\nconst config: Config = {\n moduleFileExtensions: ['js', 'json', 'ts'],\n rootDir: '.',\n testRegex: '.*\\\\.spec\\\\.ts

Unit Testing Skill Unit testing validates individual functions, methods, and classes in isolation by mocking all external dependencies. --- Workflows For guided, step-by-step execution of unit testing tasks, use the appropriate workflow: | Workflow | Purpose | When to Use | |----------|---------|-------------| | Setup | Initialize test infrastructure | New project or missing test setup | | Writing | Write new unit tests | Creating tests for components | | Reviewing | Review existing tests | Code review, quality audit | | Running | Execute tests | Running tests, analyzing results | | Debugging…

,\n transform: {\n '^.+\\\\.(t|j)s

Unit Testing Skill Unit testing validates individual functions, methods, and classes in isolation by mocking all external dependencies. --- Workflows For guided, step-by-step execution of unit testing tasks, use the appropriate workflow: | Workflow | Purpose | When to Use | |----------|---------|-------------| | Setup | Initialize test infrastructure | New project or missing test setup | | Writing | Write new unit tests | Creating tests for components | | Reviewing | Review existing tests | Code review, quality audit | | Running | Execute tests | Running tests, analyzing results | | Debugging…

: 'ts-jest',\n },\n collectCoverageFrom: ['**/*.(t|j)s'],\n coverageDirectory: './coverage',\n testEnvironment: 'node',\n roots: ['\u003crootDir>/src/', '\u003crootDir>/test/'],\n moduleNameMapper: {\n '^src/(.*)

Unit Testing Skill Unit testing validates individual functions, methods, and classes in isolation by mocking all external dependencies. --- Workflows For guided, step-by-step execution of unit testing tasks, use the appropriate workflow: | Workflow | Purpose | When to Use | |----------|---------|-------------| | Setup | Initialize test infrastructure | New project or missing test setup | | Writing | Write new unit tests | Creating tests for components | | Reviewing | Review existing tests | Code review, quality audit | | Running | Execute tests | Running tests, analyzing results | | Debugging…

: '\u003crootDir>/src/$1',\n },\n};\n\nexport default config;\n```\n\n### 2b. If Config Exists — Verify It\n\nCheck that the existing configuration includes:\n- Correct `testRegex` pattern for `.spec.ts` files\n- `ts-jest` transform for TypeScript compilation\n- Coverage configuration (`collectCoverageFrom`, `coverageDirectory`)\n- `roots` including both `src/` and `test/` directories\n- Path alias mapping via `moduleNameMapper` (if project uses path aliases in `tsconfig.json`)\n\nReport any missing or incorrect settings and propose corrections.\n\n### 3. Append to Report\n\nAppend to the output document:\n\n```markdown\n## Step 3: Jest Configuration\n\n**Config File**: [created/verified] at [path]\n**Settings Verified:**\n- testRegex: [OK/Fixed]\n- ts-jest transform: [OK/Fixed]\n- Coverage config: [OK/Fixed]\n- Module name mapper: [OK/Fixed]\n- Roots: [OK/Fixed]\n\n**Changes Made**: [list changes or \"No changes needed\"]\n```\n\n## PRESENT FINDINGS\n\nPresent the Jest configuration status:\n\n```\nStep 3: Jest Configuration\n==========================\n\nConfig: [Created new / Verified existing] jest.config.ts\n - testRegex: OK\n - ts-jest transform: OK\n - Coverage: OK\n - Module mapper: OK\n - Roots: OK\n\nChanges: [list or \"None needed\"]\n```\n\nThen ask: **[C] Continue to Step 4: Create Test Helpers**\n\n## FRONTMATTER UPDATE\n\nUpdate the output document:\n- Add `3` to `stepsCompleted`\n- Append the Jest configuration section to the report\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-04-test-helpers.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2457,"content_sha256":"fa4fb05c65e1363e7f2d401819e2c3fb8ef8e01d0c92d03f206cc2c9b192d54e"},{"filename":"workflows/setup/steps/step-04-test-helpers.md","content":"---\nname: 'step-04-test-helpers'\ndescription: 'Create test helper utilities'\nnextStepFile: './step-05-verify-setup.md'\n---\n\n# Step 4: Create Test Helpers\n\n## STEP GOAL\n\nCreate the test helpers directory structure and the `MockLoggerService` utility so tests can suppress logger output and have a consistent mock available.\n\n## EXECUTION\n\n### 1. Create Directory Structure\n\nEnsure the following directory structure exists in the project:\n\n```\ntest/\n└── helpers/\n └── mock-logger.service.ts\n```\n\n### 2. Check for Existing Logger\n\nBefore creating the mock:\n- Check if the project has a custom logger service (e.g., extending `LoggerService`)\n- If a custom logger exists, adapt the `MockLoggerService` to match its interface\n\n### 3. Create MockLoggerService\n\nCreate `test/helpers/mock-logger.service.ts`:\n\n```typescript\n// test/helpers/mock-logger.service.ts\nimport { LoggerService } from '@nestjs/common';\n\nexport class MockLoggerService implements LoggerService {\n log(): void {}\n error(): void {}\n warn(): void {}\n debug(): void {}\n verbose(): void {}\n}\n```\n\nIf the project has a custom logger with additional methods, extend the mock accordingly.\n\n### 4. Append to Report\n\nAppend to the output document:\n\n```markdown\n## Step 4: Test Helpers\n\n**Directory Created**: test/helpers/\n**Files Created:**\n- `test/helpers/mock-logger.service.ts` — MockLoggerService\n\n**Custom Logger**: [Found at path — adapted mock / Not found — using standard NestJS LoggerService interface]\n```\n\n## PRESENT FINDINGS\n\nPresent the test helper status:\n\n```\nStep 4: Test Helpers\n====================\n\nDirectory: test/helpers/ [created/already existed]\nFiles:\n mock-logger.service.ts [created/already existed]\n\nCustom Logger: [Adapted for custom logger at path / Standard NestJS interface used]\n```\n\nThen ask: **[C] Continue to Step 5: Verify Setup**\n\n## FRONTMATTER UPDATE\n\nUpdate the output document:\n- Add `4` to `stepsCompleted`\n- Append the test helpers section to the report\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-05-verify-setup.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2056,"content_sha256":"a2156278358f81f4e76537a85e5c8e710f3c95757a489790d4b96732f7434f8f"},{"filename":"workflows/setup/steps/step-05-verify-setup.md","content":"---\nname: 'step-05-verify-setup'\ndescription: 'Verify setup with sample test'\nnextStepFile: './step-06-npm-scripts.md'\n---\n\n# Step 5: Verify Setup with Sample Test\n\n## STEP GOAL\n\nCreate and run a verification test that exercises the core testing infrastructure — `@nestjs/testing` module creation, `MockLoggerService`, and `@golevelup/ts-jest` deep mocking — to confirm the setup is working correctly.\n\n## EXECUTION\n\n### 1. Create Verification Test\n\nCreate `test/setup.spec.ts` with the following content (skip if the file already exists):\n\n```typescript\n// test/setup.spec.ts\nimport { Test, TestingModule } from '@nestjs/testing';\nimport { createMock, DeepMocked } from '@golevelup/ts-jest';\nimport { MockLoggerService } from './helpers/mock-logger.service';\n\ndescribe('Test Setup Verification', () => {\n it('should create testing module successfully', async () => {\n const module: TestingModule = await Test.createTestingModule({\n providers: [],\n })\n .setLogger(new MockLoggerService())\n .compile();\n\n expect(module).toBeDefined();\n });\n\n it('should create mocks with DeepMocked', () => {\n interface SampleService {\n getData(): Promise\u003cstring>;\n }\n\n const mockService: DeepMocked\u003cSampleService> = createMock\u003cSampleService>();\n mockService.getData.mockResolvedValue('test');\n\n expect(mockService.getData).toBeDefined();\n });\n});\n```\n\n### 2. Run Verification Test\n\nExecute the verification test:\n\n```bash\nnpm test -- test/setup.spec.ts\n```\n\n### 3. Evaluate Results\n\n- If tests pass: Setup is confirmed working\n- If tests fail: Analyze the error output and identify the root cause (missing dependency, configuration issue, path resolution problem)\n- Propose and apply fixes if needed, then re-run\n\n### 4. Append to Report\n\nAppend to the output document:\n\n```markdown\n## Step 5: Setup Verification\n\n**Verification Test**: test/setup.spec.ts [created/already existed]\n**Test Results:**\n- TestingModule creation: [PASS/FAIL]\n- DeepMocked creation: [PASS/FAIL]\n\n**Issues Found**: [list issues and fixes applied, or \"None\"]\n```\n\n## PRESENT FINDINGS\n\nPresent the verification results:\n\n```\nStep 5: Setup Verification\n==========================\n\nVerification Test: test/setup.spec.ts\n [PASS] should create testing module successfully\n [PASS] should create mocks with DeepMocked\n\nResult: [All tests passing / Issues found and fixed / Issues require attention]\n```\n\nThen ask: **[C] Continue to Step 6: Configure npm Scripts**\n\n## FRONTMATTER UPDATE\n\nUpdate the output document:\n- Add `5` to `stepsCompleted`\n- Append the verification section to the report\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-06-npm-scripts.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2673,"content_sha256":"0ee90a12041711ea7d18024da7fddda9f37050183c2d06441eb300a8d6028732"},{"filename":"workflows/setup/steps/step-06-npm-scripts.md","content":"---\nname: 'step-06-npm-scripts'\ndescription: 'Configure npm test scripts'\n---\n\n# Step 6: Configure npm Scripts\n\n## STEP GOAL\n\nVerify and add the required npm test scripts to `package.json` so the project has consistent commands for running, watching, debugging, and generating coverage for tests.\n\n## EXECUTION\n\n### 1. Check Existing Scripts\n\nRead the `scripts` section of `package.json` and check for the following required scripts:\n\n```json\n{\n \"scripts\": {\n \"test\": \"jest\",\n \"test:watch\": \"jest --watch\",\n \"test:cov\": \"jest --coverage\",\n \"test:debug\": \"node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand\"\n }\n}\n```\n\n### 2. Add Missing Scripts\n\nFor each missing script:\n- Present the script to the user\n- Add it to `package.json`\n\nDo not overwrite existing scripts that already serve the same purpose (e.g., if `test` already runs Jest with custom flags, confirm with the user before replacing).\n\n### 3. Verify Scripts Work\n\nRun a quick verification:\n\n```bash\nnpm test -- --help\n```\n\nConfirm the test command is wired up correctly.\n\n### 4. Append to Report\n\nAppend to the output document:\n\n```markdown\n## Step 6: npm Scripts\n\n**Scripts Verified:**\n- `test`: [present/added] — `jest`\n- `test:watch`: [present/added] — `jest --watch`\n- `test:cov`: [present/added] — `jest --coverage`\n- `test:debug`: [present/added] — `node --inspect-brk ...`\n\n**Changes Made**: [list changes or \"All scripts already present\"]\n```\n\n## PRESENT FINDINGS\n\nPresent the npm scripts status and full completion summary:\n\n```\nStep 6: npm Scripts\n===================\n\nScripts:\n test: [present/added]\n test:watch: [present/added]\n test:cov: [present/added]\n test:debug: [present/added]\n\nChanges: [list or \"None needed\"]\n\n========================================\nUnit Test Setup Complete\n========================================\n\n [1] Infrastructure Analysis DONE\n [2] Dependencies Installed DONE\n [3] Jest Configured DONE\n [4] Test Helpers Created DONE\n [5] Setup Verified DONE\n [6] npm Scripts Configured DONE\n\nNext Steps:\n- Use the `writing-unit-test.md` workflow to write tests\n- Follow AAA pattern and naming conventions from references/common/rules.md\n- Target 80%+ coverage for new code\n```\n\n## FRONTMATTER UPDATE\n\nUpdate the output document frontmatter:\n- Add `6` to `stepsCompleted`\n- Set `status` to `'complete'`\n- Append the npm scripts section and completion summary to the report\n\n## WORKFLOW COMPLETE\n\nThe unit test setup workflow is complete. The full report is saved at the output path.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2607,"content_sha256":"43affbbcf155c59b223f978ddcd6dc7d924cc1d14cd8090bab3b476559fd66fd"},{"filename":"workflows/setup/templates/output-template.md","content":"---\nstepsCompleted: []\nworkflowType: 'setup'\nstatus: 'in-progress'\ndate: ''\ntargetProject: ''\noutputPath: ''\n---\n\n# Unit Test Setup Report\n\n**Date**: {{date}}\n**Project**: {{targetProject}}\n**Status**: {{status}}\n\n---\n\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":219,"content_sha256":"ecc1cf862f9da0d84b662f9f555e806b6d003a40356aabd827b577cbf9a7da72"},{"filename":"workflows/setup/workflow.md","content":"---\nname: 'setup'\ndescription: 'Initialize unit testing infrastructure for NestJS/TypeScript projects'\nfirstStepFile: './steps/step-01-init.md'\ntemplateFile: './templates/output-template.md'\n---\n\n# Unit Test Setup Workflow\n\nInitialize unit testing infrastructure for a NestJS/TypeScript project. This workflow ensures consistent test setup across all projects by walking through dependency installation, Jest configuration, test helper creation, and verification.\n\n## When to Use\n\n- Setting up unit testing in a new NestJS/TypeScript project\n- Verifying and fixing an existing test infrastructure\n- Standardizing test setup across multiple projects\n\n## Prerequisites\n\nHave access to the target NestJS/TypeScript project with a valid `package.json`.\n\n## Step-File Architecture\n\nThis workflow uses a **step-file architecture** for context-safe execution:\n\n- Each step is a separate file loaded sequentially\n- Progress is tracked via `stepsCompleted` in the output document's YAML frontmatter\n- If context is compacted mid-workflow, step-01 detects existing output and resumes from the last completed step via step-01b\n\n### Steps\n\n| Step | File | Description |\n|------|------|-------------|\n| 1 | `step-01-init.md` | Initialize workflow, analyze existing infrastructure, detect continuation |\n| 1b | `step-01b-continue.md` | Resume from last completed step |\n| 2 | `step-02-dependencies.md` | Install missing testing dependencies |\n| 3 | `step-03-configure-jest.md` | Configure Jest for the project |\n| 4 | `step-04-test-helpers.md` | Create test helper utilities |\n| 5 | `step-05-verify-setup.md` | Verify setup with sample test |\n| 6 | `step-06-npm-scripts.md` | Configure npm test scripts |\n\n### Rules\n\n1. **Load one step at a time** - Read the step file, execute it, then load the next\n2. **Update frontmatter after each step** - Add the step number to `stepsCompleted`\n3. **Wait for user confirmation** - Present findings and wait for `[C]` before proceeding\n4. **Load reference files** - Each step specifies which reference files to load before execution\n\n## Begin\n\nLoad `steps/step-01-init.md` to start.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2108,"content_sha256":"9f84ba090abf83c575bfcc20e94aea127f9f7d64102faac0b483c907733b52be"},{"filename":"workflows/writing/steps/step-01-init.md","content":"---\nname: 'step-01-init'\ndescription: 'Initialize writing workflow — identify target component and detect continuation'\nnextStepFile: './step-02-test-file-structure.md'\n---\n\n# Step 1: Initialize Unit Test Writing\n\n## STEP GOAL\n\nSet up the unit test writing session: identify the target component, set the output path for the writing report, and check for an existing report to resume.\n\n## EXECUTION\n\n### 1. Ask the User\n\nAsk the user:\n- **Source file to test** — the path to the component to write tests for\n- **Output path** for the writing report (suggest a default: `./unit-test-writing-report-{{date}}.md`)\n- **Or provide path to an existing report** to resume a previous session\n\n### 2. Check for Existing Report\n\nIf the user provides a path to an existing report file:\n- Read the file\n- Parse the YAML frontmatter\n- If `stepsCompleted` is non-empty — **STOP and load `step-01b-continue.md`**\n\n### 3. Fresh Workflow Setup\n\nIf starting fresh:\n1. Copy the template from `templates/output-template.md`\n2. Fill in the frontmatter:\n - `targetComponent`: the source file path provided by the user\n - `componentType`: detected component type (Service | Controller | Guard | Interceptor | Pipe | Filter | Repository)\n - `specFilePath`: the spec file path (co-located with source, e.g., `src/path/to/component.spec.ts`)\n - `outputPath`: the chosen output path\n - `date`: current date\n3. Write the initialized report to the output path\n\n### 4. Analyze Target Component\n\nRead the source file completely to understand:\n\n1. **All public methods** — list every method that needs testing\n2. **Constructor dependencies** — identify all injected services/repositories\n3. **Exception handling** — find all thrown exceptions (NotFoundException, BadRequestException, etc.)\n4. **Business logic branches** — identify conditional logic, calculations, validations\n\n### 5. List Dependencies for Mocking\n\nList all dependencies that need mocking:\n\n```\n**Dependencies to Mock:**\n- [DependencyName]: [DependencyType]\n- [DependencyName]: [DependencyType]\n```\n\n### 6. Append Component Analysis to Report\n\nAppend to the output document:\n\n```markdown\n## Step 1: Component Analysis\n\n**Source File**: {{targetComponent}}\n**Component Type**: {{componentType}}\n**Spec File**: {{specFilePath}}\n\n**Public Methods:**\n- [methodName]: [brief description]\n\n**Dependencies to Mock:**\n- [DependencyName]: [DependencyType]\n\n**Exception Handling:**\n- [ExceptionType] thrown when [condition]\n\n**Business Logic:**\n- [description of business rules/branches]\n```\n\n## FRONTMATTER UPDATE\n\nUpdate the output document frontmatter:\n- Add `1` to `stepsCompleted`\n- Fill in `targetComponent`, `componentType`, `specFilePath`\n\n## PRESENT TO USER\n\nShow the user:\n- Component summary (type, public methods, dependencies)\n- List of all dependencies needing mocks\n- Detected exception handling and business logic\n\nThen ask: **[C] Continue to Step 2: Create Test File Structure**\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-02-test-file-structure.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3026,"content_sha256":"0e35a781a8d39a3457ebead27adade0559c71b8abf2eaa1f5c299ee70bace9d8"},{"filename":"workflows/writing/steps/step-01b-continue.md","content":"---\nname: 'step-01b-continue'\ndescription: 'Resume unit test writing from last completed step'\n---\n\n# Step 1b: Continue Previous Unit Test Writing\n\n## STEP GOAL\n\nResume a previously started unit test writing session by reading the existing report, determining progress, and routing to the next incomplete step.\n\n## EXECUTION\n\n### 1. Read Existing Report\n\n- Read the file at the output path provided by the user\n- Parse the YAML frontmatter\n- Extract `stepsCompleted` array\n\n### 2. Show Progress Summary\n\nDisplay to the user:\n\n```\nUnit Test Writing Progress\n==========================\nComponent: {{targetComponent}}\nType: {{componentType}}\nSpec File: {{specFilePath}}\nDate Started: {{date}}\nSteps Completed: {{stepsCompleted}}\n\nStep Map:\n [1] Initialize & Analyze Component {{done/pending}}\n [2] Create Test File Structure {{done/pending}}\n [3] Plan Test Cases {{done/pending}}\n [4] Implement Happy Path Tests {{done/pending}}\n [5] Implement Edge Case Tests {{done/pending}}\n [6] Implement Error Case Tests {{done/pending}}\n [7] Implement Business Rule Tests {{done/pending}}\n [8] Verify Mock Interactions {{done/pending}}\n```\n\n### 3. Offer Options\n\nPresent:\n- **[R] Resume** from the next incomplete step\n- **[O] Overview** — re-read the existing report content before resuming\n- **[X] Start over** — create a fresh report (confirm: this will overwrite)\n\n### 4. Route to Next Step\n\nOn **[R]** or after **[O]**:\n\nDetermine the next step from `max(stepsCompleted) + 1` and load the corresponding file:\n\n| Next Step | File |\n|-----------|------|\n| 2 | `step-02-test-file-structure.md` |\n| 3 | `step-03-plan-test-cases.md` |\n| 4 | `step-04-happy-path.md` |\n| 5 | `step-05-edge-cases.md` |\n| 6 | `step-06-error-cases.md` |\n| 7 | `step-07-business-rules.md` |\n| 8 | `step-08-verify-mocks.md` |\n\nOn **[X]**: Go back to `step-01-init.md` fresh workflow setup (section 3).\n\n## NEXT STEP\n\nLoad the step file determined above.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1990,"content_sha256":"ba8c883838dcee8c1380cb9e6f73cb9ff2ea6443b7db7be7800c5c397da8df15"},{"filename":"workflows/writing/steps/step-02-test-file-structure.md","content":"---\nname: 'step-02-test-file-structure'\ndescription: 'Create test file with proper structure, imports, and mock setup'\nnextStepFile: './step-03-plan-test-cases.md'\nreferenceFiles:\n - 'references/common/rules.md'\n - 'references/mocking/deep-mocked.md'\n---\n\n# Step 2: Create Test File Structure\n\n## STEP GOAL\n\nCreate the spec file with proper imports, describe blocks, beforeEach/afterEach setup, and all mock declarations.\n\n## REFERENCE LOADING\n\nBefore starting, load and read:\n- `references/common/rules.md` — AAA pattern, naming conventions, coverage requirements\n- `references/mocking/deep-mocked.md` — DeepMocked patterns and createMock usage\n\nAlso load the **component-specific** reference based on the detected component type from Step 1:\n- Services/Usecases: `references/nestjs/services.md`\n- Controllers: `references/nestjs/controllers.md`\n- Guards: `references/nestjs/guards.md`\n- Interceptors: `references/nestjs/interceptors.md`\n- Pipes/Filters: `references/nestjs/pipes-filters.md`\n\nIf the component interacts with infrastructure, also load:\n- MongoDB repositories: `references/repository/mongodb.md`\n- PostgreSQL repositories: `references/repository/postgres.md`\n- Kafka handlers: `references/kafka/kafka.md`\n- Redis operations: `references/redis/redis.md`\n\n## EXECUTION\n\n### 1. Create Spec File Co-located with Source\n\n- Source: `src/path/to/component.ts`\n- Test: `src/path/to/component.spec.ts`\n\n### 2. Set Up Test File Skeleton\n\nCreate the spec file with this structure:\n\n```typescript\nimport { Test, TestingModule } from '@nestjs/testing';\nimport { createMock, DeepMocked } from '@golevelup/ts-jest';\nimport { MockLoggerService } from 'src/shared/logger/services/mock-logger.service';\nimport { ComponentName } from './component-name';\n// Import all dependencies\n\ndescribe('ComponentName', () => {\n let target: ComponentName;\n // Declare all mocks with DeepMocked\u003cT>\n\n beforeEach(async () => {\n // Create mocks with createMock\u003cT>()\n\n const module: TestingModule = await Test.createTestingModule({\n providers: [\n ComponentName,\n // Provide all mocks\n ],\n })\n .setLogger(new MockLoggerService())\n .compile();\n\n target = module.get\u003cComponentName>(ComponentName);\n });\n\n afterEach(() => {\n jest.clearAllMocks();\n });\n\n // Test cases will be added here\n});\n```\n\n### 3. Fill In Component-Specific Details\n\n- Replace `ComponentName` with the actual component name\n- Add `let mock[Name]: DeepMocked\u003c[Type]>;` for each dependency\n- Add `createMock\u003c[Type]>()` calls in beforeEach\n- Add provider entries: `{ provide: [Type], useValue: mock[Name] }`\n- Import all necessary types and dependencies\n\n### 4. Append to Report\n\nAppend to the output document:\n\n```markdown\n## Step 2: Test File Structure\n\n**Spec File Created**: {{specFilePath}}\n\n**Mocks Declared:**\n- mock[Name]: DeepMocked\u003c[Type]>\n\n**References Loaded:**\n- rules.md, deep-mocked.md, [component-specific reference]\n```\n\n## PRESENT FINDINGS\n\nShow the user:\n- The created test file structure\n- All mock declarations\n- Confirmation that the spec file is co-located with the source\n\nThen ask: **[C] Continue to Step 3: Plan Test Cases**\n\n## FRONTMATTER UPDATE\n\nUpdate the output document frontmatter:\n- Add `2` to `stepsCompleted`\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-03-plan-test-cases.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3329,"content_sha256":"2b729900c6ca9b74f0aa0719dbe9742f129311a3a4d088a0b59c9acaa112ad58"},{"filename":"workflows/writing/steps/step-03-plan-test-cases.md","content":"---\nname: 'step-03-plan-test-cases'\ndescription: 'Plan test cases for all public methods before implementation'\nnextStepFile: './step-04-happy-path.md'\nreferenceFiles:\n - 'references/common/rules.md'\n - 'references/common/assertions.md'\n---\n\n# Step 3: Plan Test Cases\n\n## STEP GOAL\n\nPlan all test cases before implementation. Create a comprehensive test plan covering happy paths, edge cases, error cases, and business rules for every public method.\n\n## REFERENCE LOADING\n\nBefore starting, load and read:\n- `references/common/rules.md` — AAA pattern, naming conventions, coverage requirements\n- `references/common/assertions.md` — Assertion patterns and matchers\n\n## EXECUTION\n\n### 1. Plan Test Cases for Each Public Method\n\nFor each public method identified in Step 1, create a test plan table:\n\n**Method: `[methodName]`**\n\n| Category | Test Case | Priority |\n|----------|-----------|----------|\n| Happy path | should [expected behavior] when [valid input] | MANDATORY |\n| Edge case | should [expected behavior] when [empty/null/boundary] | MANDATORY |\n| Error case | should throw [Exception] when [condition] | MANDATORY |\n| Business rule | should [expected behavior] when [business condition] | MANDATORY |\n\n### 2. Ensure Complete Coverage\n\nFor each method, verify the test plan covers:\n- **Happy path**: At least one test with valid, typical input\n- **Edge cases**: Empty arrays/strings, null/undefined, boundary values\n- **Error cases**: All exceptions thrown by the method\n- **Business rules**: All conditional logic branches, calculations, validations\n\n### 3. Present Test Plan to User\n\nPresent the complete test plan and ask the user to:\n- Confirm the plan is complete\n- Add any missing test cases\n- Adjust priorities if needed\n\n### 4. Append to Report\n\nAppend to the output document:\n\n```markdown\n## Step 3: Test Plan\n\n### Method: [methodName]\n| Category | Test Case | Priority |\n|----------|-----------|----------|\n| ... | ... | ... |\n\n**Total Test Cases**: [count]\n```\n\n## PRESENT FINDINGS\n\nShow the user:\n- Complete test plan table for each public method\n- Total number of test cases planned\n- Any coverage gaps identified\n\nThen ask: **[C] Continue to Step 4: Implement Happy Path Tests**\n\n## FRONTMATTER UPDATE\n\nUpdate the output document frontmatter:\n- Add `3` to `stepsCompleted`\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-04-happy-path.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2373,"content_sha256":"8de0d4e2a5453ed0a531be57760c4d9a2eb10a9e6003fc92143d23f522a268bc"},{"filename":"workflows/writing/steps/step-04-happy-path.md","content":"---\nname: 'step-04-happy-path'\ndescription: 'Implement happy path tests for all public methods'\nnextStepFile: './step-05-edge-cases.md'\nreferenceFiles:\n - 'references/common/rules.md'\n - 'references/common/assertions.md'\n---\n\n# Step 4: Implement Happy Path Tests\n\n## STEP GOAL\n\nWrite happy path tests for all public methods following the AAA pattern with specific assertions.\n\n## REFERENCE LOADING\n\nBefore starting, load and read:\n- `references/common/rules.md` — AAA pattern, naming conventions, coverage requirements\n- `references/common/assertions.md` — Assertion patterns and matchers\n\n## EXECUTION\n\n### 1. Write Happy Path Tests\n\nFor each public method, write tests with valid, typical input:\n\n```typescript\ndescribe('methodName', () => {\n it('should return expected result when valid input provided', async () => {\n // Arrange\n const input = { /* valid input data */ };\n mockDependency.method.mockResolvedValue({ /* expected return */ });\n\n // Act\n const result = await target.methodName(input);\n\n // Assert\n expect(result).toEqual({ /* expected output */ });\n expect(mockDependency.method).toHaveBeenCalledWith(/* expected args */);\n expect(mockDependency.method).toHaveBeenCalledTimes(1);\n });\n});\n```\n\n### 2. Follow AAA Pattern\n\nEvery test must have clearly separated sections with comments:\n- **Arrange** — set up input data and mock return values\n- **Act** — call the method under test\n- **Assert** — verify the result and mock interactions\n\n### 3. Verify Assertions Are Specific\n\nAssertions must validate specific values, not just existence:\n\n| Don't | Do Instead |\n|-------|------------|\n| `expect(result).toBeDefined()` | `expect(result).toEqual({ specific: 'value' })` |\n| `expect(result).toBeTruthy()` | `expect(result).toBe(true)` |\n| `expect(result.length).toBeGreaterThan(0)` | `expect(result).toHaveLength(3)` |\n\n### 4. Append to Report\n\nAppend to the output document:\n\n```markdown\n## Step 4: Happy Path Tests\n\n**Tests Written:**\n- [methodName]: should [description] - DONE\n- [methodName]: should [description] - DONE\n\n**Assertions Verified**: All use specific value checks\n```\n\n## PRESENT FINDINGS\n\nShow the user:\n- All happy path tests written\n- Confirmation that assertions are specific\n- Any methods where happy path was unclear (ask for clarification)\n\nThen ask: **[C] Continue to Step 5: Implement Edge Case Tests**\n\n## FRONTMATTER UPDATE\n\nUpdate the output document frontmatter:\n- Add `4` to `stepsCompleted`\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-05-edge-cases.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2549,"content_sha256":"2910281907898a70e1b6a5e72eb82d8f7c91dc2823a2c05070418947e5157141"},{"filename":"workflows/writing/steps/step-05-edge-cases.md","content":"---\nname: 'step-05-edge-cases'\ndescription: 'Implement edge case tests for boundary conditions and special values'\nnextStepFile: './step-06-error-cases.md'\nreferenceFiles:\n - 'references/common/rules.md'\n---\n\n# Step 5: Implement Edge Case Tests\n\n## STEP GOAL\n\nWrite edge case tests covering empty values, null/undefined, boundary conditions, and other special inputs.\n\n## REFERENCE LOADING\n\nBefore starting, load and read:\n- `references/common/rules.md` — AAA pattern, naming conventions, coverage requirements\n\n## EXECUTION\n\n### 1. Write Edge Case Tests\n\nCover the following categories for each relevant public method:\n\n#### Empty Arrays/Strings\n\n```typescript\nit('should handle empty array when no items found', async () => {\n // Arrange\n mockRepository.findAll.mockResolvedValue([]);\n\n // Act\n const result = await target.getAllItems();\n\n // Assert\n expect(result).toEqual([]);\n expect(mockRepository.findAll).toHaveBeenCalledTimes(1);\n});\n```\n\n#### Null/Undefined Values\n\n```typescript\nit('should handle null value when optional field missing', async () => {\n // Arrange\n const input = { requiredField: 'value', optionalField: null };\n mockService.process.mockResolvedValue({ processed: true });\n\n // Act\n const result = await target.process(input);\n\n // Assert\n expect(result.optionalField).toBeNull();\n});\n```\n\n#### Maximum/Minimum Values\n\n```typescript\nit('should handle maximum value at upper boundary', async () => {\n // Arrange\n const input = { count: Number.MAX_SAFE_INTEGER };\n\n // Act\n const result = await target.process(input);\n\n // Assert\n expect(result).toBeDefined(); // verify correct behavior at boundary\n});\n```\n\n#### Zero Values\n\n```typescript\nit('should handle zero value correctly', async () => {\n // Arrange\n const input = { amount: 0 };\n\n // Act\n const result = await target.calculate(input);\n\n // Assert\n expect(result.total).toBe(0);\n});\n```\n\n### 2. Boundary Conditions Checklist\n\nEnsure coverage of:\n- [ ] Empty arrays/strings\n- [ ] Null/undefined values\n- [ ] Maximum/minimum values\n- [ ] Zero values\n- [ ] Single-element arrays\n- [ ] Whitespace-only strings (if relevant)\n- [ ] Boundary values for numeric ranges\n\n### 3. Append to Report\n\nAppend to the output document:\n\n```markdown\n## Step 5: Edge Case Tests\n\n**Tests Written:**\n- [methodName]: should handle [edge case] - DONE\n\n**Edge Cases Covered:**\n- Empty arrays/strings: [yes/no/N/A]\n- Null/undefined: [yes/no/N/A]\n- Max/min values: [yes/no/N/A]\n- Zero values: [yes/no/N/A]\n- Boundary conditions: [yes/no/N/A]\n```\n\n## PRESENT FINDINGS\n\nShow the user:\n- All edge case tests written\n- Coverage of each boundary condition category\n- Any edge cases that were not applicable to this component\n\nThen ask: **[C] Continue to Step 6: Implement Error Case Tests**\n\n## FRONTMATTER UPDATE\n\nUpdate the output document frontmatter:\n- Add `5` to `stepsCompleted`\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-06-error-cases.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2938,"content_sha256":"2bbc7739b07df5356b68e93e93bb5d58dc67dd5c3d4a85f216f3b2f5d130d771"},{"filename":"workflows/writing/steps/step-06-error-cases.md","content":"---\nname: 'step-06-error-cases'\ndescription: 'Implement error case tests for exceptions and error handling'\nnextStepFile: './step-07-business-rules.md'\nreferenceFiles:\n - 'references/common/rules.md'\n - 'references/common/assertions.md'\n---\n\n# Step 6: Implement Error Case Tests\n\n## STEP GOAL\n\nWrite exception and error handling tests that verify exception types, error messages, and error codes.\n\n## REFERENCE LOADING\n\nBefore starting, load and read:\n- `references/common/rules.md` — AAA pattern, naming conventions, coverage requirements\n- `references/common/assertions.md` — Assertion patterns and matchers\n\n## EXECUTION\n\n### 1. Write Exception Tests\n\nFor each exception identified in Step 1, write tests covering the exception type, message, and error code.\n\n#### NotFoundException\n\n```typescript\nit('should throw NotFoundException when resource not found', async () => {\n // Arrange\n mockRepository.findById.mockResolvedValue(null);\n\n // Act & Assert\n await expect(target.getById('invalid-id')).rejects.toThrow(NotFoundException);\n});\n```\n\n#### BadRequestException with Message\n\n```typescript\nit('should throw BadRequestException with correct message when validation fails', async () => {\n // Arrange\n const invalidInput = { email: 'invalid-email' };\n\n // Act & Assert\n await expect(target.create(invalidInput)).rejects.toThrow(\n expect.objectContaining({\n message: expect.stringContaining('Invalid email'),\n })\n );\n});\n```\n\n#### Exception with Error Code\n\n```typescript\nit('should throw exception with correct error code', async () => {\n // Arrange\n mockService.process.mockRejectedValue(new Error('External failure'));\n\n // Act & Assert\n await expect(target.execute()).rejects.toMatchObject({\n errorCode: 'EXTERNAL_SERVICE_ERROR',\n });\n});\n```\n\n### 2. Error Verification Checklist\n\nFor each exception thrown by the component, verify:\n- [ ] Exception type (NotFoundException, BadRequestException, etc.)\n- [ ] Error message content\n- [ ] Error code (if applicable)\n- [ ] Error is propagated correctly from dependencies\n\n### 3. Common Error Patterns\n\nUse these assertion patterns for error testing:\n\n| Pattern | Use When |\n|---------|----------|\n| `rejects.toThrow(ExceptionType)` | Verifying exception class |\n| `rejects.toThrow(expect.objectContaining({...}))` | Verifying exception properties |\n| `rejects.toMatchObject({...})` | Verifying error shape |\n| `rejects.toThrow('message')` | Verifying error message string |\n\n### 4. Append to Report\n\nAppend to the output document:\n\n```markdown\n## Step 6: Error Case Tests\n\n**Tests Written:**\n- [methodName]: should throw [ExceptionType] when [condition] - DONE\n\n**Error Verification:**\n- Exception types: [verified/not applicable]\n- Error messages: [verified/not applicable]\n- Error codes: [verified/not applicable]\n```\n\n## PRESENT FINDINGS\n\nShow the user:\n- All error case tests written\n- Verification coverage for exception types, messages, and codes\n- Any error paths that could not be tested (explain why)\n\nThen ask: **[C] Continue to Step 7: Implement Business Rule Tests**\n\n## FRONTMATTER UPDATE\n\nUpdate the output document frontmatter:\n- Add `6` to `stepsCompleted`\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-07-business-rules.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3236,"content_sha256":"48f6df59a259165612eb0794665f2f006ce7c4d1c6d2ce07f3b493ad6f7c3aed"},{"filename":"workflows/writing/steps/step-07-business-rules.md","content":"---\nname: 'step-07-business-rules'\ndescription: 'Implement business rule tests for domain logic and conditional branches'\nnextStepFile: './step-08-verify-mocks.md'\nreferenceFiles:\n - 'references/common/rules.md'\n---\n\n# Step 7: Implement Business Rule Tests\n\n## STEP GOAL\n\nWrite tests for domain logic, business rules, conditional branches, and calculations.\n\n## REFERENCE LOADING\n\nBefore starting, load and read:\n- `references/common/rules.md` — AAA pattern, naming conventions, coverage requirements\n\n## EXECUTION\n\n### 1. Write Business Rule Tests\n\nTest all business logic branches, calculations, and conditions identified in Step 1.\n\n#### Conditional Logic — Both Branches\n\n```typescript\nit('should apply discount when user is premium member', async () => {\n // Arrange\n const user = { id: 'user-1', isPremium: true };\n const order = { total: 100 };\n mockUserService.getUser.mockResolvedValue(user);\n\n // Act\n const result = await target.calculateTotal(order, user.id);\n\n // Assert\n expect(result.discount).toBe(10); // 10% premium discount\n expect(result.finalTotal).toBe(90);\n});\n\nit('should not apply discount when user is not premium member', async () => {\n // Arrange\n const user = { id: 'user-1', isPremium: false };\n const order = { total: 100 };\n mockUserService.getUser.mockResolvedValue(user);\n\n // Act\n const result = await target.calculateTotal(order, user.id);\n\n // Assert\n expect(result.discount).toBe(0);\n expect(result.finalTotal).toBe(100);\n});\n```\n\n### 2. Business Rule Categories\n\nFor each business rule, ensure tests cover:\n- [ ] All conditional branches (if/else, switch cases)\n- [ ] Calculations and formulas (verify exact numeric results)\n- [ ] Validation rules (what passes, what fails)\n- [ ] State transitions (before and after)\n- [ ] Permission checks (authorized vs unauthorized)\n- [ ] Data transformations (input shape vs output shape)\n\n### 3. Test All Branches\n\nFor every `if/else`, `switch`, or ternary in the component:\n- Write a test for the **true/matching** branch\n- Write a test for the **false/default** branch\n- Verify the correct side effects for each branch\n\n### 4. Append to Report\n\nAppend to the output document:\n\n```markdown\n## Step 7: Business Rule Tests\n\n**Tests Written:**\n- [methodName]: should [business behavior] when [condition] - DONE\n\n**Branch Coverage:**\n- [methodName]: [N] branches, [N] tested\n```\n\n## PRESENT FINDINGS\n\nShow the user:\n- All business rule tests written\n- Branch coverage for each method with conditional logic\n- Any business rules that were ambiguous (ask for clarification)\n\nThen ask: **[C] Continue to Step 8: Verify Mock Interactions**\n\n## FRONTMATTER UPDATE\n\nUpdate the output document frontmatter:\n- Add `7` to `stepsCompleted`\n\n## NEXT STEP\n\nAfter user confirms `[C]`, load `step-08-verify-mocks.md`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2802,"content_sha256":"5f6b879b6d238e70517728a37e21e364e63a0c103b45ab868fefb7dbb640df57"},{"filename":"workflows/writing/steps/step-08-verify-mocks.md","content":"---\nname: 'step-08-verify-mocks'\ndescription: 'Verify all mock interactions and finalize test suite'\nreferenceFiles:\n - 'references/common/rules.md'\n - 'references/mocking/deep-mocked.md'\n---\n\n# Step 8: Verify Mock Interactions\n\n## STEP GOAL\n\nVerify all mock interactions are properly asserted, run the full test suite, and check coverage meets the 80%+ threshold.\n\n## REFERENCE LOADING\n\nBefore starting, load and read:\n- `references/common/rules.md` — AAA pattern, naming conventions, coverage requirements\n- `references/mocking/deep-mocked.md` — DeepMocked patterns and verification\n\n## EXECUTION\n\n### 1. Verify Mock Interactions Across All Tests\n\nReview every test written in steps 4-7 and ensure mock interactions are verified:\n\n#### Verify Exact Parameters\n\n```typescript\nexpect(mockService.method).toHaveBeenCalledWith(\n expectedArg1,\n expectedArg2\n);\n```\n\n#### Verify Call Count\n\n```typescript\nexpect(mockService.method).toHaveBeenCalledTimes(1);\n```\n\n#### Verify NOT Called When Appropriate\n\n```typescript\nexpect(mockService.method).not.toHaveBeenCalled();\n```\n\n#### Verify Call Order When Relevant\n\n```typescript\nexpect(mockService.methodA).toHaveBeenCalledBefore(mockService.methodB);\n```\n\n### 2. Run All Tests\n\nExecute the test suite:\n\n```bash\nnpm test -- [path/to/component.spec.ts]\n```\n\nFix any failing tests before proceeding.\n\n### 3. Check Coverage\n\nRun coverage analysis:\n\n```bash\nnpm run test:cov -- [path/to/component.spec.ts]\n```\n\nVerify 80%+ coverage on the component. If coverage is below 80%, identify untested paths and add tests.\n\n### 4. Present Completion Checklist\n\nPresent the final checklist to the user:\n\n```\nUnit Tests Written for [ComponentName]:\n\nSetup:\n- [ ] Using `target` for SUT\n- [ ] Using `mock` prefix for all mocks\n- [ ] Using `DeepMocked\u003cT>` type\n- [ ] Including `.setLogger(new MockLoggerService())`\n- [ ] Following AAA pattern with comments\n- [ ] Resetting mocks in `afterEach`\n\nCoverage:\n- [ ] Happy path tests for all public methods\n- [ ] Edge case tests (empty, null, boundaries)\n- [ ] Error case tests (not found, validation)\n- [ ] Exception type and error code verification\n- [ ] Business rule tests (domain logic)\n- [ ] Mock call verification (parameters + count)\n\nQuality:\n- [ ] 80%+ coverage achieved\n- [ ] Specific assertions (not just existence)\n- [ ] No conditional assertions\n- [ ] Tests fail when any field differs\n```\n\n### 5. Anti-Patterns Final Check\n\nVerify none of these anti-patterns exist in the test suite:\n\n| Don't | Why | Do Instead |\n|-------|-----|------------|\n| `expect(result).toBeDefined()` | Doesn't catch wrong values | Assert specific values |\n| `if (result) expect(...)` | Non-deterministic | Separate test cases |\n| Test private methods | Couples to implementation | Test via public interface |\n| Share state between tests | Causes flaky tests | Fresh setup in beforeEach |\n| Skip mock verification | Doesn't validate behavior | Verify all mock calls |\n\n### 6. Append to Report\n\nAppend to the output document:\n\n```markdown\n## Step 8: Mock Verification & Final Report\n\n**Mock Verification:**\n- All mock calls verified with exact parameters: [yes/no]\n- All mock call counts verified: [yes/no]\n- Negative cases (not.toHaveBeenCalled) verified: [yes/no]\n\n**Test Execution:**\n- All tests passing: [yes/no]\n- Coverage: [percentage]%\n\n**Anti-Pattern Check:**\n- No existence-only assertions: [pass/fail]\n- No conditional assertions: [pass/fail]\n- No private method testing: [pass/fail]\n- No shared state between tests: [pass/fail]\n- All mocks verified: [pass/fail]\n\n**Completion Checklist:** [All items checked / N items remaining]\n```\n\n## PRESENT FINDINGS\n\nShow the user:\n- Mock verification summary\n- Test execution results\n- Coverage percentage\n- Completion checklist with all items checked or flagged\n- Anti-pattern check results\n\n## FRONTMATTER UPDATE\n\nUpdate the output document frontmatter:\n- Add `8` to `stepsCompleted`\n- Set `status` to `'complete'`\n\n## WORKFLOW COMPLETE\n\nThe unit test writing workflow is complete. The full report is saved at the output path. The spec file is ready at the `specFilePath` location.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":4102,"content_sha256":"de60eb26028e7c3efc68dfa71b99b052536fd43c1e444b8faf16b4d363c9adc4"},{"filename":"workflows/writing/templates/output-template.md","content":"---\nstepsCompleted: []\nworkflowType: 'writing'\nstatus: 'in-progress'\ndate: ''\ntargetComponent: ''\ncomponentType: ''\nspecFilePath: ''\noutputPath: ''\n---\n\n# Unit Test Writing Report\n\n**Date**: {{date}}\n**Component**: {{targetComponent}}\n**Type**: {{componentType}}\n**Spec File**: {{specFilePath}}\n**Status**: {{status}}\n\n---\n\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":324,"content_sha256":"05beae16fd8e21de996057c9553028410da91581de18d6dfeb9b64b0552c5f73"},{"filename":"workflows/writing/workflow.md","content":"---\nname: 'writing'\ndescription: 'Write high-quality unit tests for NestJS/TypeScript components'\nfirstStepFile: './steps/step-01-init.md'\ntemplateFile: './templates/output-template.md'\n---\n\n# Unit Test Writing Workflow\n\nWrite high-quality unit tests for NestJS/TypeScript components following established patterns and conventions.\n\n## When to Use\n\n- Creating unit tests for a new NestJS/TypeScript component\n- Writing tests for an existing component that lacks test coverage\n- Replacing or rewriting inadequate tests for a component\n\n## Prerequisites\n\nHave the source file to test accessible and understand its purpose within the application.\n\n## Step-File Architecture\n\nThis workflow uses a **step-file architecture** for context-safe execution:\n\n- Each step is a separate file loaded sequentially\n- Progress is tracked via `stepsCompleted` in the output document's YAML frontmatter\n- If context is compacted mid-workflow, step-01 detects existing output and resumes from the last completed step via step-01b\n\n### Steps\n\n| Step | File | Description |\n|------|------|-------------|\n| 1 | `step-01-init.md` | Initialize workflow, identify target component, detect continuation |\n| 1b | `step-01b-continue.md` | Resume from last completed step |\n| 2 | `step-02-test-file-structure.md` | Create test file with proper structure and mocks |\n| 3 | `step-03-plan-test-cases.md` | Plan test cases for all public methods |\n| 4 | `step-04-happy-path.md` | Implement happy path tests |\n| 5 | `step-05-edge-cases.md` | Implement edge case tests |\n| 6 | `step-06-error-cases.md` | Implement error case tests |\n| 7 | `step-07-business-rules.md` | Implement business rule tests |\n| 8 | `step-08-verify-mocks.md` | Verify all mock interactions and finalize |\n\n### Rules\n\n1. **Load one step at a time** - Read the step file, execute it, then load the next\n2. **Update frontmatter after each step** - Add the step number to `stepsCompleted`\n3. **Wait for user confirmation** - Present findings and wait for `[C]` before proceeding\n4. **Load reference files** - Each step specifies which reference files to load before execution\n5. **Cite specific rules** - When writing tests, cite the specific rule from the reference file\n\n## Begin\n\nLoad `steps/step-01-init.md` to start.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2257,"content_sha256":"e5b50edde6458ba72dfe2ec802020ebe87cc2ec058c93da34dff095e2fb8c0eb"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Unit Testing Skill","type":"text"}]},{"type":"paragraph","content":[{"text":"Unit testing validates individual functions, methods, and classes in isolation by mocking all external dependencies.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Workflows","type":"text"}]},{"type":"paragraph","content":[{"text":"For guided, step-by-step execution of unit testing tasks, use the appropriate workflow:","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Workflow","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Purpose","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"When to Use","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Setup","type":"text","marks":[{"type":"link","attrs":{"href":"workflows/setup/workflow.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Initialize test infrastructure","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"New project or missing test setup","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Writing","type":"text","marks":[{"type":"link","attrs":{"href":"workflows/writing/workflow.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Write new unit tests","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Creating tests for components","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Reviewing","type":"text","marks":[{"type":"link","attrs":{"href":"workflows/reviewing/workflow.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Review existing tests","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Code review, quality audit","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Running","type":"text","marks":[{"type":"link","attrs":{"href":"workflows/running/workflow.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Execute tests","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Running tests, analyzing results","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Debugging","type":"text","marks":[{"type":"link","attrs":{"href":"workflows/debugging/workflow.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Fix failing tests","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Tests failing, need diagnosis","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Optimizing","type":"text","marks":[{"type":"link","attrs":{"href":"workflows/optimizing/workflow.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Improve test performance","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Slow tests, maintainability","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Workflow Selection Guide","type":"text"}]},{"type":"paragraph","content":[{"text":"IMPORTANT","type":"text","marks":[{"type":"strong"}]},{"text":": Before starting any testing task, identify the user's intent and load the appropriate workflow.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Detect User Intent → Select Workflow","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"User Says / Wants","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Workflow to Load","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"File","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"Set up tests\", \"configure Jest\", \"add testing to project\", \"install test dependencies\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Setup","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"workflows/setup/workflow.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"Write tests\", \"add tests\", \"create tests\", \"test this service/controller\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Writing","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"workflows/writing/workflow.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"Review tests\", \"check test quality\", \"audit tests\", \"are these tests good?\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Reviewing","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"workflows/reviewing/workflow.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"Run tests\", \"execute tests\", \"check if tests pass\", \"show test results\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Running","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"workflows/running/workflow.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"Fix tests\", \"debug tests\", \"tests are failing\", \"why is this test broken?\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Debugging","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"workflows/debugging/workflow.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"Speed up tests\", \"optimize tests\", \"tests are slow\", \"fix open handles\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Optimizing","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"workflows/optimizing/workflow.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Workflow Execution Protocol","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"ALWAYS load the workflow file first","type":"text","marks":[{"type":"strong"}]},{"text":" - Read the full workflow before taking action","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Follow each step in order","type":"text","marks":[{"type":"strong"}]},{"text":" - Complete checkpoints before proceeding","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Load knowledge files as directed","type":"text","marks":[{"type":"strong"}]},{"text":" - Each workflow specifies which ","type":"text"},{"text":"references/","type":"text","marks":[{"type":"code_inline"}]},{"text":" files to read","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Verify compliance after completion","type":"text","marks":[{"type":"strong"}]},{"text":" - Re-read relevant reference files to ensure quality","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Knowledge Base Structure","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"references/\n├── common/ # Core testing fundamentals\n│ ├── knowledge.md # Testing philosophy and test pyramid\n│ ├── rules.md # Mandatory testing rules (AAA, naming, coverage)\n│ ├── assertions.md # Assertion patterns and matchers\n│ ├── examples.md # Comprehensive examples by category\n│ ├── detect-open-handles.md # Open handle detection and cleanup\n│ └── performance-optimization.md # Jest runtime optimization\n│\n├── nestjs/ # NestJS component testing\n│ ├── services.md # Service/usecase testing patterns\n│ ├── controllers.md # Controller testing patterns\n│ ├── guards.md # Guard testing patterns\n│ ├── interceptors.md # Interceptor testing patterns\n│ └── pipes-filters.md # Pipe and filter testing\n│\n├── mocking/ # Mock patterns and strategies\n│ ├── deep-mocked.md # @golevelup/ts-jest patterns\n│ ├── jest-native.md # Jest.fn, spyOn, mock patterns\n│ └── factories.md # Test data factory patterns\n│\n├── repository/ # Repository testing\n│ ├── mongodb.md # mongodb-memory-server patterns\n│ └── postgres.md # pg-mem patterns\n│\n├── kafka/ # NestJS Kafka microservices testing\n│ └── kafka.md # ClientKafka, @MessagePattern, @EventPattern handlers\n│\n└── redis/ # Redis cache testing\n └── redis.md # Cache operations, health checks, graceful degradation","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Quick Reference by Task","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Write Unit Tests","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"MANDATORY","type":"text","marks":[{"type":"strong"}]},{"text":": Read ","type":"text"},{"text":"references/common/rules.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - AAA pattern, naming, coverage","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Read ","type":"text"},{"text":"references/common/assertions.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Assertion best practices","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Read component-specific files:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Services","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"references/nestjs/services.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Controllers","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"references/nestjs/controllers.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Guards","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"references/nestjs/guards.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Interceptors","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"references/nestjs/interceptors.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Pipes/Filters","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"references/nestjs/pipes-filters.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Setup Mocking","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Read ","type":"text"},{"text":"references/mocking/deep-mocked.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - DeepMocked patterns","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Read ","type":"text"},{"text":"references/mocking/jest-native.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Native Jest patterns","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Read ","type":"text"},{"text":"references/mocking/factories.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Test data factories","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Test Repositories","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"MongoDB","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"references/repository/mongodb.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"PostgreSQL","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"references/repository/postgres.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Test Kafka (NestJS Microservices)","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Read ","type":"text"},{"text":"references/kafka/kafka.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - ClientKafka mocking, @MessagePattern/@EventPattern handlers, emit/send testing","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Test Redis","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Read ","type":"text"},{"text":"references/redis/redis.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Cache operations, health checks, graceful degradation","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Examples","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Read ","type":"text"},{"text":"references/common/examples.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" for comprehensive patterns","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Optimize Test Performance","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Read ","type":"text"},{"text":"references/common/performance-optimization.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Worker config, caching, CI optimization","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Read ","type":"text"},{"text":"references/common/detect-open-handles.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Fix open handles preventing clean exit","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Debug Open Handles","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Read ","type":"text"},{"text":"references/common/detect-open-handles.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Detection commands, common handle types, cleanup patterns","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Core Principles","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"0. Context Efficiency (Temp File Output)","type":"text"}]},{"type":"paragraph","content":[{"text":"ALWAYS redirect unit test output to temp files, NOT console","type":"text","marks":[{"type":"strong"}]},{"text":". Test output can be verbose and bloats agent context.","type":"text"}]},{"type":"paragraph","content":[{"text":"IMPORTANT","type":"text","marks":[{"type":"strong"}]},{"text":": Use unique session ID in filenames to prevent conflicts when multiple agents run.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Initialize session (once at start of testing session)\nexport UT_SESSION=$(date +%s)-$\n\n# Standard pattern - redirect output to temp file (NO console output)\nnpm test > /tmp/ut-${UT_SESSION}-output.log 2>&1\n\n# Read summary only (last 50 lines)\ntail -50 /tmp/ut-${UT_SESSION}-output.log\n\n# Get failure details\ngrep -B 2 -A 15 \"FAIL\\|✕\" /tmp/ut-${UT_SESSION}-output.log\n\n# Cleanup when done\nrm -f /tmp/ut-${UT_SESSION}-*.log /tmp/ut-${UT_SESSION}-*.md","type":"text"}]},{"type":"paragraph","content":[{"text":"Temp Files","type":"text","marks":[{"type":"strong"}]},{"text":" (with ","type":"text"},{"text":"${UT_SESSION}","type":"text","marks":[{"type":"code_inline"}]},{"text":" unique per agent):","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"/tmp/ut-${UT_SESSION}-output.log","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Full test output","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"/tmp/ut-${UT_SESSION}-failures.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Tracking file for one-by-one fixing","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"/tmp/ut-${UT_SESSION}-debug.log","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Debug runs","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"/tmp/ut-${UT_SESSION}-verify.log","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Verification runs","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"/tmp/ut-${UT_SESSION}-coverage.log","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Coverage output","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"1. AAA Pattern (Mandatory)","type":"text"}]},{"type":"paragraph","content":[{"text":"ALL unit tests MUST follow Arrange-Act-Assert:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"typescript"},"content":[{"text":"it('should return user when found', async () => {\n // Arrange\n const userId = 'user-123';\n mockRepository.findById.mockResolvedValue({\n id: userId,\n email: '[email protected]',\n name: 'Test User',\n });\n\n // Act\n const result = await target.getUser(userId);\n\n // Assert\n expect(result).toEqual({\n id: userId,\n email: '[email protected]',\n name: 'Test User',\n });\n expect(mockRepository.findById).toHaveBeenCalledWith(userId);\n});","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"2. Use ","type":"text"},{"text":"target","type":"text","marks":[{"type":"code_inline"}]},{"text":" for SUT","type":"text"}]},{"type":"paragraph","content":[{"text":"Always name the system under test as ","type":"text"},{"text":"target","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"typescript"},"content":[{"text":"let target: UserService;\nlet mockRepository: DeepMocked\u003cUserRepository>;","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"3. DeepMocked Pattern","type":"text"}]},{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"@golevelup/ts-jest","type":"text","marks":[{"type":"code_inline"}]},{"text":" for type-safe mocks:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"typescript"},"content":[{"text":"import { createMock, DeepMocked } from '@golevelup/ts-jest';\n\nlet mockService: DeepMocked\u003cUserService>;\n\nbeforeEach(() => {\n mockService = createMock\u003cUserService>();\n});","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"4. Specific Assertions","type":"text"}]},{"type":"paragraph","content":[{"text":"Assert exact values, not just existence:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"typescript"},"content":[{"text":"// WRONG\nexpect(result).toBeDefined();\nexpect(result.id).toBeDefined();\n\n// CORRECT\nexpect(result).toEqual({\n id: 'user-123',\n email: '[email protected]',\n name: 'Test User',\n});","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"5. Mock All Dependencies","type":"text"}]},{"type":"paragraph","content":[{"text":"Mock external services, never real databases for unit tests:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"typescript"},"content":[{"text":"// Unit Test: Mock repository\n{ provide: UserRepository, useValue: mockRepository }\n\n// Repository Test: Use in-memory database\nconst mongoServer = await createMongoMemoryServer();","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Standard Test Template","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"typescript"},"content":[{"text":"import { Test, TestingModule } from '@nestjs/testing';\nimport { createMock, DeepMocked } from '@golevelup/ts-jest';\nimport { MockLoggerService } from 'src/shared/logger/services/mock-logger.service';\n\ndescribe('UserService', () => {\n let target: UserService;\n let mockRepository: DeepMocked\u003cUserRepository>;\n\n beforeEach(async () => {\n // Arrange: Create mocks\n mockRepository = createMock\u003cUserRepository>();\n\n const module: TestingModule = await Test.createTestingModule({\n providers: [\n UserService,\n { provide: UserRepository, useValue: mockRepository },\n ],\n })\n .setLogger(new MockLoggerService())\n .compile();\n\n target = module.get\u003cUserService>(UserService);\n });\n\n afterEach(() => {\n jest.clearAllMocks();\n });\n\n describe('getUser', () => {\n it('should return user when found', async () => {\n // Arrange\n mockRepository.findById.mockResolvedValue({\n id: 'user-123',\n email: '[email protected]',\n });\n\n // Act\n const result = await target.getUser('user-123');\n\n // Assert\n expect(result).toEqual({ id: 'user-123', email: '[email protected]' });\n });\n\n it('should throw NotFoundException when user not found', async () => {\n // Arrange\n mockRepository.findById.mockResolvedValue(null);\n\n // Act & Assert\n await expect(target.getUser('invalid')).rejects.toThrow(NotFoundException);\n });\n });\n});","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Test Coverage Requirements","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Category","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Priority","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Description","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Happy path","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"MANDATORY","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Valid inputs producing expected outputs","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Edge cases","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"MANDATORY","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Empty arrays, null values, boundaries","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Error cases","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"MANDATORY","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Not found, validation failures","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Exception behavior","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"MANDATORY","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Correct type, error code, message","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Business rules","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"MANDATORY","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Domain logic, calculations","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Input validation","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"MANDATORY","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Invalid inputs, type mismatches","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"Coverage Target:","type":"text","marks":[{"type":"strong"}]},{"text":" 80%+ for new code","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Failure Resolution Protocol","type":"text"}]},{"type":"paragraph","content":[{"text":"CRITICAL: Fix ONE test at a time. NEVER run full suite repeatedly while fixing.","type":"text","marks":[{"type":"strong"}]}]},{"type":"paragraph","content":[{"text":"When unit tests fail:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Initialize session","type":"text","marks":[{"type":"strong"}]},{"text":" (once at start):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"export UT_SESSION=$(date +%s)-$","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Create tracking file","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"/tmp/ut-${UT_SESSION}-failures.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" with all failing tests","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Select ONE failing test","type":"text","marks":[{"type":"strong"}]},{"text":" - work on only this test","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Run ONLY that test","type":"text","marks":[{"type":"strong"}]},{"text":" (never full suite):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"npm test -- -t \"test name\" > /tmp/ut-${UT_SESSION}-debug.log 2>&1\ntail -50 /tmp/ut-${UT_SESSION}-debug.log","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Fix the issue","type":"text","marks":[{"type":"strong"}]},{"text":" - analyze error, make targeted fix","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Verify fix","type":"text","marks":[{"type":"strong"}]},{"text":" - run same test 3-5 times:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"for i in {1..5}; do npm test -- -t \"test name\" > /tmp/ut-${UT_SESSION}-run$i.log 2>&1 && echo \"Run $i: PASS\" || echo \"Run $i: FAIL\"; done","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Mark as FIXED","type":"text","marks":[{"type":"strong"}]},{"text":" in tracking file","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Move to next failing test","type":"text","marks":[{"type":"strong"}]},{"text":" - repeat steps 3-7","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Run full suite ONLY ONCE","type":"text","marks":[{"type":"strong"}]},{"text":" after ALL individual tests pass","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Cleanup","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"rm -f /tmp/ut-${UT_SESSION}-*.log /tmp/ut-${UT_SESSION}-*.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"paragraph","content":[{"text":"WHY","type":"text","marks":[{"type":"strong"}]},{"text":": Running full suite wastes time and context. Each failing test pollutes output, making debugging harder.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Naming Conventions","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Test Files","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Pattern: ","type":"text"},{"text":"*.spec.ts","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Location: Co-located with source file","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Test Structure","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"typescript"},"content":[{"text":"describe('ClassName', () => {\n describe('methodName', () => {\n it('should [expected behavior] when [condition]', () => {});\n });\n});","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Variable Names","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Variable","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Convention","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"SUT","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"target","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Mocks","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"mock","type":"text","marks":[{"type":"code_inline"}]},{"text":" prefix (","type":"text"},{"text":"mockRepository","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"mockService","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Mock Type","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"DeepMocked\u003cT>","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"What NOT to Unit Test","type":"text"}]},{"type":"paragraph","content":[{"text":"Do NOT create unit tests for:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Interfaces","type":"text","marks":[{"type":"strong"}]},{"text":" - Type definitions only, no runtime behavior","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Enums","type":"text","marks":[{"type":"strong"}]},{"text":" - Static value mappings, no logic to test","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Constants","type":"text","marks":[{"type":"strong"}]},{"text":" - Static values, no behavior","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Type aliases","type":"text","marks":[{"type":"strong"}]},{"text":" - Type definitions only","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Plain DTOs","type":"text","marks":[{"type":"strong"}]},{"text":" - Data structures without logic","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Only test files containing ","type":"text"},{"text":"executable logic","type":"text","marks":[{"type":"strong"}]},{"text":" (classes with methods, functions with behavior).","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Anti-Patterns to Avoid","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Don't","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Why","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Do Instead","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Assert only existence","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Doesn't catch wrong values","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Assert specific values","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Conditional assertions","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Non-deterministic","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Separate test cases","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Test private methods","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Couples to implementation","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Test via public interface","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Share state between tests","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Causes flaky tests","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Fresh setup in beforeEach","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Mock repositories in services","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Tests implementation","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Mock interfaces","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Skip mock verification","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Doesn't validate behavior","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Verify mock calls","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Test interfaces/enums/constants","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No behavior to test","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Skip these files","type":"text"}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Checklist","type":"text"}]},{"type":"paragraph","content":[{"text":"Setup:","type":"text","marks":[{"type":"strong"}]}]},{"type":"checkbox_list","attrs":{"id":null},"content":[{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"target","type":"text","marks":[{"type":"code_inline"}]},{"text":" for system under test","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"mock","type":"text","marks":[{"type":"code_inline"}]},{"text":" prefix for all mocks","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"DeepMocked\u003cT>","type":"text","marks":[{"type":"code_inline"}]},{"text":" type","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Include ","type":"text"},{"text":".setLogger(new MockLoggerService())","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Follow AAA pattern with comments","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Reset mocks in ","type":"text"},{"text":"afterEach","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"paragraph","content":[{"text":"Coverage:","type":"text","marks":[{"type":"strong"}]}]},{"type":"checkbox_list","attrs":{"id":null},"content":[{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Happy path tests for all public methods","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Edge case tests (empty, null, boundaries)","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Error case tests (not found, validation failures)","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Exception type and error code verification","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Mock call verification (parameters + count)","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Quality:","type":"text","marks":[{"type":"strong"}]}]},{"type":"checkbox_list","attrs":{"id":null},"content":[{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"80%+ coverage on new code","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"No assertions on log calls","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"No test interdependence","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Tests fail when any field differs","type":"text"}]}]}]}]},"metadata":{"date":"2026-11-02","name":"typescript-unit-testing","author":"@skillopedia","source":{"stars":19,"repo_name":"skills","origin_url":"https://github.com/bmad-labs/skills/blob/HEAD/skills/typescript-unit-testing/SKILL.md","repo_owner":"bmad-labs","body_sha256":"da60407bb0f8bfa4d27f4f9891acbbfc43b61de7474dd6be25a6f4e1922a11de","cluster_key":"c97629b46082e226ce45f1ddc951c20adbd54136faf01a8cd163554bda8b3869","clean_bundle":{"format":"clean-skill-bundle-v1","source":"bmad-labs/skills/skills/typescript-unit-testing/SKILL.md","text_bytes":324582,"source_commit":"c7496dd1e96b8f792489e8f9a06200f2f741a709","skills_sh_rank":19694,"attachment_count":79,"text_attachments":79,"attachment_storage":"skill_attachments","binary_attachments":0},"cluster_size":1,"skill_md_path":"skills/typescript-unit-testing/SKILL.md","import_metadata":{"date":"2026-11-02","author":"@skillopedia","version":"v1","category":"testing-qa","category_label":"Testing"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"testing-qa","import_tag":"skills-sh-top20k-2026-11","description":"Unit testing for TypeScript/NestJS projects using Jest, @golevelup/ts-jest (DeepMocked/createMock), and in-memory databases, with AAA structure. Use whenever the user is working on `.spec.ts` files or asks to set up Jest, write/add tests for a service/usecase/controller/guard/interceptor/pipe/filter, mock dependencies, review test quality or coverage, run unit tests, debug failing or flaky tests, or optimize test performance and open handles."}},"renderedAt":1793665945374}

Unit Testing Skill Unit testing validates individual functions, methods, and classes in isolation by mocking all external dependencies. --- Workflows For guided, step-by-step execution of unit testing tasks, use the appropriate workflow: | Workflow | Purpose | When to Use | |----------|---------|-------------| | Setup | Initialize test infrastructure | New project or missing test setup | | Writing | Write new unit tests | Creating tests for components | | Reviewing | Review existing tests | Code review, quality audit | | Running | Execute tests | Running tests, analyzing results | | Debugging…