delivery, catalogue: the node-engine's compose-candidate verb, compositions carrying their plan or refusal, the kept import form and a build's imports on its key, so the engine alone composes and the indexer switches form without a rebuild (ADR 0593 §4, §6, §7)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
package delivery
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
)
|
||||
|
||||
// The verb compose-candidate's ask and answer, and a composition that carries its plan or its refusal (ADR 0593 §6, §7):
|
||||
// each round-trips, and a reader refuses what it could not take, naming it.
|
||||
func TestComposeCandidateAndComposedPlanRoundTripAndRefuse(t *testing.T) {
|
||||
key, err := CompositionKey("mesh-sdk#7", "0123456789ab")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
raw, err := EncodeComposeCandidateAsk(ComposeCandidateAsk{Candidate: key, Input: true})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if a, err := DecodeComposeCandidateAsk(raw); err != nil || a.Candidate != key || !a.Input {
|
||||
t.Fatalf("ask round trip: %v, %+v", err, a)
|
||||
}
|
||||
if _, err := DecodeComposeCandidateAsk([]byte(`{"candidate":"` + key + `","other":1}`)); err == nil {
|
||||
t.Error("an ask with an unknown key was read")
|
||||
}
|
||||
if _, err := EncodeComposeCandidateAsk(ComposeCandidateAsk{Candidate: "nope"}); err == nil {
|
||||
t.Error("an ask naming no candidate key was encoded")
|
||||
}
|
||||
|
||||
good := ComposeCandidateAnswer{Node: "ace", Candidate: key, Verdict: ComposeCandidateRefused, Took: 812,
|
||||
Own: ModuleComposed{Module: "node", State: ModuleComposedState, Digest: "sha256:00"},
|
||||
Modules: []ModuleComposed{
|
||||
{Module: "netcheck", FP: "src2:x", State: ModuleComposedState, Digest: "sha256:01", Changed: true},
|
||||
{Module: "route-proxy", State: ModuleRefusedState, Words: "the setting domain has no value"},
|
||||
{Module: "zz", State: ModuleHeldState, Words: "held before"},
|
||||
}, Input: json.RawMessage(`{"machines":["ace"]}`)}
|
||||
raw, err = EncodeComposeCandidateAnswer(good)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
back, err := DecodeComposeCandidateAnswer(raw)
|
||||
if err != nil || back.Verdict != ComposeCandidateRefused || len(back.Modules) != 3 || string(back.Input) != string(good.Input) {
|
||||
t.Fatalf("answer round trip: %v, %+v", err, back)
|
||||
}
|
||||
for name, mutate := range map[string]func(a *ComposeCandidateAnswer){
|
||||
"verdict not the modules'": func(a *ComposeCandidateAnswer) { a.Verdict = ComposeCandidateComposed },
|
||||
"unknown state": func(a *ComposeCandidateAnswer) { a.Modules[0].State = "fine" },
|
||||
"composed without digest": func(a *ComposeCandidateAnswer) { a.Modules[0].Digest = "" },
|
||||
"refused without words": func(a *ComposeCandidateAnswer) { a.Modules[1].Words = "" },
|
||||
"unsorted": func(a *ComposeCandidateAnswer) { a.Modules[0], a.Modules[1] = a.Modules[1], a.Modules[0] },
|
||||
"own misnamed": func(a *ComposeCandidateAnswer) { a.Own.Module = "ace" },
|
||||
"whole refusal w/ modules": func(a *ComposeCandidateAnswer) { a.Refused = "the bound passed" },
|
||||
} {
|
||||
a := good
|
||||
a.Modules = append([]ModuleComposed(nil), good.Modules...)
|
||||
mutate(&a)
|
||||
if _, err := EncodeComposeCandidateAnswer(a); err == nil {
|
||||
t.Errorf("%s: encoded", name)
|
||||
}
|
||||
}
|
||||
whole := ComposeCandidateAnswer{Node: "ace", Candidate: key, Verdict: ComposeCandidateRefused, Refused: "the bound passed"}
|
||||
if _, err := EncodeComposeCandidateAnswer(whole); err != nil {
|
||||
t.Errorf("a whole refusal was not encoded: %v", err)
|
||||
}
|
||||
if _, err := DecodeComposeCandidateAnswer([]byte(strings.Replace(string(raw), `"took"`, `"other":1,"took"`, 1))); err == nil {
|
||||
t.Error("an answer with an unknown key was read")
|
||||
}
|
||||
|
||||
fp := "src2:" + strings.Repeat("a", 64)
|
||||
planned := Composed{Node: "ace", Module: "netcheck", FP: fp, Resources: []json.RawMessage{},
|
||||
Plan: &catalogue.Plan{Files: []catalogue.PlanFile{}}}
|
||||
raw, err = EncodeComposed(planned)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
c, err := DecodeComposed(raw)
|
||||
if err != nil || c.Plan == nil {
|
||||
t.Fatalf("a composition's plan did not round-trip: %v, %+v", err, c)
|
||||
}
|
||||
d1, _ := ComposedDigestOf(planned)
|
||||
d0, _ := ComposedDigestOf(Composed{Node: "ace", Module: "netcheck", FP: fp, Resources: []json.RawMessage{}})
|
||||
if d1 == "" || d1 == d0 {
|
||||
t.Error("the digest does not move with the plan")
|
||||
}
|
||||
refused := Composed{Node: "ace", Module: "netcheck", FP: fp, Refused: "the setting domain has no value"}
|
||||
if raw, err = EncodeComposed(refused); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if c, err := DecodeComposed(raw); err != nil || c.Refused != refused.Refused {
|
||||
t.Fatalf("a refused composition did not round-trip: %v", err)
|
||||
}
|
||||
if _, err := ComposedDigestOf(refused); err == nil {
|
||||
t.Error("a refused composition has a digest")
|
||||
}
|
||||
refused.Data = true
|
||||
if _, err := EncodeComposed(refused); err == nil {
|
||||
t.Error("a refused composition holding data was encoded")
|
||||
}
|
||||
if _, err := EncodeComposed(Composed{Node: "ace", Resources: []json.RawMessage{}, Refused: "x"}); err == nil {
|
||||
t.Error("the node's own composition carried a refusal")
|
||||
}
|
||||
}
|
||||
|
||||
// A kept import form is set only with imports, and in the fingerprint's form (ADR 0593 §4).
|
||||
func TestAKeptImportFormNeedsImports(t *testing.T) {
|
||||
form := SourcePrefix2 + strings.Repeat("b", 64)
|
||||
in := KeptInputs2{Tree: "t", Imports: []string{"git.novox.be/novox/mesh-sdk/go"}, ImportForm: form}
|
||||
m := Module{FP2: form, Manifest: strings.Repeat("c", 64), Inputs2: &in, Repository: "novox/x", Commit: "c"}
|
||||
if err := m.check(); err != nil && strings.Contains(err.Error(), "import form") {
|
||||
t.Fatalf("a good import form refused: %v", err)
|
||||
}
|
||||
for _, bad := range []KeptInputs2{{Tree: "t", ImportForm: form}, {Tree: "t", Imports: in.Imports, ImportForm: "src2:short"}} {
|
||||
m.Inputs2 = &bad
|
||||
if err := m.check(); err == nil || !strings.Contains(err.Error(), "import form") {
|
||||
t.Errorf("%+v: %v", bad, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user