test(metis): complete per-file coverage gate

This commit is contained in:
codex 2026-04-21 06:21:16 -03:00
parent 096735fe89
commit ef9124e5c0
3 changed files with 557 additions and 0 deletions

View File

@ -2,6 +2,7 @@ package main
import ( import (
"encoding/json" "encoding/json"
"fmt"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"os" "os"
@ -57,11 +58,22 @@ func TestSentinelHelpers(t *testing.T) {
if got := getenvInt("METIS_SENTINEL_INTERVAL_SEC", 300); got != 300 { if got := getenvInt("METIS_SENTINEL_INTERVAL_SEC", 300); got != 300 {
t.Fatalf("getenvInt fallback = %d", got) t.Fatalf("getenvInt fallback = %d", got)
} }
t.Setenv("METIS_SENTINEL_INTERVAL_SEC", "not-int")
if got := getenvInt("METIS_SENTINEL_INTERVAL_SEC", 300); got != 300 {
t.Fatalf("getenvInt invalid fallback = %d", got)
}
t.Setenv("METIS_SENTINEL_INTERVAL_SEC", "5") t.Setenv("METIS_SENTINEL_INTERVAL_SEC", "5")
if got := getenvInt("METIS_SENTINEL_INTERVAL_SEC", 300); got != 5 { if got := getenvInt("METIS_SENTINEL_INTERVAL_SEC", 300); got != 5 {
t.Fatalf("getenvInt = %d", got) t.Fatalf("getenvInt = %d", got)
} }
writeHistory("", &sentinel.Snapshot{Hostname: "skip"})
blocker := filepath.Join(t.TempDir(), "blocker")
if err := os.WriteFile(blocker, []byte("x"), 0o644); err != nil {
t.Fatal(err)
}
writeHistory(filepath.Join(blocker, "child"), &sentinel.Snapshot{Hostname: "skip"})
dir := t.TempDir() dir := t.TempDir()
snap := &sentinel.Snapshot{Hostname: "titan-13", Kernel: "6.6.63"} snap := &sentinel.Snapshot{Hostname: "titan-13", Kernel: "6.6.63"}
writeHistory(dir, snap) writeHistory(dir, snap)
@ -87,6 +99,49 @@ func TestSentinelHelpers(t *testing.T) {
if err := pushSnapshot(srv.URL, snap); err != nil { if err := pushSnapshot(srv.URL, snap); err != nil {
t.Fatalf("pushSnapshot: %v", err) t.Fatalf("pushSnapshot: %v", err)
} }
if err := pushSnapshot("://bad-url", snap); err == nil {
t.Fatal("expected pushSnapshot bad URL error")
}
failSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "nope", http.StatusBadGateway)
}))
defer failSrv.Close()
if err := pushSnapshot(failSrv.URL, snap); err == nil || !strings.Contains(err.Error(), "502") {
t.Fatalf("expected pushSnapshot status error, got %v", err)
}
}
func TestSentinelMainPushAndEncodeFailures(t *testing.T) {
fakeDir := fakeSentinelCommands(t)
t.Setenv("PATH", fakeDir+string(os.PathListSeparator)+os.Getenv("PATH"))
failSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "nope", http.StatusBadGateway)
}))
defer failSrv.Close()
t.Setenv("METIS_SENTINEL_RUN_ONCE", "1")
t.Setenv("METIS_SENTINEL_PUSH_URL", failSrv.URL)
main()
oldFatalf := fatalf
fatalf = func(format string, args ...any) {
panic(fmt.Sprintf(format, args...))
}
defer func() { fatalf = oldFatalf }()
oldStdout := os.Stdout
reader, writer, err := os.Pipe()
if err != nil {
t.Fatal(err)
}
os.Stdout = writer
_ = reader.Close()
_ = writer.Close()
defer func() { os.Stdout = oldStdout }()
defer func() {
if got := recover(); got == nil || !strings.Contains(fmt.Sprint(got), "encode") {
t.Fatalf("expected encode fatal, got %v", got)
}
}()
main()
} }
func fakeSentinelCommands(t *testing.T) string { func fakeSentinelCommands(t *testing.T) string {

View File

@ -0,0 +1,400 @@
package main
import (
"bytes"
"fmt"
"io"
"math"
"os"
"path/filepath"
"strings"
"testing"
"metis/pkg/service"
)
func TestRemoteLocalDeviceAndProgressEdges(t *testing.T) {
dir := fakeCommandDir(t, map[string]string{
"lsblk": `cat <<'JSON'
{"blockdevices":[
{"name":"loop0","path":"/dev/loop0","rm":false,"hotplug":false,"size":"100","model":"loop","tran":"","type":"loop"},
{"name":"huge","path":"/dev/sdx","rm":true,"hotplug":true,"size":"999999999999","model":"huge","tran":"usb","type":"disk"},
{"name":"nvme0n1","path":"/dev/nvme0n1","rm":false,"hotplug":false,"size":32000000000,"model":"nvme","tran":"nvme","type":"disk"},
{"name":"mounted","path":"/dev/sdy","rm":true,"hotplug":true,"size":"32000000000","model":"SD","tran":"usb","type":"disk","mountpoint":"/mnt"},
{"name":"child-mounted","path":"/dev/sdz","rm":true,"hotplug":true,"size":"32000000000","model":"SD","tran":"usb","type":"disk","children":[{"mountpoint":"/boot"}]},
{"name":"bad-size","path":"/dev/sdw","rm":true,"hotplug":true,"size":"bad","model":"SD","tran":"usb","type":"disk"},
{"name":"good","path":"/dev/sdv","rm":true,"hotplug":true,"size":16000000000,"model":"SD Card","tran":"usb","type":"disk"}
]}
JSON`,
})
t.Setenv("PATH", dir+string(os.PathListSeparator)+os.Getenv("PATH"))
devices, err := localFlashDevices(40000000000, "/host-tmp/")
if err != nil {
t.Fatalf("localFlashDevices: %v", err)
}
if len(devices) != 2 || devices[0].Path != "/dev/sdv" || !strings.Contains(devices[1].Note, "/tmp") {
t.Fatalf("unexpected filtered devices: %#v", devices)
}
if got := localDeviceScore(service.Device{Path: "hosttmp:///tmp"}); got != -100 {
t.Fatalf("hosttmp localDeviceScore = %d", got)
}
if got := humanHostPath("/host-tmp/"); got != "/tmp" {
t.Fatalf("humanHostPath root = %q", got)
}
if got := humanHostPath("/host-tmp"); got != "/tmp" {
t.Fatalf("humanHostPath mount root = %q", got)
}
if got := getenvOr("METIS_REMOTE_UNSET", "fallback"); got != "fallback" {
t.Fatalf("getenvOr fallback = %q", got)
}
stdout, _ := captureStreams(t, func() {
emitProgress(service.RemoteProgressUpdate{ProgressPct: math.NaN()})
emitter := newProgressEmitter("flash", 92, 98, "Writing", true)
emitter(1, 0)
emitter(20, 10)
emitter(20, 10)
})
if strings.Count(stdout, "METIS_PROGRESS ") != 1 || !strings.Contains(stdout, `"progress_pct":98`) {
t.Fatalf("unexpected progress output: %q", stdout)
}
}
func TestRemoteDevicesSortAndDecodeEdges(t *testing.T) {
t.Run("invalid lsblk json", func(t *testing.T) {
dir := fakeCommandDir(t, map[string]string{"lsblk": `printf '{'`})
t.Setenv("PATH", dir+string(os.PathListSeparator)+os.Getenv("PATH"))
if _, err := localFlashDevices(40000000000, "/tmp"); err == nil {
t.Fatal("expected invalid lsblk JSON error")
}
})
t.Run("remote devices tie-break by size and path", func(t *testing.T) {
dir := fakeCommandDir(t, map[string]string{
"lsblk": `cat <<'JSON'
{"blockdevices":[
{"name":"sdc","path":"/dev/sdc","rm":true,"hotplug":true,"size":"64000000000","model":"Micro SD","tran":"usb","type":"disk"},
{"name":"sdb","path":"/dev/sdb","rm":true,"hotplug":true,"size":"32000000000","model":"Micro SD","tran":"usb","type":"disk"},
{"name":"sda","path":"/dev/sda","rm":true,"hotplug":true,"size":"32000000000","model":"Micro SD","tran":"usb","type":"disk"}
]}
JSON`,
})
t.Setenv("PATH", dir+string(os.PathListSeparator)+os.Getenv("PATH"))
stdout, _ := captureStreams(t, func() {
remoteDevicesCmd([]string{"--max-device-bytes", "100000000000"})
})
if strings.Index(stdout, "/dev/sda") > strings.Index(stdout, "/dev/sdb") || strings.Index(stdout, "/dev/sdb") > strings.Index(stdout, "/dev/sdc") {
t.Fatalf("devices not sorted by size/path: %s", stdout)
}
})
}
func TestRemoteArtifactFailureEdges(t *testing.T) {
dir := fakeCommandDir(t, map[string]string{
"oras": `printf 'oras failed' >&2; exit 7`,
})
t.Setenv("PATH", dir+string(os.PathListSeparator)+os.Getenv("PATH"))
artifactDir := t.TempDir()
imagePath := filepath.Join(artifactDir, "image.img")
metadataPath := filepath.Join(artifactDir, "metadata.json")
if err := os.WriteFile(imagePath, []byte("image"), 0o644); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(metadataPath, []byte("{}"), 0o644); err != nil {
t.Fatal(err)
}
for name, fn := range map[string]func() error{
"login": func() error { return orasLogin("registry.example", "u", "p") },
"push": func() error { return orasPush("registry.example/metis/node:tag", imagePath, metadataPath) },
"tag": func() error { return orasTag("registry.example/metis/node:tag", "latest") },
"pull": func() error { return orasPull("registry.example/metis/node:latest", artifactDir) },
} {
t.Run(name, func(t *testing.T) {
if err := fn(); err == nil || !strings.Contains(err.Error(), "oras failed") {
t.Fatalf("expected oras failure, got %v", err)
}
})
}
emptyDir := t.TempDir()
if _, _, err := resolvePulledArtifact(emptyDir); err == nil || !strings.Contains(err.Error(), "no .img") {
t.Fatalf("expected empty artifact dir error, got %v", err)
}
if err := orasPush("ref", filepath.Join(artifactDir, "one", "a.img"), filepath.Join(artifactDir, "two", "metadata.json")); err == nil || !strings.Contains(err.Error(), "one directory") {
t.Fatalf("expected push invocation error, got %v", err)
}
}
func TestRemoteCommandFatalEdges(t *testing.T) {
expectFatal := func(t *testing.T, want string, fn func()) {
t.Helper()
oldFatalf := fatalf
fatalf = func(format string, args ...any) {
panic(fmt.Sprintf(format, args...))
}
defer func() { fatalf = oldFatalf }()
defer func() {
got := recover()
if got == nil {
t.Fatalf("expected fatal containing %q", want)
}
if !strings.Contains(fmt.Sprint(got), want) {
t.Fatalf("fatal = %v, want %q", got, want)
}
}()
fn()
}
t.Run("remote devices lsblk failure", func(t *testing.T) {
dir := fakeCommandDir(t, map[string]string{"lsblk": `exit 2`})
t.Setenv("PATH", dir+string(os.PathListSeparator)+os.Getenv("PATH"))
expectFatal(t, "remote devices", func() { remoteDevicesCmd(nil) })
})
t.Run("remote build mkdir failure", func(t *testing.T) {
root := t.TempDir()
blocker := filepath.Join(root, "blocker")
if err := os.WriteFile(blocker, []byte("x"), 0o644); err != nil {
t.Fatal(err)
}
expectFatal(t, "mkdir workdir", func() {
remoteBuildCmd([]string{"--node", "titan-15", "--artifact-ref", "registry.example/metis/titan-15", "--build-tag", "build-1", "--work-dir", filepath.Join(blocker, "child")})
})
})
t.Run("remote build plan failure", func(t *testing.T) {
root := t.TempDir()
invPath, _ := writeTestInventory(t, root)
expectFatal(t, "build plan", func() {
remoteBuildCmd([]string{"--inventory", invPath, "--node", "missing", "--artifact-ref", "registry.example/metis/missing", "--build-tag", "build-1", "--work-dir", filepath.Join(root, "build")})
})
})
t.Run("remote flash mkdir failure", func(t *testing.T) {
root := t.TempDir()
blocker := filepath.Join(root, "blocker")
if err := os.WriteFile(blocker, []byte("x"), 0o644); err != nil {
t.Fatal(err)
}
expectFatal(t, "mkdir workdir", func() {
remoteFlashCmd([]string{"--node", "titan-15", "--device", "/dev/sdz", "--artifact-ref", "registry.example/metis/titan-15", "--work-dir", filepath.Join(blocker, "child")})
})
})
t.Run("remote flash missing credentials", func(t *testing.T) {
expectFatal(t, "oras login", func() {
remoteFlashCmd([]string{"--node", "titan-15", "--device", "/dev/sdz", "--artifact-ref", "registry.example/metis/titan-15", "--work-dir", t.TempDir()})
})
})
}
func TestPlanAndBurnCommandFlagBranches(t *testing.T) {
root := t.TempDir()
invPath, _ := writeTestInventory(t, root)
boot := filepath.Join(root, "boot")
rootfs := filepath.Join(root, "root")
stdout, _ := captureStreams(t, func() {
planCmd([]string{"--inventory", invPath, "--node", "titan-15", "--boot", boot, "--root", rootfs})
})
if !strings.Contains(stdout, `"node": "titan-15"`) || os.Getenv("METIS_BOOT_PATH") != boot || os.Getenv("METIS_ROOT_PATH") != rootfs {
t.Fatalf("plan command did not apply boot/root flags: stdout=%s", stdout)
}
fakeTools := fakeCommandDir(t, map[string]string{
"sfdisk": `cat <<'JSON'
{"partitiontable":{"sectorsize":512,"partitions":[{"start":3,"size":1,"type":"ef"},{"start":1,"size":2,"type":"83"}]}}
JSON`,
"debugfs": `exit 0`,
})
t.Setenv("PATH", fakeTools+string(os.PathListSeparator)+os.Getenv("PATH"))
stdout, _ = captureStreams(t, func() {
burnCmd([]string{"--inventory", invPath, "--node", "titan-15", "--device", filepath.Join(root, "out.img"), "--boot", boot, "--root", rootfs, "--auto-mount", "--yes"})
})
if !strings.Contains(stdout, "Burn complete") || os.Getenv("METIS_AUTO_MOUNT") != "1" {
t.Fatalf("burn command did not complete confirmed path: stdout=%s", stdout)
}
}
func TestPlanBurnAndRemoteBuildFatalStageEdges(t *testing.T) {
root := t.TempDir()
invPath, baseImage := writeTestInventory(t, root)
expectCommandFatal(t, "--node is required", func() { planCmd(nil) })
expectCommandFatal(t, "build plan", func() {
planCmd([]string{"--inventory", invPath, "--node", "missing"})
})
expectCommandFatal(t, "--node and --device are required", func() { burnCmd(nil) })
expectCommandFatal(t, "burn", func() {
burnCmd([]string{"--inventory", invPath, "--node", "missing", "--device", filepath.Join(root, "out.img")})
})
t.Run("remote build download checksum failure", func(t *testing.T) {
badInv := filepath.Join(root, "bad-inventory.yaml")
data, err := os.ReadFile(invPath)
if err != nil {
t.Fatal(err)
}
data = []byte(strings.Replace(string(data), "checksum: sha256:"+sha256SumHex(t, make([]byte, 4096)), "checksum: sha256:0000", 1))
if err := os.WriteFile(badInv, data, 0o644); err != nil {
t.Fatal(err)
}
expectCommandFatal(t, "download image", func() {
remoteBuildCmd([]string{"--inventory", badInv, "--node", "titan-15", "--artifact-ref", "registry.example/metis/titan-15", "--build-tag", "build-1", "--work-dir", filepath.Join(root, "download-fail"), "--cache", filepath.Join(root, "cache-download")})
})
})
t.Run("remote build xz failure", func(t *testing.T) {
tools := remoteBuildToolDir(t, baseImage, `exit 9`, `exit 0`)
t.Setenv("PATH", tools+string(os.PathListSeparator)+os.Getenv("PATH"))
expectCommandFatal(t, "xz compress", func() {
remoteBuildCmd([]string{"--inventory", invPath, "--node", "titan-15", "--artifact-ref", "registry.example/metis/titan-15", "--build-tag", "build-1", "--work-dir", filepath.Join(root, "xz-fail"), "--cache", filepath.Join(root, "cache-xz"), "--harbor-username", "admin", "--harbor-password", "pw"})
})
})
t.Run("remote build missing compressed artifact", func(t *testing.T) {
tools := remoteBuildToolDir(t, baseImage, `exit 0`, `exit 0`)
t.Setenv("PATH", tools+string(os.PathListSeparator)+os.Getenv("PATH"))
expectCommandFatal(t, "stat compressed image", func() {
remoteBuildCmd([]string{"--inventory", invPath, "--node", "titan-15", "--artifact-ref", "registry.example/metis/titan-15", "--build-tag", "build-1", "--work-dir", filepath.Join(root, "stat-fail"), "--cache", filepath.Join(root, "cache-stat"), "--harbor-username", "admin", "--harbor-password", "pw"})
})
})
t.Run("remote build missing harbor credentials", func(t *testing.T) {
tools := remoteBuildToolDir(t, baseImage, `cp "${@: -1}" "${@: -1}.xz"`, `exit 0`)
t.Setenv("PATH", tools+string(os.PathListSeparator)+os.Getenv("PATH"))
t.Setenv("METIS_HARBOR_USERNAME", "")
t.Setenv("METIS_HARBOR_PASSWORD", "")
expectCommandFatal(t, "oras login", func() {
remoteBuildCmd([]string{"--inventory", invPath, "--node", "titan-15", "--artifact-ref", "registry.example/metis/titan-15", "--build-tag", "build-1", "--work-dir", filepath.Join(root, "login-fail"), "--cache", filepath.Join(root, "cache-login")})
})
})
}
func TestRemoteFlashFatalAndDeviceFlushEdges(t *testing.T) {
root := t.TempDir()
rawTools := fakeCommandDir(t, map[string]string{
"oras": `case "${1:-}" in
login) exit 0 ;;
pull) outdir="${@: -1}"; printf 'image' > "${outdir}/artifact.img"; exit 0 ;;
esac
exit 0`,
"sync": `exit 0`,
"blockdev": `exit 0`,
})
t.Setenv("PATH", rawTools+string(os.PathListSeparator)+os.Getenv("PATH"))
blocker := filepath.Join(root, "blocker")
if err := os.WriteFile(blocker, []byte("x"), 0o644); err != nil {
t.Fatal(err)
}
expectCommandFatal(t, "mkdir host tmp dir", func() {
remoteFlashCmd([]string{"--node", "titan-15", "--device", "hosttmp:///tmp", "--artifact-ref", "registry.example/metis/titan-15", "--work-dir", filepath.Join(root, "flash-hosttmp"), "--host-tmp-dir", filepath.Join(blocker, "child"), "--harbor-username", "admin", "--harbor-password", "pw"})
})
expectCommandFatal(t, "write image", func() {
remoteFlashCmd([]string{"--node", "titan-15", "--device", filepath.Join(blocker, "child"), "--artifact-ref", "registry.example/metis/titan-15", "--work-dir", filepath.Join(root, "flash-write"), "--harbor-username", "admin", "--harbor-password", "pw"})
})
pullFailTools := fakeCommandDir(t, map[string]string{
"oras": `case "${1:-}" in
login) exit 0 ;;
pull) printf 'pull failed' >&2; exit 4 ;;
esac
exit 0`,
})
t.Setenv("PATH", pullFailTools+string(os.PathListSeparator)+os.Getenv("PATH"))
expectCommandFatal(t, "oras pull", func() {
remoteFlashCmd([]string{"--node", "titan-15", "--device", filepath.Join(root, "out.img"), "--artifact-ref", "registry.example/metis/titan-15", "--work-dir", filepath.Join(root, "flash-pull"), "--harbor-username", "admin", "--harbor-password", "pw"})
})
emptyPullTools := fakeCommandDir(t, map[string]string{
"oras": `case "${1:-}" in
login) exit 0 ;;
pull) exit 0 ;;
esac
exit 0`,
})
t.Setenv("PATH", emptyPullTools+string(os.PathListSeparator)+os.Getenv("PATH"))
expectCommandFatal(t, "resolve artifact", func() {
remoteFlashCmd([]string{"--node", "titan-15", "--device", filepath.Join(root, "out.img"), "--artifact-ref", "registry.example/metis/titan-15", "--work-dir", filepath.Join(root, "flash-empty"), "--harbor-username", "admin", "--harbor-password", "pw"})
})
compressedTools := fakeCommandDir(t, map[string]string{
"oras": `case "${1:-}" in
login) exit 0 ;;
pull) outdir="${@: -1}"; printf 'compressed' > "${outdir}/artifact.img.xz"; exit 0 ;;
esac
exit 0`,
"xz": `exit 8`,
})
t.Setenv("PATH", compressedTools+string(os.PathListSeparator)+os.Getenv("PATH"))
expectCommandFatal(t, "xz stream decompress", func() {
remoteFlashCmd([]string{"--node", "titan-15", "--device", filepath.Join(root, "out.img"), "--artifact-ref", "registry.example/metis/titan-15", "--work-dir", filepath.Join(root, "flash-xz"), "--harbor-username", "admin", "--harbor-password", "pw"})
})
}
func TestCaptureStreamsAllowsBinaryOutput(t *testing.T) {
out, _ := captureStreams(t, func() {
_, _ = io.Copy(os.Stdout, bytes.NewBuffer([]byte("ok")))
})
if out != "ok" {
t.Fatalf("captureStreams = %q", out)
}
expectCommandFatal(t, "encode result", func() {
writeStructuredResult(map[string]any{"bad": func() {}})
})
}
func expectCommandFatal(t *testing.T, want string, fn func()) {
t.Helper()
oldFatalf := fatalf
fatalf = func(format string, args ...any) {
panic(fmt.Sprintf(format, args...))
}
defer func() { fatalf = oldFatalf }()
defer func() {
got := recover()
if got == nil {
t.Fatalf("expected fatal containing %q", want)
}
if !strings.Contains(fmt.Sprint(got), want) {
t.Fatalf("fatal = %v, want %q", got, want)
}
}()
fn()
}
func remoteBuildToolDir(t *testing.T, baseImage, xzBody, orasBody string) string {
t.Helper()
return fakeCommandDir(t, map[string]string{
"sfdisk": `cat <<'JSON'
{"partitiontable":{"sectorsize":512,"partitions":[{"start":3,"size":1,"type":"ef"},{"start":1,"size":2,"type":"83"}]}}
JSON`,
"debugfs": `if [[ "${1:-}" == "-w" ]]; then
cp "${3:-}" "${4:-}.commands"
exit 0
fi
if [[ "${1:-}" == "-R" ]]; then
state="${3:-}.commands"
set -- $2
case "${1:-}" in
stat)
mode="$(awk -v path="${2:-}" '$1=="sif" && $2==path {print $4}' "${state}" | tail -n1)"
mode="${mode: -4}"
printf 'Mode: %s\n' "${mode}"
exit 0
;;
dump)
local_path="$(awk -v path="${2:-}" '$1=="write" && $3==path {print $2}' "${state}" | tail -n1)"
cat "${local_path}" > "${3:-}"
exit 0
;;
esac
fi
exit 0`,
"xz": xzBody,
"oras": orasBody,
"sync": `exit 0`,
"blockdev": `exit 0`,
"cp-base": `cp "` + baseImage + `" "$1"`,
})
}

View File

@ -63,6 +63,108 @@ func TestRunAndTrimAndPkgVersionFallbacks(t *testing.T) {
} }
} }
func TestCollectUSBScratchMissingAndMalformedConfig(t *testing.T) {
cases := []struct {
name string
cat string
}{
{name: "cat error", cat: `exit 1`},
{name: "empty config", cat: `printf ' '`},
{name: "malformed config", cat: `printf '{'`},
{name: "missing scratch config", cat: `printf '{}'`},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
dir := fakeCollectorCommands(t, map[string]string{"cat": tc.cat})
t.Setenv("PATH", dir+string(os.PathListSeparator)+os.Getenv("PATH"))
if got := collectUSBScratch(); got != nil {
t.Fatalf("expected nil USB scratch config, got %#v", got)
}
})
}
}
func TestCollectUSBScratchDeviceResolutionAndBindHealth(t *testing.T) {
dir := fakeCollectorCommands(t, map[string]string{
"cat": `printf '%s\n' '{"usb_scratch":{"mountpoint":"/mnt/scratch","uuid":"u1","label":"scratch","fs":"xfs","bind_targets":["","/bad","/bind"]}}'`,
"findmnt": `target=""
for ((i=1; i<=$#; i++)); do
if [[ "${!i}" == "-T" ]]; then
j=$((i + 1))
target="${!j}"
break
fi
done
case "${target}" in
/mnt/scratch) printf 'SOURCE="" TARGET="/mnt/scratch" FSTYPE="ext4"\n' ;;
/bind) printf 'SOURCE="/mnt/scratch" TARGET="/bind" FSTYPE="none"\n' ;;
*) exit 1 ;;
esac`,
"blkid": `case "${1:-}" in
-U) printf '/dev/by-uuid/u1\n' ;;
-L) printf '/dev/by-label/scratch\n' ;;
-o) printf 'UUID=other\nLABEL=scratch\nTYPE=xfs\n' ;;
esac`,
})
t.Setenv("PATH", dir+string(os.PathListSeparator)+os.Getenv("PATH"))
scratch := collectUSBScratch()
if scratch == nil {
t.Fatal("expected USB scratch data")
}
if scratch.MountHealthy || scratch.UUIDHealthy || !scratch.LabelHealthy || scratch.BindHealthy {
t.Fatalf("unexpected scratch health: %#v", scratch)
}
if len(scratch.BindTargets) != 3 || scratch.BindTargets[2].Path != "/bind" || !scratch.BindTargets[2].Healthy {
t.Fatalf("unexpected bind target health: %#v", scratch.BindTargets)
}
}
func TestCollectUSBScratchDefaultsAndParserEdges(t *testing.T) {
dir := fakeCollectorCommands(t, map[string]string{
"cat": `printf '%s\n' '{"usb_scratch":{"mountpoint":"/mnt/scratch"}}'`,
"findmnt": `printf 'SOURCE="/dev/sdz1" TARGET="/mnt/scratch" FSTYPE="ext4"\n'`,
"blkid": `exit 1`,
})
t.Setenv("PATH", dir+string(os.PathListSeparator)+os.Getenv("PATH"))
scratch := collectUSBScratch()
if scratch == nil || !scratch.MountHealthy || !scratch.BindHealthy || scratch.FS != "ext4" {
t.Fatalf("unexpected default scratch health: %#v", scratch)
}
if source, fsType, mounted := mountInfo(""); source != "" || fsType != "" || mounted {
t.Fatalf("empty mountInfo = %q %q %v", source, fsType, mounted)
}
if bindHealthy("", "/mnt/scratch") || bindHealthy("/bind", "") {
t.Fatal("empty bind inputs should be unhealthy")
}
if got := resolveDeviceByUUID(""); got != "" {
t.Fatalf("empty UUID resolved to %q", got)
}
if got := resolveDeviceByLabel(""); got != "" {
t.Fatalf("empty label resolved to %q", got)
}
if got := blkidExport(""); len(got) != 0 {
t.Fatalf("empty blkid export = %#v", got)
}
values := parseKeyValues(`BROKEN A="b" C=d`)
if values["A"] != "b" || values["C"] != "d" {
t.Fatalf("parseKeyValues = %#v", values)
}
}
func fakeCollectorCommands(t *testing.T, scripts map[string]string) string {
t.Helper()
dir := t.TempDir()
for name, body := range scripts {
path := filepath.Join(dir, name)
if err := os.WriteFile(path, []byte("#!/usr/bin/env bash\nset -eu\n"+body+"\n"), 0o755); err != nil {
t.Fatalf("write %s: %v", name, err)
}
}
return dir
}
func fakeSentinelCommands(t *testing.T) string { func fakeSentinelCommands(t *testing.T) string {
t.Helper() t.Helper()
dir := t.TempDir() dir := t.TempDir()