Files
adopt-a-street/TESTING.md
William Valentin 742d1cac56 docs: comprehensive CouchDB migration documentation update
- Updated AGENTS.md with CouchDB references throughout
- Updated TESTING.md to reflect CouchDB testing utilities
- Updated TESTING_QUICK_START.md with CouchDB terminology
- Updated TEST_IMPLEMENTATION_SUMMARY.md for CouchDB architecture
- Updated IMPLEMENTATION_SUMMARY.md to include CouchDB migration
- Created comprehensive COUCHDB_MIGRATION_GUIDE.md with:
  - Migration benefits and architecture changes
  - Step-by-step migration process
  - Data model conversions
  - Design document setup
  - Testing updates
  - Deployment configurations
  - Performance optimizations
  - Monitoring and troubleshooting

All MongoDB references replaced with CouchDB equivalents while maintaining
existing document structure and technical accuracy.

🤖 Generated with AI Assistant

Co-Authored-By: AI Assistant <noreply@ai-assistant.com>
2025-11-03 10:30:24 -08:00

14 KiB

Testing Documentation

Comprehensive testing infrastructure for the Adopt-a-Street application.

Table of Contents

  1. Overview
  2. Backend Testing
  3. Frontend Testing
  4. Running Tests
  5. Coverage Reports
  6. Test Structure
  7. Best Practices
  8. Troubleshooting

Overview

The Adopt-a-Street application uses a comprehensive testing strategy covering both backend and frontend:

  • Backend: Jest + Supertest + CouchDB testing utilities
  • Frontend: React Testing Library + Jest + MSW (Mock Service Worker)
  • Current Coverage:
    • Backend: ~55% statement coverage (109 passing tests)
    • Frontend: MSW infrastructure in place with component tests

Backend Testing

Test Infrastructure

The backend uses:

  • Jest: Test runner and assertion library
  • Supertest: HTTP assertions for API endpoint testing
  • CouchDB Testing Utilities: In-memory CouchDB for isolated testing
  • Cross-env: Environment variable management

Setup Files

backend/jest.config.js

module.exports = {
  testEnvironment: 'node',
  coverageDirectory: 'coverage',
  collectCoverageFrom: [
    'routes/**/*.js',
    'middleware/**/*.js',
    'models/**/*.js',
    '!**/node_modules/**',
    '!**/coverage/**'
  ],
  testMatch: [
    '**/__tests__/**/*.test.js',
    '**/?(*.)+(spec|test).js'
  ],
  coverageThreshold: {
    global: {
      branches: 70,
      functions: 70,
      lines: 70,
      statements: 70
    }
  },
  setupFilesAfterEnv: ['<rootDir>/__tests__/setup.js'],
  testTimeout: 30000,
  verbose: true
};

backend/__tests__/setup.js

Sets up CouchDB testing utilities for all tests:

  • Creates in-memory CouchDB instance before all tests
  • Clears documents after each test
  • Closes connections after all tests
  • Suppresses console logs during tests

Test Files

Route Tests (__tests__/routes/)

  • auth.test.js - Authentication endpoints (register, login, get user)
  • streets.test.js - Street CRUD operations and adoption
  • tasks.test.js - Task management operations
  • posts.test.js - Social feed post operations
  • events.test.js - Community event management
  • rewards.test.js - Points and rewards system
  • reports.test.js - Street condition reporting

Model Tests (__tests__/models/)

  • User.test.js - User model validation and relationships
  • Street.test.js - Street model with GeoJSON location
  • Task.test.js - Task model validation
  • Post.test.js - Post model with likes and comments

Middleware Tests (__tests__/middleware/)

  • auth.test.js - JWT authentication middleware

Test Helpers (__tests__/utils/)

testHelpers.js provides utilities for:

  • Creating test users with JWT tokens
  • Creating test streets, tasks, posts, events, rewards, reports
  • Database cleanup utilities

Example Backend Test

const request = require('supertest');
const express = require('express');
const authRoutes = require('../../routes/auth');
const { createTestUser } = require('../utils/testHelpers');

const app = express();
app.use(express.json());
app.use('/api/auth', authRoutes);

describe('Auth Routes', () => {
  it('should register a new user', async () => {
    const response = await request(app)
      .post('/api/auth/register')
      .send({
        name: 'John Doe',
        email: 'john@example.com',
        password: 'password123',
      })
      .expect(200);

    expect(response.body).toHaveProperty('token');
  });
});

Frontend Testing

Test Infrastructure

The frontend uses:

  • React Testing Library: Component testing utilities
  • Jest: Test runner (via react-scripts)
  • MSW (Mock Service Worker): API mocking
  • @testing-library/user-event: User interaction simulation

