The node-engine reads a resource's module and a declaration's builds, says it does in every report (reads_modules), groups the apply's outcome per module with every failing resource named, and writes have.<node>.<module> and have.<node>.node to the bucket only when a value differs, never ahead of or in place of the report (novox/hq ADR 0429 section 4).
77 lines
2.9 KiB
Go
77 lines
2.9 KiB
Go
package apply
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/novox/mesh-host/internal/declaration"
|
|
)
|
|
|
|
func twoModules(t *testing.T) *declaration.Declaration {
|
|
t.Helper()
|
|
d, err := declaration.Parse([]byte(`{"declaration":1,"builds":{"a":"b-1","b":"b-2"},"resources":[
|
|
{"id":"a.dir","type":"directory","path":"/srv/a","module":"a"},
|
|
{"id":"b.dir","type":"directory","path":"/srv/b","module":"b"},
|
|
{"id":"b.conf","type":"file","path":"/srv/b/c","content":"x","module":"b"},
|
|
{"id":"c.dir","type":"directory","path":"/srv/c","module":"c"}]}`))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return d
|
|
}
|
|
|
|
// 1132/AC3: every resource of module A applied and one of module B failed: A is a success, B failed with the failing
|
|
// resource and its error named; a module gated by another's failure is failed too.
|
|
func TestAnApplyIsGroupedPerModule(t *testing.T) {
|
|
d := twoModules(t)
|
|
done := Report{Outcomes: []Outcome{
|
|
{ID: "a.dir", Action: "created"},
|
|
{ID: "c.dir", Action: "skipped", Detail: "a step this module declares did not complete"},
|
|
}}
|
|
err := &Error{Resource: "b.dir", Err: errors.New("no space left"), Done: done, Others: 1,
|
|
Failures: []Failure{{Resource: "b.dir", Err: errors.New("no space left")}, {Resource: "b.conf", Err: errors.New("denied")}},
|
|
Modules: []string{"c"}}
|
|
got := ModuleResults(d, Report{}, err)
|
|
if len(got) != 3 {
|
|
t.Fatalf("modules %v, want a, b and c", got)
|
|
}
|
|
if got["a"].Failed || got["a"].Detail != "" {
|
|
t.Errorf("a: %+v, want a clean success", got["a"])
|
|
}
|
|
b := got["b"]
|
|
if !b.Failed || !strings.Contains(b.Detail, "b.dir") || !strings.Contains(b.Detail, "no space left") ||
|
|
!strings.Contains(b.Detail, "b.conf") || !strings.Contains(b.Detail, "denied") {
|
|
t.Errorf("b: %+v, want both failing resources and their errors", b)
|
|
}
|
|
c := got["c"]
|
|
if !c.Failed || !strings.Contains(c.Detail, "did not complete") {
|
|
t.Errorf("c: %+v, want it failed as gated, with why", c)
|
|
}
|
|
}
|
|
|
|
// 1132/AC3: an apply that did not fail is a success for every module the declaration names.
|
|
func TestACleanApplyIsASuccessForEveryModule(t *testing.T) {
|
|
got := ModuleResults(twoModules(t), Report{}, nil)
|
|
for _, m := range []string{"a", "b", "c"} {
|
|
if r, ok := got[m]; !ok || r.Failed {
|
|
t.Errorf("%s: %+v (%v), want a success", m, r, ok)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 1132/AC3: a step that belongs to no module stops the apply there; a module with a resource not attempted is not a
|
|
// success.
|
|
func TestAModuleNotAttemptedIsNotASuccess(t *testing.T) {
|
|
d := twoModules(t)
|
|
err := &Error{Resource: "boot", Err: errors.New("boom"), Stopped: true, Failures: []Failure{{Resource: "boot", Err: errors.New("boom")}},
|
|
Done: Report{Outcomes: []Outcome{{ID: "a.dir", Action: "created"}}}}
|
|
got := ModuleResults(d, Report{}, err)
|
|
if got["a"].Failed {
|
|
t.Errorf("a was done: %+v", got["a"])
|
|
}
|
|
if !got["b"].Failed || !strings.Contains(got["b"].Detail, "boot") {
|
|
t.Errorf("b was not attempted: %+v", got["b"])
|
|
}
|
|
}
|