package profile import ( "context" "errors" "strings" "testing" "time" ) // The decision each test defends is named in the test, per novox/hq ADR 0017. 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 0010. 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) } } }