From a01715ddf508daa4ae171e4e9816ef3eeb5bb013 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 26 Oct 2026 12:05:09 +0100 Subject: [PATCH] Skip the Node build module's toolchain tests by name where no Node toolchain is installed, instead of installing one from the registry in a level that has no network; merge three tests that share one fixture On ace, which holds no Node build module, five integration tests each spent 70 s in npm's network retries and failed every mesh-catalog check run there (five verdicts since 00:07). The tests now copy the node's installed toolchain or are skipped as NOT RUN; the registry is never reached. Three tests sharing the fixture are one, so the toolchain is copied three times instead of five (10 integration tests in the module, now 8). --- .../mesh-build-node/build_integration_test.go | 94 +++++++++---------- 1 file changed, 43 insertions(+), 51 deletions(-) diff --git a/modules/mesh-build-node/cmd/mesh-build-node/build_integration_test.go b/modules/mesh-build-node/cmd/mesh-build-node/build_integration_test.go index bd66ceea..c113308f 100644 --- a/modules/mesh-build-node/cmd/mesh-build-node/build_integration_test.go +++ b/modules/mesh-build-node/cmd/mesh-build-node/build_integration_test.go @@ -17,11 +17,11 @@ import ( "git.novox.be/novox/mesh-sdk/go/forgekey" ) -// realMachine gives the tests the real toolchain of the module's own lock and makes two workspaces holding one fixture -// bundle. The toolchain is the one this node's installed Node build module keeps (ADR 0533 §1): the integration level -// is given its toolchains read-only and no network, so it is copied into the test's own cache from there. Only where -// no toolchain is installed (a desk) is it installed with `npm ci --ignore-scripts` from the mesh's registry, as the -// module does, which needs the network. +// realMachine gives the tests the real toolchain of the module's own lock and makes three workspaces holding one fixture +// bundle. The toolchain is the one this node's installed Node build module keeps (ADR 0533 §1), copied into the test's +// own cache: the integration level is given its toolchains read-only and no network, so it is never installed from the +// registry here. Where no Node build module has installed one (a node without it, a desk), the test is skipped by +// name: an install from the public registry inside a shut-off level only fails, after 70 s of npm's retries. func realMachine(t *testing.T) (*Module, string) { t.Helper() for _, p := range []string{"node", "npm", "bwrap"} { // bwrap: tsc, esbuild and the scripts run shut off (1309) @@ -44,16 +44,19 @@ func realMachine(t *testing.T) (*Module, string) { run := forgekey.ExecRunnerFor(0) m := &Module{Node: "ace", Recipe: lock, Work: work, Run: run, Ctx: context.Background(), Toolchain: &Toolchain{Paths: Paths{Home: home, Cache: cache}, Run: run, Registry: func() (Registry, error) { - return Registry{URL: "https://git.novox.be/api/packages/novox/npm/"}, nil + return Registry{}, fmt.Errorf("the integration test installs no toolchain from a registry: it has no network") }}} withStore(t, m) - if from := installedToolchain(t, filepath.Join(CacheDir, "toolchain"), filepath.Join(home, "toolchain", "package-lock.json")); from != "" { - copyToolchain(t, from, m.Toolchain.Paths.Dir(m.Recipe.Version())) + from := installedToolchain(t, filepath.Join(CacheDir, "toolchain"), filepath.Join(home, "toolchain", "package-lock.json")) + if from == "" { + t.Skipf("NOT RUN: no Node toolchain is installed under %s for this account to read (no Node build module on this node); "+ + "the integration level has no network to install one, so this runs on a node that holds the Node build module", filepath.Join(CacheDir, "toolchain")) } - if err := m.Toolchain.Ensure(context.Background(), m.Recipe.Version()); err != nil { - t.Fatalf("no toolchain of this lock is installed under %s, and installing it from the registry failed: %v", filepath.Join(CacheDir, "toolchain"), err) + copyToolchain(t, from, m.Toolchain.Paths.Dir(m.Recipe.Version())) + if err := m.Toolchain.Ensure(context.Background(), m.Recipe.Version()); err != nil { // present: opens it, installs nothing + t.Fatalf("the toolchain copied from %s cannot be used: %v", from, err) } - for _, id := range []string{"w1", "w2"} { + for _, id := range []string{"w1", "w2", "w3"} { for p, body := range map[string]string{ "modules/m/module.json": `{"module":"m","build":{"artifacts":[{"name":"tools","kind":"bundle","language":"typescript","entrypoints":["tools/index.js"]}]}}`, "modules/m/package.json": `{"name":"m","type":"module","scripts":{"test":"node -e \"process.exit(0)\""}}`, @@ -65,10 +68,36 @@ func realMachine(t *testing.T) (*Module, string) { return m, work } -// 1490/AC2: a fixture TypeScript bundle importing @novox/mesh-sdk, built twice by the real tsc and esbuild, has equal -// digests, and its archive holds the bundled .js, the .serve.mjs launcher and package.json. -func TestAFixtureBundleBuiltTwiceHasOneDigest(t *testing.T) { +// 1490/AC2, 1491 and issue 1869, in one test so the toolchain is copied once: the real type-check and test script of the +// fixture pass the unit level; a package whose test imports its own build output (as mesh-sdk's do) passes the unit level, +// the test step emitting it with the real tsc, shut off, before the test script runs; and the fixture TypeScript bundle +// importing @novox/mesh-sdk, built twice by the real tsc and esbuild, has equal digests, its archive holding the bundled +// .js, the .serve.mjs launcher and package.json. The unit levels run in w3, so the two builds' workspaces stay alike. +func TestTheFixturePassesTheUnitLevelAndBuildsTwiceToOneDigest(t *testing.T) { m, work := realMachine(t) + out, err := m.test(context.Background(), map[string]any{"workspace": "w3", "module": "modules/m", "level": "unit"}) + if err != nil { + t.Fatal(err) + } + if r := out.(*TestResult); !r.Passed || r.NoTests || len(r.Ran) != 2 { + t.Errorf("the fixture's unit level: result = %+v", r) + } + for p, body := range map[string]string{ + "modules/p/package.json": `{"name":"p","type":"module","scripts":{"build":"tsc","test":"node --test test/a.test.mjs","prepare":"tsc"}}`, + "modules/p/tsconfig.json": `{"compilerOptions":{"module":"NodeNext","moduleResolution":"NodeNext","target":"ES2022","outDir":"dist","rootDir":"src"},"include":["src"]}`, + "modules/p/src/index.ts": "export const answer: number = 42;\n", + "modules/p/test/a.test.mjs": "import { test } from 'node:test';\nimport assert from 'node:assert';\nimport { answer } from '../dist/index.js';\n" + + "test('the build is there', () => assert.equal(answer, 42));\n", + } { + write(t, filepath.Join(work, "w3", "tree", filepath.FromSlash(p)), body, 0o644) + } + out, err = m.test(context.Background(), map[string]any{"workspace": "w3", "module": "modules/p", "level": "unit"}) + if err != nil { + t.Fatal(err) + } + if r := out.(*TestResult); !r.Passed || !slices.Equal(r.Ran, []string{"build", "test"}) { + t.Errorf("the package importing its build: result = %+v", r) + } var digests []string for _, id := range []string{"w1", "w2"} { out, err := m.build(context.Background(), map[string]any{"workspace": id, "module": "modules/m", "artifact": "tools"}) @@ -89,43 +118,6 @@ func TestAFixtureBundleBuiltTwiceHasOneDigest(t *testing.T) { } } -// 1491: the real type-check and test script of the same fixture pass the unit level. -func TestTheFixturePassesTheUnitLevel(t *testing.T) { - m, _ := realMachine(t) - out, err := m.test(context.Background(), map[string]any{"workspace": "w1", "module": "modules/m", "level": "unit"}) - if err != nil { - t.Fatal(err) - } - if r := out.(*TestResult); !r.Passed || r.NoTests || len(r.Ran) != 2 { - t.Errorf("result = %+v", r) - } -} - -// Issue 1869: a package whose test imports its own build output (as mesh-sdk's do) passes the unit level: the test step -// emits it with the real tsc, shut off, before the test script runs. -func TestAPackageWhoseTestsImportItsBuildPassesTheUnitLevel(t *testing.T) { - if _, err := exec.LookPath("bwrap"); err != nil { - t.Skip("NOT RUN: no bubblewrap installed where this check runs; every build node has it (the module declares it)") - } - m, work := realMachine(t) - for p, body := range map[string]string{ - "modules/p/package.json": `{"name":"p","type":"module","scripts":{"build":"tsc","test":"node --test test/a.test.mjs","prepare":"tsc"}}`, - "modules/p/tsconfig.json": `{"compilerOptions":{"module":"NodeNext","moduleResolution":"NodeNext","target":"ES2022","outDir":"dist","rootDir":"src"},"include":["src"]}`, - "modules/p/src/index.ts": "export const answer: number = 42;\n", - "modules/p/test/a.test.mjs": "import { test } from 'node:test';\nimport assert from 'node:assert';\nimport { answer } from '../dist/index.js';\n" + - "test('the build is there', () => assert.equal(answer, 42));\n", - } { - write(t, filepath.Join(work, "w1", "tree", filepath.FromSlash(p)), body, 0o644) - } - out, err := m.test(context.Background(), map[string]any{"workspace": "w1", "module": "modules/p", "level": "unit"}) - if err != nil { - t.Fatal(err) - } - if r := out.(*TestResult); !r.Passed || !slices.Equal(r.Ran, []string{"build", "test"}) { - t.Errorf("result = %+v", r) - } -} - // installedToolchain is the toolchain directory under toolchains that this node's Node build module installed: the // one installed from the lock at lockPath, byte for byte, or, when the change carries a lock no delivered release has // installed yet, the one installed last, said in the test's log (ADR 0533 §3: a new lock is proven by the module that -- 2.54.0