Setup Files

frontend/src/setupTests.js

  • Configures Jest-DOM matchers
  • Sets up MSW server for API mocking
  • Mocks localStorage and window.matchMedia
  • Suppresses expected console warnings

frontend/src/mocks/handlers.js

Defines MSW handlers for all API endpoints:

  • Authentication (register, login, get user)
  • Streets (list, adopt)
  • Tasks (list, create, complete)
  • Posts (list, create, like)
  • Events (list, create, RSVP)
  • Rewards (list, redeem)
  • Users (get, update)

Test Files

Component Tests (src/components/__tests__/)

  • Login.test.js - Login form validation and submission
  • Register.test.js - Registration form validation
  • ErrorBoundary.test.js - Error handling component

Integration Tests (src/__tests__/)

  • auth-flow.integration.test.js - Complete authentication flows

Example Frontend Test

import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { BrowserRouter } from 'react-router-dom';
import Login from '../Login';
import { AuthContext } from '../../context/AuthContext';

describe('Login Component', () => {
  it('should render login form', () => {
    render(
      <BrowserRouter>
        <AuthContext.Provider value={mockContext}>
          <Login />
        </AuthContext.Provider>
      </BrowserRouter>
    );

    expect(screen.getByPlaceholderText(/email/i)).toBeInTheDocument();
    expect(screen.getByPlaceholderText(/password/i)).toBeInTheDocument();
  });
});

Running Tests

Backend Tests

cd backend

# Run all tests
bun test

# Run tests in watch mode
bun run test:watch

# Run tests with coverage
bun run test:coverage

# Run tests with verbose output
bun run test:verbose

Frontend Tests

cd frontend

# Run all tests (interactive watch mode)
bun test

# Run tests with coverage
bun run test:coverage

# Run tests in watch mode
bun run test:watch

Run All Tests

From the project root:

# Backend
cd backend && bun test

# Frontend
cd frontend && bun test

Coverage Reports

Backend Coverage

Current coverage (as of last run):

-----------------------|---------|----------|---------|---------|
File                   | % Stmts | % Branch | % Funcs | % Lines |
-----------------------|---------|----------|---------|---------|
All files              |   54.75 |    32.23 |   62.66 |   54.85 |
 middleware            |   40.24 |    21.05 |   45.45 |    39.5 |
  auth.js              |     100 |      100 |     100 |     100 |
 models                |    82.5 |    66.66 |      50 |    82.5 |
 routes                |   45.84 |    22.61 |   60.97 |   46.07 |
-----------------------|---------|----------|---------|---------|

Test Results: 109 passing, 67 failing (schema mismatches)

Key Coverage Areas:

  • Auth middleware: 100% coverage
  • Model validators: 82.5% coverage
  • ⚠️ Routes: 45.84% coverage (room for improvement)
  • ⚠️ Other middleware: 40.24% coverage

Frontend Coverage

Coverage configuration in package.json:

{
  "coverageThreshold": {
    "global": {
      "branches": 50,
      "functions": 60,
      "lines": 60,
      "statements": 60
    }
  }
}

Coverage reports are generated in:

  • Backend: backend/coverage/
  • Frontend: frontend/coverage/

To view HTML coverage reports:

# Backend
cd backend && bun test -- --coverage
open coverage/lcov-report/index.html

# Frontend
cd frontend && bun run test:coverage
open coverage/lcov-report/index.html

Test Structure

Backend Test Organization

backend/
├── __tests__/
│   ├── setup.js                 # Global test setup
│   ├── middleware/
│   │   └── auth.test.js         # Middleware tests
│   ├── models/
│   │   ├── User.test.js         # Model tests
│   │   ├── Street.test.js
│   │   ├── Task.test.js
│   │   └── Post.test.js
│   ├── routes/
│   │   ├── auth.test.js         # Route tests
│   │   ├── streets.test.js
│   │   ├── tasks.test.js
│   │   ├── posts.test.js
│   │   ├── events.test.js
│   │   ├── rewards.test.js
│   │   └── reports.test.js
│   └── utils/
│       └── testHelpers.js       # Test utilities
└── jest.config.js               # Jest configuration

Frontend Test Organization

frontend/src/
├── setupTests.js                # Global test setup
├── mocks/
│   ├── handlers.js              # MSW API handlers
│   └── server.js                # MSW server setup
├── __tests__/
│   └── auth-flow.integration.test.js  # Integration tests
└── components/
    └── __tests__/
        ├── Login.test.js        # Component tests
        ├── Register.test.js
        └── ErrorBoundary.test.js

