Files
mesh-host/internal/profile/profile_test.go
T
jschoubben 73c010e7ef Stage 1 — the host reports what a machine is and can do
Tier 0's first slice, per novox/hq 03-DESIGN/01-to-be/05-the-node-host.md. It
applies nothing, connects to nothing, listens on nothing. 2.9 MB, static, no
dynamic dependencies: copy it onto a machine and run it is the whole install,
which is the property ADR 0041 rests on.

A capability is detected, never assumed. Every detector runs something that only
succeeds if the thing FUNCTIONS — the daemon is asked for its version, the
package database is queried, the firewall is asked to list a ruleset, which
needs the privilege as well as the tool. 04-ISSUES/007 is the fault this
prevents: a client on disk with its daemon down looks exactly like a working
runtime, and a node assigned work on that basis fails when the work arrives.

Every verdict carries the reason and the method. A capability reported absent
with no reason is the same fault in a new place: something nobody can act on.

Two bugs found by running rather than reasoning, both silent:

systemctl is-system-running exits non-zero for every state except `running` —
including `degraded`, which means units failed and the init is emphatically
there. Reading the exit code reported NO service manager on a machine whose init
it was. That is 007 in the mirror, and both directions place work wrongly. A
verdict now reads what a tool says about itself, not only how it exited.

And `mesh-host inventory --json` printed text: the standard library stops
parsing at the first non-flag argument, so the flag sat unread and the command
exited 0 having ignored what was asked. The parser now takes the subcommand off
the front, and a stray or mistyped argument is refused rather than dropped.

Detection deliberately does NOT follow ADR 0008. That rule governs applying
state, where a failed step means the machine is not what was asked for. A failed
probe is a finding — "absent, because the probe failed" — and aborting would
replace one legible absence with total ignorance of the rest.

25 tests: structure and logic with a fake runner, and the same detectors against
this machine, because a test that fakes the system under detection asserts only
that the fake behaves as expected.
2026-08-26 00:25:08 +02:00

196 lines
7.5 KiB
Go

