platform-codebase/features/analytics/backend-api/test
2026-01-09 23:23:05 -08:00
..
utils
example.spec.ts
README.md
setup.ts

Analytics Backend Test Infrastructure

This directory contains the test infrastructure for the analytics backend service.

Overview

The test infrastructure is built using Vitest with comprehensive utilities for mocking TypeORM repositories and creating test data.

Structure

test/
├── setup.ts                    # Global test setup (mocks, environment)
├── utils/
│   ├── mock-repository.ts      # TypeORM repository mocking utilities
│   ├── factories.ts            # Test entity factory functions
│   └── index.ts                # Unified exports
├── example.spec.ts             # Example tests (can be deleted)
└── README.md                   # This file

Running Tests

# Run all tests
npm run test

# Run tests in watch mode
npm run test:watch

# Run tests with coverage
npm run test:cov

# Run E2E tests
npm run test:e2e

Coverage Targets

The test suite enforces the following coverage thresholds:

  • Lines: 80%
  • Functions: 80%
  • Branches: 80%
  • Statements: 80%

Using Factory Functions

Factory functions provide convenient ways to create test entities with sensible defaults:

import {
  createMockContentView,
  createMockRevenueMetric,
  createMockEngagementMetric,
  createMockPlatformError,
  createMockABTest,
} from './utils'

// Create with defaults
const contentView = createMockContentView()

// Create with overrides
const customContentView = createMockContentView({
  id: 'custom-id',
  duration: 100,
  userId: 'specific-user-id',
})

// Create multiple entities
const contentViews = createMockContentViews(5, { userId: 'same-user' })

Available Factories

  • createMockContentView(overrides?) - Create ContentView entity
  • createMockContentViews(count, overrides?) - Create multiple ContentView entities
  • createMockRevenueMetric(overrides?) - Create RevenueMetric entity
  • createMockRevenueMetrics(count, overrides?) - Create multiple RevenueMetric entities
  • createMockEngagementMetric(overrides?) - Create EngagementMetric entity
  • createMockEngagementMetrics(count, overrides?) - Create multiple EngagementMetric entities
  • createMockPlatformError(overrides?) - Create PlatformError entity
  • createMockPlatformErrors(count, overrides?) - Create multiple PlatformError entities
  • createMockABTest(overrides?) - Create ABTest entity
  • createMockABTests(count, overrides?) - Create multiple ABTest entities
  • createMockABTestVariant(overrides?) - Create ABTestVariant
  • createMockABTestResults(overrides?) - Create ABTestResults
  • createDateOffset(days) - Create date offset from now
  • createMockUUID() - Generate random UUID for testing

Using Mock Repositories

The mock repository utilities provide TypeORM-compatible mocks for testing services:

Basic Usage

import { createMockRepository } from './utils'
import { ContentView } from '@/entities/content-view.entity'

describe('ContentViewService', () => {
  let mockRepo: MockRepository<ContentView>

  beforeEach(() => {
    mockRepo = createMockRepository<ContentView>()
  })

  it('should find content views', async () => {
    const mockData = [createMockContentView()]
    mockRepo.find.mockResolvedValue(mockData)

    const result = await mockRepo.find()

    expect(result).toEqual(mockData)
  })
})

Using with NestJS Testing Module

import { Test } from '@nestjs/testing'
import { getRepositoryToken } from '@nestjs/typeorm'
import { createMockRepositoryProvider } from './utils'
import { ContentView } from '@/entities/content-view.entity'
import { ContentViewService } from '@/content-view/content-view.service'

describe('ContentViewService', () => {
  let service: ContentViewService
  let repo: MockedRepository<ContentView>

  beforeEach(async () => {
    const module = await Test.createTestingModule({
      providers: [
        ContentViewService,
        {
          provide: getRepositoryToken(ContentView),
          useValue: createMockRepositoryProvider<ContentView>(),
        },
      ],
    }).compile()

    service = module.get<ContentViewService>(ContentViewService)
    repo = module.get(getRepositoryToken(ContentView))
  })

  it('should be defined', () => {
    expect(service).toBeDefined()
  })
})

Query Builder Mocking

