Running Tests

GCMS exposes a single NPM script for running the full test suite, plus two helper scripts for triggering email and notification flows manually.

Prerequisites

Before running tests, make sure:

  • All env files are in place, including .env.test (see Environment Configuration)

  • The test database referenced by .env.test exists and is reachable

  • npm install has been run to install Jest and its dependencies

Available scripts

npm test

Runs every test file in new_tests/ with coverage reporting, after resetting the test database from scratch.

npm test

The script does three things in order:

  1. cross-env NODE_ENV=test node node_scripts/install_test_db.js — drops and recreates the test database, then applies db/schema.sql and db/seed_dev.sql

  2. cross-env NODE_ENV=test — sets the environment so that app.js loads .env.test and installs the auth bypass middleware

  3. jest --runInBand --coverage — runs the test files sequentially (avoiding database contention) and generates a coverage report, excluding model files, scripts, and static client-side code from coverage measurement

Manual flow scripts

In addition to the automated test suite, two NPM scripts trigger specific real-world flows for manual verification. These do not run under Jest and do not produce a coverage report.

npm run email:test

Runs testEmail.mjs, which sends a sample notification email via Mailtrap using the credentials in .env.mailconfig. Use this to verify that SMTP credentials are valid without going through the full notification trigger path.

npm run notification:test

Runs testNotification.mjs, which exercises the end-to-end notification flow including database insertion, Socket.io emission, and email delivery. Useful when validating that all parts of the notification pipeline are wired correctly.

Coverage report

After npm test runs, Jest writes a coverage report to coverage/ in the project root. Open coverage/lcov-report/index.html in a browser for an interactive view of which lines and branches were exercised by the test suite.

Coverage is reported per file and per metric (statements, branches, functions, lines). Aim for at least 80% line coverage on any new controller you add.

The following are intentionally excluded from coverage measurement:

  • models/ — covered by integration tests rather than unit tests

  • node_scripts/ — standalone setup utilities

  • listen.js — single-line server boot

  • public/scripts/ — client-side JS, not in scope for backend testing

Troubleshooting

If tests fail with database errors

The test database may not have been recreated cleanly. Check that DB_URL in .env.test points to a database the current user can drop and recreate (Supabase service role accounts can; local PostgreSQL users may need elevated privileges).

If integration tests hang

Jest may be waiting on an open database connection or socket. If you have added a new integration test, make sure any open resources are closed in an afterAll hook.

If unit tests can’t find a mock

Unit tests use jest.unstable_mockModule() for ESM imports, which must be declared before the dynamic import() of the module under test. See an existing unit test file for the correct pattern.