Testing
Automated Testing
A lot of work went into making some automated tests for the server side. However, the testing approach is fairly non-standard for now. The tests are not all the way end-to-end. They run all the way from the service to the database. So, we really just skipped where the controllers call the services - which is pretty trivial stuff anyway.
Environment
One prerequisite: the local MySQL 8 container must be up (docker compose up -d,
see First time setup). That's it — no env file, no FACILITY, no NODE_ENV.
A test target is not a deployment, so the harness (zf-server/src/testing/)
deliberately bypasses the env-file/ConfigService path. Each jest worker gets its own
throwaway schema (zfm_test_w<n>) on the container, built by running the real,
validated migration chain; suites sharing a worker share the schema but every suite
starts with all tables truncated. Connection values default to the container
(127.0.0.1:3308, root) and can be overridden with TEST_DB_* variables for CI.
The retired way — a hand-made test.env selected via FACILITY=test, pointed at a
benchmark database that a stray synchronize could wreck — is gone, and nothing
mourns it.
Approach to Testing
All the complicated logic lies in the servers and repos, so I test there and test thoroughly. I do not worry about controllers. I just do not understand everyone saying "unit test, unit test" - I'm way too lazy to mock every little thing. Plus, the compile and test cycle is so fast, that there is really no reason to run stuff right to the database.
I number tests with big random(ish) numbers. Rationale is given below.
I also like to run against an empty database. Every test that sticks objects in the database is responsible for taking them out again. Also, every test that sticks objects in the database sticks the test number in the comment field of the object. That way it's easy to figure out what test left what crap lying around the database (by accident).
I tend to use random numbers (converted to strings) for fields that have to be unique (like names or nicknames). This reduces the chances of tests run in parallel to fight with each other. Even more so it takes all the burden of the test writers to keep coming up with uniques stings for things like mutation names.
Running Tests
cd zf-server && npm test, or the WebStorm jest tools — both hit the same
jest.config.ts. The full suite runs in about a minute; expect one skipped suite
(user.service.spec, pending its harness rework). Controllers are no longer entirely
untested, by the way: src/auth/testing/auth-matrix.spec.ts drives real HTTP through
the actual guards to pin the whole role/permission matrix.
Debugging
The webstorm tools are great. They point you at the test case and the code when things go wrong.
Note that the policy of sticking the test number in any object that goes into the database makes it super easy to track down the source of stray objects if test cases fail to clean up after themselves.
The browser walk
The client's unit suite runs in Node, where React Native's web rendering and the browser's own APIs
are stubbed or absent, so a screen can pass every test and still fail to draw in Chrome. The
browser walk is the check for that layer: Playwright signs in to the gui-test.test facility,
visits every screen an admin can reach, opens the first record in each manager, confirms a guest
and a signed-out visitor are turned away where they should be, and fails on any uncaught error,
console error or request the server refused.
cd zf-react-client && npm run e2e # against Metro's bundle, on gui-test.test
cd zf-react-client && npm run e2e:built # exports first, then against the export, on gui-test-built.test
The two differ in what the browser is handed. Metro's bundle is what development runs; the export is what a facility is deployed with, and it goes through different processing, so a fault can live in one and not the other. The built run is the one that counts before a tag; the Metro run is the quick one while working.
Both need the gui_test facility serving, and the built run its second address too (see
Serve a dev facility), and a one-time npx playwright install chromium. The
walk is not part of npm test: run it on demand, and always before a release tag. It lives in
zf-react-client/e2e/; a failed check leaves a trace under e2e/.results/ that
npx playwright show-trace <trace.zip> replays.