132 lines
6.2 KiB
Go
132 lines
6.2 KiB
Go
package main
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// An item may carry attributes and a module may give several named items to one collection; a placeholder selecting
|
|
// by attributes takes only the items that carry them, in the order of the old forms: order, contributor, item name
|
|
// (design 57 L3, issue 777). Against a real bus, on the client's own account.
|
|
|
|
const barsConf = "/etc/bars.conf"
|
|
|
|
// barCollector fills one file from the bottom bar's status blocks, the top bar's, and all of them.
|
|
func barCollector(t *testing.T) Plan {
|
|
tpl, _ := templates(t, map[string]string{"bars": "[bottom]\n${collected:blocks:bar=bottom,place=status}" +
|
|
"[top]\n${collected:blocks:bar=top,place=status}[all]\n${collected:blocks}"})
|
|
return Plan{Templates: tpl, Files: []PlannedFile{{ID: "conf", Path: barsConf, Template: "bars",
|
|
OwnerAtRuntime: "owned"}}, Collections: []PlannedCollection{{Name: "blocks", Comment: "#"}}}
|
|
}
|
|
|
|
func blocksOf(t *testing.T, items map[string]PlannedItem, texts map[string]string) Plan {
|
|
tpl, _ := templates(t, texts)
|
|
p := Plan{Templates: tpl}
|
|
for _, name := range sortedKeys(items) {
|
|
p.Items = append(p.Items, items[name])
|
|
}
|
|
return p
|
|
}
|
|
|
|
func TestNatsASelectingPlaceholderTakesOnlyTheItemsCarryingItsAttributesInOrder(t *testing.T) {
|
|
b := startBus(t)
|
|
e := startEngine(t, map[string]string{barsConf: "owned"})
|
|
b.assign(t, "bars", barCollector(t))
|
|
b.assign(t, "power", blocksOf(t, map[string]PlannedItem{
|
|
"battery": {Collection: "blocks", Name: "battery", Template: "battery",
|
|
Attributes: map[string]string{"bar": "bottom", "place": "status"}},
|
|
"cpu": {Collection: "blocks", Name: "cpu", Template: "cpu",
|
|
Attributes: map[string]string{"bar": "bottom", "place": "status", "order": "20"}},
|
|
"clock": {Collection: "blocks", Name: "clock", Template: "clock",
|
|
Attributes: map[string]string{"bar": "top", "place": "status"}},
|
|
}, map[string]string{"battery": "battery\n", "cpu": "cpu\n", "clock": "clock\n"}))
|
|
b.assign(t, "another", blocksOf(t, map[string]PlannedItem{
|
|
"disk": {Collection: "blocks", Template: "disk", Attributes: map[string]string{"bar": "bottom", "place": "status", "order": "20"}},
|
|
}, map[string]string{"disk": "disk\n"}))
|
|
aClient(t, b, e, t.TempDir(), time.Hour)
|
|
|
|
want := "[bottom]\n# another\ndisk\n# power\ncpu\n# power\nbattery\n" +
|
|
"[top]\n# power\nclock\n" +
|
|
"[all]\n# another\ndisk\n# power\ncpu\n# power\nbattery\n# power\nclock\n"
|
|
eventually(t, "the file holds each placeholder's items", func() bool { return e.read(t, barsConf) == want })
|
|
eventually(t, "the named items are recorded under their module and name", func() bool {
|
|
got := b.keys(t, CollectBucket, node+".blocks.")
|
|
return len(got) == 4 && got[0] == "g14.blocks.another" && got[1] == "g14.blocks.power/battery" &&
|
|
got[2] == "g14.blocks.power/clock" && got[3] == "g14.blocks.power/cpu"
|
|
})
|
|
|
|
// An item no longer contributed goes, and only that one.
|
|
b.assign(t, "power", blocksOf(t, map[string]PlannedItem{
|
|
"clock": {Collection: "blocks", Name: "clock", Template: "clock", Attributes: map[string]string{"bar": "top", "place": "status"}},
|
|
}, map[string]string{"clock": "clock\n"}))
|
|
eventually(t, "the items of the module's others go", func() bool {
|
|
return strings.Contains(e.read(t, barsConf), "[bottom]\n# another\ndisk\n[top]")
|
|
})
|
|
if got := b.keys(t, CollectBucket, node+".blocks."); len(got) != 2 {
|
|
t.Errorf("kept %v", got)
|
|
}
|
|
}
|
|
|
|
func TestASelectorIsReadFromAPlaceholderOfAnyNamespace(t *testing.T) {
|
|
for n, want := range map[Need]string{
|
|
{Kind: "collected", Name: "blocks:bar=top,place=status"}: "blocks",
|
|
{Kind: "collected", Name: "blocks"}: "blocks",
|
|
{Kind: "collected", Name: "a:b:k=v"}: "a:b",
|
|
{Kind: "contribution", Name: "node-bar:block:bar=top,place=left"}: "contribution:node-bar:block",
|
|
} {
|
|
if got, ok := collectionOf(n); !ok || got != want {
|
|
t.Errorf("%s is the collection %q (%v), want %q", n, got, ok, want)
|
|
}
|
|
}
|
|
if where, err := selectorOfNeed(Need{Kind: "collected", Name: "blocks:bar=top,place=status"}); err != nil ||
|
|
len(where) != 2 || where["bar"] != "top" || where["place"] != "status" {
|
|
t.Errorf("the selector is %v (%v)", where, err)
|
|
}
|
|
for _, bad := range []string{"blocks:bar=top,bar=top", "blocks:bar=", "blocks:bar=top,"} {
|
|
if _, err := selectorOfNeed(Need{Kind: "collected", Name: bad}); err == nil {
|
|
t.Errorf("%s was read as a selector", bad)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAnItemsKeyHoldsItsName(t *testing.T) {
|
|
key := CollectKey(node, "blocks", itemID("power", "battery"))
|
|
if key != "g14.blocks.power/battery" {
|
|
t.Fatal(key)
|
|
}
|
|
if c, id, ok := itemOfKey(node, key); !ok || c != "blocks" || id != "power/battery" {
|
|
t.Errorf("reads back as %q %q %v", c, id, ok)
|
|
}
|
|
for _, bad := range []string{"g14.blocks.power/", "g14.blocks./x", "g14.blocks.a/b/c"} {
|
|
if _, _, ok := itemOfKey(node, bad); ok {
|
|
t.Errorf("%s read as an item's key", bad)
|
|
}
|
|
}
|
|
p := Plan{Templates: Templates{Source: "x", Digest: "sha256:" + strings.Repeat("0", 64)},
|
|
Items: []PlannedItem{{Collection: "a", Name: "n", Template: "i"}, {Collection: "a", Name: "n", Template: "j"},
|
|
{Collection: "a", Name: "Big", Template: "k", Attributes: map[string]string{"order": "100", "Bar": "x"}}}}
|
|
got := strings.Join(p.problems(), "\n")
|
|
for _, want := range []string{"item a/n is named twice", `"Big" is not a name`, "0 to 99", "attribute Bar"} {
|
|
if !strings.Contains(got, want) {
|
|
t.Errorf("the problems %q do not say %q", got, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAnItemOfAPlanReadsItsNameAndAttributesAndRefusesAnUnknownField(t *testing.T) {
|
|
plan := func(item string) string {
|
|
return `{"node":"g14","module":"power","provisioning":{"templates":{"source":"x","digest":"sha256:` +
|
|
strings.Repeat("0", 64) + `"},"files":[],"items":[` + item + `]}}`
|
|
}
|
|
_, p, err := planOf("g14", "g14.power", []byte(plan(
|
|
`{"collection":"blocks","name":"battery","attributes":{"bar":"top"},"template":"battery"}`)))
|
|
if err != nil || len(p.Items) != 1 || p.Items[0].Name != "battery" || p.Items[0].Attributes["bar"] != "top" {
|
|
t.Fatalf("read %+v (%v)", p.Items, err)
|
|
}
|
|
if _, _, err := planOf("g14", "g14.power", []byte(plan(
|
|
`{"collection":"blocks","template":"battery","colour":"red"}`))); err == nil || !strings.Contains(err.Error(), "colour") {
|
|
t.Errorf("an unknown item field was accepted: %v", err)
|
|
}
|
|
}
|