Testing
Features do not ship without tests. The same entry points work locally and in CI.
Principles
Section titled “Principles”- Test-driven development: write or update tests alongside the code, not after. A PR that adds behavior without corresponding test coverage is incomplete.
- Bug fixes start with a test: reproduce the bug in a test first, then fix the code. The test proves the fix and prevents regression.
- Same entry point everywhere:
make test-allruns locally and in CI — no divergence - Keep it simple: standard library
testingpackage for Go, Playwright for e2e - No mocking the system under test: integration tests use real Postgres; e2e tests use the real stack with Zabbix, core, and all services
Test Tiers
Section titled “Test Tiers”| Tier | Command | What it tests | Dependencies |
|---|---|---|---|
| Unit | make test-unit | Go packages in isolation — pure logic, no external deps | Go toolchain |
| Integration | make test-integration | Go service layer against Omniglass-owned schemas in Postgres | Go + Postgres |
| E2E | make test-e2e | Full stack from the outside — smoke checks then feature tests | Docker, Bun, Playwright |
| All | make test-all | Unit + e2e | All of the above |
Unit tests
Section titled “Unit tests”Standard Go tests. No external dependencies. Run fast.
make test-unitTest pure logic: validation, parsing, data transforms. If a function doesn’t touch the network or database, it belongs here.
Integration tests
Section titled “Integration tests”Tests that require a running Postgres instance with the Omniglass schema. Guarded by a build tag. These validate SQL queries, constraint behavior, transaction logic, and service-layer operations against Omniglass-owned tables.
make test-integration# or directly:cd components/core && go test -tags=integration -count=1 ./...Integration tests are gated by //go:build integration at the top of the test file. They are excluded from make test-unit and included only when explicitly requested. They do not require Zabbix server or any service other than Postgres.
E2E tests
Section titled “E2E tests”Full-stack tests using Playwright. Start the entire Compose stack (Postgres, Zabbix server, Zabbix web, core, nodered, gateway), run tests against the live system, tear down.
make test-e2e# optional: filter by test namemake test-e2e GREP="login"E2e tests live in tests/e2e/specs/ and run in alphabetical order. Smoke checks are prefixed 00-* through 04-* so they run first:
| Phase | Specs | What they verify |
|---|---|---|
| Smoke | 00-health-check through 04-vanilla | Stack boots, containers healthy, login works, each page renders, Node-RED loads |
| Features | system-types, systems-reconciliation, etc. | CRUD lifecycles, API correctness, cross-service behavior |
If smoke fails, there is no point running feature tests — the stack is broken.
Writing Tests
Section titled “Writing Tests”Unit tests
Section titled “Unit tests”- Place
_test.gofiles alongside the code they test - Use the standard
testingpackage - No Go test frameworks (no testify, no ginkgo, no gomock)
- Table-driven tests where appropriate
- If you need a helper, write a plain function in
_test.go
Integration tests
Section titled “Integration tests”- Use the
//go:build integrationbuild tag - Connect to a real Postgres instance via
DATABASE_URLenv var - Use
testdb.Open(t)andtestdb.Truncate(t, db)for shared setup - Each test starts with a clean slate — truncate at the start, not deferred
E2E tests
Section titled “E2E tests”- Browser tests use
{ page }fixture; API tests use{ request }fixture - Smoke specs capture screenshots at key points for visual verification
- Feature specs create test data, verify behavior, and clean up in
finallyblocks - All specs share the same Playwright config and stack lifecycle
CI Integration
Section titled “CI Integration”CI calls the same Makefile targets:
- run: make test-unit- run: make test-e2eNo CI-specific test scripts. No test-specific Docker images. If it passes locally with make test-all, it passes in CI.
What NOT to Do
Section titled “What NOT to Do”- No Go test frameworks: no testify, ginkgo, gomock. Standard library
testingpackage only. (E2E tests use Playwright, which is the right tool for browser automation.) - No mocking Zabbix: integration tests talk to real Postgres; e2e tests talk to the real stack. Don’t mock the system you’re testing against.
- No shipping without tests: a feature PR without tests is incomplete. A bug fix PR without a regression test is incomplete.
- No coverage thresholds: coverage is a tool, not a target. Write meaningful tests.
- No test-specific Docker images: e2e tests use the same images as production.
- No conditional CI branches: the same
run.shandmaketargets run locally and in CI.