170 lines
6.2 KiB
Go
170 lines
6.2 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"slices"
|
|
"strconv"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
|
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
"git.novox.be/novox/mesh-sdk/go/forgekey"
|
|
)
|
|
|
|
// integrationShutOffWith runs the integration level through the fake bubblewrap and answers what its one shut-off
|
|
// process was given.
|
|
func integrationShutOffWith(t *testing.T, toolchains string) shutOff {
|
|
t.Helper()
|
|
m, states, ws, _ := workspaceFor(t, "failing")
|
|
m.Executable = "/opt/bundle/mesh-build-go"
|
|
m.NodeToolchains = toolchains
|
|
want := TestResult{Level: LevelIntegration, Passed: true, Tests: 1, Packages: []PackageResult{}, Store: "postgres 18"}
|
|
var seen []shutOff
|
|
m.Run = throughBubblewrap(t, func(ctx context.Context, env []string, name string, args ...string) forgekey.Ran {
|
|
switch {
|
|
case name == "postgres" && len(args) == 1 && args[0] == "--version":
|
|
return forgekey.Ran{Stdout: "postgres (PostgreSQL) 18.6\n"}
|
|
case name == m.Executable && len(args) == 2 && args[0] == levelArg:
|
|
raw, _ := json.Marshal(levelAnswer{Result: &want})
|
|
return forgekey.Ran{Stdout: string(raw) + "\n"}
|
|
}
|
|
return forgekey.Ran{Status: 1, Err: "not expected here"}
|
|
}, &seen)
|
|
run := stepTool(t, m, "build_go_test")
|
|
if _, err := run(map[string]any{"workspace": ws, "level": LevelIntegration}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if st := states.waitEnd(t); st.State != delivery.StepPassed {
|
|
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
|
|
}
|
|
if len(seen) != 1 {
|
|
t.Fatalf("shut off %d processes, want one: %v", len(seen), seen)
|
|
}
|
|
checkShutOff(t, m, seen[0], false)
|
|
return seen[0]
|
|
}
|
|
|
|
// 1891 (ADR 0533 §1): the integration level is given the Node build module's installed toolchains, read-only, so the
|
|
// Node build module's own integration tests build with the real tsc and esbuild and reach no registry.
|
|
func TestTheIntegrationLevelIsGivenTheNodeToolchainsReadOnly(t *testing.T) {
|
|
dir := t.TempDir()
|
|
s := integrationShutOffWith(t, dir)
|
|
if !slices.Contains(s.roBinds, dir) {
|
|
t.Errorf("the node toolchains %s are not bound read-only: %v", dir, s.roBinds)
|
|
}
|
|
if slices.Contains(s.binds, dir) || slices.Contains(s.overlays, dir) {
|
|
t.Errorf("the node toolchains %s may be written: binds %v, overlays %v", dir, s.binds, s.overlays)
|
|
}
|
|
if slices.Contains(s.roBinds, filepath.Dir(dir)) {
|
|
t.Errorf("the whole cache %s is bound, not its toolchains alone: %v", filepath.Dir(dir), s.roBinds)
|
|
}
|
|
}
|
|
|
|
// 1891 (ADR 0533 §1): a node where the Node build module has installed no toolchain runs the level as before, with
|
|
// nothing more bound (bubblewrap binds only what is there).
|
|
func TestANodeWithoutNodeToolchainsRunsTheLevelWithNothingMoreBound(t *testing.T) {
|
|
missing := filepath.Join(t.TempDir(), "none")
|
|
s := integrationShutOffWith(t, missing)
|
|
if slices.Contains(s.roBinds, missing) {
|
|
t.Errorf("a missing %s is bound: %v", missing, s.roBinds)
|
|
}
|
|
|
|
// Issue 890/AC4: such a node lacks node-build-node; one with an installed toolchain and node and npm has it; nothing
|
|
// else is had.
|
|
bin := t.TempDir()
|
|
for _, p := range []string{"node", "npm", "bwrap"} {
|
|
if err := os.WriteFile(filepath.Join(bin, p), []byte("#!/bin/sh\n"), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
t.Setenv("PATH", bin)
|
|
m := &Module{NodeToolchains: missing}
|
|
if m.Has(delivery.NeedNodeBuildNode) {
|
|
t.Error("a node with no Node toolchain has node-build-node")
|
|
}
|
|
installed := t.TempDir()
|
|
m.NodeToolchains = installed
|
|
if m.Has(delivery.NeedNodeBuildNode) {
|
|
t.Error("a node with an empty toolchains directory has node-build-node")
|
|
}
|
|
half := filepath.Join(installed, "0.1.0")
|
|
if err := os.Mkdir(half, 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(half, "package-lock.json"), []byte("{}"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if m.Has(delivery.NeedNodeBuildNode) {
|
|
t.Error("a toolchain without its installed mark counts")
|
|
}
|
|
if err := os.WriteFile(filepath.Join(half, nodeToolchainMark), nil, 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !m.Has(delivery.NeedNodeBuildNode) || m.Has("gpu") {
|
|
t.Errorf("with a toolchain installed: node-build-node %v, gpu %v; want true, false", m.Has(delivery.NeedNodeBuildNode), m.Has("gpu"))
|
|
}
|
|
if err := os.Remove(filepath.Join(bin, "bwrap")); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if m.Has(delivery.NeedNodeBuildNode) {
|
|
t.Error("a node with no bubblewrap installed has node-build-node")
|
|
}
|
|
}
|
|
|
|
// 1891 (ADR 0533 §1): the unit level is not given them.
|
|
func TestTheUnitLevelIsNotGivenTheNodeToolchains(t *testing.T) {
|
|
m, states, ws, _ := workspaceFor(t, "failing")
|
|
dir := t.TempDir()
|
|
m.NodeToolchains = dir
|
|
var seen []shutOff
|
|
m.Run = throughBubblewrap(t, m.Run, &seen)
|
|
if _, st := runUnit(t, m, states, ws); st.State != delivery.StepPassed {
|
|
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
|
|
}
|
|
for _, s := range seen {
|
|
if slices.Contains(s.roBinds, dir) || slices.Contains(s.binds, dir) {
|
|
t.Errorf("%s was given the node toolchains", s.command)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 1891 (ADR 0533 §1, §2): the place bound is the Node build module's own: its manifest's cache directory joined with
|
|
// toolchain, and that cache directory lets other accounts enter it without listing it (0711).
|
|
func TestTheNodeToolchainsAreTheNodeBuildModulesCache(t *testing.T) {
|
|
if _, err := catalogue.ReadTreeManifest(catalogueRoot, filepath.Join(catalogueRoot, "modules", "mesh-build-node")); err != nil {
|
|
t.Fatalf("mesh-sdk's validator refuses mesh-build-node's manifest: %v", err)
|
|
}
|
|
raw, err := os.ReadFile(filepath.Join(catalogueRoot, "modules", "mesh-build-node", "module.json"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var man struct {
|
|
Resources []struct {
|
|
ID, Path, Mode string
|
|
} `json:"resources"`
|
|
}
|
|
if err := json.Unmarshal(raw, &man); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
found := false
|
|
for _, r := range man.Resources {
|
|
if r.ID != "cache" {
|
|
continue
|
|
}
|
|
found = true
|
|
if want := filepath.Join(r.Path, "toolchain"); NodeToolchains != want {
|
|
t.Errorf("NodeToolchains = %s, want %s (mesh-build-node's cache)", NodeToolchains, want)
|
|
}
|
|
mode, err := strconv.ParseUint(r.Mode, 8, 32)
|
|
if err != nil || mode&0o001 == 0 || mode&0o004 != 0 {
|
|
t.Errorf("mesh-build-node's cache has mode %q; want others to enter it and not list it (0711)", r.Mode)
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatal("mesh-build-node's manifest declares no cache directory")
|
|
}
|
|
}
|