WayrApp Backend & Ecosystem Documentation - v1.0.0
    Preparing search index...

    Module TestInfo

    Testing Guide for WayrApp

    This file contains critical documentation about testing in WayrApp, including setup, best practices, and troubleshooting information.

    Exequiel Trujillo

    1.0.0

    // Quick setup for testing
    // 1. Create test database
    // 2. Configure .env.test
    // 3. Run: npm run test:db:setup
    // 4. Run: npm run test:integration:safe

    Testing Guide

    This document provides information about testing in WayrApp, including setup, best practices, and troubleshooting.

    CRITICAL: WayrApp uses a separate test database to prevent production data loss. Tests perform complete database cleanup after each test, which would delete all production data if run against the production database.

    ARCHITECTURAL DECISION: ALL tests (unit and integration) require a test database configuration via .env.test. This is intentional to enforce security and prevent accidental production data loss across the entire development team.

    • โœ… Mandatory test database - ALL tests require .env.test configuration
    • โœ… Automatic validation prevents accidental use of production database
    • โœ… Safe test commands that setup test database before running tests
    • โœ… Database isolation with complete cleanup after each test
    • โœ… Team consistency - All developers must follow the same setup process

    REQUIRED FOR ALL TESTS - You cannot run any tests without this setup.

    Create a separate database for testing (never use your production database):

    Option A: Using Neon (Recommended)

    1. Go to Neon Console (https://console.neon.tech/)
    2. Create a new database or project for testing
    3. Copy the connection URL

    Option B: Local PostgreSQL

    createdb wayrapp_test
    
    # Copy example configuration
    cp .env.example .env.test
    # Edit .env.test with your test database URL
    # IMPORTANT: Use a DIFFERENT database than production

    Example .env.test:

    NODE_ENV="test"
    DATABASE_URL="postgresql://user:pass@host/wayrapp_test?sslmode=require"
    JWT_SECRET="your-jwt-secret"
    JWT_REFRESH_SECRET="your-refresh-secret"
    PORT=3001
    LOG_LEVEL="error"
    BCRYPT_ROUNDS=4
    # Check that test and production databases are separate
    npm run test:db:check
    # Initialize test database schema
    npm run test:db:setup
    # Setup test DB and run integration tests safely
    npm run test:integration:safe

    # Run all backend tests (unit + integration)
    npm run test:backend

    # Check test database configuration
    npm run test:db:check

    NOTE: All commands require test database configuration via .env.test

    # Unit tests (requires test database configuration)
    npm test

    # Integration tests (requires test database)
    npm run test:integration

    # Setup test database schema
    npm run test:db:setup

    NOTE: Requires test database configuration via .env.test

    # Run unit tests in watch mode (requires test database)
    npm run test:watch

    IMPORTANT: All test types require test database configuration for security.

    1. Unit Tests (.test.ts)

      • Fast, isolated tests
      • Mock external dependencies when possible
      • Requires test database configuration (architectural decision)
    2. Integration Tests (.integration.test.ts)

      • Full API endpoint testing
      • Uses test database extensively
      • Tests complete request/response cycle
      • Requires test database configuration
    src/
    โ”œโ”€โ”€ __tests__/ # Integration tests
    โ”‚ โ””โ”€โ”€ integration/ # Cross-module integration tests
    โ”‚ โ”œโ”€โ”€ auth.integration.test.ts
    โ”‚ โ”œโ”€โ”€ content.integration.test.ts
    โ”‚ โ”œโ”€โ”€ database.integration.test.ts
    โ”‚ โ”œโ”€โ”€ crossModule.integration.test.ts
    โ”‚ โ””โ”€โ”€ errorHandling.integration.test.ts
    โ”œโ”€โ”€ modules/ # Feature modules (domain-driven design)
    โ”‚ โ”œโ”€โ”€ users/ # User management & authentication
    โ”‚ โ”‚ โ”œโ”€โ”€ controllers/
    โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ __tests__/ # Controller unit tests
    โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ userController.test.ts
    โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ authController.test.ts
    โ”‚ โ”‚ โ”œโ”€โ”€ services/
    โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ userService.test.ts # Service unit tests
    โ”‚ โ”‚ โ”œโ”€โ”€ routes/
    โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ __tests__/ # Route unit tests
    โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ userRoutes.test.ts
    โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ authRoutes.test.ts
    โ”‚ โ”‚ โ””โ”€โ”€ __tests__/ # Module integration tests
    โ”‚ โ”œโ”€โ”€ content/ # Course content management
    โ”‚ โ”‚ โ”œโ”€โ”€ controllers/
    โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ __tests__/ # Content controller tests
    โ”‚ โ”‚ โ”œโ”€โ”€ services/
    โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ __tests__/ # Content service tests
    โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ContentService.test.ts
    โ”‚ โ”‚ โ””โ”€โ”€ __tests__/ # Content module tests
    โ”‚ โ”‚ โ”œโ”€โ”€ packagedContent.test.ts
    โ”‚ โ”‚ โ”œโ”€โ”€ packagedContentController.test.ts
    โ”‚ โ”‚ โ””โ”€โ”€ contentService.integration.test.ts
    โ”‚ โ””โ”€โ”€ progress/ # Progress tracking & gamification
    โ”‚ โ”œโ”€โ”€ controllers/
    โ”‚ โ”œโ”€โ”€ services/
    โ”‚ โ”‚ โ””โ”€โ”€ __tests__/ # Progress service tests
    โ”‚ โ”‚ โ””โ”€โ”€ progressService.test.ts
    โ”‚ โ””โ”€โ”€ __tests__/ # Progress module tests
    โ”‚ โ””โ”€โ”€ progressController.integration.test.ts
    โ””โ”€โ”€ shared/ # Shared utilities and infrastructure
    โ”œโ”€โ”€ database/
    โ”‚ โ””โ”€โ”€ __tests__/ # Database utility tests
    โ”‚ โ””โ”€โ”€ optimization.test.ts
    โ”œโ”€โ”€ middleware/
    โ”‚ โ””โ”€โ”€ __tests__/ # Middleware tests
    โ”‚ โ”œโ”€โ”€ integration.test.ts
    โ”‚ โ”œโ”€โ”€ index.test.ts
    โ”‚ โ””โ”€โ”€ errorHandler.test.ts
    โ”œโ”€โ”€ schemas/
    โ”‚ โ””โ”€โ”€ __tests__/ # Schema validation tests
    โ”‚ โ”œโ”€โ”€ validation.test.ts
    โ”‚ โ””โ”€โ”€ common.test.ts
    โ”œโ”€โ”€ utils/
    โ”‚ โ””โ”€โ”€ __tests__/ # Utility function tests
    โ””โ”€โ”€ test/ # Test utilities and setup
    โ”œโ”€โ”€ factories/ # Test data factories
    โ”‚ โ”œโ”€โ”€ userFactory.ts
    โ”‚ โ””โ”€โ”€ contentFactory.ts
    โ”œโ”€โ”€ fixtures/ # Test fixtures
    โ”œโ”€โ”€ utils/ # Test helper functions
    โ”‚ โ””โ”€โ”€ __tests__/
    โ”‚ โ””โ”€โ”€ setupServiceTest.test.ts
    โ”œโ”€โ”€ setup.ts # Global test configuration
    โ”œโ”€โ”€ testDb.ts # Test database utilities
    โ””โ”€โ”€ mocks.ts # Global mocks
    • Unit Tests: jest.config.js
    • Integration Tests: jest.integration.config.js
    • Separate test environment with NODE_ENV=test
    • Database isolation with automatic cleanup
    • Longer timeouts for integration tests (30s)
    • Sequential execution to avoid database conflicts
    • TypeScript support with path mapping

    Use factories to create consistent test data:

    import { UserFactory } from '@/shared/test/factories/userFactory';
    import { CourseFactory } from '@/shared/test/factories/contentFactory';

    // Create test user
    const testUser = await prisma.user.create({
    data: UserFactory.build({
    email: 'test@example.com'
    })
    });

    // Create test course
    const testCourse = await prisma.course.create({
    data: CourseFactory.build({
    name: 'Test Course'
    })
    });
    // src/modules/users/services/userService.test.ts
    import { UserService } from './userService';
    import { prisma } from '@/shared/database/connection';

    // Mock Prisma
    jest.mock('@/shared/database/connection', () => ({
    prisma: {
    user: {
    findUnique: jest.fn(),
    create: jest.fn(),
    }
    }
    }));

    describe('UserService', () => {
    beforeEach(() => {
    jest.clearAllMocks();
    });

    it('should find user by email', async () => {
    const mockUser = { id: '1', email: 'test@example.com' };
    (prisma.user.findUnique as jest.Mock).mockResolvedValue(mockUser);

    const result = await UserService.findByEmail('test@example.com');

    expect(result).toEqual(mockUser);
    expect(prisma.user.findUnique).toHaveBeenCalledWith({
    where: { email: 'test@example.com' }
    });
    });
    });
    // src/__tests__/integration/auth.integration.test.ts
    import request from 'supertest';
    import app from '../../app';
    import { prisma } from '../../shared/database/connection';

    describe('Authentication Integration Tests', () => {
    afterEach(async () => {
    // Clean up test data
    await prisma.user.deleteMany();
    await prisma.revokedToken.deleteMany();
    });

    afterAll(async () => {
    await prisma.$disconnect();
    });

    it('should register a new user', async () => {
    const userData = {
    email: 'test@example.com',
    password: 'SecurePass123!',
    username: 'testuser'
    };

    const response = await request(app)
    .post('/api/v1/auth/register')
    .send(userData)
    .expect(201);

    expect(response.body.success).toBe(true);
    expect(response.body.data.user.email).toBe(userData.email);
    });
    });

    Problem: ALL tests fail with database configuration error. Cause: This is intentional - our architectural decision requires test database for all tests. Solution:

    # Check configuration
    npm run test:db:check

    # Create .env.test file (REQUIRED for all tests)
    cp .env.example .env.test
    # Edit .env.test with test database URL

    # Setup test database
    npm run test:db:setup

    Problem: Safety check prevents using production database for tests. Solution:

    • Create a separate test database
    • Update .env.test with the test database URL
    • Ensure the test database URL is different from production

    Problem: Tests fail to connect to test database. Solution:

    • Verify test database exists and is accessible
    • Check connection string format in .env.test
    • Ensure database permissions are correct

    Problem: Tests fail due to outdated database schema. Solution:

    # Reset and update test database schema
    npm run test:db:setup

    Problem: Authentication tests fail due to rate limiting. Solution: Tests include automatic rate limit handling with retries and graceful skipping.

    Run tests with verbose output:

    # Verbose test output
    npm run test:integration -- --verbose

    # Debug specific test file
    npm run test:integration -- --testNamePattern="Authentication"

    Generate test coverage reports:

    # Generate coverage report
    npm run test:coverage

    # View coverage report
    open coverage/lcov-report/index.html

    Tests run automatically in CI/CD pipelines:

    # .github/workflows/test.yml
    - name: Setup Test Database
    run: npm run test:db:setup

    - name: Run Tests
    run: npm run test:backend
    1. Use descriptive test names that explain what is being tested
    2. Follow AAA pattern: Arrange, Act, Assert
    3. Clean up test data in afterEach hooks
    4. Use factories for consistent test data creation
    5. Mock external dependencies in unit tests
    6. Test error cases as well as success cases
    1. Always use test database - never test against production
    2. Clean up after each test to ensure test isolation
    3. Use transactions for faster test cleanup when possible
    4. Test database constraints and relationships
    5. Verify cascading deletes work correctly
    1. Keep unit tests fast (< 100ms each)
    2. Limit integration test scope to essential functionality
    3. Use beforeAll/afterAll for expensive setup/teardown
    4. Run integration tests sequentially to avoid conflicts

    Variables

    testInfo