# Testing Guide — validating this build actually works

**Read this first:** every file in this project was written and
cross-checked by careful reading — signatures matched against call
sites, the Prisma schema checked for consistency, imports traced by
hand. None of it has been executed. There is no substitute for
actually running it. This guide is the fastest path from "a pile of
code" to "I know this works," roughly in order of how much confidence
each step buys you per minute spent.

## 0. The single fastest signal: does it even compile?

```bash
npm install
npx prisma generate
npm run build
```

This alone will catch a large class of possible mistakes — a typo'd
import, a mismatched function signature, a Prisma field referenced
that doesn't exist. If this fails, nothing past this point matters
until it's fixed. If it's the first time you're seeing this project
run, **do this before anything else.**

## 1. Unit tests — no database required

```bash
npm test
```

This runs the pure-logic tests (grading, auto-marking, ranking,
randomization, permission guards, bulk-import validation). It needs
zero setup and should pass on a completely fresh checkout. If it
doesn't, something is broken at a fairly fundamental level.

## 2. Bring up real infrastructure

Easiest path — Docker (see README "Running with Docker" for details):

```bash
docker compose up -d postgres minio
docker compose run --rm migrate
docker compose run --rm create-bucket
docker compose run --rm seed
docker compose up -d app
```

Or locally: provision Postgres, `cp .env.example .env` and fill in
`DATABASE_URL`/`NEXTAUTH_SECRET`, then:

```bash
npm run prisma:migrate
npm run prisma:seed
npm run dev
```

Visit `http://localhost:3000/login`. If this page doesn't load, or
migrations fail, stop here — that's the actual first bug to fix, more
informative than anything below.

## 3. Integration test against a real database

```bash
export TEST_DATABASE_URL="postgresql://cbt:cbt_dev_password@localhost:5432/cbt_platform_test?schema=public"
DATABASE_URL=$TEST_DATABASE_URL npx prisma migrate deploy
npm run test:integration
```

This automatically walks the highest-value path — school → class →
subject → approved question → live exam → student → attempt →
submit → auto-mark → CA score → generated result — and asserts the
final grade matches a hand-calculated expectation. If this passes,
the core grading pipeline is verified correct without you doing it by
hand. **This is the most important single test in the whole project.**

## 4. Manual walkthrough — the actual "does it work" pass

