Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.pyqdeck.in/llms.txt

Use this file to discover all available pages before exploring further.

Overview

PyqDeck uses a pragmatic testing strategy: unit tests for backend logic and component tests for UI. We prioritize tests that give the most confidence with the least maintenance burden.

Testing Stack

LayerToolPurpose
Backend Unit/IntegrationVitestServices, repositories, controllers, middleware
Backend Load Testingk6API performance and stress testing
Frontend ComponentsVitest + StorybookUI component testing
E2E (future)PlaywrightFull user journey testing

When to Write What

Backend Tests (Vitest)

Write Vitest tests for:
  • Services - Business logic (e.g., paper parsing, question extraction)
  • Repositories - Database queries and aggregations
  • Controllers - Request/response handling, validation
  • Middleware - Auth, pagination, rate limiting, error handling
  • Utilities - Formatters, validators, error classes
Rule of thumb: If it has logic (conditionals, loops, transformations), it needs tests.

Load Tests (k6)

Write k6 tests for:
  • Critical paths - /papers, /questions, /search
  • Before major releases - Verify performance hasn’t degraded
  • After database changes - Ensure query performance is acceptable

Frontend Tests (Storybook + Vitest)

Write component tests for:
  • Complex UI components - Those with multiple states (loading, error, empty)
  • Interactive components - Forms, filters, modals
  • Accessibility-critical components - Navigation, forms, modals
Rule of thumb: If a component has props that change its appearance/behavior, test it.

Backend Testing Guide

Test Structure

Tests live in backend/tests/ mirroring the source structure:
backend/tests/
  setup.js              -- Global setup (MongoMemoryServer, mocks)
  controllers/
    subject.test.js
    paper.test.js
  services/
    paper.test.js
    university.test.js
  repositories/
    university.test.js
    paper.test.js
  middlewares/
    pagination.test.js
    rateLimiter.test.js
  utils/
    formatters.test.js
    validators.test.js
  load/
    smoke.js
    stress.js

Example: Service Test

import { describe, it, expect, vi } from 'vitest';
import { PaperService } from '../../src/services/paper.service.js';

describe('PaperService', () => {
  it('should return papers filtered by university', async () => {
    const mockRepo = {
      findByUniversity: vi.fn().mockResolvedValue([
        { id: '1', title: 'Math 2023', universityId: 'u1' }
      ])
    };
    const service = new PaperService(mockRepo);

    const result = await service.getPapersByUniversity('u1');

    expect(result).toHaveLength(1);
    expect(mockRepo.findByUniversity).toHaveBeenCalledWith('u1');
  });
});

Running Tests

# Run all tests
pnpm test

# Watch mode (for development)
pnpm test:watch

# With coverage
pnpm test:coverage

Coverage Thresholds

The project enforces minimum coverage:
MetricThreshold
Lines80%
Functions80%
Statements80%
Branches70%

Load Testing Guide

Running Load Tests

# Smoke test (3 VUs, 1 minute)
pnpm test:load

# Stress test (ramp to 50 VUs)
pnpm test:stress

Smoke Test Example

// tests/load/smoke.js
import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  vus: 3,
  duration: '1m',
};

export default function () {
  const res = http.get('http://localhost:3000/api/v1/health');
  check(res, {
    'status is 200': (r) => r.status === 200,
    'response time < 200ms': (r) => r.timings.duration < 200,
  });
  sleep(1);
}

Frontend Testing Guide

Component Testing with Storybook

Each UI component has a matching Storybook story:
// components/ui/button.stories.jsx
import { Button } from './button';

export default {
  component: Button,
  title: 'UI/Button',
};

export const Primary = {
  args: { variant: 'primary', children: 'Click me' },
};

export const Loading = {
  args: { loading: true, children: 'Loading...' },
};
Run Storybook tests:
cd frontend
pnpm storybook

Running Frontend Tests

cd frontend
pnpm test  # Runs Storybook component tests via Vitest

CI Integration

All tests run in CI on every PR:
  1. Backend tests - Vitest with coverage thresholds
  2. Contract check - Verifies openapi.json is in sync
  3. Frontend build - Validates SDK generation and Next.js build
  4. Load tests - Run against a live MongoDB service
Tests must pass before deployment. No exceptions.

Anti-Patterns to Avoid

  • Don’t test implementation details - Test behavior, not internal state
  • Don’t mock everything - Use real MongoDB (via MongoMemoryServer) when possible
  • Don’t skip error path tests - Test failure cases as thoroughly as success cases
  • Don’t write E2E tests for everything - Reserve E2E for critical user journeys only

Next Steps