package profile import ( "context" "encoding/json" "errors" "os" "runtime" "strings" "testing" "time" ) // The parsing layer, with the machine's files given (novox/hq ADR 0017 allows a fake here and only here); // the real machine is asked below. var files = map[string]string{ "/proc/sys/kernel/osrelease": "7.2.8-arch1-2\n", "/etc/os-release": "NAME=\"Arch Linux\"\nPRETTY_NAME=\"Arch Linux\"\nID=arch\nBUILD_ID=rolling\n", "/proc/cpuinfo": "processor\t: 0\nmodel name\t: AMD Ryzen 9 5900HS\nphysical id\t: 0\ncore id\t\t: 0\n\n" + "processor\t: 1\nmodel name\t: AMD Ryzen 9 5900HS\nphysical id\t: 0\ncore id\t\t: 0\n\n" + "processor\t: 2\nmodel name\t: AMD Ryzen 9 5900HS\nphysical id\t: 0\ncore id\t\t: 1\n", "/proc/meminfo": "MemTotal: 32000000 kB\nMemFree: 1 kB\n", "/proc/stat": "cpu 1 2 3\nbtime 1791800000\n", "/proc/self/mounts": "/dev/nvme0n1p2 / btrfs rw 0 0\n/dev/nvme0n1p2 /home btrfs rw 0 0\n" + "proc /proc proc rw 0 0\ntank/data /srv/my\\040data zfs rw 0 0\n" + "server:/x /mnt/x nfs4 rw 0 0\n/dev/loop3 /snap/core/1 squashfs ro 0 0\n", } func given(read map[string]string) Sources { return Sources{ Read: func(p string) ([]byte, error) { if s, ok := read[p]; ok { return []byte(s), nil } return nil, &os.PathError{Op: "open", Path: p, Err: os.ErrNotExist} }, Readlink: func(string) (string, error) { return "/usr/share/zoneinfo/Europe/Brussels", nil }, Run: func(context.Context, string, ...string) (string, error) { return "none\n", errors.New("exited 1") }, Statfs: func(p string) (int64, int64, error) { return 1000, 400, nil }, Now: func() time.Time { return time.Date(2026, 10, 12, 15, 0, 0, 0, time.UTC) }, } } func TestMachine_saysEveryFactItCanRead(t *testing.T) { m := DescribeMachine(context.Background(), given(files)) if m.KernelRelease != "7.2.8-arch1-2" || m.Distribution != "Arch Linux" || m.Version != "rolling" { t.Errorf("kernel %q, distribution %q %q", m.KernelRelease, m.Distribution, m.Version) } if m.Processor != "AMD Ryzen 9 5900HS" || m.Cores != 2 || m.Threads != 3 { t.Errorf("processor %q, %d cores, %d threads", m.Processor, m.Cores, m.Threads) } if m.MemoryBytes != 32000000*1024 { t.Errorf("memory %d", m.MemoryBytes) } if m.Virtual != "physical" { t.Errorf("systemd-detect-virt said none and exited 1; read as %q", m.Virtual) } if m.BootedAt == nil || m.BootedAt.Unix() != 1791800000 { t.Errorf("booted at %v", m.BootedAt) } if m.Timezone != "Europe/Brussels" { t.Errorf("timezone %q", m.Timezone) } // One btrfs device under its shortest mount, the zfs dataset with its escaped space, and no network or // read-only image filesystem. var mounts []string for _, f := range m.Filesystems { mounts = append(mounts, f.Mount) if f.SizeBytes != 1000 || f.FreeBytes != 400 { t.Errorf("%s: size %d free %d", f.Mount, f.SizeBytes, f.FreeBytes) } } if got := strings.Join(mounts, ","); got != "/,/srv/my data" { t.Errorf("filesystems %q", got) } // Everything read is not unknown; the public address is, with why. for fact := range m.Unknown { if fact != FactPublicAddress { t.Errorf("%s said unknown though it was readable: %s", fact, m.Unknown[fact]) } } if m.Unknown[FactPublicAddress] == "" { t.Error("the public address is not said, and not said unknown either") } } // The acceptance of issue 160: a machine that cannot determine a fact says so rather than reporting a zero. func TestMachine_aFactItCannotReadIsSaidUnknownNeverZero(t *testing.T) { src := given(map[string]string{}) src.Readlink = func(string) (string, error) { return "", os.ErrNotExist } src.Run = func(context.Context, string, ...string) (string, error) { return "", errors.New("not found") } m := DescribeMachine(context.Background(), src) raw, _ := json.Marshal(m) var said map[string]any _ = json.Unmarshal(raw, &said) for key, v := range said { if key == "observed_at" || key == "unknown" { continue } t.Errorf("%s is said as %v from a machine where nothing could be read", key, v) } for _, fact := range MachineFacts { if strings.TrimSpace(m.Unknown[fact]) == "" { t.Errorf("%s is neither said nor said unknown with why", fact) } } } func TestMachine_aHungFilesystemCannotHangTheReport(t *testing.T) { src := given(files) block := make(chan struct{}) defer close(block) src.Statfs = func(string) (int64, int64, error) { <-block; return 0, 0, nil } ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) defer cancel() m := DescribeMachine(ctx, src) if len(m.Filesystems) != 0 || !strings.Contains(m.Unknown[FactFilesystems], "did not finish") { t.Errorf("filesystems %v, unknown %q", m.Filesystems, m.Unknown[FactFilesystems]) } } func TestMachine_coresUnsaidAreUnknownNotGuessed(t *testing.T) { p := processorOf("processor\t: 0\nModel\t: Raspberry Pi 4\nprocessor\t: 1\n") if p.model != "Raspberry Pi 4" || p.threads != 2 || p.cores != 0 || p.unknown[FactCores] == "" { t.Errorf("%+v", p) } } // Against the real machine (ADR 0017): what it says agrees with what this process sees. func TestAgainstThisMachine_machineSaysWhatItIs(t *testing.T) { if runtime.GOOS != "linux" { t.Skip("the facts are read from Linux's own files") } ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() m := DescribeMachine(ctx, RealSources()) if m.MemoryBytes <= 0 && m.Unknown[FactMemory] == "" { t.Error("memory neither said nor unknown") } if m.Threads != 0 && m.Threads < runtime.NumCPU() { t.Errorf("%d threads said, and this process alone may use %d", m.Threads, runtime.NumCPU()) } if m.BootedAt != nil && m.BootedAt.After(time.Now()) { t.Errorf("booted in the future: %v", m.BootedAt) } for _, f := range m.Filesystems { if f.SizeBytes <= 0 || f.FreeBytes > f.SizeBytes { t.Errorf("%s: size %d free %d", f.Mount, f.SizeBytes, f.FreeBytes) } } for _, fact := range MachineFacts { if why, unknown := m.Unknown[fact]; unknown && strings.TrimSpace(why) == "" { t.Errorf("%s unknown with no why", fact) } } raw, _ := json.MarshalIndent(m, "", " ") t.Logf("%s", raw) }