Use the seeded demo school (`admin@greenwood.local` /
`ChangeMe123!`, school code `greenwood-college`) or create your own.
Most of this has no dedicated UI yet — you'll be calling the API
directly (curl, Postman, or your browser's dev tools console) for
several steps. That's expected, not a sign of missing work; see
"What's not tested here" below.

### A. Question bank → exam setup (as school admin)
1. Log in at `/login` → land on `/admin/dashboard`
2. `/admin/questions` → create a manual MCQ → click **Approve**
3. `/admin/exams/new` → pick that subject/class/term, set the start
   time to *right now*, select the approved question → create
4. `/admin/exams` → advance status: Draft → Scheduled → Active

### B. Create a student and take the exam
5. Create a student via API (no UI form exists yet):
   ```bash
   curl -X POST http://localhost:3000/api/students \
     -H "Content-Type: application/json" -H "Cookie: <admin session cookie>" \
     -d '{"admissionNumber":"TEST-01","firstName":"Ada","lastName":"Obi","classId":"...","academicSessionId":"..."}'
   ```
   Note the `generatedPin` in the response — you'll need it to log in.
6. Log in as the student (Student tab: school code + admission number
   + PIN)
7. Start the attempt via API: `POST /api/exams/:id/start` → note the
   returned `attemptId` → visit `/student/exam/:attemptId` directly
   (there's no "available exams" list page yet — see gaps below)
8. Answer the question, submit
9. **Edge case worth checking specifically:** refresh the page
   mid-exam and confirm your answer is still there (autosave), and
   try leaving an exam running past its duration to confirm it
   auto-submits on your next request rather than needing a page action

### C. Grading pipeline
10. `POST /api/ca-scores` — record a CA score for the same subject/term
11. `POST /api/exams/:id/generate-results` — check the resulting
    status: `REVIEWED` if everything's objective and marked,
    `MARKING` if a subjective answer is still ungraded
12. `POST /api/results/:id/approve`, then `POST /api/results/publish`
    (bulk, by class/subject/term)
13. As the student: `GET /api/results/my` → confirm the grade appears
    and the percentage matches CA-weight × CA-score + exam-weight ×
    exam-score

### D. Report card
14. `POST /api/report-cards/generate`
15. `PATCH /api/report-cards/:id` with a teacher/principal comment
16. `POST /api/report-cards/:id/publish`
17. `GET /api/report-cards/:id/pdf` → open the PDF, check the layout,
    confirm the QR code is present and scannable
18. Visit `/verify/:code` (the code shown in the PDF) → confirm it
    shows the right student/school/term and says "Verified"

### E. AI features (only if `ANTHROPIC_API_KEY` is set)
19. `POST /api/ai/generate-question` → confirm it lands in the bank as
    `AI_GENERATED` → approve it through the normal UI, same as any
    other question
20. On a THEORY question a student has answered: `POST
    /api/ai/mark-essay` → confirm `aiSuggestedScore` is populated →
    `POST /api/student-answers/:id/mark` to confirm a final score →
    re-run step 11's generate-results and confirm it now moves from
    `MARKING` to `REVIEWED`

### F. Parents
21. `POST /api/parents` (admin) with `initialChildAdmissionNumbers`
22. Log in as that parent (Staff/Admin/Parent tab, email+password) →
    `GET /api/parents/my-children`, then `.../results` and
    `.../report-card` for that child

### G. Subscriptions/payments (only if `PAYSTACK_SECRET_KEY` is set)
23. `GET /api/subscriptions` → confirm the default `BASIC` plan and
    its `studentLimit`
24. Try creating students past that limit → confirm it's blocked with
    a clear message, not a generic error
25. `POST /api/payments/initiate` → follow the returned
    `authorizationUrl` to Paystack's test checkout → complete a test
    payment. The webhook needs a publicly reachable URL in local dev
    (use `ngrok http 3000` or similar and point Paystack's test
    webhook config at it) — confirm the plan upgrades after payment

### H. Bulk import
26. Build a small `.csv` with a few student rows (columns:
    `admissionNumber, firstName, lastName, className`, etc.) → `POST`
    it to `/api/students/bulk-import/validate` → review the returned
    errors → `POST` the valid rows to `/api/students/bulk-import/commit`
    → confirm the students now exist

### Worth checking specifically (these are exactly the kind of bug that hides easily)
- **Tenant isolation:** create a second school, log in as its admin,
  confirm it cannot see the first school's students/classes/results
  under any endpoint
- **Wrong PIN / wrong password:** confirm login actually rejects bad
  credentials rather than just erroring
- **Duplicate submission:** try starting a second attempt on an exam
  with `maxAttempts: 1` while one is already `IN_PROGRESS` — confirm
  it's rejected or correctly resumes rather than creating a second row

## 5. What's genuinely not covered by any of this

- No UI page lists a student's available exams — you get the
  `attemptId` from the API and navigate to the URL directly (step B.7)
- No admin UI for creating students, recording attendance, or
  building non-MCQ questions (MATCHING/ESSAY/etc.) — API only
- No UI for the parent portal beyond what the API returns as JSON
- Route-handler-level tests don't exist — the walkthrough above *is*
  currently the test for that layer

See the main `README.md`'s "Known gaps" section for the full,
currently-accurate list — it's been kept honest and updated through
every phase of this build rather than left stale.

## 6. If you only have time for one thing

Run `npm run build` and `npm run test:integration`. Together they're
the closest thing to an automated answer to "did this actually work"
that exists right now — everything else in this guide is what fills
the gap those two don't cover.
