package inventory import ( "errors" "reflect" "testing" ) // novox/hq ADR 0100: a node is adopted or converged, and the controller records which. func TestANodeAddedWithoutSayingIsConverged(t *testing.T) { inv := fresh(t) if _, err := inv.AddNode(t.Context(), "anchor"); err != nil { t.Fatal(err) } n, err := inv.NodeByName(t.Context(), "anchor") if err != nil { t.Fatal(err) } if n.Adopted || !n.AdoptedSince.IsZero() { t.Fatalf("a node nobody said anything about reads as adopted: %+v", n) } } func TestAnAdoptedNodeRoundTripsThroughEveryReading(t *testing.T) { inv := fresh(t) made, err := inv.AddNodeAs(t.Context(), "anchor", true) if err != nil { t.Fatal(err) } if !made.Adopted || made.AdoptedSince.IsZero() { t.Fatalf("added adopted, got %+v", made) } byName, err := inv.NodeByName(t.Context(), "anchor") if err != nil { t.Fatal(err) } all, err := inv.Nodes(t.Context()) if err != nil { t.Fatal(err) } if !byName.Adopted || len(all) != 1 || !all[0].Adopted { t.Fatalf("adoption did not survive reading back: %+v %+v", byName, all) } // And through a token: enrolment reads the node from the token it spends. issued, err := inv.IssueToken(t.Context(), "anchor", 60e9) if err != nil { t.Fatal(err) } claimed, err := inv.Claim(t.Context(), issued.Secret, "a-key", false) if err != nil { t.Fatal(err) } if !claimed.Adopted { t.Fatal("the node a token claims lost its mode") } } func TestTakingIsRefusedOnAConvergedNodeAndForAnUnassignedModule(t *testing.T) { inv := fresh(t) if err := inv.RegisterModule(t.Context(), manifest("hello-web", nil, nil), Source{}); err != nil { t.Fatal(err) } if _, err := inv.AddNode(t.Context(), "converged"); err != nil { t.Fatal(err) } if err := inv.Assign(t.Context(), "converged", "hello-web"); err != nil { t.Fatal(err) } if err := inv.Take(t.Context(), "converged", "hello-web"); !errors.Is(err, ErrNotAdopted) { t.Fatalf("taking on a converged node gave %v", err) } if _, err := inv.AddNodeAs(t.Context(), "anchor", true); err != nil { t.Fatal(err) } if err := inv.Take(t.Context(), "anchor", "hello-web"); !errors.Is(err, ErrNotAssigned) { t.Fatalf("taking an unassigned module gave %v", err) } } func TestATakenModuleOutlivesItsAssignmentAndReturningToAdopted(t *testing.T) { inv := fresh(t) for _, m := range []string{"hello-web", "postgres"} { if err := inv.RegisterModule(t.Context(), manifest(m, nil, nil), Source{}); err != nil { t.Fatal(err) } } if _, err := inv.AddNodeAs(t.Context(), "anchor", true); err != nil { t.Fatal(err) } for _, m := range []string{"hello-web", "postgres"} { if err := inv.Assign(t.Context(), "anchor", m); err != nil { t.Fatal(err) } } if err := inv.Take(t.Context(), "anchor", "postgres"); err != nil { t.Fatal(err) } if err := inv.Take(t.Context(), "anchor", "postgres"); err != nil { t.Fatalf("taking twice is not an error: %v", err) } if err := inv.Unassign(t.Context(), "anchor", "postgres"); err != nil { t.Fatal(err) } taken, err := inv.Taken(t.Context(), "anchor") if err != nil { t.Fatal(err) } if !reflect.DeepEqual(taken, []string{"postgres"}) { t.Fatalf("unassigning un-took it: %v", taken) } took, err := inv.Converge(t.Context(), "anchor") if err != nil { t.Fatal(err) } if !reflect.DeepEqual(took, []string{"hello-web"}) { t.Fatalf("converging took %v; it takes every assigned module not yet taken", took) } n, _ := inv.NodeByName(t.Context(), "anchor") if n.Adopted { t.Fatal("converged node still reads adopted") } if err := inv.SetAdopted(t.Context(), "anchor", true); err != nil { t.Fatal(err) } taken, _ = inv.Taken(t.Context(), "anchor") if !reflect.DeepEqual(taken, []string{"hello-web", "postgres"}) { t.Fatalf("returning to adopted lost what was taken: %v", taken) } }