diff --git a/merge-check.sh b/merge-check.sh deleted file mode 100644 index 46acd95..0000000 --- a/merge-check.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/sh -# mesh-check-toolchain: go -# -# The tool runtime's own check (novox/hq ADR 0237 as amended): the second layer of a pull request's merge -# check, `mesh/repo-check`, run by the build seat in the mesh's Go toolchain — and by hand. -# -# The gate — every machine of the facts snapshot composed with this change — is the build seat's first -# layer (`mesh/merge-gate`), run because the mesh's module graph builds node-tools and mesh-tools from -# here. This is the code's own: node-tools (Go) formatted, vetted and its suite, every test on a bus of its -# own at the release the mesh runs (internal/meshtest), packages in parallel, under the race detector when -# the toolchain has a C compiler. -# -# **Said, never passed silently**: the runtime's and the console's tests launch TypeScript and Python -# bundles that import the mesh's own package (@novox/mesh-sdk), which comes from the package registry — and -# a check of code nobody has approved is given no credential for it. Where node and that package are not -# present those two packages are not tested here, and the TypeScript in node-tools/src neither. -set -eu -cd node-tools - -unformatted=$(gofmt -l cmd internal) -if [ -n "$unformatted" ]; then - echo "not gofmt'd:" - echo "$unformatted" - exit 1 -fi -CGO_ENABLED=0 go vet ./... - -packages=$(go list ./...) -bundleless="" -if ! command -v node >/dev/null 2>&1 || [ ! -d node_modules/@novox/mesh-sdk ]; then - echo "NOT TESTED HERE: internal/runtime and internal/console launch bundles that need node and @novox/mesh-sdk" - packages=$(printf '%s\n' "$packages" | grep -v -e '/internal/runtime$' -e '/internal/console$') - # What of the console launches no bundle runs anyway: how an address resolves (novox/hq issue 287), - # and how a search ranks (novox/hq ADR 0245), and what discovery heard and how a round that heard nothing is - # answered (novox/hq issue 554), and how a session waits on the bus (novox/hq ADR 0367). - bundleless='^(TestMeshWait.*|TestTheManifestNamesTheFirstFamiliesOneByOne|TestASeatAndAModuleOfOneNameAreEachReached|TestSearch.*|TestASearchByCommandSaysWhatItReplaces|TestARuntimeThatAnswersLateIsFoundAndOneThatNeverDoesIsNamed|TestARuntimeHeardBeforeAndNotNowIsNamed|TestACallAfterARoundThatHeardNothingUsesWhatWasHeardBefore|TestAnAddressKnownOnlyFromAnEarlierAnswerSaysHowOld)$' - # And what of the runtime launches no bundle: how a bundle's seat traffic reaches the bus, what its - # environment holds, what a runtime on a module's own account serves (novox/hq ADR 0259), and that a write - # by compare-and-set loses no update under contention (novox/hq research 042), and a module's log verbs - # (novox/hq ADR 0297), and a key's history (novox/hq ADR 0307), and a module's values asked of its machine's - # provisioner client (novox/hq ADR 0370). - runtimeless='^(TestALogAnswersEachVerbOnTheBus|TestALogPageIsBoundedByItsBytes|TestAnAppendAtAStaleExpectIsRefusedAsALostUpdate|TestALogNotIssuedIsRefusedWithTheReason|TestWhatALogCannotHoldIsRefusedWithTheReason|TestALogRefusesAValueNamingACredential|TestAKeysHistoryIsEveryValueKeptOldestFirst|TestAKeysHistoryIsRefusedToAModuleNotIssuedTheState|TestARuntimeOnAModulesOwnAccountServesThatModuleAlone|TestNoBusWordReachesABundle|TestAToolCallNeverReachesASeatsEventAcceptOrProof|TestABundlesSeatTrafficGoesThroughItsModulesBus|TestALostCompareAndSetCarriesItsCode|TestTwoWritersIncrementingOneCounterOnTheBusLoseNoUpdate|TestAStaleWriteIsRefusedAndWritesNothing|TestACallOnTheCallerNamedSubjectReachesTheModuleWithItsCaller|TestTheSubjectThatNamesNoCallerStillWorksAndNamesNone|TestABundleAsksForItsValuesAsItsOwnModule|TestABundlesWatchIsHandedTheValueAndEachChange)$' -fi -if command -v gcc >/dev/null 2>&1; then - CGO_ENABLED=1 go test -race -count=1 $packages -else - echo "NOT RACE-CHECKED: the toolchain holds no C compiler; the suite runs without the race detector" - CGO_ENABLED=0 go test -count=1 $packages -fi -if [ -n "$bundleless" ]; then - CGO_ENABLED=0 go test -count=1 -run "$bundleless" ./internal/console -fi -if [ -n "${runtimeless:-}" ]; then - CGO_ENABLED=0 go test -count=1 -run "$runtimeless" ./internal/runtime -fi -echo "NOT TESTED HERE: node-tools/src (TypeScript) needs @novox/mesh-sdk from the package registry" diff --git a/node-tools/internal/console/address_test.go b/node-tools/internal/console/address_test.go index 6a333b7..6342941 100644 --- a/node-tools/internal/console/address_test.go +++ b/node-tools/internal/console/address_test.go @@ -39,6 +39,7 @@ func call(t *testing.T, endpoint, tool string, args map[string]any) (string, boo // novox/hq ADR 0195 and ADR 0367: the console announces seven tools, and everything the mesh answers is reached // through them by one address per layer. func TestTheMeshsToolsAreFoundByAddress(t *testing.T) { + mt.NeedBundles(t) was := IndexKept IndexKept = 0 // every discovery asks the mesh, so a module arriving mid-test is found t.Cleanup(func() { IndexKept = was }) diff --git a/node-tools/internal/console/console_test.go b/node-tools/internal/console/console_test.go index d41b55f..c8e557b 100644 --- a/node-tools/internal/console/console_test.go +++ b/node-tools/internal/console/console_test.go @@ -42,6 +42,7 @@ func post(t *testing.T, endpoint string, body string) map[string]any { // As node-tools, the runtime serves the bundles and is the console on loopback: the listing is what // the modules and the mesh's records answered, and a call reaches the module on the machine named. func TestTheConsoleListsAndCallsOverHTTP(t *testing.T) { + mt.NeedBundles(t) mesh := mt.New(t) mesh.Issue(t, mt.MembershipOf("alpha", "desk", true, nil)) mesh.Issue(t, mt.MembershipOf("beta", "desk", false, map[string][]string{"node-shelf": {"list", "clear"}})) diff --git a/node-tools/internal/meshtest/bundles.go b/node-tools/internal/meshtest/bundles.go new file mode 100644 index 0000000..a6281f5 --- /dev/null +++ b/node-tools/internal/meshtest/bundles.go @@ -0,0 +1,38 @@ +package meshtest + +import ( + "os" + "os/exec" + "path/filepath" + "runtime" + "testing" +) + +// bundlesWhy returns the reason bundles cannot be launched from a checkout rooted at root, or "" when they can: the +// TypeScript and Python bundles the runtime and the console launch need node and the package +// @novox/mesh-sdk, which comes from the package registry and is installed beside node-tools/package.json +// (novox/hq ADR 0529 clause 1: what merge-check.sh said "NOT TESTED HERE" about is a skip with its reason). +func bundlesWhy(root string, haveNode bool) string { + if !haveNode { + return "node is not installed" + } + if _, err := os.Stat(filepath.Join(root, "node_modules", "@novox", "mesh-sdk")); err != nil { + return "@novox/mesh-sdk is not installed in " + filepath.Join(root, "node_modules") + } + return "" +} + +// NeedBundles skips the test, saying why, where the bundles it launches cannot run here. +func NeedBundles(t *testing.T) { + t.Helper() + _, err := exec.LookPath("node") + if why := bundlesWhy(moduleRoot(), err == nil); why != "" { + t.Skip("NOT TESTED HERE: this test launches bundles, and " + why) + } +} + +// moduleRoot is node-tools/, where package.json and node_modules are. +func moduleRoot() string { + _, here, _, _ := runtime.Caller(0) + return filepath.Join(filepath.Dir(here), "..", "..") +} diff --git a/node-tools/internal/meshtest/meshtest_test.go b/node-tools/internal/meshtest/meshtest_test.go index 220557e..374cd93 100644 --- a/node-tools/internal/meshtest/meshtest_test.go +++ b/node-tools/internal/meshtest/meshtest_test.go @@ -73,3 +73,20 @@ func TestEachTestHasABusOfItsOwn(t *testing.T) { } }) } + +// What merge-check.sh said was "NOT TESTED HERE" is a skip with its reason: no node, or no package, each says so. +func TestBundlesCannotBeLaunchedWithoutNodeOrTheSDKAndTheSkipSaysWhy(t *testing.T) { + root := t.TempDir() + if why := bundlesWhy(root, false); !strings.Contains(why, "node is not installed") { + t.Errorf("no node said %q", why) + } + if why := bundlesWhy(root, true); !strings.Contains(why, "@novox/mesh-sdk") { + t.Errorf("no package said %q", why) + } + if err := os.MkdirAll(filepath.Join(root, "node_modules", "@novox", "mesh-sdk"), 0o755); err != nil { + t.Fatal(err) + } + if why := bundlesWhy(root, true); why != "" { + t.Errorf("node and the package present said %q", why) + } +} diff --git a/node-tools/internal/runtime/announce_test.go b/node-tools/internal/runtime/announce_test.go index ce7d88b..ab49ada 100644 --- a/node-tools/internal/runtime/announce_test.go +++ b/node-tools/internal/runtime/announce_test.go @@ -18,6 +18,7 @@ import ( // read here with NATS's own types, each endpoint a subject actually served — and the announcement // follows a re-issued membership. func TestTheRuntimeAnnouncesWhatItServesInTheServicesProtocol(t *testing.T) { + mt.NeedBundles(t) mesh := mt.New(t) mesh.Issue(t, mt.MembershipOf("alpha", "anchor", false, nil)) mesh.Issue(t, mt.MembershipOf("beta", "anchor", false, map[string][]string{"node-shelf": {"list", "clear"}})) diff --git a/node-tools/internal/runtime/events_test.go b/node-tools/internal/runtime/events_test.go index 2cf56c9..bea321b 100644 --- a/node-tools/internal/runtime/events_test.go +++ b/node-tools/internal/runtime/events_test.go @@ -50,6 +50,7 @@ func countLines(t *testing.T, path, want string) int { // is its bus — it binds the module's own consumer, hands each event to the bundle, and acknowledges it // only when the bundle has taken it; one the bundle failed or died on is offered again. func TestTheRuntimeHandsAModulesEventsToItsBundleAndAcknowledgesThemOnlyWhenTaken(t *testing.T) { + mt.NeedBundles(t) mesh := mt.New(t) restore := bus.NakDelay bus.NakDelay = 300 * time.Millisecond @@ -128,6 +129,7 @@ func TestTheRuntimeHandsAModulesEventsToItsBundleAndAcknowledgesThemOnlyWhenTake // A long-running bundle that exits is started again, without waiting for a call (ADR 0198). func TestALongRunningBundleThatExitsIsStartedAgain(t *testing.T) { + mt.NeedBundles(t) mesh := mt.New(t) firstPause := launch.RestartFirst launch.RestartFirst = 100 * time.Millisecond diff --git a/node-tools/internal/runtime/runtime_test.go b/node-tools/internal/runtime/runtime_test.go index 1c1040f..d962bed 100644 --- a/node-tools/internal/runtime/runtime_test.go +++ b/node-tools/internal/runtime/runtime_test.go @@ -46,6 +46,7 @@ func same(t *testing.T, got, want string) { // The node's runtime serves five modules' bundles on one credential — two TypeScript, one broken, // one Python, one written against the protocol — and follows a re-issued membership (ADR 0175, 0193). func TestTheNodesRuntimeServesFiveModulesAndFollowsAReissuedMembership(t *testing.T) { + mt.NeedBundles(t) mesh := mt.New(t) mesh.Issue(t, mt.MembershipOf("alpha", "anchor", true, nil)) mesh.Issue(t, mt.MembershipOf("beta", "anchor", false, map[string][]string{"node-shelf": {"list", "clear"}})) @@ -152,6 +153,7 @@ func TestTheNodesRuntimeServesFiveModulesAndFollowsAReissuedMembership(t *testin // Each bundle is given its own environment and none of another's (ADR 0192), and the composed // environments are not left in the runtime's. func TestEachBundleIsGivenItsOwnEnvironment(t *testing.T) { + mt.NeedBundles(t) mesh := mt.New(t) for _, m := range []string{"gamma", "delta", "zeta"} { mesh.Issue(t, mt.MembershipOf(m, "anchor", false, nil)) @@ -192,6 +194,7 @@ func TestEachBundleIsGivenItsOwnEnvironment(t *testing.T) { // An entrypoint that is not executable is refused by name, and the others serve (ADR 0193). func TestAnEntrypointThatIsNotExecutableIsRefused(t *testing.T) { + mt.NeedBundles(t) mesh := mt.New(t) mesh.Issue(t, mt.MembershipOf("alpha", "anchor", false, nil)) mesh.Issue(t, mt.MembershipOf("plain", "anchor", false, nil)) @@ -218,6 +221,7 @@ func TestAnEntrypointThatIsNotExecutableIsRefused(t *testing.T) { // A launched bundle is told the module it serves, so its seat's verbs stay the seat's (ADR 0193). func TestALaunchedBundleRegisteringItsSeatFirstServesTheSeat(t *testing.T) { + mt.NeedBundles(t) mesh := mt.New(t) mesh.Issue(t, mt.MembershipOf("theta", "anchor", false, map[string][]string{"node-shelf": {"list"}})) nodeTools := connect(t, "node-tools", "anchor") diff --git a/node-tools/internal/runtime/state_test.go b/node-tools/internal/runtime/state_test.go index d0f258d..b88888b 100644 --- a/node-tools/internal/runtime/state_test.go +++ b/node-tools/internal/runtime/state_test.go @@ -139,6 +139,7 @@ func waitFor(t *testing.T, path, line string) { // The TypeScript SDK against the runtime (ADR 0201): two watches of one state, each handed only its // own changes, the current values handled before the module's import goes on. func TestTheSDKsStateReachesTheRuntime(t *testing.T) { + mt.NeedBundles(t) mesh := mt.New(t) kv := mesh.Bucket(t, "sdkkeeper_servers") if _, err := kv.Put("all.one", []byte(`{"url":"http://one"}`)); err != nil {