Merge pull request 'Phase 5 (3/4): every test on a bus of its own, at the release the mesh runs (hq ADR 0237)' (#97) from feat/a-suite-that-cannot-flake into main
mesh/delivery delivered

This commit was merged in pull request #97.
This commit is contained in:
2026-10-06 19:19:41 +00:00
627 changed files with 437174 additions and 166 deletions
+4 -7
View File
@@ -2,24 +2,21 @@ package broker
import (
"fmt"
"os"
"testing"
"time"
"github.com/nats-io/nats.go"
"github.com/novox/mesh-controller/internal/testbus"
)
// A consumer that reacts to announcements, made on a stream that keeps a week of them, starts from now:
// made from the start it replays every merge and build of that week (novox/hq issue 248). And a reset
// re-makes a stuck one from now, its configuration otherwise kept, and refuses a work queue.
//
// docker run -d --rm --name t -p 14231:4222 nats:2.10-alpine -js
// MESH_TEST_NATS=nats://127.0.0.1:14231 go test ./internal/broker/ -run TestAConsumerMadeFromNow
// go test ./internal/broker/ -run TestAConsumerMadeFromNow (each test on a bus of its own: internal/testbus)
func TestAConsumerMadeFromNowNeverReplaysTheStreamsHistory(t *testing.T) {
url := os.Getenv("MESH_TEST_NATS")
if url == "" {
t.Skip("MESH_TEST_NATS unset")
}
url := testbus.URL(t)
js, err := Dial(url)
if err != nil {
t.Fatal(err)
+5 -11
View File
@@ -2,12 +2,13 @@ package broker
import (
"fmt"
"os"
"strings"
"testing"
"time"
"github.com/nats-io/nats.go"
"github.com/novox/mesh-controller/internal/testbus"
)
const twoSeconds = 2 * time.Second
@@ -17,13 +18,9 @@ const twoSeconds = 2 * time.Second
// so bringing one to match must replace it — and must not replay what it already acknowledged
// (novox/hq 04-ISSUES/156).
//
// docker run -d --rm --name t -p 14231:4222 nats:2.10-alpine -js
// MESH_TEST_NATS=nats://127.0.0.1:14231 go test ./internal/broker/ -run TestUpgrading
// go test ./internal/broker/ -run TestUpgrading (each test on a bus of its own: internal/testbus)
func TestUpgradingAConsumerWhoseDeliverySubjectMoved(t *testing.T) {
url := os.Getenv("MESH_TEST_NATS")
if url == "" {
t.Skip("MESH_TEST_NATS unset")
}
url := testbus.URL(t)
js, err := Dial(url)
if err != nil {
t.Fatal(err)
@@ -133,10 +130,7 @@ func TestUpgradingAConsumerWhoseDeliverySubjectMoved(t *testing.T) {
// not undo. The controller's own two consumers are in exactly this position: it asserts them before
// it subscribes.
func TestAConsumerNothingIsBoundToDoesMove(t *testing.T) {
url := os.Getenv("MESH_TEST_NATS")
if url == "" {
t.Skip("MESH_TEST_NATS unset")
}
url := testbus.URL(t)
js, err := Dial(url)
if err != nil {
t.Fatal(err)
+4 -7
View File
@@ -1,8 +1,9 @@
package broker
import (
"os"
"testing"
"github.com/novox/mesh-controller/internal/testbus"
)
// Against a real server, because the questions here are all "does the server accept this" —
@@ -10,13 +11,9 @@ import (
//
// Skipped unless MESH_TEST_NATS names one, so the ordinary suite stays fast and offline:
//
// docker run -d --rm --name t -p 14222:4222 nats:2.10-alpine -js
// MESH_TEST_NATS=nats://127.0.0.1:14222 go test ./internal/broker/ -run TestAgainstARealServer
// go test ./internal/broker/ -run TestAgainstARealServer (each test on a bus of its own: internal/testbus)
func TestAgainstARealServer(t *testing.T) {
url := os.Getenv("MESH_TEST_NATS")
if url == "" {
t.Skip("MESH_TEST_NATS unset")
}
url := testbus.URL(t)
js, err := Dial(url)
if err != nil {
t.Fatal(err)
+4 -7
View File
@@ -1,10 +1,11 @@
package broker
import (
"os"
"testing"
"github.com/nats-io/nats.go"
"github.com/novox/mesh-controller/internal/testbus"
)
// Raising the bus's objects against a real server.
@@ -14,15 +15,11 @@ import (
// could answer: that asserting twice changes nothing, and that a consumer really is bound to the one
// subject its node is allowed to read.
//
// docker run -d --rm --name t -p 14227:4222 nats:2.10-alpine -js
// MESH_TEST_NATS=nats://127.0.0.1:14227 go test ./internal/broker/ -run TestRaising
// go test ./internal/broker/ -run TestRaising (each test on a bus of its own: internal/testbus)
func aLiveBus(t *testing.T) *JetStream {
t.Helper()
url := os.Getenv("MESH_TEST_NATS")
if url == "" {
t.Skip("MESH_TEST_NATS unset")
}
url := testbus.URL(t)
js, err := Dial(url)
if err != nil {
t.Fatal(err)
+5 -11
View File
@@ -1,11 +1,12 @@
package broker
import (
"os"
"testing"
"time"
"github.com/nats-io/nats.go"
"github.com/novox/mesh-controller/internal/testbus"
)
// A seat's worker that changed from push to pull delivery strands a holder built for the new shape
@@ -14,13 +15,9 @@ import (
// shape, type included: on a work queue it re-makes one of the wrong type, losing nothing, and a
// pull subscription then binds and takes what was pending.
//
// docker run -d --rm --name t -p 14231:4222 nats:2.10-alpine -js
// MESH_TEST_NATS=nats://127.0.0.1:14231 go test ./internal/broker/ -run TestAWorker
// go test ./internal/broker/ -run TestAWorker (each test on a bus of its own: internal/testbus)
func TestAWorkerOfTheWrongTypeIsRemadeOnAWorkQueueAndAPullThenBinds(t *testing.T) {
url := os.Getenv("MESH_TEST_NATS")
if url == "" {
t.Skip("MESH_TEST_NATS unset")
}
url := testbus.URL(t)
js, err := Dial(url)
if err != nil {
t.Fatal(err)
@@ -112,10 +109,7 @@ func TestAWorkerOfTheWrongTypeIsRemadeOnAWorkQueueAndAPullThenBinds(t *testing.T
// re-making it from the start would replay what it acknowledged (novox/hq issue 156), and leaving it
// for a hand re-made it exactly that way on 2026-10-03 (issue 207).
func TestAWorkerOfTheWrongTypeOnAHistoryStreamIsRemadeFromNowOn(t *testing.T) {
url := os.Getenv("MESH_TEST_NATS")
if url == "" {
t.Skip("MESH_TEST_NATS unset")
}
url := testbus.URL(t)
js, err := Dial(url)
if err != nil {
t.Fatal(err)
+3 -5
View File
@@ -2,13 +2,14 @@ package conditions
import (
"context"
"os"
"testing"
"time"
"github.com/nats-io/nats.go/jetstream"
"github.com/novox/mesh-controller/internal/broker"
"github.com/novox/mesh-controller/internal/testbus"
)
// The condition store against a real server: compare-and-set, an unreadable store refused, and the
@@ -16,10 +17,7 @@ import (
func busStoreForTest(t *testing.T) (*broker.JetStream, Backend, History) {
t.Helper()
url := os.Getenv("MESH_TEST_NATS")
if url == "" {
t.Skip("MESH_TEST_NATS unset")
}
url := testbus.URL(t)
js, err := broker.Dial(url)
if err != nil {
t.Fatal(err)
+11 -8
View File
@@ -69,16 +69,19 @@ func TestAWaiterPinsNoConnectionAndGivesUp(t *testing.T) {
done <- err
}()
}
// While they wait, the pool lends nothing to them for longer than a look.
pinned := 0
for range 10 {
time.Sleep(60 * time.Millisecond)
if n := int(pool.Stat().AcquiredConns() - base); n > pinned {
pinned = n
// While they wait, the pool lends nothing to them for longer than a look: **pinned is held throughout**,
// so at some moment of the wait fewer than all of them hold one. Asked of the least seen over many
// looks, not of the most: a look that lands while every waiter is mid-query is a busy machine, not a
// pinned connection, and on a loaded one (the suite's packages at once, the race detector) it did land.
least := waiters
for range 100 {
time.Sleep(10 * time.Millisecond)
if n := int(pool.Stat().AcquiredConns() - base); n < least {
least = n
}
}
if pinned >= waiters {
t.Fatalf("%d connections were held by %d waiters", pinned, waiters)
if least >= waiters {
t.Fatalf("%d waiters held %d connections at every look across their wait", waiters, least)
}
for range waiters {
if err := <-done; !errors.Is(err, ErrNodeBusy) || !strings.Contains(err.Error(), "anchor") {
+49
View File
@@ -0,0 +1,49 @@
package inventory
import (
"encoding/json"
"os"
"strings"
"testing"
)
// **The store the tests run is the store the mesh runs** (novox/hq ADR 0227 rule 9, ADR 0237). In a merge
// check the build seat raises the store of the major release the facts snapshot says the mesh's store
// runs; this holds the two equal there, so a test passing here is a test passing on that release. Without
// a snapshot — on a workstation — it says what it ran against and compares nothing.
func TestTheStoreTheTestsRunIsTheStoreTheMeshRuns(t *testing.T) {
inv := ForTest(t)
ran, err := inv.ServerVersionForTest(t)
if err != nil {
t.Fatal(err)
}
path := os.Getenv("MESH_FACTS")
if path == "" {
t.Logf("the tests ran the store at %s; with no MESH_FACTS, nothing to compare it with", ran)
return
}
body, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
var f struct {
Versions struct {
Store string `json:"store"`
} `json:"versions"`
}
if err := json.Unmarshal(body, &f); err != nil {
t.Fatal(err)
}
major := func(v string) string { m, _, _ := strings.Cut(strings.TrimSpace(v), "."); return m }
if f.Versions.Store != "" && major(ran) != major(f.Versions.Store) {
t.Errorf("the tests ran the store at %s and the mesh's store runs %s", ran, f.Versions.Store)
}
}
// ServerVersionForTest is the store's own word for its release.
func (i *Inventory) ServerVersionForTest(t *testing.T) (string, error) {
t.Helper()
var v string
err := i.store.Pool().QueryRow(t.Context(), `show server_version`).Scan(&v)
return v, err
}
+6 -11
View File
@@ -4,13 +4,14 @@ import (
"context"
"errors"
"fmt"
"os"
"sync"
"testing"
"time"
"github.com/nats-io/nats.go"
"github.com/nats-io/nats.go/jetstream"
"github.com/novox/mesh-controller/internal/testbus"
)
// Two controllers at once, on a real bus (novox/hq to-be 45 §6, replay R1's half that lives here):
@@ -18,7 +19,7 @@ import (
// stops renewing is taken over once its key expires, and has stopped acting before then; a renewal
// refused is a loss at once; and a bucket raised again from nothing never issues an epoch twice.
//
// MESH_TEST_NATS=nats://127.0.0.1:14222 go test ./internal/lease/
// go test ./internal/lease/ (each test on a bus of its own: internal/testbus)
// The bounds in these tests are the design's divided by five, so the cases run in seconds.
const (
@@ -30,10 +31,7 @@ const (
// aBucket is a fresh lease bucket on the test bus, with the test's age.
func aBucket(t *testing.T) (jetstream.JetStream, string) {
t.Helper()
url := os.Getenv("MESH_TEST_NATS")
if url == "" {
t.Skip("MESH_TEST_NATS unset")
}
url := testbus.URL(t)
conn, err := nats.Connect(url)
if err != nil {
t.Fatal(err)
@@ -55,7 +53,7 @@ func aBucket(t *testing.T) (jetstream.JetStream, string) {
// aController is one controller instance's lease over the bucket, on a connection of its own.
func aController(t *testing.T, bucket, name string, floor func(context.Context) (uint64, error)) *Lease {
t.Helper()
conn, err := nats.Connect(os.Getenv("MESH_TEST_NATS"))
conn, err := nats.Connect(testbus.URL(t))
if err != nil {
t.Fatal(err)
}
@@ -275,10 +273,7 @@ func TestAnEpochIsNeverIssuedTwiceWhenTheBucketStartsOver(t *testing.T) {
}
func TestALeaseBucketWithoutAnAgeIsRefused(t *testing.T) {
url := os.Getenv("MESH_TEST_NATS")
if url == "" {
t.Skip("MESH_TEST_NATS unset")
}
url := testbus.URL(t)
conn, err := nats.Connect(url)
if err != nil {
t.Fatal(err)
+3 -4
View File
@@ -8,6 +8,8 @@ import (
"time"
"github.com/nats-io/nats.go"
"github.com/novox/mesh-controller/internal/testbus"
)
// **A reader's own consumer gone is not a consumer lost**: every bucket watch and stream read-back
@@ -64,10 +66,7 @@ func TestNatsARefusedQuestionIsSaidAtOnce(t *testing.T) {
// The refusal reaches whoever waits on that subject, and only them.
func TestNatsARefusalReachesItsWaiter(t *testing.T) {
url := os.Getenv("MESH_TEST_NATS")
if url == "" {
t.Skip("MESH_TEST_NATS unset")
}
url := testbus.URL(t)
conn, err := nats.Connect(url)
if err != nil {
t.Fatal(err)
+5 -8
View File
@@ -5,7 +5,6 @@ import (
"encoding/json"
"io"
"log"
"os"
"sync"
"testing"
"time"
@@ -13,6 +12,8 @@ import (
"github.com/nats-io/nats.go"
"github.com/novox/mesh-controller/internal/broker"
"github.com/novox/mesh-controller/internal/testbus"
)
// A build, end to end, against a real server.
@@ -22,15 +23,11 @@ import (
// catalogue places it. On the old bus that fan-out came from a shared exchange, and it would be easy
// to write a version where only the asker hears it and nobody notices for weeks.
//
// docker run -d --rm --name t -p 14230:4222 nats:2.10-alpine -js
// MESH_TEST_NATS=nats://127.0.0.1:14230 go test ./internal/link/ -run TestNatsABuild
// go test ./internal/link/ -run TestNatsABuild (each test on a bus of its own: internal/testbus)
func aBusWithTheBuildRole(t *testing.T) *broker.JetStream {
t.Helper()
url := os.Getenv("MESH_TEST_NATS")
if url == "" {
t.Skip("MESH_TEST_NATS unset")
}
url := testbus.URL(t)
js, err := broker.Dial(url)
if err != nil {
t.Fatal(err)
@@ -349,7 +346,7 @@ func TestNatsAMachineOnTheRetiredBuildRoleTakesThatRolesAsks(t *testing.T) {
})
}()
asker, err := BuildsOverNATSOn(os.Getenv("MESH_TEST_NATS"), TheBuildMachineBefore)
asker, err := BuildsOverNATSOn(testbus.URL(t), TheBuildMachineBefore)
if err != nil {
t.Fatal(err)
}
+6 -15
View File
@@ -3,12 +3,13 @@ package link
import (
"context"
"encoding/json"
"os"
"strings"
"testing"
"time"
"github.com/nats-io/nats.go"
"github.com/novox/mesh-controller/internal/testbus"
)
// **Both implementations, one fixture.** The point of the seam is not that the transport can be
@@ -17,13 +18,9 @@ import (
//
// Against a real server, because what the fixture pins is what reaches the wire:
//
// docker run -d --rm --name t -p 14222:4222 nats:2.10-alpine -js
// MESH_TEST_NATS=nats://127.0.0.1:14222 go test ./internal/link/ -run TestTheNatsBus
// go test ./internal/link/ -run TestTheNatsBus (each test on a bus of its own: internal/testbus)
func TestTheNatsBusEmitsTheEnvelopeTheFixturePins(t *testing.T) {
url := os.Getenv("MESH_TEST_NATS")
if url == "" {
t.Skip("MESH_TEST_NATS unset")
}
url := testbus.URL(t)
f := loadFixture(t, "events/module-event.json")
conn, err := nats.Connect(url)
@@ -99,10 +96,7 @@ func TestAnEventsSubjectIsDerivedFromItsSource(t *testing.T) {
// tool is slow" is the first thing a person asking wants, and waiting out the timeout to say it
// is how a fast answer becomes a slow non-answer.
func TestAskingAToolNobodyServesFailsAtOnce(t *testing.T) {
url := os.Getenv("MESH_TEST_NATS")
if url == "" {
t.Skip("MESH_TEST_NATS unset")
}
url := testbus.URL(t)
conn, err := nats.Connect(url)
if err != nil {
t.Fatal(err)
@@ -125,10 +119,7 @@ func TestAskingAToolNobodyServesFailsAtOnce(t *testing.T) {
// And a served tool answers, with no reply queue to declare and no correlation to check.
func TestAskingAServedToolAnswers(t *testing.T) {
url := os.Getenv("MESH_TEST_NATS")
if url == "" {
t.Skip("MESH_TEST_NATS unset")
}
url := testbus.URL(t)
conn, err := nats.Connect(url)
if err != nil {
t.Fatal(err)
+4 -7
View File
@@ -1,11 +1,12 @@
package link
import (
"os"
"testing"
"time"
"github.com/nats-io/nats.go"
"github.com/novox/mesh-controller/internal/testbus"
)
// **Verified 2026-09-27**: the caller asked for a reply to `_INBOX.LCr3M83q…` and the consumer
@@ -18,13 +19,9 @@ import (
// enrolment design rests on it — the reply subject travels in the payload instead — so it is
// checked rather than believed.
//
// docker run -d --rm --name t -p 14222:4222 nats:2.10-alpine -js
// MESH_TEST_NATS=nats://127.0.0.1:14222 go test ./internal/link/ -run TestAReplyAddress
// go test ./internal/link/ -run TestAReplyAddress (each test on a bus of its own: internal/testbus)
func TestAReplyAddressDoesNotSurviveAStream(t *testing.T) {
url := os.Getenv("MESH_TEST_NATS")
if url == "" {
t.Skip("MESH_TEST_NATS unset")
}
url := testbus.URL(t)
conn, err := nats.Connect(url)
if err != nil {
t.Fatal(err)
+4 -7
View File
@@ -6,7 +6,6 @@ import (
"errors"
"io"
"log"
"os"
"sync"
"testing"
"time"
@@ -14,6 +13,8 @@ import (
"github.com/nats-io/nats.go"
"github.com/novox/mesh-controller/internal/broker"
"github.com/novox/mesh-controller/internal/testbus"
)
// The consume side against a real server, because what is being checked is what the server does.
@@ -23,15 +24,11 @@ import (
// to an address carried in the payload reaches a caller waiting on its own inbox. Each is a claim
// about a server, so each is asked of one:
//
// docker run -d --rm --name t -p 14222:4222 nats:2.10-alpine -js
// MESH_TEST_NATS=nats://127.0.0.1:14222 go test ./internal/link/ -run TestNats
// go test ./internal/link/ -run TestNats (each test on a bus of its own: internal/testbus)
func aBus(t *testing.T) *broker.JetStream {
t.Helper()
url := os.Getenv("MESH_TEST_NATS")
if url == "" {
t.Skip("MESH_TEST_NATS unset")
}
url := testbus.URL(t)
js, err := broker.Dial(url)
if err != nil {
t.Fatal(err)
+3 -2
View File
@@ -3,11 +3,12 @@ package link
import (
"context"
"encoding/json"
"os"
"testing"
"time"
"github.com/novox/mesh-controller/internal/broker"
"github.com/novox/mesh-controller/internal/testbus"
)
// novox/hq issue 213: while a machine hands its controller over, the new controller (the process)
@@ -29,7 +30,7 @@ func TestNatsASecondControllerStandsByAndTakesOverWhenTheFirstLetsGo(t *testing.
})
// The new one, on a connection of its own as the process would have.
second, err := broker.Dial(os.Getenv("MESH_TEST_NATS"))
second, err := broker.Dial(testbus.URL(t))
if err != nil {
t.Fatal(err)
}
+77
View File
@@ -0,0 +1,77 @@
// Package testbus is the bus a test runs against: a server of its own, of the release the mesh runs,
// started for the one test and gone after it.
//
// **Why one per test.** The live tests reached one shared bus, and on it they assert, read and remove the
// mesh's own objects — streams and consumers with fixed names, because those names are the mesh's. Two
// packages at once was one deleting a consumer the other was reading through, and a test left a stream
// behind for the next to trip on: the suite passed one package at a time and failed or hung run as Go
// runs it, which taught everybody to read a red suite as noise. A server per test makes every test start
// from nothing and leave nothing, so the suite is the same run in any order, at any parallelism, under
// the race detector, on a workstation or on the build seat.
//
// **Why this release.** The server is linked in at the version go.mod pins, and a test beside this one
// holds that pin to the release the mesh runs — the catalogue's bus image, and the facts snapshot's when
// a merge check has one (novox/hq ADR 0227 rule 9: a version the mesh runs is the one its tests run).
// A bus the 2.10 line skipped messages on (issue 266) would have been caught by the tests that ran on it,
// had they run on it.
//
// A person may still point a run at a bus of their own — another release, a lab's — with
// MESH_TEST_NATS_EXTERNAL=1 and MESH_TEST_NATS; then the run is theirs to serialise.
package testbus
import (
"os"
"sync"
"testing"
"time"
"github.com/nats-io/nats-server/v2/server"
)
// Version is the release of the server the tests run.
const Version = server.VERSION
// URL is the bus of this test alone: started at its first ask, the same one at every ask after — a
// test that dials twice, or hands the address to the code it tests, reaches one bus — and shut down
// when the test ends.
func URL(t testing.TB) string {
t.Helper()
if os.Getenv("MESH_TEST_NATS_EXTERNAL") == "1" {
if url := os.Getenv("MESH_TEST_NATS"); url != "" {
return url
}
t.Fatal("MESH_TEST_NATS_EXTERNAL=1 and MESH_TEST_NATS names no bus")
}
if url, ok := buses.Load(t); ok {
return url.(string)
}
url := Start(t).ClientURL()
buses.Store(t, url)
t.Cleanup(func() { buses.Delete(t) })
return url
}
// buses are the running tests' buses, by test.
var buses sync.Map
// Start is a server with JetStream, for this test alone: on loopback, on a port the system chooses, its
// store in the test's own directory.
func Start(t testing.TB) *server.Server {
t.Helper()
opts := &server.Options{Host: "127.0.0.1", Port: server.RANDOM_PORT, JetStream: true, StoreDir: t.TempDir(),
NoLog: true, NoSigs: true}
s, err := server.NewServer(opts)
if err != nil {
t.Fatalf("a bus for this test could not be made: %v", err)
}
go s.Start()
if !s.ReadyForConnections(30 * time.Second) {
s.Shutdown()
t.Fatal("a bus for this test did not come up within 30s")
}
t.Cleanup(func() {
s.Shutdown()
s.WaitForShutdown()
})
return s
}
+96
View File
@@ -0,0 +1,96 @@
package testbus
import (
"os"
"path/filepath"
"regexp"
"strings"
"testing"
"encoding/json"
"github.com/nats-io/nats.go"
)
// **The bus the tests run is the bus the mesh runs** (novox/hq ADR 0227 rule 9, to-be 45 §9). The server
// linked into the tests is held to the release the catalogue's bus image is — read from beside this
// checkout — and, in a merge check that has the facts snapshot, to the release the mesh's bus server says
// it runs. A bus upgrade moves the catalogue's pin; this then fails until the tests' pin moves with it,
// so the controller is never tested against a bus the mesh no longer runs, or not yet.
func TestTheBusTheTestsRunIsTheBusTheMeshRuns(t *testing.T) {
dockerfile := filepath.Join("..", "..", "..", "mesh-catalog", "modules", "nats", "Dockerfile")
raw, err := os.ReadFile(dockerfile)
switch {
case err == nil:
pinned := regexp.MustCompile(`upstream: nats ([0-9.]+)-alpine`).FindSubmatch(raw)
if pinned == nil {
t.Fatalf("%s says no release its digest is", dockerfile)
}
if string(pinned[1]) != Version {
t.Errorf("the tests run bus %s and the catalogue's bus image is %s: move go.mod's nats-server pin "+
"(and `go mod vendor`) with the image", Version, pinned[1])
}
case os.IsNotExist(err):
t.Logf("the catalogue is not beside this checkout, so its bus image is not compared")
default:
t.Fatal(err)
}
path := os.Getenv("MESH_FACTS")
if path == "" {
t.Logf("no MESH_FACTS: the bus the mesh runs is compared in a merge check, which has it")
return
}
body, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
// Read as little of the snapshot as this needs (internal/facts holds the whole shape).
var f struct {
Taken string `json:"taken"`
Versions struct {
Bus string `json:"bus"`
} `json:"versions"`
}
if err := json.Unmarshal(body, &f); err != nil {
t.Fatal(err)
}
if f.Versions.Bus != "" && strings.TrimPrefix(f.Versions.Bus, "v") != Version {
t.Errorf("the tests run bus %s and the mesh's bus runs %s (facts of %s)", Version, f.Versions.Bus, f.Taken)
}
}
// A test's bus is its own: what one test makes, the next never sees.
func TestEveryTestHasABusOfItsOwn(t *testing.T) {
seen := map[string]bool{}
for i := 0; i < 2; i++ {
t.Run("one", func(t *testing.T) {
url := URL(t)
if seen[url] {
t.Fatalf("two tests were handed %s", url)
}
seen[url] = true
conn, err := nats.Connect(url)
if err != nil {
t.Fatal(err)
}
defer conn.Close()
js, err := conn.JetStream()
if err != nil {
t.Fatal(err)
}
if _, err := js.StreamInfo("EVENTS"); err == nil {
t.Fatal("a stream an earlier test made is here")
}
if _, err := js.AddStream(&nats.StreamConfig{Name: "EVENTS", Subjects: []string{"mesh.>"}}); err != nil {
t.Fatal(err)
}
})
}
// Asked twice, one test reaches one bus.
if a, b := URL(t), URL(t); a != b {
t.Errorf("one test was handed two buses: %s and %s", a, b)
}
if v := strings.Count(Version, "."); v != 2 {
t.Errorf("the server's release reads %q", Version)
}
}