package resolution import ( "os" "path/filepath" "reflect" "testing" mcat "git.novox.be/novox/mesh-sdk/go/catalogue" ) const collectedBarsText = "top\n${collected:contribution:node-bar:block:bar=top,place=status}" + "bottom\n${collected:contribution:node-bar:block:bar=bottom,place=status}" const collectedBars = `{"module":"bars","version":"1","moved":true, "claims":[{"name":"node-bar","scope":"node","renders":{"block":"{{.shows}}"}}], "build":{"artifacts":[{"name":"files","kind":"archive","from":"files"}]}, "resources":[{"id":"conf","type":"file","path":"/etc/bars.conf","template":"files/bars"}]}` const itemsOfPower = `{"module":"power","version":"1","moved":true, "items":[{"collection":"contribution:node-bar:block","name":"battery","if-capability":"battery","attributes":{"bar":"bottom","place":"status","order":"40"},"template":"files/battery"}, {"collection":"contribution:node-bar:block","name":"draw","attributes":{"bar":"bottom","place":"status","order":"45"},"template":"files/draw"}], "build":{"artifacts":[{"name":"files","kind":"archive","from":"files"}]}}` func collectedBarsOn(t *testing.T, caps map[string]bool) mcat.Resolution { t.Helper() bars := held(t, collectedBars, builtWith("bars", map[string]string{"bars": collectedBarsText})) power := held(t, itemsOfPower, builtWith("power", map[string]string{"battery": "B\n", "draw": "D ${machine:name}\n"})) r, err := Resolve(shelf(bars, power), []string{"bars", "power"}, workstation(), mcat.World{}) if err != nil { t.Fatal(err) } r.Account, r.Capabilities = "op", caps return r } func TestACollectorMayNameTheBarsBlocksAsACollectionAndNoOtherDataKind(t *testing.T) { bars := held(t, collectedBars, builtWith("bars", map[string]string{"bars": collectedBarsText})) if _, err := Compose(collectedBarsOn(t, nil), handingTo("store:5000")); err != nil { t.Fatalf("the handed collector is refused: %v", err) } if got := collectedPlaceholderProblems(bars, map[string]any{"id": "x", "content": "${collected:contribution:node-bar:block:bar=top,place=status}"}); len(got) != 0 { t.Fatalf("the check refuses it: %v", got) } // A data kind whose shape does not say its items are text stays refused. _, plain, _ := receivable(mcat.BarSeat, mcat.BarKindBlock) plain.Shape = &mcat.Shape{} if collectableAsText(plain) { t.Fatal("a shape not marked as text is collectable") } if _, _, err := oldCollection("contribution:node-bar:block", false); err != nil { t.Fatal(err) } } // The bars of the catalogue's i3status-rust, with the blocks of power's items, compose to the files the old shaped // contribution made (issue 781). testdata/bars holds the catalogue's bars and block texts as of the move and the bars as // composed before it, for a machine with neither capability, the battery, the power meter and both. Where the // collector is not handed the controller fills the files; where it is, it names the items for the client, which // fills the same files (mesh-catalog's provisioner client holds that half against the same golden files). func TestTheBarsOfTheCatalogueAreTheSameHandedOrNot(t *testing.T) { read := func(name string) string { b, err := os.ReadFile(filepath.Join("testdata", "bars", name)) if err != nil { t.Fatal(err) } return string(b) } collector := `{"module":"bars","version":"1","moved":true,"claims":[{"name":"node-bar","scope":"node"}], "build":{"artifacts":[{"name":"files","kind":"archive","from":"files"}]}, "resources":[{"id":"top","type":"file","path":"/t","template":"files/top"}, {"id":"bottom","type":"file","path":"/b","template":"files/bottom"}]}` power := `{"module":"power","version":"1","moved":true, "items":[{"collection":"contribution:node-bar:block","name":"battery","if-capability":"battery","attributes":{"bar":"bottom","place":"status","order":"40"},"template":"files/battery"}, {"collection":"contribution:node-bar:block","name":"power-draw","if-capability":"power-meter","attributes":{"bar":"bottom","place":"status","order":"45"},"template":"files/draw"}], "build":{"artifacts":[{"name":"files","kind":"archive","from":"files"}]}}` for name, caps := range map[string]map[string]bool{"none": nil, "battery": {"battery": true}, "power-meter": {"power-meter": true}, "both": {"battery": true, "power-meter": true}} { bars := held(t, collector, builtWith("bars", map[string]string{"top": read("top-bar.toml"), "bottom": read("bottom-bar.toml")})) blocks := held(t, power, builtWith("power", map[string]string{"battery": read("bar-battery"), "draw": read("bar-power-draw")})) r, err := Resolve(shelf(bars, blocks), []string{"bars", "power"}, workstation(), mcat.World{}) if err != nil { t.Fatal(err) } r.Account, r.Capabilities = "op", caps // Not handed: the controller fills them. for id, which := range map[string]string{"bars.top": "top", "bars.bottom": "bottom"} { if got := composedFile(t, r, id); got != read(name+"."+which+"-bar.toml") { t.Errorf("%s, %s bar, not handed:\n%s\nwant\n%s", name, which, got, read(name+"."+which+"-bar.toml")) } } // Handed: the plan names the items the machine's capabilities allow, for the client to fill. c, _ := composeWith(t, r, handingTo("store:5000")) var names []string for _, it := range c.Plans["power"].Items { names = append(names, it.Name) } want := []string{} if caps["battery"] { want = append(want, "battery") } if caps["power-meter"] { want = append(want, "power-draw") } if !reflect.DeepEqual(append([]string{}, names...), want) { t.Errorf("%s: the plan names %v, want %v", name, names, want) } } }