package main import ( "context" "testing" "time" "github.com/novox/mesh-controller/internal/catalogue" "github.com/novox/mesh-controller/internal/conditions" "github.com/novox/mesh-controller/internal/inventory" "github.com/novox/mesh-controller/internal/link" ) // A module may declare its unhealthy urgent (novox/hq ADR 0355): its module-unhealthy condition is then raised // urgent; without the field it stays a warning; a wait for a new login stays a warning whatever the module says. // judgedTwice judges one machine's statement twice, over a catalogue in which mail declares its unhealthy // urgent and notes says nothing, and returns what is open. func judgedTwice(t *testing.T, unhealthy map[string][]inventory.ResourceHealth) []conditions.Condition { t.Helper() ctx := t.Context() k, _ := withConditionsInMemory(t) was := readHoldingFor t.Cleanup(func() { readHoldingFor = was }) readHoldingFor = func(_ context.Context, _ *inventory.Inventory, open []conditions.Condition) (*holding, error) { return &holding{ctx: context.Background(), open: open, providers: map[string]providerLookup{}, shelf: map[string]catalogue.Manifest{ "mail": {Module: "mail", Version: "1", Unhealthy: &catalogue.UnhealthyPolicy{ Severity: catalogue.UnhealthyUrgent, Why: "mail is refused or stuck for everyone with a mailbox"}}, "notes": {Module: "notes", Version: "1"}, }}, nil } streaks := map[string]int{} for look := 1; look <= 2; look++ { for m := range unhealthy { streaks[m] = look } if err := judgeModuleHealth(ctx, nil, k, "anchor", unhealthy, streaks, time.Now()); err != nil { t.Fatal(err) } } open, err := k.Open(ctx) if err != nil { t.Fatal(err) } return open } func containerDown(module string) inventory.ResourceHealth { return inventory.ResourceHealth{Module: module, Resource: module + ".web", Kind: "container", Target: module, State: link.StateUnhealthy, Reason: "down"} } func TestAModuleThatDeclaresItsUnhealthyUrgentIsRaisedUrgent(t *testing.T) { open := judgedTwice(t, map[string][]inventory.ResourceHealth{"mail": {containerDown("mail")}}) c := conditionOf(open, "module.mail.anchor.unhealthy") if c == nil { t.Fatalf("mail's unhealthy condition was not raised: %+v", open) } if c.Severity != conditions.Urgent { t.Errorf("mail declares its unhealthy urgent, and it was raised %s", c.Severity) } } func TestAModuleThatSaysNothingOfItsUnhealthyStaysAWarning(t *testing.T) { open := judgedTwice(t, map[string][]inventory.ResourceHealth{"notes": {containerDown("notes")}}) c := conditionOf(open, "module.notes.anchor.unhealthy") if c == nil { t.Fatalf("notes' unhealthy condition was not raised: %+v", open) } if c.Severity != conditions.Warning { t.Errorf("notes says nothing of its unhealthy, and it was raised %s", c.Severity) } } // Only the module-unhealthy condition changes (ADR 0355 ยง4): a wait for a new login (ADR 0254) of a module that // declares its unhealthy urgent stays a warning. func TestAWaitForANewLoginStaysAWarningWhenTheModuleDeclaresItsUnhealthyUrgent(t *testing.T) { open := judgedTwice(t, map[string][]inventory.ResourceHealth{"mail": {relogin("mail", "operator"), userUnit("mail", "operator")}}) c := conditionOf(open, "module.mail.anchor.relogin-needed") if c == nil { t.Fatalf("mail's relogin-needed condition was not raised: %+v", open) } if c.Severity != conditions.Warning { t.Errorf("a wait for a new login was raised %s; want a warning", c.Severity) } if conditionOf(open, "module.mail.anchor.unhealthy") != nil { t.Errorf("a wait for a new login raised mail's unhealthy condition beside it: %+v", open) } }