package main import ( "encoding/json" "os" "sort" "strings" "testing" "time" stdio "git.novox.be/novox/mesh-sdk/go" ) func tool(t *testing.T, m *Machine, name string) stdio.Tool { t.Helper() for _, tl := range Tools(m) { if tl.Name == name { return tl } } t.Fatalf("no tool %s", name) return stdio.Tool{} } func TestTheManifestNamesExactlyTheToolsTheBundleServesAndNoMachine(t *testing.T) { raw, err := os.ReadFile("../../module.json") if err != nil { t.Fatal(err) } var m struct { Tools []string `json:"tools"` Resources []any `json:"resources"` } if err := json.Unmarshal(raw, &m); err != nil { t.Fatal(err) } var served []string for _, tl := range Tools(newFake(t).machine()) { served = append(served, tl.Name) if !strings.HasSuffix(tl.Description, "(r)") { t.Errorf("%s only reads and does not say so", tl.Name) } } sort.Strings(served) sort.Strings(m.Tools) if strings.Join(served, ",") != strings.Join(m.Tools, ",") { t.Fatalf("served %v, listed %v", served, m.Tools) } if len(m.Resources) != 0 { t.Errorf("the module only reads, and declares %d resources", len(m.Resources)) } for _, banned := range []string{"g14", "novox", "ace", "shanks", "jochen", "/home/"} { if strings.Contains(string(raw), banned) { t.Errorf("the manifest says %q", banned) } } } func TestDiskLoadSamplesTwoSecondsAndNamesFiveProcessesByDefault(t *testing.T) { f := aServerUnderWrites(t) out, err := tool(t, f.machine(), "disk_load").Run(map[string]any{}) if err != nil { t.Fatal(err) } l := out.(Load) if f.waited != 2*time.Second || l.WindowS != 2 || len(l.Processes) != 3 || len(l.Units) != 3 || len(l.Filesystems) != 3 { t.Errorf("waited %s, %+v", f.waited, l) } } func TestTheWindowIsAWholeNumberOfSecondsHeldAtTen(t *testing.T) { f := aServerUnderWrites(t) load := tool(t, f.machine(), "disk_load") for _, bad := range []any{0.0, -1.0, 1.5, "soon", true} { if _, err := load.Run(map[string]any{"seconds": bad}); err == nil { t.Errorf("seconds %v was accepted", bad) } } if _, err := load.Run(map[string]any{"seconds": 30.0}); err != nil || f.waited != 10*time.Second { t.Errorf("30 s was not held at 10: waited %s, %v", f.waited, err) } if _, err := load.Run(map[string]any{"processes": -1.0}); err == nil { t.Error("processes -1 was accepted") } } func TestDiskTopRanksUnitsFromTheirCgroupsCountingEachByteOnce(t *testing.T) { f := aServerUnderWrites(t) out, err := tool(t, f.machine(), "disk_top").Run(map[string]any{}) if err != nil { t.Fatal(err) } if out.(map[string]any)["by"] != "unit" || f.waited != 2*time.Second { t.Errorf("the default: %+v, waited %s", out, f.waited) } got := out.(map[string]any)["top"].([]Process) want := []Process{ {Unit: "docker-" + store + ".scope", Container: "store-check", ReadMiBs: 5, WriteMiBs: 50, ReadMiB: 10, WriteMiB: 100, ReadIOPS: 100, WriteIOPS: 500}, {Unit: "editor.scope", WriteMiBs: 2.5, WriteMiB: 5, WriteIOPS: 25}, {Unit: "systemd-journald.service", WriteMiBs: 1, WriteMiB: 2, WriteIOPS: 10}, } if g, w := must(json.Marshal(got)), must(json.Marshal(want)); string(g) != string(w) { t.Errorf("by unit\n got %s\nwant %s", g, w) } } func TestDiskTopByContainerNamesOnlyContainers(t *testing.T) { out, err := tool(t, aServerUnderWrites(t).machine(), "disk_top").Run(map[string]any{"by": "container", "seconds": 1.0}) if err != nil { t.Fatal(err) } got := out.(map[string]any)["top"].([]Process) if len(got) != 1 || got[0].Container != "store-check" || got[0].Unit != "" || got[0].WriteMiBs != 100 { t.Errorf("by container %+v", got) } } func TestDiskTopByProcessSaysWhatItCouldNotRead(t *testing.T) { f := aServerUnderWrites(t) top := tool(t, f.machine(), "disk_top") out, err := top.Run(map[string]any{"by": "process", "limit": 2.0}) if err != nil { t.Fatal(err) } got := out.(map[string]any)["top"].([]Process) if len(got) != 2 || got[0].PID != 100 || got[1].PID != 101 || got[1].ReadMiBs != 5 || got[1].Command != "postgres" { t.Errorf("by process %+v", got) } if out.(map[string]any)["processes_unreadable"] != 1 { t.Errorf("unreadable not said: %+v", out) } if _, err := top.Run(map[string]any{"by": "disk"}); err == nil { t.Error("by=disk was accepted") } } func TestAContainerTheRuntimeCannotNameIsSaidByItsShortID(t *testing.T) { f := aServerUnderWrites(t) delete(f.answers, "docker ps --no-trunc --format {{.ID}}\t{{.Names}}") out, err := tool(t, f.machine(), "disk_top").Run(map[string]any{"by": "process"}) if err != nil { t.Fatal(err) } if got := out.(map[string]any)["top"].([]Process); got[0].Container != store[:12] { t.Errorf("%+v", got[0]) } if n := len(f.calls); n != 1 { t.Errorf("the runtime was asked %d times, not once", n) } } func TestFilesystemsAreSaidOnceWithTheirOtherMounts(t *testing.T) { out, err := tool(t, aServerUnderWrites(t).machine(), "disk_filesystems").Run(nil) if err != nil { t.Fatal(err) } fss := out.(map[string]any)["filesystems"].([]Filesystem) var mounts []string for _, fs := range fss { mounts = append(mounts, fs.Mount) } if strings.Join(mounts, ",") != "/,/boot,/mnt/back up" { t.Fatalf("mounts %v: proc, tmpfs and overlay are not stored data, and a second mount is not a second filesystem", mounts) } root := fss[0] if strings.Join(root.AlsoAt, ",") != "/var/lib/docker" || root.Device != "dm-0" || root.Type != "ext4" || root.SizeGiB != 100 || root.UsedGiB != 90 || root.AvailGiB != 5 || root.UsedPct != 94.7 || root.InodesUsed != 40 { t.Errorf("root %+v", root) } if fss[1].Device != "nvme0n1p1" || fss[1].UsedPct != 50 || fss[1].InodesUsed != 0 { t.Errorf("boot %+v", fss[1]) } if fss[2].Unanswered == "" { t.Errorf("a filesystem statfs could not read says so: %+v", fss[2]) } said := strings.Join(out.(map[string]any)["said"].([]string), "\n") if !strings.Contains(said, "/ (ext4, /dev/mapper/root): 94.7 % used, 5.0 GiB free of 100.0 GiB — NEARLY FULL") || !strings.Contains(said, "/boot (vfat, /dev/nvme0n1p1): 50.0 % used, 0.5 GiB free of 1.0 GiB\n") { t.Errorf("said:\n%s", said) } } func TestTheMountTablesEscapesAreUndone(t *testing.T) { for in, want := range map[string]string{`/a\040b`: "/a b", `/tab\011x`: "/tab\tx", `/plain`: "/plain", `/end\04`: `/end\04`, `/back\134slash`: `/back\slash`} { if got := unescape(in); got != want { t.Errorf("%q: %q, not %q", in, got, want) } } } func TestDiskDevicesAnswersWithoutWaiting(t *testing.T) { f := aServerUnderWrites(t) out, err := tool(t, f.machine(), "disk_devices").Run(map[string]any{}) if err != nil { t.Fatal(err) } if f.waited != 0 || len(out.(map[string]any)["devices"].([]DeviceInfo)) != 3 { t.Errorf("waited %s, %+v", f.waited, out) } }