192 lines
8.9 KiB
Go
192 lines
8.9 KiB
Go
package assign
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/asks"
|
|
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
)
|
|
|
|
// 1297/B9 (the assigner's half): the ask about a held fingerprint is a valid ask of the router and of mesh-sdk's layout at
|
|
// its key, of a kind the view may answer.
|
|
func TestTheHoldAskIsValid(t *testing.T) {
|
|
key, err := delivery.HoldAskKey("mail", fNew)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
now := time.Now()
|
|
a := HoldAsk(key, "mail", fNew, "ace", "health failed, phase running: connection refused", 0, now)
|
|
if err := a.Ask.Check(now); err != nil {
|
|
t.Errorf("the router refuses the ask: %v", err)
|
|
}
|
|
if _, err := delivery.EncodeAssignerAsk(key, a); err != nil {
|
|
t.Errorf("the ask cannot be written: %v", err)
|
|
}
|
|
// 1906/H3: the ask names the machine whose check failed and the hold's words.
|
|
if !strings.Contains(a.Ask.Explanation, "on ace") || !strings.Contains(a.Ask.Explanation, "connection refused") {
|
|
t.Errorf("the ask does not name the node and the hold's words: %q", a.Ask.Explanation)
|
|
}
|
|
// 1906/H2: an ask written again after a lift is another asking, under an id of its own.
|
|
again := HoldAsk(key, "mail", fNew, "ace", "x", 7, now)
|
|
if again.Asking != 7 || again.Ask.ID != delivery.AskingID(key, 7) || again.Ask.ID == a.Ask.ID {
|
|
t.Errorf("the asking after a lift = %d %s", again.Asking, again.Ask.ID)
|
|
}
|
|
if _, err := delivery.EncodeAssignerAsk(key, again); err != nil {
|
|
t.Errorf("the ask asked again cannot be written: %v", err)
|
|
}
|
|
if _, err := delivery.ViewAnswerKey(key); err != nil {
|
|
t.Errorf("the view may not answer it: %v", err)
|
|
}
|
|
}
|
|
|
|
// 1906/D3 (ADR 0539 §19): the ask about a data module held on a node is a valid ask of the router and of mesh-sdk's layout at
|
|
// its restore key, of a kind the view may answer; it offers restoring (losing the newer writes) and leaving it stopped.
|
|
func TestTheRestoreAskIsValid(t *testing.T) {
|
|
key, err := delivery.RestoreAskKey("ace", "mail", fNew)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
now := time.Now()
|
|
a := RestoreAsk(key, "ace", "mail", fNew, "connection refused", now)
|
|
if err := a.Ask.Check(now); err != nil {
|
|
t.Errorf("the router refuses the ask: %v", err)
|
|
}
|
|
if _, err := delivery.EncodeAssignerAsk(key, a); err != nil {
|
|
t.Errorf("the ask cannot be written: %v", err)
|
|
}
|
|
if _, err := delivery.ViewAnswerKey(key); err != nil {
|
|
t.Errorf("the view may not answer it: %v", err)
|
|
}
|
|
var ids []string
|
|
for _, o := range a.Ask.Options {
|
|
ids = append(ids, o.ID)
|
|
}
|
|
if len(ids) != 2 || ids[0] != OptionRestore || ids[1] != OptionLeaveStopped {
|
|
t.Errorf("options = %v", ids)
|
|
}
|
|
if !strings.Contains(a.Ask.Explanation, "connection refused") || !strings.Contains(a.Ask.Explanation, "ace") {
|
|
t.Errorf("the ask does not name the node and the check's words: %q", a.Ask.Explanation)
|
|
}
|
|
}
|
|
|
|
// 1906/D3: the condition for a held fingerprint names the module, the node and the check's words, under the assigner's own
|
|
// name (ADR 0437 clause 9), and is cleared under the same key.
|
|
func TestTheHeldConditionNamesModuleNodeAndWords(t *testing.T) {
|
|
c := HeldCondition("mail", fNew, "ace", "connection refused", true, true)
|
|
if _, err := asks.EncodeCondition(c); err != nil {
|
|
t.Fatalf("the condition is refused: %v", err)
|
|
}
|
|
if c.State != asks.StateRaised || c.Subject.ID != "mail" || c.Subject.Machine != "ace" || !strings.Contains(c.Explanation, "connection refused") || !strings.Contains(c.Key, "mail") {
|
|
t.Errorf("condition = %+v", c)
|
|
}
|
|
if got := HeldCondition("mail", fNew, "", "", false, false); got.State != asks.StateCleared || got.Key != c.Key {
|
|
t.Errorf("cleared = %+v", got)
|
|
}
|
|
}
|
|
|
|
// 1906/H2: a hold lifted by itself clears the held condition under its key, saying why.
|
|
func TestALiftedHoldClearsItsConditionSayingWhy(t *testing.T) {
|
|
c := LiftedCondition("mail", fNew)
|
|
if _, err := asks.EncodeCondition(c); err != nil {
|
|
t.Fatalf("the condition is refused: %v", err)
|
|
}
|
|
if c.State != asks.StateCleared || c.Key != HeldCondition("mail", fNew, "ace", "x", true, false).Key || !strings.Contains(c.Resolved, "lifted its hold by itself") {
|
|
t.Errorf("condition = %+v", c)
|
|
}
|
|
}
|
|
|
|
// 1906/D4: the record ask and the backup ask are valid asks of the router and of mesh-sdk's layout at their keys, answerable in
|
|
// the view, and name the move and the node.
|
|
func TestTheRecordAndBackupAsksAreValid(t *testing.T) {
|
|
now := time.Now()
|
|
rkey, err := delivery.RecordAskKey("ace", "mail", "sha256:"+strings.Repeat("b", 64))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r := RecordAsk(rkey, "ace", "mail", fNew, RecordNeed{Node: "ace", Digest: "sha256:" + strings.Repeat("b", 64), Judgement: delivery.JudgedCannot, Words: "the composition it runs is gone"}, now)
|
|
bkey, err := delivery.BackupAskKey("ace", "mail", fNew)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
dgst := "sha256:" + strings.Repeat("b", 64)
|
|
b := BackupAsk(bkey, "ace", "mail", fNew, dgst, "no space left", 0, now)
|
|
for key, a := range map[string]delivery.AssignerAsk{rkey: r, bkey: b} {
|
|
if err := a.Ask.Check(now); err != nil {
|
|
t.Errorf("%s: the router refuses the ask: %v", key, err)
|
|
}
|
|
if _, err := delivery.EncodeAssignerAsk(key, a); err != nil {
|
|
t.Errorf("%s: cannot be written: %v", key, err)
|
|
}
|
|
if _, err := delivery.ViewAnswerKey(key); err != nil {
|
|
t.Errorf("%s: the view may not answer it: %v", key, err)
|
|
}
|
|
}
|
|
if !strings.Contains(r.Ask.Explanation, "gone") || r.Ask.Options[0].ID != OptionYes {
|
|
t.Errorf("the record ask = %+v", r.Ask)
|
|
}
|
|
if !strings.Contains(b.Ask.Explanation, "no space left") || b.Ask.Options[0].ID != delivery.BackupAgain || b.Ask.Options[1].ID != delivery.BackupSkip {
|
|
t.Errorf("the backup ask = %+v", b.Ask)
|
|
}
|
|
// the id binds the ask to the composition and to its number (review round 1, items 3 and 4)
|
|
if again := BackupAsk(bkey, "ace", "mail", fNew, dgst, "x", NextBackupAsking(0), now); again.Ask.ID == b.Ask.ID {
|
|
t.Errorf("asked again under the same id")
|
|
}
|
|
if NextBackupAsking(-1) != 0 || NextBackupAsking(0) != 2 || NextBackupAsking(2) != 3 {
|
|
t.Errorf("the numbers of the askings: %d %d %d", NextBackupAsking(-1), NextBackupAsking(0), NextBackupAsking(2))
|
|
}
|
|
if BackupAskDigest(b) != dgst {
|
|
t.Errorf("the ask names composition %q, want %q", BackupAskDigest(b), dgst)
|
|
}
|
|
if BackupAnswerWords("again", 0) == BackupAnswerWords("again", 1) {
|
|
t.Errorf("two answers of the same option are one want value")
|
|
}
|
|
c := BackupCondition("ace", "mail", fNew, "no space left", true)
|
|
if _, err := asks.EncodeCondition(c); err != nil {
|
|
t.Errorf("the backup condition is refused: %v", err)
|
|
}
|
|
// ADR 0546 clauses 7 and 8: a put's ask is valid at the record ask's key; for the bus its yes approves and binds the act,
|
|
// so the view cannot answer it, and it lasts no longer than an approving ask may.
|
|
put := delivery.Put{FP: fNew, Why: "the bus's user list", Hold: true}
|
|
d := delivery.ComposedDigest{Digest: dgst, Judgement: delivery.JudgedCannot, Words: "the have names no composition"}
|
|
for _, bus := range []bool{false, true} {
|
|
a, err := PutAsk(rkey, "ace", "mail", put, d, bus, now)
|
|
if err != nil {
|
|
t.Fatalf("bus %v: %v", bus, err)
|
|
}
|
|
if err := a.Ask.Check(now); err != nil {
|
|
t.Errorf("bus %v: the router refuses the put's ask: %v", bus, err)
|
|
}
|
|
if _, err := delivery.EncodeAssignerAsk(rkey, a); err != nil {
|
|
t.Errorf("bus %v: the put's ask cannot be written: %v", bus, err)
|
|
}
|
|
viewed := delivery.ViewAnswerFits(delivery.ViewAnswer{Asking: a.Ask.ID, Option: OptionYes}, a)
|
|
yes := a.Ask.Options[0]
|
|
switch {
|
|
case !strings.Contains(a.Ask.Explanation, "the bus's user list"):
|
|
t.Errorf("bus %v: the put's ask does not say why: %s", bus, a.Ask.Explanation)
|
|
case bus && (yes.Level != asks.Approve || yes.Binds == "" || viewed == nil || a.Ask.Expires.After(now.Add(asks.ApproveLasts))):
|
|
t.Errorf("bus: the yes is %+v, the view's answer %v, expiring %s", yes, viewed, a.Ask.Expires)
|
|
case !bus && (yes.Level != asks.Acknowledge || viewed != nil):
|
|
t.Errorf("a recorded module: the yes is %+v, the view's answer %v", yes, viewed)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 1945/AC21: the condition of a module on neither list is urgent, names the module, the node and the rule holding its want back,
|
|
// under the assigner's own name keyed by node and module, and is cleared under the same key.
|
|
func TestTheNeitherConditionNamesModuleNodeAndRule(t *testing.T) {
|
|
c := NeitherCondition("g14", "i3status-rust", Holding{Rule: delivery.RuleProviders, Words: "no provider of x11-display runs healthy on g14 or at its canary"}, true)
|
|
if _, err := asks.EncodeCondition(c); err != nil {
|
|
t.Fatalf("the condition is refused: %v", err)
|
|
}
|
|
if c.State != asks.StateRaised || c.Severity != asks.SeverityUrgent || c.Subject.ID != "i3status-rust" || c.Subject.Machine != "g14" ||
|
|
!strings.Contains(c.Explanation, "x11-display") || !strings.Contains(c.Explanation, "rule 5") || c.Key != "mesh-delivery-assigner.neither.g14.i3status-rust" {
|
|
t.Errorf("condition = %+v", c)
|
|
}
|
|
if got := NeitherCondition("g14", "i3status-rust", Holding{}, false); got.State != asks.StateCleared || got.Key != c.Key {
|
|
t.Errorf("cleared = %+v", got)
|
|
}
|
|
}
|