ADR 0593 §7: the composer with its refusals leaves mesh-sdk, so mesh-sdk carries no composition rule. The catalogue functions only the node-engine's composition reaches (Resolve, Resolution's Compose, Declaration, DataItems, LeftOut, Rules and the rest, the settings, placeholder and template refusals, the rendering helpers) are copied with their tests into internal/compose/resolution; a method of a mesh-sdk shape becomes a function taking it first. The composition calls its own copies, and the captured catalogue its tests read lives here now, shared by mesh-compose's tests. Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
111 lines
5.5 KiB
Go
111 lines
5.5 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|