Files
mesh-contracts/go/clients/nodeengineclient/have_test.go
T

151 lines
5.5 KiB
Go

package nodeengineclient
import (
"errors"
"strings"
"testing"
"time"
seat "git.novox.be/novox/mesh-contracts/go/seats/nodeengine"
"git.novox.be/novox/mesh-sdk/go/keys"
)
const digestA = "sha256:0000000000000000000000000000000000000000000000000000000000000001"
func TestHaveCodec(t *testing.T) {
good := seat.HaveByNodeModuleValue{Phase: PhaseRunning, Health: HealthOK, Composed: digestA, Fp: "src2:x"}
waiting := seat.HaveByNodeModuleValue{Phase: PhaseWaitingYes, Health: HealthOK, Judgement: JudgedRecreates, Waits: digestA}
pull := &seat.PullWait{Image: "i", Holder: "h"}
tests := map[string]struct {
have seat.HaveByNodeModuleValue
bad string
}{
"running": {good, ""},
"waiting for a yes": {waiting, ""},
"unknown phase": {seat.HaveByNodeModuleValue{Phase: "gone", Health: HealthOK}, "phase"},
"unknown health": {seat.HaveByNodeModuleValue{Phase: PhaseRunning, Health: "fine"}, "health"},
"judgement outside a yes": {seat.HaveByNodeModuleValue{Phase: PhaseRunning, Health: HealthOK, Judgement: JudgedCannot}, "judgement"},
"a yes without a digest": {seat.HaveByNodeModuleValue{Phase: PhaseWaitingYes, Health: HealthOK, Judgement: JudgedCannot, Waits: "x"}, "sha256"},
"pull wait on a running": {seat.HaveByNodeModuleValue{Phase: PhaseRunning, Health: HealthOK, PullWait: pull}, "pull"},
"pull wait without holder": {seat.HaveByNodeModuleValue{Phase: PhaseFailed, Health: HealthFailed, PullWait: &seat.PullWait{Image: "i"}}, "holder"},
"bad composed digest": {seat.HaveByNodeModuleValue{Phase: PhaseRunning, Health: HealthOK, Composed: "abc"}, "sha256"},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
raw, err := EncodeHave(tc.have)
if tc.bad != "" {
if err == nil || !strings.Contains(err.Error(), tc.bad) {
t.Fatalf("encode: %v, want it to name %q", err, tc.bad)
}
return
}
if err != nil {
t.Fatal(err)
}
back, err := DecodeHave(raw)
if err != nil {
t.Fatal(err)
}
tc.have.V = keys.Version
if !SameHave(back, tc.have) {
t.Fatalf("round trip differs: %+v", back)
}
})
}
if _, err := DecodeHave([]byte(`{"v":2,"phase":"running","health":"ok"}`)); !errors.Is(err, keys.ErrUnknownVersion) {
t.Fatalf("version 2: %v", err)
}
if _, err := DecodeHave([]byte(`{"v":1,"phase":"running","health":"ok","extra":1}`)); err == nil {
t.Fatal("an unknown field is read")
}
}
func TestSameHaveComparesTimesAsInstants(t *testing.T) {
at := time.Date(2026, 11, 5, 12, 0, 0, 0, time.UTC)
zone := time.FixedZone("x", 3600)
mk := func(tm time.Time) seat.HaveByNodeModuleValue {
return seat.HaveByNodeModuleValue{Phase: PhaseFailed, Health: HealthFailed,
PullWait: &seat.PullWait{Image: "i", Holder: "h", Began: tm},
UserList: &seat.UserListWritten{Source: "s", At: tm}}
}
if !SameHave(mk(at), mk(at.In(zone))) {
t.Fatal("the same instant in another zone differs")
}
if SameHave(mk(at), mk(at.Add(time.Second))) {
t.Fatal("another instant is the same")
}
}
func TestHaveNodeCodec(t *testing.T) {
n := seat.HaveByNodeNodeValue{Composed: digestA, Composes: ComposesOwn,
Candidates: map[string]seat.CandidateAnswer{"g": {Proposal: "p", Verdict: seat.CandidateAnswerVerdictSame}}}
raw, err := EncodeHaveNode(n)
if err != nil {
t.Fatal(err)
}
back, err := DecodeHaveNode(raw)
if err != nil {
t.Fatal(err)
}
n.V = keys.Version
if !SameHaveNode(back, n) {
t.Fatalf("round trip differs: %+v", back)
}
for name, bad := range map[string]seat.HaveByNodeNodeValue{
"composes": {Composes: "other"},
"no group": {Candidates: map[string]seat.CandidateAnswer{"": {Verdict: "same"}}},
"a verdict": {Candidates: map[string]seat.CandidateAnswer{"g": {Verdict: "maybe"}}},
} {
if _, err := EncodeHaveNode(bad); err == nil {
t.Errorf("%s: encoded", name)
}
}
a, b := seat.NodeRecord{Node: "n", V: 1}, seat.NodeRecord{Node: "n", V: 2}
if !SameNodeRecord(a, b) {
t.Error("a record differs by its version")
}
}
func TestHealthAndDataCodecs(t *testing.T) {
at := time.Date(2026, 11, 5, 12, 0, 0, 0, time.UTC)
h := seat.HealthByNodeValue{Node: "n", At: at, Fallback: &seat.LoginFallback{Node: "n", From: "old", Why: "w", At: at}}
raw, err := EncodeHealth(h)
if err != nil {
t.Fatal(err)
}
back, err := DecodeHealth(raw)
h.V = keys.Version
if err != nil || !SameHealth(back, h) {
t.Fatalf("round trip: %v %+v", err, back)
}
later := h
later.At = at.Add(time.Hour)
if !SameHealth(h, later) {
t.Error("a report differs by its time of writing")
}
for name, bad := range map[string]seat.HealthByNodeValue{
"no node": {},
"another node": {Node: "n", Fallback: &seat.LoginFallback{Node: "m", From: "o", Why: "w", At: at}},
"no time": {Node: "n", Fallback: &seat.LoginFallback{Node: "n", From: "o", Why: "w"}},
"no login from": {Node: "n", Fallback: &seat.LoginFallback{Node: "n", Why: "w", At: at}},
} {
if _, err := EncodeHealth(bad); err == nil {
t.Errorf("%s: encoded", name)
}
}
if _, err := DecodeHealth([]byte(`{"v":2,"node":"n"}`)); !errors.Is(err, keys.ErrUnknownVersion) {
t.Errorf("version 2: %v", err)
}
d := seat.DataByNodeModuleValue{Node: "n", Module: "m", Digest: digestA}
raw, err = EncodeData(d)
if err != nil || !strings.Contains(string(raw), `"items":[]`) {
t.Fatalf("data: %v %s", err, raw)
}
if _, err := EncodeData(seat.DataByNodeModuleValue{Node: "n", Module: "node"}); err == nil {
t.Error("the module name node keys")
}
if k, err := HaveNodeKey("n"); err != nil || k != "n.node" {
t.Errorf("node key %q %v", k, err)
}
}