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) } }