main.
Run from Meridian/
These scripts match what you should run locally before opening or updating a PR:
Meridian/ in the monorepo) and installed dependencies in Meridian/, Meridian/backend/, and Meridian/frontend/.
Expectations when writing new code
General
- Deterministic: no real network, no wall-clock races, no dependence on “today’s” date unless frozen or injected. Use in-memory Mongo for backend route tests, not a shared dev DB.
- Readable failures: assertion messages or test names should make regressions obvious in CI logs.
- Colocate with the change: tests live next to the layer they protect (see layout sections below). Avoid giant catch-all files unless that pattern already exists for the area you are editing.
Backend
| You change… | Prefer… |
|---|---|
| Pure helpers, parsers, small services with no HTTP | tests/unit/ — fast, no I/O. |
| Middleware wiring, app boot, shallow HTTP | tests/integration/ — Supertest against the app. |
| Routes that read/write Mongo, permissions, status codes, multi-step API behavior | tests/route-outcomes/ — this is the default choice for “did the API do the right thing?” |
req.db, req.school, and sometimes req.globalDb. Route-outcome tests must set that context the same way production middleware does. If a route uses global identity (authGlobalService, etc.), use createMongoMemoryConnection({ withGlobalDb: true }) and assign req.globalDb per docs/TESTING_FRAMEWORK.md and backend/tests/helpers/mongoMemory.js.
Frontend
| You change… | Prefer… |
|---|---|
| Components, hooks, small UI logic | Tests under frontend/src/**/__tests__/ (or colocated *.test.js where the repo already does that). |
postRequest, useFetch, refresh-token retry, error mapping | Tests under frontend/src/utils/__tests__/ (or equivalent) so network/auth behavior stays covered without axios scattered in components. |
window, cookies, or browser-only APIs deterministically.
Before you open a PR
- Run
npm run test:cifromMeridian/(or at minimum the package you touched:npm run test:backend/npm run test:frontend). - If you only changed backend HTTP behavior,
npm run test:backend:routesis a quick gate before the full coverage run. - If CI fails on coverage or unit-tests, reproduce locally with the same npm scripts as in the workflow (below).
Backend (Meridian/backend/)
Stack: Jest, Supertest, mongodb-memory-server for route outcomes.
| Command | Purpose |
|---|---|
npm run test:backend | Full backend coverage (unit + integration + route outcomes) |
npm --prefix backend run test:unit | tests/unit |
npm --prefix backend run test:integration | tests/integration |
npm --prefix backend run test:routes | tests/route-outcomes |
npm --prefix backend run test:coverage | Full suite with coverage (used in CI coverage job) |
Layout
backend/tests/unit/— isolated logic and utilities.backend/tests/integration/— HTTP behavior against the app.backend/tests/route-outcomes/— end-to-end request/response + Mongoose on in-memory DB.
backend/jest.config.js. Helpers: backend/tests/helpers/mongoMemory.js.
Frontend (Meridian/frontend/)
Stack: Jest + React Testing Library (runner via scripts/test.js).
frontend/src/**/__tests__/**/*.test.js and targeted utils tests for request helpers.
Jest basics (if this stack is new)
Meridian uses the same Jest runner everywhere:describe('…', () => { … })— groups related specs (often one file per module or route file).test('…', () => { … })orit('…', …)— a single example; useasyncwhen the body awaits promises.expect(value).toBe(…),.toEqual(…),.toContain(…),expect(fn).toHaveBeenCalled()— assertions; Jest fails the test when any assertion fails.beforeEach/afterEach/beforeAll/afterAll— setup and teardown (open DB, reset mocks, close connections).jest.mock('modulePath', factory)— replace a module with a fake for the current test file (common in route and integration tests).
request(app).get('/path') returns a response object with statusCode, body, headers, etc.
Minimal patterns (Meridian-shaped)
The snippets below are small teaching sketches—valid Jest/Supertest style, heavily commented, focused on syntax and Meridian conventions. They are not copy-pasted production tests. For full imports, schemas, andbeforeEach seed data, open Meridian/backend/tests/** and Meridian/frontend/src/**/__tests__.
1) Backend unit — no req, no Mongo
2) Backend HTTP — Supertest + mocked infra
3) Backend route outcomes — req.db, getModelService, in-memory Mongo
Meridian handlers use getModels(req, 'User', 'Event', …) (through getModelService) so every query runs on the tenant Mongoose connection attached as req.db. Route-outcome tests reproduce that with mongodb-memory-server (createMongoMemoryConnection in backend/tests/helpers/mongoMemory.js) and a jest.mock('../../services/getModelService', …) factory that binds schemas with getOrCreateModel(req.db, …)—never mongoose.model() on the global connection for tenant data.
jest.mock('../../services/getModelService', …) returning getOrCreateModel(req.db, …) per schema, mount the real router, and assert both HTTP and documents in the in-memory DB. Meridian/backend/tests/route-outcomes/ has full examples (including withGlobalDb when routes need req.globalDb).
Meridian-specific checklist
- Attach
req.db(andreq.globalDbwhen the route uses global identity) before handlers run. - Mock
getModelServicesogetModels(req, …)matches production binding. - Reset the in-memory database between tests (
mongo.reset()). - Assert HTTP and, when it matters, documents in Mongo via the same models the route used.
4) Frontend — postRequest and axios mocks (no browser network)
5) Frontend — components (React Testing Library)
useFetch or the module that wraps postRequest so tests never hit localhost:5001 accidentally.
CI integration (Meridian/.github/workflows/ci.yml)
Workflow file: .github/workflows/ci.yml. Triggers: pull_request and push to main.
Meridian CI is not a single “test” step: it uses three jobs so build, fast test signal, and coverage artifacts stay separated.
1. build
- Validates
private-deps.lock(events.refmust be a 40-character hex SHA). - Sets up SSH, runs
bin/fetch_private_deps(Events-Backend and other private deps for a faithful tree). npm installinbackend/andfrontend/.- Runs
npm --prefix frontend run build(withCI=falsefor that step as defined in the workflow).
2. unit-tests
Same lockfile + SSH + fetch_private_deps + installs, then:
npm --prefix backend run test:unitnpm --prefix backend run test:integrationnpm --prefix backend run test:routesnpm --prefix frontend run test:ci
3. coverage
Same bootstrap, then:
- Backend:
npm --prefix backend run test:coveragewith JSON test output underbackend/coverage/. - Frontend:
npm --prefix frontend run test:coveragewith lcov, clover, json-summary, and JSON test results underfrontend/coverage/.
- A step summary is appended to the GitHub Actions run (tables built from
coverage/test-results.jsonandcoverage-summary.jsonwhere present). - Artifacts are uploaded: backend and frontend lcov, summaries, HTML lcov reports, and related files for download from the run.
npm run test:ci locally aligns with running the full coverage-style backend + frontend commands you rely on for merge confidence; CI additionally splits unit / integration / routes in the unit-tests job and runs a production build in build.
Going deeper
In the Meridian repo,docs/TESTING_FRAMEWORK.md is the canonical in-tree reference for layout, multi-tenant route tests, and “adding new tests” checklists.
Related pages
Getting started
Stack,
meridian setup, and commands to run before npm run test:*.Development
Day-to-day workflow, debugging, and troubleshooting.
Backend best practices
req.db, auth middleware, and patterns you assert on in route tests.Multi-tenant test scenarios
Tenant isolation and membership cases worth mirroring in new tests.
Authentication overview
How login and tokens work when testing auth and refresh flows.
Web client best practices
useFetch / postRequest patterns to reflect in frontend tests.Meridian CLI
Branch helpers and local workflow around the same repo you test.
Deployment
CI jobs, coverage artifacts, and how merge gates align with local
test:ci.