201 lines
6.6 KiB
Go
201 lines
6.6 KiB
Go
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)
|
|
}
|
|
}
|