Files
mesh-controller/vendor/github.com/nats-io/nats.go/TESTING.md
T
jochen e1f5d4fdf0 Vendor every dependency, so no build fetches the host's validator (hq to-be 45 D1)
The controller imports mesh-host/validate through a replace onto the forge
that holds it, and every build — the build agent's go build in a fresh
toolchain container, the Dockerfile's go mod download — would have fetched
it through the public proxy and checksum database at build time: a merge
breaking main on the network, the class Phase 1 removes. vendor/ is
committed; go builds from it with nothing fetched, and refuses to build
when it and go.mod disagree, so a pin moved without go mod vendor fails at
once. The Dockerfile copies vendor/ and builds with GOPROXY=off.
2026-10-06 10:29:10 +02:00

215 lines
8.7 KiB
Markdown

# Testing
## TL;DR
```shell
make test # full race-enabled suite, no docker
make test T=TestName PKG=./test/... # iterate on a single test
```
## How the suite is set up
The repo uses two module files: `go.mod` for production (minimal dependencies)
and `go_test.mod` for testing. Always pass `-modfile=go_test.mod` to `go test`;
the make targets below do this for you.
Unit tests live at the repo root (package `nats`, white-box) and run with plain
`go test`. Integration tests live in `./test`, `./jetstream/test`, and
`./micro/test`; they bring up real NATS servers by talking to a **tester**
service, which spawns `nats-server` instances, clusters, and super-clusters on
demand. Tests drive it through the `github.com/synadia-io/orbit.go/ntf-client`
package.
There are two ways to run that tester, and tests reach both the same way:
- **In-process (the default)** — a `TestMain` starts
`github.com/synadia-io/orbit.go/ntf` inside each test binary. No docker, no
setup, nothing to tear down.
- **Out-of-process** — the `synadia/ntf-server` docker image, located via the
`TESTER_NATS_URL` environment variable. This is what CI uses.
`TESTER_NATS_URL` always wins: set it and the tests talk to that tester instead
of starting their own. The `make test` targets clear it so an exported value
left over in your shell cannot silently redirect them, but a raw `go test`
inherits it — unset it if a plain `go test` tries to reach a container you did
not intend.
The two paths do **not** necessarily run the same server build: in-process links
whatever `nats-server` `orbit.go/ntf` depends on, while the container ships its
own (pinned in `.github/workflows/ci.yaml`). To stop CI drifting onto the wrong
one, `TestMain` refuses to start the in-process tester when `GITHUB_ACTIONS` is
set but `TESTER_NATS_URL` is not. `GITHUB_ACTIONS` rather than the more general
`CI` because devcontainers and other runners set that one too, and they have no
reason to be forced onto a container.
That guard only catches a missing URL, not a version difference, so the first
test to reach the tester prints the server it got:
```text
tester at nats://localhost:62354: nats-server 2.14.5
```
`go test` shows this whenever the package is run with `-v` (all CI steps are) or
when a test in it fails. If a failure reproduces locally but not in CI, or the
reverse, compare that line against the image tag in `ci.yaml` first.
## In-process mode (default, no docker)
```shell
make test # everything
make test T=TestSubSubject PKG=./test/... # one test, verbose
make test PKG=./jetstream/test/... # one package
make test-norace # NoRace tests (race detector off)
```
Equivalent to:
```shell
go test -modfile=go_test.mod -tags=internal_testing -race -p=1 ./... --failfast -vet=off
```
Because the tester and the `nats-server` instances it spawns run inside the test
binary, `-race` instruments the server too: expect somewhat slower runs, and note
that a data race in `nats-server` surfaces here as a nats.go test failure.
This is also why `go_test.mod` declares `go 1.26.0` while `go.mod` stays at
`1.25.0` — `orbit.go/ntf` requires 1.26, and merely requiring a module raises the
floor for the whole test module. Production builds are unaffected; 1.26 is the
floor for the CI test matrix.
## Testing against a different nats-server
The in-process tester links `nats-server` as an ordinary Go dependency, so
pointing it at another branch, tag, commit, or local checkout is a module
operation — no docker, no image build:
```shell
make server-replace V=main # nats-server main
make server-replace V=v2.13.0 # a specific tag
make server-replace V=/path/to/your/nats-server # a local checkout
make test # run against it
make server-replace-drop # undo
```
Verify what you are actually running:
```shell
go list -m -modfile=go_test.mod github.com/nats-io/nats-server/v2
```
**Do not use `go get` for this.** A branch pseudo-version is derived from the
last reachable tag, so `@main` can sort *below* the release `orbit.go/ntf`
requires. `go get` resolves that conflict by removing ntf from the module graph,
which silently leaves you with no in-process tester:
```text
go: downgraded github.com/nats-io/nats-server/v2 => v2.14.1-0.2026...
go: removed github.com/synadia-io/orbit.go/ntf
```
A `replace` directive overrides the version outright and has no such failure
mode, which is what `make server-replace` uses.
`go_test.mod` is a tracked file, so the replace must not be committed. Note that
`make server-replace-drop` removes the directive but does **not** fully restore
the file: while the replacement was active, `go mod tidy` recorded the replaced
module's own newer dependencies, and MVS never downgrades them again. The target
warns when this happens; for an exact restore use
`git checkout -- go_test.mod go_test.sum`.
The docker path can also test a different server, but only versions Synadia
publishes as images — this is what the nightly `latest-server.yaml` workflow
uses:
```shell
make tester-up-host TESTER_IMAGE=synadia/ntf-server:nightly-latest-nats-main
make test-docker
```
## Host-side mode (docker, iterating on individual tests)
`make tester-up-host` starts the tester with its ports published, so `go test`
run from your terminal can reach the spawned servers via localhost.
```shell
make tester-up-host
make test-docker T=TestSubSubject PKG=./test/... # one test, verbose
make test-docker PKG=./jetstream/test/... # one package
make test-docker # everything
```
Use the `test-docker` targets, not `test` — plain `make test` ignores the
container and uses the in-process tester.
`make test-docker` wraps the full invocation, which is equivalent to:
```shell
TESTER_NATS_URL=nats://localhost:4222 \
go test -modfile=go_test.mod -tags=internal_testing -race -p=1 ./... --failfast -vet=off
```
`-p=1` is required here: a shared container tester does not tolerate concurrent
CreateServer calls from independent test binaries. The in-process tester is
per-binary and could run packages in parallel — measurably faster — but `-p=1`
is kept deliberately, since more concurrent server churn risks intermittent
connection failures and the wall-clock saving is not worth chasing flakes.
Known caveat: on macOS, docker-proxy races the tester's port handover, so in
heavy suites roughly 5-10% of server creations can fail with
`bind: address already in use`. Rerun the failing test, or use
sibling-container mode for full-suite runs.
## Sibling-container mode (full suite, matches CI)
```shell
make tester-up
make test-tester
make tester-down
```
`make test-tester` runs the whole suite (NoRace pass, then the race-enabled
pass) inside a Go container on the same docker network as the tester — no
published ports, so the docker-proxy race above does not apply. This is the
same shape CI uses, with the tester attached as a service container
(`.github/workflows/ci.yaml`).
## NoRace tests
Tests prefixed `TestNoRace` are guarded by `//go:build !race &&
!skip_no_race_tests` and must run with the race detector off:
`make test-norace`.
## Build tags
- `internal_testing` — exposes internal test hooks from `testing_internal.go`;
required by some tests in `./test`. The make targets set it.
- `skip_no_race_tests` — excludes the NoRace tests from a non-race run (used
by `scripts/cov.sh`).
- `compat` — compatibility tests in `test/compat_test.go`, which connect to an
external NATS server via `NATS_URL`.
- `go1.23` — iterator-based tests in `test/nats_iter_test.go`.
## Coverage
```shell
TESTER_NATS_URL=nats://localhost:4222 ./scripts/cov.sh
```
Merges unit and integration coverage into `acc.out` and opens the HTML report
(CI passes an argument to skip the browser).
## Troubleshooting
- `cannot reach the tester at ...` — `TESTER_NATS_URL` is set but nothing is
answering there. Either start the container (`make tester-up-host`) or unset
the variable to use the in-process tester.
- `TESTER_NATS_URL must be set in CI` — running under GitHub Actions without
`TESTER_NATS_URL`. This guard exists so CI cannot silently swap the pinned
container server for the in-process one, which is a different version. Point
`TESTER_NATS_URL` at a tester, or unset `GITHUB_ACTIONS`.
- Tester container misbehaving — `docker logs -f nats-tester` shows server spawn
and config errors; `docker restart nats-tester` restarts it while keeping
those logs. `make tester-down` removes the container and discards them.
- Updating test-only dependencies: `go mod tidy -modfile=go_test.mod` (never
change the main `go.mod` for test dependencies).