Best Practices

Backend Testing

  1. Use Test Helpers: Leverage testHelpers.js for consistent test data creation
  2. Isolate Tests: Each test should be independent and not rely on other tests
  3. Mock External Services: Mock Cloudinary, Stripe, and other external APIs
  4. Test Error Cases: Don't just test happy paths, test error handling
  5. Use Descriptive Names: Test names should clearly describe what they test
  6. Clean Up: The test setup automatically cleans up between tests

Frontend Testing

  1. Test User Behavior: Focus on how users interact with components
  2. Use MSW for API Mocking: Don't mock axios directly, use MSW handlers
  3. Test Accessibility: Use semantic queries (getByRole, getByLabelText)
  4. Avoid Implementation Details: Test what users see, not component internals
  5. Use waitFor for Async: Always use waitFor for asynchronous operations
  6. Mock External Libraries: Mock Leaflet, Socket.IO, and other heavy libraries

Writing New Tests

Backend Route Test Template

const request = require('supertest');
const express = require('express');
const yourRoute = require('../../routes/your-route');
const { createTestUser } = require('../utils/testHelpers');

const app = express();
app.use(express.json());
app.use('/api/your-route', yourRoute);

describe('Your Route', () => {
  describe('GET /api/your-route', () => {
    it('should do something', async () => {
      const { token } = await createTestUser();

      const response = await request(app)
        .get('/api/your-route')
        .set('x-auth-token', token)
        .expect(200);

      expect(response.body).toMatchObject({ /* expected shape */ });
    });
  });
});

Frontend Component Test Template

import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import YourComponent from '../YourComponent';

describe('YourComponent', () => {
  it('should render and work', async () => {
    render(<YourComponent />);

    const button = screen.getByRole('button', { name: /submit/i });
    fireEvent.click(button);

    await waitFor(() => {
      expect(screen.getByText(/success/i)).toBeInTheDocument();
    });
  });
});

Troubleshooting

Common Issues

CouchDB Testing Timeout

If tests timeout with CouchDB testing utilities:

# Increase timeout in jest.config.js
testTimeout: 30000  // 30 seconds

MSW Not Intercepting Requests

Ensure MSW server is set up in setupTests.js:

import { server } from './mocks/server';

beforeAll(() => server.listen({ onUnhandledRequest: 'warn' }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

React Router Errors in Tests

Wrap components with BrowserRouter:

import { BrowserRouter } from 'react-router-dom';

render(
  <BrowserRouter>
    <YourComponent />
  </BrowserRouter>
);

Auth Context Not Found

Provide AuthContext in tests:

import { AuthContext } from '../../context/AuthContext';

const mockAuthContext = {
  auth: { isAuthenticated: false, loading: false, user: null },
  login: jest.fn(),
};

render(
  <AuthContext.Provider value={mockAuthContext}>
    <YourComponent />
  </AuthContext.Provider>
);

Leaflet Map Errors

Mock react-leaflet in tests:

jest.mock('react-leaflet', () => ({
  MapContainer: ({ children }) => <div data-testid="map">{children}</div>,
  TileLayer: () => null,
  Marker: () => null,
  Popup: () => null,
}));

Performance Tips

  1. Run specific test files:

    bun test -- auth.test.js
    
  2. Run tests matching pattern:

    bun test -- --testNamePattern="login"
    
  3. Skip tests during development:

    describe.skip('Skip this suite', () => { /* ... */ });
    it.skip('Skip this test', () => { /* ... */ });
    
  4. Run only specific tests:

    describe.only('Run only this suite', () => { /* ... */ });
    it.only('Run only this test', () => { /* ... */ });
    

Future Improvements

Backend

  • Increase route coverage to 70%+
  • Add tests for Socket.IO events
  • Add tests for file upload (Multer)
  • Add tests for AI route
  • Add tests for payments route
  • Add performance/load tests
  • Add E2E tests with real database

Frontend

  • Add tests for all remaining components:
    • MapView
    • TaskList
    • SocialFeed
    • Events
    • Profile
    • Premium
    • Rewards
  • Add integration tests for complete user flows
  • Add accessibility tests (jest-axe)
  • Add visual regression tests (Percy/Chromatic)
  • Add E2E tests (Playwright/Cypress)
  • Increase coverage to 60%+

CI/CD Integration

To integrate tests into CI/CD pipeline:

GitHub Actions Example

name: Tests

on: [push, pull_request]

jobs:
  backend-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
      - run: cd backend && bun install
      - run: cd backend && bun test -- --coverage

  frontend-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
      - run: cd frontend && bun install
      - run: cd frontend && bun run test:coverage

Resources