package profile
import (
"context"
"errors"
"strings"
"testing"
"time"
)
// The decision each test defends is named in the test, per novox/hq ADR 0034. These cover
// structure and logic; profile_system_test.go covers the same detectors against the real
// machine, because a test that fakes the system under detection asserts only that the fake
// behaves as expected.
func TestIssue007_installedIsNotUsable(t *testing.T) {
// 04-ISSUES/007 — an installed package is not a capability. The client was present and the
// daemon was down, and the node took work it could not do. A detector whose command fails
// must report ABSENT, however installed the thing looks.
failing := func(context.Context, string, ...string) (string, error) {
return "", errors.New("docker exited 1: Cannot connect to the Docker daemon")
}
got := Detect(context.Background(), Default(failing), time.Second)
if got.Has(CapContainerRuntime) {
t.Fatal("a runtime whose daemon refuses the connection was reported present")
}
for _, v := range got.Capabilities {
if v.Name != CapContainerRuntime {
continue
}
if !strings.Contains(v.Detail, "Cannot connect") {
t.Fatalf("the reason was lost; detail was %q", v.Detail)
}
if v.How == "" {
t.Fatal("a verdict with no stated method cannot be checked when it is wrong")
}
}
}
func TestEveryVerdictSaysHowItKnows(t *testing.T) {
// A capability reported absent with no reason is the fault in a new place: something
// nobody can act on. Both outcomes must carry a method.
for _, runner := range []Runner{
func(context.Context, string, ...string) (string, error) { return "ok", nil },
func(context.Context, string, ...string) (string, error) { return "", errors.New("nope") },
} {
for _, v := range Detect(context.Background(), Default(runner), time.Second).Capabilities {
if strings.TrimSpace(v.How) == "" {
t.Errorf("%s reports present=%v with no stated method", v.Name, v.Present)
}
if strings.TrimSpace(v.Detail) == "" {
t.Errorf("%s reports present=%v with no detail", v.Name, v.Present)
}
}
}
}
func TestDetectionSurvivesAFailingProbe(t *testing.T) {
// Deliberately NOT ADR 0008. That rule governs APPLYING state, where a failed step means
// the machine is not what was asked for. A failed probe is a finding, and aborting would
// replace one legible absence with total ignorance of the rest.
only := func(ctx context.Context, name string, args ...string) (string, error) {
if name == "pacman" {
return "pacman 7.0.0", nil
}
return "", errors.New("not here")
}
got := Detect(context.Background(), Default(only), time.Second)
if len(got.Capabilities) != len(Default(nil)) {
t.Fatalf("expected every detector to report, got %d of %d",
len(got.Capabilities), len(Default(nil)))
}
if !got.Has(CapPackageManager) {
t.Error("the one working capability was lost among the failures")
}
}
func TestAProbeCannotHangTheHost(t *testing.T) {
// A host that blocks forever on a wedged command reports nothing at all, which is worse
// than reporting the capability absent.
blocking := func(ctx context.Context, name string, args ...string) (string, error) {
<-ctx.Done()
return "", ctx.Err()
}
done := make(chan Profile, 1)
go func() { done <- Detect(context.Background(), Default(blocking), 50*time.Millisecond) }()
select {
case got := <-done:
if got.Has(CapFirewall) {
t.Error("a probe that never answered was reported present")
}
case <-time.After(5 * time.Second):
t.Fatal("detection did not return — a wedged probe hung the host")
}
}
func TestUnknownCapabilityIsAbsentNotAnError(t *testing.T) {
// Asking about a capability nothing detects is a question with a true answer.
p := Detect(context.Background(), nil, time.Second)
if p.Has("something-nothing-detects") {
t.Error("an undetected capability reported present")
}
}
func TestMissingListsWhatCannotBeAskedOf(t *testing.T) {
// What a node CANNOT do is the half that decides whether work may be placed on it.
none := func(context.Context, string, ...string) (string, error) { return "", errors.New("no") }
missing := Detect(context.Background(), Default(none), time.Second).Missing()
if len(missing) == 0 {
t.Fatal("everything failed and nothing was reported missing")
}
for i := 1; i < len(missing); i++ {
if missing[i-1] > missing[i] {
t.Fatalf("Missing() is not ordered: %v", missing)
}
}
}
func TestTheProfileIsOrdered(t *testing.T) {
// Two runs on an unchanged machine produce the same profile. Without this, comparing what
// a node was against what it is would report differences that are only ordering.
ok := func(context.Context, string, ...string) (string, error) { return "fine", nil }
first := Detect(context.Background(), Default(ok), time.Second)
second := Detect(context.Background(), Default(ok), time.Second)
if len(first.Capabilities) != len(second.Capabilities) {
t.Fatal("two runs disagreed on how many capabilities exist")
}
for i := range first.Capabilities {
if first.Capabilities[i].Name != second.Capabilities[i].Name {
t.Fatalf("ordering is not stable at %d: %q then %q",
i, first.Capabilities[i].Name, second.Capabilities[i].Name)
}
}
}
func TestADegradedInitIsStillAnInit(t *testing.T) {
// Found by running against a real machine, not by reasoning. `systemctl is-system-running`
// exits non-zero for every state except `running` — including `degraded`, which means some
// units failed and the init is emphatically present. Reading the exit code declared no
// service manager on a machine whose init it was.
//
// This is 04-ISSUES/007 in the mirror: 007 is installed-but-broken reported present; this
// is working-but-imperfect reported absent. Both make the mesh place work wrongly.
degraded := func(ctx context.Context, name string, args ...string) (string, error) {
if name == "systemctl" {
return "degraded\n", errors.New("systemctl exited 1: ")
}
return "", errors.New("not here")
}
got := Detect(context.Background(), Default(degraded), time.Second)
if !got.Has(CapServiceManager) {
t.Fatal("a degraded init was reported absent — the machine would refuse work it can do")
}
for _, v := range got.Capabilities {
if v.Name == CapServiceManager && v.Detail != "degraded" {
t.Errorf("the state was lost; detail was %q", v.Detail)
}
}
}
func TestAnInitThatIsNotManagingThisMachineIsAbsent(t *testing.T) {
// The other side of the same rule. `offline` means it is installed and not in charge —
// which must not be read as present just because a state came back.
for _, state := range []string{"offline", "unknown"} {
runner := func(ctx context.Context, name string, args ...string) (string, error) {
if name == "systemctl" {
return state + "\n", errors.New("systemctl exited 1: ")
}
return "", errors.New("not here")
}
if Detect(context.Background(), Default(runner), time.Second).Has(CapServiceManager) {
t.Errorf("state %q was reported as a working service manager", state)
}
}
}
func TestAFailureWithNoMessageStillCarriesAReason(t *testing.T) {
// systemctl fails with empty stderr, which produced a verdict reading "exited 1:" — absent,
// with nothing anyone could act on.
silent := func(context.Context, string, ...string) (string, error) { return "", errors.New("") }
for _, v := range Detect(context.Background(), Default(silent), time.Second).Capabilities {
if v.Present {
continue
}
if strings.TrimSpace(v.Detail) == "" || strings.HasSuffix(strings.TrimSpace(v.Detail), ":") {
t.Errorf("%s is absent for no stated reason: %q", v.Name, v.Detail)
}
}
}