package inventory import ( "testing" "github.com/novox/mesh-controller/internal/catalogue" ) // novox/hq ADR 0217: a settings layer can be read, and the one a set or a clear replaced is kept, so // a previous value is one command away rather than in a database backup (issue 246). func TestTheLayerASetReplacesIsKeptAndReadable(t *testing.T) { inv := fresh(t) ctx := t.Context() if _, err := inv.AddNode(ctx, "anchor"); err != nil { t.Fatal(err) } web := catalogue.Manifest{Module: "web", Version: "1", Resources: []map[string]any{ {"id": "server", "type": "container", "name": "web", "image": "x"}, {"id": "conf", "type": "file", "path": "/etc/web.json", "content": "{}", "merge": "json"}, }} if err := inv.RegisterModule(ctx, web, Source{}); err != nil { t.Fatal(err) } if _, has, err := inv.Layer(ctx, "anchor", "web"); err != nil || has { t.Fatalf("a layer nobody set: %v %v", has, err) } first := map[string]any{"colour": "blue", "size": "large"} if err := inv.SetSettings(ctx, "anchor", "web", first); err != nil { t.Fatal(err) } got, has, err := inv.Layer(ctx, "anchor", "web") if err != nil || !has || got["colour"] != "blue" || got["size"] != "large" { t.Fatalf("the layer read back: %v %v %v", got, has, err) } if past, err := inv.SettingsHistory(ctx, "anchor", "web"); err != nil || len(past) != 0 { t.Fatalf("a first set replaced something: %v %v", past, err) } if err := inv.SetSettings(ctx, "anchor", "web", map[string]any{"colour": "red"}); err != nil { t.Fatal(err) } if err := inv.ClearSettings(ctx, "anchor", "web"); err != nil { t.Fatal(err) } past, err := inv.SettingsHistory(ctx, "anchor", "web") if err != nil || len(past) != 2 { t.Fatalf("history %v %v", past, err) } // The latest first: the clear took the red layer, the set took the first. if past[0].ReplacedBy != "clear" || past[0].Values["colour"] != "red" { t.Errorf("the cleared layer: %+v", past[0]) } if past[1].ReplacedBy != "set" || past[1].Values["size"] != "large" { t.Errorf("the replaced layer still has what the set dropped: %+v", past[1]) } // The mesh-wide layer keeps its own history, apart from the node's. if err := inv.SetSettings(ctx, "", "web", map[string]any{"colour": "green"}); err != nil { t.Fatal(err) } if err := inv.SetSettings(ctx, "", "web", map[string]any{"colour": "grey"}); err != nil { t.Fatal(err) } mesh, err := inv.SettingsHistory(ctx, "", "web") if err != nil || len(mesh) != 1 || mesh[0].Values["colour"] != "green" { t.Fatalf("the mesh-wide history %v %v", mesh, err) } } // What a machine was last sent is kept, summarised, and read back by its name; a machine sent // nothing since has nothing to compare with. func TestWhatAMachineWasSentIsKeptForComparison(t *testing.T) { inv := fresh(t) ctx := t.Context() n, err := inv.AddNode(ctx, "anchor") if err != nil { t.Fatal(err) } if got, err := inv.SentSummary(ctx, "anchor"); err != nil || len(got) != 0 { t.Fatalf("a machine never sent anything: %s %v", got, err) } if err := inv.RecordSentSummary(ctx, n.ID, []byte(`[{"id":"web.server","type":"container"}]`)); err != nil { t.Fatal(err) } got, err := inv.SentSummary(ctx, "anchor") if err != nil || len(got) == 0 { t.Fatalf("read back: %s %v", got, err) } }