> ## 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.

# Testing Standards

> When and how to write tests in the PyqDeck monorepo

## 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

| Layer                    | Tool                   | Purpose                                         |
| ------------------------ | ---------------------- | ----------------------------------------------- |
| Backend Unit/Integration | **Vitest**             | Services, repositories, controllers, middleware |
| Backend Load Testing     | **k6**                 | API performance and stress testing              |
| Frontend Components      | **Vitest + Storybook** | UI component testing                            |
| E2E (future)             | **Playwright**         | Full 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

```javascript theme={null}
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

```bash theme={null}
# Run all tests
pnpm test

# Watch mode (for development)
pnpm test:watch

# With coverage
pnpm test:coverage
```

### Coverage Thresholds

The project enforces minimum coverage:

| Metric     | Threshold |
| ---------- | --------- |
| Lines      | 80%       |
| Functions  | 80%       |
| Statements | 80%       |
| Branches   | 65%       |

## Load Testing Guide

### Running Load Tests

```bash theme={null}
# Smoke test (3 VUs, 1 minute)
pnpm test:load

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

### Smoke Test Example

```javascript theme={null}
// 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:

```javascript theme={null}
// 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:

```bash theme={null}
cd frontend
pnpm storybook
```

### Running Frontend Tests

```bash theme={null}
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

* Read about [the SDK flow](/workflows/sdk-flow)
* Explore the [monorepo architecture](/system-overview/architecture)
* Check the [deployment pipeline](/infrastructure/deployment)
