//go:build integration package main import ( "bytes" "context" "fmt" "io/fs" "os" "os/exec" "path/filepath" "slices" "testing" "time" "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. 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) if _, err := exec.LookPath(p); err != nil { t.Skipf("NOT RUN: no %s installed where this check runs (the mesh's Go toolchain image holds none); proven live on a build node", p) } } home, cache, work := t.TempDir(), t.TempDir(), t.TempDir() for _, n := range []string{"package.json", "package-lock.json"} { raw, err := os.ReadFile(filepath.Join(moduleDir, "toolchain", n)) if err != nil { t.Fatal(err) } write(t, filepath.Join(home, "toolchain", n), string(raw), 0o644) } lock, err := ReadRecipe(filepath.Join(home, "toolchain", "package-lock.json")) if err != nil { t.Fatal(err) } 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 }}} 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())) } 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) } for _, id := range []string{"w1", "w2"} { 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)\""}}`, "modules/m/tools/index.ts": "import { serveRegisteredOverStdio } from \"@novox/mesh-sdk/stdio\";\nexport const f: () => Promise = serveRegisteredOverStdio as () => Promise;\n", } { write(t, filepath.Join(work, id, "tree", filepath.FromSlash(p)), body, 0o644) } } 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) { m, work := realMachine(t) 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"}) if err != nil { t.Fatal(err) } r := out.(BuildResult) digests = append(digests, r.Digest) if want := []string{"package.json", "tools/index.js", "tools/index.serve.mjs"}; !slices.Equal(r.Files, want) { t.Errorf("files = %v, want %v", r.Files, want) } } if digests[0] != digests[1] { t.Errorf("digests differ: %v", digests) } if links := symlinksUnder(t, work); len(links) != 0 { t.Errorf("symlinks in the workspaces: %v", links) } } // 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 // installs it on its first node). "" when none is there or the directory cannot be read: no Node build module here. func installedToolchain(t *testing.T, toolchains, lockPath string) string { t.Helper() want, err := os.ReadFile(lockPath) if err != nil { t.Fatal(err) } entries, err := os.ReadDir(toolchains) if err != nil { return "" } last, lastAt := "", time.Time{} for _, e := range entries { dir := filepath.Join(toolchains, e.Name()) mark, err := os.Stat(filepath.Join(dir, installedMark)) if err != nil { continue } got, err := os.ReadFile(filepath.Join(dir, "package-lock.json")) if err != nil { continue } if bytes.Equal(got, want) { t.Logf("the toolchain is this node's installed %s, of this change's lock", dir) return dir } if mark.ModTime().After(lastAt) { last, lastAt = dir, mark.ModTime() } } if last != "" { t.Logf("NOT THIS LOCK: no toolchain of this change's lock is installed here; the tests run with the one installed last, %s", last) } return last } // copyToolchain copies the installed toolchain from into to whole, files and folders with their modes, so the test's // steps run behind a toolchain of their own and the installed one is only read. func copyToolchain(t *testing.T, from, to string) { t.Helper() err := filepath.WalkDir(from, func(p string, d fs.DirEntry, err error) error { if err != nil { return err } rel, _ := filepath.Rel(from, p) dst := filepath.Join(to, rel) info, err := d.Info() if err != nil { return err } switch { case d.IsDir(): return os.MkdirAll(dst, 0o755) case info.Mode().IsRegular(): raw, err := os.ReadFile(p) if err != nil { return err } return os.WriteFile(dst, raw, info.Mode().Perm()|0o600) default: return fmt.Errorf("%s is neither a file nor a folder", p) } }) if err != nil { t.Fatalf("copying the installed toolchain %s: %v", from, err) } }