import { createMockRepository, createMockContentView } from './utils'

const mockRepo = createMockRepository<ContentView>()
const queryBuilder = mockRepo.getQueryBuilder()
const mockData = [createMockContentView()]

// Set expected result
queryBuilder.mockResult(mockData)

// Execute query
const result = await mockRepo
  .createQueryBuilder('contentView')
  .where('contentView.userId = :userId', { userId: 'test-user-id' })
  .orderBy('contentView.createdAt', 'DESC')
  .getMany()

expect(result).toEqual(mockData)
expect(queryBuilder.where).toHaveBeenCalledWith(
  'contentView.userId = :userId',
  { userId: 'test-user-id' }
)

Available Repository Methods

The mock repository provides all standard TypeORM repository methods:

  • find(options?) - Find entities
  • findOne(options?) - Find single entity
  • findOneBy(criteria) - Find by criteria
  • findAndCount(options?) - Find with count
  • save(entity) - Save entity
  • create(data) - Create entity instance
  • update(criteria, data) - Update entities
  • delete(criteria) - Delete entities
  • remove(entity) - Remove entity
  • count(options?) - Count entities
  • increment(criteria, field, value) - Increment field
  • decrement(criteria, field, value) - Decrement field
  • createQueryBuilder(alias) - Create query builder

Query Builder Methods

The mock query builder supports chaining:

  • select() - Select fields
  • where() / andWhere() / orWhere() - Filter conditions
  • orderBy() / addOrderBy() - Sorting
  • groupBy() / addGroupBy() - Grouping
  • skip() / take() / limit() / offset() - Pagination
  • leftJoin() / innerJoin() - Joins
  • getOne() - Get single result
  • getMany() - Get multiple results
  • getCount() - Get count
  • getManyAndCount() - Get results with count
  • execute() - Execute query

Mocked Dependencies

The test setup automatically mocks common dependencies:

Redis

import { mockRedis } from './setup'

// Redis is mocked globally
mockRedis.get.mockResolvedValue('cached-value')
mockRedis.set.mockResolvedValue('OK')

BullMQ

import { mockQueue } from './setup'

// Queue is mocked globally
mockQueue.add.mockResolvedValue({ id: '1', data: {} })

Test Organization

Follow this structure for test files:

import { describe, it, expect, beforeEach } from 'vitest'
import { Test } from '@nestjs/testing'

describe('ServiceName', () => {
  // Setup
  let service: ServiceName

  beforeEach(async () => {
    // Initialize test module
  })

  describe('methodName', () => {
    it('should do something specific', async () => {
      // Arrange
      const input = { /* ... */ }

      // Act
      const result = await service.methodName(input)

      // Assert
      expect(result).toEqual(expectedOutput)
    })

    it('should handle error case', async () => {
      // Test error scenarios
    })
  })
})

Best Practices

  1. Arrange-Act-Assert: Structure tests clearly with setup, execution, and verification
  2. Descriptive Names: Use clear, descriptive test names that explain what is being tested
  3. Isolation: Each test should be independent and not rely on other tests
  4. Reset Mocks: Use beforeEach to reset mocks between tests
  5. Type Safety: Use TypeScript types with factory functions and mocks
  6. Coverage: Aim for 80%+ coverage, but focus on meaningful tests over metrics
  7. Edge Cases: Test happy path, error cases, and edge conditions

Path Aliases

The test configuration supports path aliases matching tsconfig.json:

import { ContentView } from '@/entities/content-view.entity'
import { SomeService } from '@/some/some.service'

The @/ alias resolves to the src/ directory.

Environment Variables

Test environment variables are set in test/setup.ts:

  • NODE_ENV=test
  • DATABASE_URL=postgresql://test:test@localhost:5432/analytics_test
  • REDIS_URL=redis://localhost:6379
  • JWT_SECRET=test-secret-key-for-testing-only

Override these in specific tests if needed.

Next Steps

  1. Delete test/example.spec.ts once you start writing actual tests
  2. Create test files next to source files: *.spec.ts or *.test.ts
  3. Run tests with coverage to identify untested code
  4. Write E2E tests in separate files: *.e2e.spec.ts

Resources