Files
mesh-sdk/go/delivery/verdicts_test.go
T

141 lines
6.8 KiB
Go

package delivery
import (
"encoding/json"
"reflect"
"strings"
"testing"
"time"
"git.novox.be/novox/mesh-sdk/go/catalogue"
)
// A verdict on a manifest round-trips, refuses what it cannot be, and a reader takes a missing one as accepted (novox/hq
// ADR 0578 §1, §3); a modules value carries it.
func TestAManifestVerdictIsTheContractReadersTake(t *testing.T) {
at := time.Date(2026, 11, 1, 2, 0, 0, 0, time.UTC)
refused := ManifestVerdict{Module: "nats", State: VerdictRefused, Problems: []string{"a", "b"}, Judge: "src2:x", At: at}
data, err := EncodeManifestVerdict(refused)
if err != nil {
t.Fatal(err)
}
back, err := DecodeManifestVerdict(data)
if err != nil || !back.Same(refused) || back.Judge != "src2:x" || !back.At.Equal(at) {
t.Fatalf("round trip: %+v %v", back, err)
}
if !strings.Contains(back.Why(), "refused the manifest of nats: a; b") {
t.Fatalf("why: %q", back.Why())
}
for name, bad := range map[string]ManifestVerdict{
"no module": {State: VerdictAccepted, Judge: "j"},
"no judge": {Module: "m", State: VerdictAccepted},
"another state": {Module: "m", State: "maybe", Judge: "j"},
"refused, no problem": {Module: "m", State: VerdictRefused, Judge: "j"},
"accepted, a problem": {Module: "m", State: VerdictAccepted, Problems: []string{"x"}, Judge: "j"},
"refused, a warning": {Module: "m", State: VerdictRefused, Problems: []string{"x"}, Warnings: []string{"y"}, Judge: "j"},
"waits, no field": {Module: "m", State: VerdictManifestWaits, Judge: "j"},
"waits, a problem": {Module: "m", State: VerdictManifestWaits, Unknown: []string{"f"}, Problems: []string{"x"}, Judge: "j"},
"fields, accepted": {Module: "m", State: VerdictAccepted, Unknown: []string{"f"}, Judge: "j"},
} {
if _, err := EncodeManifestVerdict(bad); err == nil {
t.Errorf("%s: encoded", name)
}
}
if refusedTaken, _ := TakeManifestVerdict(nil); refusedTaken {
t.Fatal("a missing verdict was taken as a refusal")
}
if r, p := TakeManifestVerdict(&refused); !r || len(p) != 2 {
t.Fatalf("a refusal was not taken: %v %v", r, p)
}
if k, err := VerdictsKey(strings.Repeat("a", 64)); err != nil || k != strings.Repeat("a", 64) {
t.Fatalf("key: %q %v", k, err)
}
if _, err := VerdictsKey("nats"); err == nil {
t.Fatal("a key that is no digest")
}
m := Module{FP2: "src2:" + strings.Repeat("b", 64), Manifest: strings.Repeat("c", 64), Repository: "novox/x",
Commit: "c", Verdict: VerdictRefused, Problems: []string{"p"}}
data, err = EncodeModule(m)
if err != nil {
t.Fatal(err)
}
got, err := DecodeModule(data)
if err != nil {
t.Fatal(err)
}
if r, p := TakeManifestVerdict(got.VerdictOf("x")); !r || p[0] != "p" {
t.Fatalf("the module's verdict: %+v", got)
}
m.Problems = nil
if _, err := EncodeModule(m); err == nil {
t.Fatal("a module refused naming no problem was encoded")
}
// ADR 0579 §1: an accepted manifest a stricter judge outgrew carries its warnings and is taken as accepted.
outgrown := ManifestVerdict{Module: "x", State: VerdictAccepted, Warnings: []string{"w"}, Judge: "j"}
if _, err := EncodeManifestVerdict(outgrown); err != nil || outgrown.Same(ManifestVerdict{Module: "x", State: VerdictAccepted}) {
t.Fatalf("an outgrown verdict: %v", err)
}
m.Verdict, m.Warnings = "", []string{"w"}
if r, _ := TakeManifestVerdict(m.VerdictOf("x")); r || len(m.VerdictOf("x").Warnings) != 1 {
t.Fatalf("an outgrown module: %+v", m.VerdictOf("x"))
}
// 1966/AC6: the accepted event of a manifest with two bus-state entries carries both whole, and its id is
// manifest-accepted-<digest>-<judge>.
entries := []catalogue.BusStateEntry{
{Name: "drafts", Storage: catalogue.StorageKeyValue, Template: catalogue.TemplateState,
Options: map[string]json.RawMessage{catalogue.OptionPastValues: json.RawMessage(`5`)}},
{Name: "records", Storage: catalogue.StorageLedger, Template: catalogue.TemplateLedger,
Types: []catalogue.LedgerType{{Name: "decision", Operations: []catalogue.LedgerOperation{{Name: "submit", Effect: catalogue.EffectSetFields}}}}},
}
digest := strings.Repeat("d", 64)
accepted := AcceptedOf(digest, ManifestVerdict{Module: "mesh-knowledge", State: VerdictAccepted, Judge: "src2:j"},
catalogue.Manifest{Module: "mesh-knowledge", BusState: entries})
raw, err := json.Marshal(accepted)
if err != nil {
t.Fatal(err)
}
var heard ManifestAccepted
if err := json.Unmarshal(raw, &heard); err != nil || heard.Module != "mesh-knowledge" || heard.Digest != digest ||
heard.V != Version || !reflect.DeepEqual(heard.BusState, entries) {
t.Fatalf("the accepted event reads as %+v (%v)", heard, err)
}
if id := heard.EventID(); id != "manifest-accepted-"+digest+"-src2:j" {
t.Fatalf("the accepted event's id is %q", id)
}
// A manifest with a field the judge does not know waits (clause 3 of the record on a release's Approve): readers hold it
// as a refused one, it round-trips through a modules value, an old value decodes unchanged, and it is not a refusal.
waits := ManifestVerdict{Module: "nats", State: VerdictManifestWaits, Unknown: []string{"core-credentials", "tool-runner"}, Judge: "j"}
data, err = EncodeManifestVerdict(waits)
if err != nil {
t.Fatal(err)
}
if back, err = DecodeManifestVerdict(data); err != nil || !back.Same(waits) || back.Same(ManifestVerdict{Module: "nats", State: VerdictManifestWaits, Unknown: []string{"x"}, Judge: "j"}) {
t.Fatalf("a waiting verdict: %+v %v", back, err)
}
if r, p := TakeManifestVerdict(&waits); !r || waits.Refused() || !waits.Held() || !strings.Contains(p[0], "core-credentials, tool-runner") ||
!strings.Contains(waits.Why(), "holds the manifest of nats") {
t.Fatalf("a waiting verdict is held like a refusal: %v %v %q", r, p, waits.Why())
}
if r := waits.AsRefused(); !r.Refused() || r.Waits() || len(r.Problems) != 1 || len(r.Unknown) != 0 || refused.AsRefused().State != VerdictRefused {
t.Fatalf("a waiting verdict as a reader before it takes it: %+v", r)
}
// In a modules value a wait is written as a refusal naming the fields (an older reader holds it as refused).
w := Module{FP2: "src2:" + strings.Repeat("b", 64), Manifest: strings.Repeat("c", 64), Repository: "novox/x", Commit: "c",
Verdict: VerdictRefused, Problems: []string{waits.WaitsOn()}, Unknown: []string{"f"}}
data, err = EncodeModule(w)
if err != nil {
t.Fatal(err)
}
if got, err = DecodeModule(data); err != nil || got.VerdictOf("x").Unknown[0] != "f" || !got.VerdictOf("x").Waits() || got.VerdictOf("x").Refused() {
t.Fatalf("the module's waiting verdict: %+v %v", got, err)
}
if r, p := TakeManifestVerdict(got.VerdictOf("x")); !r || !strings.Contains(p[0], "which the indexer's judge does not know yet") {
t.Fatalf("a waiting module is held: %v %v", r, p)
}
w.Verdict, w.Problems = "", nil
if _, err := EncodeModule(w); err == nil {
t.Fatal("unknown fields with no wait were encoded")
}
}