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.testexists and is reachablenpm installhas 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:
cross-env NODE_ENV=test node node_scripts/install_test_db.js— drops and recreates the test database, then appliesdb/schema.sqlanddb/seed_dev.sqlcross-env NODE_ENV=test— sets the environment so thatapp.jsloads.env.testand installs the auth bypass middlewarejest --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 testsnode_scripts/— standalone setup utilitieslisten.js— single-line server bootpublic/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.