type
Reference
title
Testing
description
How the suite is organised — unit, repository, template, handler, boot and in-process e2e — and what the boot tests and the RBAC drift test protect.
tags
testing, e2e, boot, drift, go
status
stable
stale_after
Fri Apr 30 2027 02:00:00 GMT+0200 (Central European Summer Time)
generated
techwriter/mimo-v2.6-flash · Sat Oct 31 2026 11:02:38 GMT+0100 (Central European Standard Time)
sources
makefile → Makefile; e2e → tests/e2e_test.go; boot → internal/infrastructure/sqlite/boot_test.go; drift → internal/domain/rbac/drift_test.go; migration → internal/infrastructure/sqlite/migration_test.go; maintest → cmd/server/main_test.go

Testing

make test is go test ./... -count=1; make lint is go vet ./... — and CI runs both plus govulncheck before any image is built. The suite lives beside the code it protects, in layers:

Layer Where What it pins
Domain unit internal/domain/**/*_test.go entities, Role.Has wildcard precedence, settings document round-trips, audit entry shape
Application unit internal/application/**/*_test.go use cases with fake repos: money posting/void/transfer/undated input, fee apply/revert, scope resolution, settings validation
Repository internal/infrastructure/sqlite/*_test.go round-trips against file-backed SQLite with the production pragmas: pagination, sorting, list filters, per-prefix numbering, salary/audit/enrollment list pages
Boot boot_test.go the graded plugin lifecycle (below)
Migration migration_test.go the baseline is exactly the two embedded files in order, and schema_migrations reflects them
Template internal/interfaces/templates/parse_test.go every page under web/templates parses and executes against shaped data
Handler internal/interfaces/http/*_test.go per-handler behaviour: auth middleware, RBAC catalogue coverage of route gates, payment/role/student scope, movement detail
E2E tests/ the full HTTP stack, in process

The e2e suite

tests/ boots the complete stack exactly like cmd/server/main.go wires it — same repos, services, middleware order, template funcMap, CSRF middleware and route registration — against a fresh SQLite database per test, and drives real HTTP through the router with a cookie jar. It cannot import cmd/server (a main package), so the wiring is deliberately copied; the cost is that wiring changes must be mirrored, the benefit is that what is tested is what ships.

The flows it walks: auth and password change, user/teacher/parent CRUD, cash boxes, ledger (read-only — the page has no add controls), receipts, transfers, attendance and marks entry, audit, guardians, grade-subject assignment, year expenses, academic-year promotion, enrollment fee inheritance, student pay (full, partial, multi-currency, multi-line, custom expenses), payment edit and void, bulk assignment, salary pay, description types, first-login setup, currency-disable guard, nav highlighting — plus two meta-tests that enumerate every protected GET route authenticated (expect 200) and unauthenticated (expect redirect to login).

The phase2* files are the scope gauntlet: a parent sees only guardian-linked children, a teacher sees only their class roster, a roles save with a bad scope is refused, a payment write at scope own is filtered before pagination, and the roles matrix shows no drift rows.

What the boot tests protect

internal/infrastructure/sqlite/boot_test.go runs bootPlugins over a throwaway database with a fake plugin and proves the graded lifecycle that the architecture promises:

Case Proven behaviour
core plugin, Init fails boot returns an error — a refusal to start, no recorded degrade
non-core plugin, Init fails boot succeeds with one recorded Failure{plugin, stage:init} — the host hides the feature and keeps serving
core plugin, clean migrate + init boots with no error and no failures

cmd/server/main_test.go complements it with a fake provisioner proving provisionAccess hands the store exactly rbacdomain.DefaultRoles() and nothing else — the stand-in for a future access plugin.

What the drift test protects

internal/domain/rbac/drift_test.go maintains a hand-written, literal list of the resources and action triples the routes_*.go files actually gate, and asserts it bidirectionally against both Catalogue() and the Resource* constants. Add a route gate without updating the catalogue (or vice versa) and the test names the drift. The http package has the matching half (rbac_catalogue_test.go): every Require argument must be a catalogue resource. This is what makes the 22-resource table in RBAC a checked fact rather than a description.

The plugin conformance kit — where it is

The settings-contract conformance kit (nxtest in nx-common: the rule that a plugin's Fields(), its struct fields and its Put keys agree) is not referenced anywhere in this repository — it lives in nx-common and runs against the plugins themselves, not against the host. From this repo's side, the guarantees are the boot tests above plus the e2e coverage of the register's surfaces (pay, void, ledger, boxes, close, transfers). The contract it enforces is documented in settings contract.

Rendered from simple-lms/testing.md · OKF bundle · regenerate with npm run render