//go:build integration package main import ( "strings" "testing" "time" "github.com/novox/mesh-controller/internal/conditions" "github.com/novox/mesh-controller/internal/inventory" "github.com/novox/mesh-controller/internal/link" ) func TestTheNeedsOperatorConditionNamesTheAct(t *testing.T) { k, _ := withConditionsInMemory(t) ctx := t.Context() rs := map[string][]inventory.ResourceHealth{"mounts": {waitingResource(passwordWait)}} if err := judgeModuleHealth(ctx, nil, k, "workstation", rs, map[string]int{"mounts": 1}, time.Now()); err != nil { t.Fatal(err) } if got, _ := needsOperatorOpen(t, k); got != nil { t.Fatal("raised on one statement") } if err := judgeModuleHealth(ctx, nil, k, "workstation", rs, map[string]int{"mounts": 2}, time.Now()); err != nil { t.Fatal(err) } got, open := needsOperatorOpen(t, k) if got == nil { t.Fatalf("not raised on two statements: %+v", open) } for _, c := range open { if c.Kind == kindModuleUnhealthy { t.Fatalf("raised as a fault too: %+v", c) } } if got.Kind != kindNeedsOperator || got.Resolver != conditions.ResolverOperator || got.Severity != conditions.Warning { t.Fatalf("the condition: %+v", got) } if !strings.Contains(got.Needs, "type the password of the source games at workstation's desk prompt") || !strings.Contains(got.Explanation, "hidden prompt opens at the desk") { t.Fatalf("its needs do not name the act: %q", got.Needs) } if strings.Contains(strings.ToLower(got.Explanation), "nothing for you") || !strings.Contains(got.Explanation, "nothing was undone") { t.Fatalf("its explanation: %q", got.Explanation) } if !strings.Contains(got.Summary, "smb-password-games") || !strings.Contains(got.Summary, "nox secret ask workstation mounts smb-password-games") { t.Fatalf("its summary does not name the secret and the line: %q", got.Summary) } // Long open is still a warning: only the operator can end it. if err := judgeModuleHealth(ctx, nil, k, "workstation", rs, map[string]int{"mounts": 3}, time.Now().Add(48*time.Hour)); err != nil { t.Fatal(err) } if got, _ := needsOperatorOpen(t, k); got == nil || got.Severity == conditions.Urgent { t.Fatalf("after two days: %+v", got) } // Given: the next statement does not say it, and it clears. if err := judgeModuleHealth(ctx, nil, k, "workstation", map[string][]inventory.ResourceHealth{}, nil, time.Now()); err != nil { t.Fatal(err) } if got, _ := needsOperatorOpen(t, k); got != nil { t.Fatal("not cleared once given") } } func TestASettingsWaitAsksForTheApproval(t *testing.T) { k, _ := withConditionsInMemory(t) ctx := t.Context() rs := map[string][]inventory.ResourceHealth{"mounts": {waitingResource(usernameWait)}} for i := 1; i <= 2; i++ { if err := judgeModuleHealth(ctx, nil, k, "workstation", rs, map[string]int{"mounts": i}, time.Now()); err != nil { t.Fatal(err) } } got, _ := needsOperatorOpen(t, k) if got == nil || !strings.Contains(got.Needs, "approve smb-users of mounts on workstation when it is proposed to you") { t.Fatalf("the condition: %+v", got) } } func TestAWaitThatFailsItsCheckRaisesUnhealthy(t *testing.T) { k, _ := withConditionsInMemory(t) ctx := t.Context() checked := checkWaiting("workstation", []inventory.ResourceHealth{waitingResource(passwordWait)}, factsGiven(map[string]time.Time{"smb-password-games": time.Now()})) rs := map[string][]inventory.ResourceHealth{"mounts": checked} for i := 1; i <= 2; i++ { if err := judgeModuleHealth(ctx, nil, k, "workstation", rs, map[string]int{"mounts": i}, time.Now()); err != nil { t.Fatal(err) } } got, open := needsOperatorOpen(t, k) if got != nil { t.Fatalf("a wait for a secret given raised needs-operator: %+v", got) } unhealthy := false for _, c := range open { unhealthy = unhealthy || c.Key == moduleUnhealthyKey("mounts", "workstation") } if !unhealthy { t.Fatalf("not raised as unhealthy: %+v", open) } } func TestANeedsOperatorThatBecameUnhealthySaysSo(t *testing.T) { k, _ := withConditionsInMemory(t) ctx := t.Context() waiting := map[string][]inventory.ResourceHealth{"mounts": {waitingResource(passwordWait)}} for i := 1; i <= 2; i++ { if err := judgeModuleHealth(ctx, nil, k, "workstation", waiting, map[string]int{"mounts": i}, time.Now()); err != nil { t.Fatal(err) } } broken := waitingResource() broken.State, broken.Reason, broken.Waits = link.StateUnhealthy, "the source games refused its login", nil for i := 3; i <= 4; i++ { if err := judgeModuleHealth(ctx, nil, k, "workstation", map[string][]inventory.ResourceHealth{"mounts": {broken}}, map[string]int{"mounts": i}, time.Now()); err != nil { t.Fatal(err) } } got, open := needsOperatorOpen(t, k) if got != nil { t.Fatal("needs-operator still open after it became a fault") } found := false for _, c := range open { found = found || c.Key == moduleUnhealthyKey("mounts", "workstation") } if !found { t.Fatalf("the fault is not raised: %+v", open) } }