The node agent applies only the composition its want names, and the assigner writes a want only once that composition exists (ADR 0539 clauses 1, 6, 14 and 16).
177 lines
7.1 KiB
Go
177 lines
7.1 KiB
Go
package delivery
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func hashOf(c byte) string { return "sha256:" + digestOf(c) }
|
|
|
|
// 1943/AC3: a want naming its fingerprint and the digest of the composition it allows round-trips, and so does one
|
|
// carrying the answer to a failed backup; a want naming a fingerprint without a digest, a digest not of the sha256
|
|
// form, a removed want naming a digest, an unknown backup answer and a rule past the seventh are refused.
|
|
func TestAWantNamesItsComposition(t *testing.T) {
|
|
for _, w := range []Want{
|
|
{FP: fp2of('a'), Composed: hashOf('1')},
|
|
{FP: fp2of('a'), Composed: hashOf('1'), Backup: BackupAgain},
|
|
{FP: fp2of('a'), Composed: hashOf('1'), Backup: BackupSkip, Words: "the operator: go on without a backup"},
|
|
{FP: fp2of('a'), Composed: hashOf('1'), Newest: fp2of('b'), Rule: RuleComposed, Words: "no composition of b for ace yet"},
|
|
{Removed: true},
|
|
} {
|
|
raw, err := EncodeWant(w)
|
|
if err != nil {
|
|
t.Fatalf("EncodeWant(%+v): %v", w, err)
|
|
}
|
|
back, err := DecodeWant(raw)
|
|
w.V = Version
|
|
if err != nil || !SameWant(back, w) {
|
|
t.Errorf("want %+v came back %+v, %v", w, back, err)
|
|
}
|
|
}
|
|
refusals := []struct {
|
|
w Want
|
|
why string
|
|
}{
|
|
{Want{FP: fp2of('a')}, "names no composition"},
|
|
{Want{FP: fp2of('a'), Composed: "sha256:12"}, "not sha256"},
|
|
{Want{Removed: true, Composed: hashOf('1')}, "removed want names no"},
|
|
{Want{FP: fp2of('a'), Composed: hashOf('1'), Backup: "later"}, `backup answer "later"`},
|
|
{Want{Removed: true, Backup: BackupAgain}, "removed want names no"},
|
|
{Want{FP: fp2of('a'), Composed: hashOf('1'), Newest: fp2of('b'), Rule: 8}, "rule 8"},
|
|
}
|
|
for _, r := range refusals {
|
|
if _, err := EncodeWant(r.w); err == nil || !strings.Contains(err.Error(), r.why) {
|
|
t.Errorf("EncodeWant(%+v) = %v, want a refusal saying %q", r.w, err, r.why)
|
|
}
|
|
}
|
|
if _, err := DecodeWant([]byte(`{"v":1,"fp":"` + fp2of('a') + `"}`)); err == nil || !strings.Contains(err.Error(), "names no composition") {
|
|
t.Errorf("a want of release 5, naming no composition, was read: %v", err)
|
|
}
|
|
if RuleComposed != 7 {
|
|
t.Errorf("RuleComposed = %d, want the seventh rule", RuleComposed)
|
|
}
|
|
}
|
|
|
|
// 1943/AC3: the node's own want is `want.<node>.node` and round-trips through its parser, while a module named node is
|
|
// still refused a want key; its value names the digest of the node's own composition and no fingerprint.
|
|
func TestTheNodesOwnWant(t *testing.T) {
|
|
key, err := WantNodeKey("ace")
|
|
if err != nil || key != "want.ace.node" {
|
|
t.Fatalf("WantNodeKey = %q, %v", key, err)
|
|
}
|
|
if node, err := ParseWantNodeKey(key); err != nil || node != "ace" {
|
|
t.Errorf("ParseWantNodeKey(%q) = %q, %v", key, node, err)
|
|
}
|
|
if _, err := WantKey("ace", "node"); err == nil {
|
|
t.Errorf(`WantKey(ace, "node") accepted`)
|
|
}
|
|
if _, _, err := ParseWantKey(key); err == nil {
|
|
t.Errorf("ParseWantKey read the node's own want as a module's")
|
|
}
|
|
for _, bad := range []string{"want.ace.mesh-mail", "want.node", "ace.node", "want.a.b.node", ""} {
|
|
if _, err := ParseWantNodeKey(bad); err == nil || !strings.Contains(err.Error(), "\""+bad+"\"") {
|
|
t.Errorf("ParseWantNodeKey(%q) = %v, want a refusal naming it", bad, err)
|
|
}
|
|
}
|
|
if _, err := WantNodeKey(""); err == nil {
|
|
t.Errorf("WantNodeKey(\"\") accepted")
|
|
}
|
|
w := NodeWant{Composed: hashOf('2'), Words: "the node's own composition"}
|
|
raw, err := EncodeNodeWant(w)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
back, err := DecodeNodeWant(raw)
|
|
w.V = Version
|
|
if err != nil || back != w {
|
|
t.Errorf("node want came back %+v, %v", back, err)
|
|
}
|
|
for _, bad := range []NodeWant{{}, {Composed: "sha256:1"}} {
|
|
if _, err := EncodeNodeWant(bad); err == nil {
|
|
t.Errorf("EncodeNodeWant(%+v) accepted", bad)
|
|
}
|
|
}
|
|
if _, err := DecodeNodeWant([]byte(`{"v":1,"composed":"` + hashOf('2') + `","fp":"` + fp2of('a') + `"}`)); err == nil {
|
|
t.Errorf("a node want naming a fingerprint was read")
|
|
}
|
|
}
|
|
|
|
// 1943/AC3: a have held after a failed backup, naming the composition it runs, round-trips; so does the node's own have
|
|
// naming its composition; a have's digest not of the sha256 form is refused.
|
|
func TestAHaveNamesItsComposition(t *testing.T) {
|
|
for _, h := range []Have{
|
|
{FP: fp2of('a'), Composed: hashOf('3'), Phase: PhaseHeld, Health: HealthOK, Detail: "the backup failed: no space left"},
|
|
{FP: fp2of('a'), Composed: hashOf('3'), Phase: PhaseRunning, Health: HealthOK},
|
|
} {
|
|
raw, err := EncodeHave(h)
|
|
if err != nil {
|
|
t.Fatalf("EncodeHave(%+v): %v", h, err)
|
|
}
|
|
back, err := DecodeHave(raw)
|
|
h.V = Version
|
|
if err != nil || !SameHave(back, h) {
|
|
t.Errorf("have %+v came back %+v, %v", h, back, err)
|
|
}
|
|
}
|
|
if _, err := EncodeHave(Have{Composed: "sha256:x", Phase: PhaseRunning, Health: HealthOK}); err == nil || !strings.Contains(err.Error(), "not sha256") {
|
|
t.Errorf("a have naming a bad digest = %v", err)
|
|
}
|
|
n := HaveNode{Composed: hashOf('4')}
|
|
raw, err := EncodeHaveNode(n)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
back, err := DecodeHaveNode(raw)
|
|
n.V = Version
|
|
if err != nil || !SameHaveNode(back, n) {
|
|
t.Errorf("node have came back %+v, %v", back, err)
|
|
}
|
|
if SameHaveNode(n, HaveNode{V: Version, Composed: hashOf('5')}) {
|
|
t.Errorf("node haves naming two compositions are the same")
|
|
}
|
|
}
|
|
|
|
// 1943/AC3: the assigner's backup ask (back up again, or go on without one, ADR 0539 clause 16) and its record ask (a
|
|
// move a person must allow, clause 14) key by node, module and fingerprint or composition, round-trip through
|
|
// ParseAnyAskKey, and the view may answer both.
|
|
func TestTheBackupAndRecordAsks(t *testing.T) {
|
|
fp, digest := fp2of('a'), hashOf('6')
|
|
backup, err := BackupAskKey("ace", "mesh-mail", fp)
|
|
if err != nil || backup != "backup.ace.mesh-mail."+strings.Repeat("a", 64) {
|
|
t.Fatalf("BackupAskKey = %q, %v", backup, err)
|
|
}
|
|
record, err := RecordAskKey("ace", "mesh-mail", digest)
|
|
if err != nil || record != "record.ace.mesh-mail."+strings.Repeat("6", 64) {
|
|
t.Fatalf("RecordAskKey = %q, %v", record, err)
|
|
}
|
|
if got, err := ParseAnyAskKey(backup); err != nil || got != (AskKey{Kind: AskKindBackup, Node: "ace", Module: "mesh-mail", FP: fp}) {
|
|
t.Errorf("ParseAnyAskKey(%q) = %+v, %v", backup, got, err)
|
|
}
|
|
if got, err := ParseAnyAskKey(record); err != nil || got != (AskKey{Kind: AskKindRecord, Node: "ace", Module: "mesh-mail", Composed: digest}) {
|
|
t.Errorf("ParseAnyAskKey(%q) = %+v, %v", record, got, err)
|
|
}
|
|
for _, kind := range []string{AskKindBackup, AskKindRecord} {
|
|
if proof, err := AskNeedsProof(kind); err != nil || proof {
|
|
t.Errorf("AskNeedsProof(%s) = %v, %v; the view may answer it", kind, proof, err)
|
|
}
|
|
}
|
|
for _, k := range []string{backup, record} {
|
|
if _, err := ViewAnswerKey(k); err != nil {
|
|
t.Errorf("the view may not answer %s: %v", k, err)
|
|
}
|
|
}
|
|
for _, bad := range [][3]string{{"", "mesh-mail", fp}, {"ace", "node", fp}, {"ace", "mesh-mail", "src2:1"}} {
|
|
if _, err := BackupAskKey(bad[0], bad[1], bad[2]); err == nil {
|
|
t.Errorf("BackupAskKey(%v) accepted", bad)
|
|
}
|
|
}
|
|
if _, err := RecordAskKey("ace", "mesh-mail", "sha256:1"); err == nil {
|
|
t.Errorf("a record ask naming a bad digest was keyed")
|
|
}
|
|
for _, bad := range []string{"backup.ace.mesh-mail", "record.ace.mesh-mail.12", "record.ace." + strings.Repeat("6", 64)} {
|
|
if _, err := ParseAnyAskKey(bad); err == nil {
|
|
t.Errorf("ParseAnyAskKey(%q) accepted", bad)
|
|
}
|
|
}
|
|
}
|