Check the tool runtime's own code before it merges, every test on a bus of its own (hq ADR 0237)
A merge-check.sh, the repository's layer of the mesh's merge check (mesh/repo-check): format, vet, and node-tools' suite under the race detector. The tests shared one bus and had to run one package at a time; like the controller's (#97), each now starts a server of its own at the release go.mod pins, held to the catalogue's bus image by a test. What cannot run in the check — bundles that need @novox/mesh-sdk — is said as not tested.
This commit is contained in:
@@ -1,9 +1,13 @@
|
||||
// Package meshtest raises what the controller would, for tests against a real bus: the ASSIGNMENTS
|
||||
// and EVENTS streams, memberships issued by hand, and a fixture's path.
|
||||
//
|
||||
// **The packages share one bus, so run them one at a time: `go test -p 1 ./...`.** Each test raises
|
||||
// the streams afresh, and the console discovers every runtime that announces itself on the bus
|
||||
// (novox/hq ADR 0197) — a runtime from another package's test is, correctly, found.
|
||||
// **Every test has a bus of its own** (novox/hq ADR 0237, the controller's internal/testbus): a server
|
||||
// linked in at the release go.mod pins — the release the mesh runs, held so by a test beside this one —
|
||||
// started for the one test and gone after it. The packages shared one bus and had to run one at a time:
|
||||
// each test raised the streams afresh, and the console discovers every runtime that announces itself on
|
||||
// the bus (ADR 0197), so a runtime from another package's test was, correctly, found. Now the suite runs
|
||||
// as Go runs it, in parallel and under the race detector. A person may still point a run at a bus of
|
||||
// their own with MESH_TEST_NATS_EXTERNAL=1 and MESH_TEST_NATS; then the run is theirs to serialise.
|
||||
package meshtest
|
||||
|
||||
import (
|
||||
@@ -12,24 +16,57 @@ import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/nats-io/nats-server/v2/server"
|
||||
"github.com/nats-io/nats.go"
|
||||
|
||||
"github.com/novox/mesh-tools/node-tools/internal/bus"
|
||||
)
|
||||
|
||||
// URL is the test bus, or the test is skipped.
|
||||
// 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.T) string {
|
||||
t.Helper()
|
||||
url := os.Getenv("MESH_TEST_NATS")
|
||||
if url == "" {
|
||||
t.Skip("MESH_TEST_NATS unset")
|
||||
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)
|
||||
}
|
||||
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")
|
||||
}
|
||||
url := s.ClientURL()
|
||||
buses.Store(t, url)
|
||||
t.Cleanup(func() {
|
||||
buses.Delete(t)
|
||||
s.Shutdown()
|
||||
s.WaitForShutdown()
|
||||
})
|
||||
return url
|
||||
}
|
||||
|
||||
// buses are the running tests' buses, by test.
|
||||
var buses sync.Map
|
||||
|
||||
// Mesh is the controller's job, done by hand.
|
||||
type Mesh struct {
|
||||
nc *nats.Conn
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
package meshtest
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// **The bus the tests run is the bus the mesh runs** (novox/hq ADR 0227 rule 9, ADR 0237): the server linked
|
||||
// into the tests is held to the release the catalogue's bus image is — read from beside this checkout, as
|
||||
// the build seat clones it for a merge check — and, given 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 go.mod's moves with it.
|
||||
func TestTheBusTheTestsRunIsTheBusTheMeshRuns(t *testing.T) {
|
||||
beside := os.Getenv("MESH_CHECK_BESIDE")
|
||||
if beside == "" {
|
||||
beside = filepath.Join("..", "..", "..", "..")
|
||||
}
|
||||
dockerfile := filepath.Join(beside, "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 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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// Each test is given a bus of its own, and the same one at every ask.
|
||||
func TestEachTestHasABusOfItsOwn(t *testing.T) {
|
||||
var first string
|
||||
t.Run("one", func(t *testing.T) {
|
||||
first = URL(t)
|
||||
if URL(t) != first {
|
||||
t.Fatal("two buses for one test")
|
||||
}
|
||||
})
|
||||
t.Run("two", func(t *testing.T) {
|
||||
if URL(t) == first {
|
||||
t.Fatal("two tests were given one bus")
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user