The controller writes one module's composition per node and fingerprint, and the assigner reads its digest and judgement before it writes a want (ADR 0539 clauses 1, 2 and 14).
118 lines
5.1 KiB
Go
118 lines
5.1 KiB
Go
package delivery
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// 1943/AC1: a node's composition bucket is named for the node, and a node with no name, or one a bucket refuses,
|
|
// is refused, naming why.
|
|
func TestComposedBucketFor(t *testing.T) {
|
|
b, err := ComposedBucketFor("ace")
|
|
if err != nil || b != "composed-ace" {
|
|
t.Fatalf("ComposedBucketFor(ace) = %q, %v", b, err)
|
|
}
|
|
for _, bad := range []string{"", "a.b", "a*", "a b", "a>"} {
|
|
if _, err := ComposedBucketFor(bad); err == nil || !strings.Contains(err.Error(), "node") {
|
|
t.Errorf("ComposedBucketFor(%q) = %v, want a refusal naming the node", bad, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 1943/AC1: a module's composition key and the node's own key round-trip through one parser; an empty node, a
|
|
// module named node, a bad fingerprint and a key of another shape are refused, naming why.
|
|
func TestComposedKeys(t *testing.T) {
|
|
fp := fp2of('c')
|
|
key, err := ComposedKey("ace", "mesh-mail", fp)
|
|
if err != nil || key != "ace.mesh-mail."+strings.Repeat("c", 64) {
|
|
t.Fatalf("ComposedKey = %q, %v", key, err)
|
|
}
|
|
node, module, back, isNode, err := ParseComposedKey(key)
|
|
if err != nil || node != "ace" || module != "mesh-mail" || back != fp || isNode {
|
|
t.Errorf("ParseComposedKey(%q) = %q, %q, %q, %v, %v", key, node, module, back, isNode, err)
|
|
}
|
|
own, err := ComposedNodeKey("ace")
|
|
if err != nil || own != "ace.node" {
|
|
t.Fatalf("ComposedNodeKey = %q, %v", own, err)
|
|
}
|
|
node, module, back, isNode, err = ParseComposedKey(own)
|
|
if err != nil || node != "ace" || module != "" || back != "" || !isNode {
|
|
t.Errorf("ParseComposedKey(%q) = %q, %q, %q, %v, %v", own, node, module, back, isNode, err)
|
|
}
|
|
|
|
refusals := []struct {
|
|
node, module, fp, why string
|
|
}{
|
|
{"", "mesh-mail", fp, "node name is empty"},
|
|
{"ace", "node", fp, `"node" is refused`},
|
|
{"ace", "", fp, "module name is empty"},
|
|
{"ace", "mesh-mail", "src2:abc", "not a fingerprint of the new form"},
|
|
{"ace", "mesh-mail", "src1:" + strings.Repeat("c", 64), "not a fingerprint of the new form"},
|
|
{"a.b", "mesh-mail", fp, "node name"},
|
|
}
|
|
for _, r := range refusals {
|
|
if _, err := ComposedKey(r.node, r.module, r.fp); err == nil || !strings.Contains(err.Error(), r.why) {
|
|
t.Errorf("ComposedKey(%q, %q, %q) = %v, want a refusal saying %q", r.node, r.module, r.fp, err, r.why)
|
|
}
|
|
}
|
|
if _, err := ComposedNodeKey(""); err == nil {
|
|
t.Errorf("ComposedNodeKey(\"\") accepted")
|
|
}
|
|
for _, bad := range []string{"", "ace", "ace.mesh-mail", "ace.node.x", "ace.mesh-mail.abc", ".mesh-mail." + strings.Repeat("c", 64),
|
|
"ace.node." + strings.Repeat("c", 64), "ace.mesh-mail." + strings.Repeat("c", 64) + ".x", "want.ace.mesh-mail"} {
|
|
if _, _, _, _, err := ParseComposedKey(bad); err == nil || !strings.Contains(err.Error(), "\""+bad+"\"") {
|
|
t.Errorf("ParseComposedKey(%q) = %v, want a refusal naming it", bad, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 1943/AC1: the controller's value beside a composition, its digest and the judgement of the move, round-trips; an
|
|
// unknown judgement, a digest not of the sha256 form, a move judged against nothing, and a cannot-judge without its
|
|
// reason are refused, naming why; an unknown version is refused as one.
|
|
func TestComposedDigestValue(t *testing.T) {
|
|
digest := "sha256:" + strings.Repeat("d", 64)
|
|
against := "sha256:" + strings.Repeat("e", 64)
|
|
for _, v := range []ComposedDigest{
|
|
{Digest: digest, Judgement: JudgedNeither},
|
|
{Digest: digest, Judgement: JudgedNeither, Against: against},
|
|
{Digest: digest, Judgement: JudgedRecreates, Against: against, Words: "the container's image changes"},
|
|
{Digest: digest, Judgement: JudgedMovesData, Against: against},
|
|
{Digest: digest, Judgement: JudgedCannot, Words: "the composition the node runs is gone"},
|
|
} {
|
|
raw, err := EncodeComposedDigest(v)
|
|
if err != nil {
|
|
t.Fatalf("EncodeComposedDigest(%+v): %v", v, err)
|
|
}
|
|
back, err := DecodeComposedDigest(raw)
|
|
v.V = Version
|
|
if err != nil || !SameComposedDigest(back, v) {
|
|
t.Errorf("round trip of %+v = %+v, %v", v, back, err)
|
|
}
|
|
}
|
|
refusals := []struct {
|
|
v ComposedDigest
|
|
why string
|
|
}{
|
|
{ComposedDigest{Digest: digest, Judgement: "maybe"}, `judgement "maybe"`},
|
|
{ComposedDigest{Digest: digest}, `judgement ""`},
|
|
{ComposedDigest{Digest: "sha256:abc", Judgement: JudgedNeither}, "not sha256"},
|
|
{ComposedDigest{Judgement: JudgedNeither}, "not sha256"},
|
|
{ComposedDigest{Digest: digest, Judgement: JudgedRecreates}, "against"},
|
|
{ComposedDigest{Digest: digest, Judgement: JudgedMovesData}, "against"},
|
|
{ComposedDigest{Digest: digest, Judgement: JudgedCannot}, "why"},
|
|
{ComposedDigest{Digest: digest, Judgement: JudgedNeither, Against: "sha256:zz"}, "not sha256"},
|
|
}
|
|
for _, r := range refusals {
|
|
if _, err := EncodeComposedDigest(r.v); err == nil || !strings.Contains(err.Error(), r.why) {
|
|
t.Errorf("EncodeComposedDigest(%+v) = %v, want a refusal saying %q", r.v, err, r.why)
|
|
}
|
|
}
|
|
if _, err := DecodeComposedDigest([]byte(`{"v":2,"digest":"` + digest + `","judgement":"neither"}`)); !errors.Is(err, ErrUnknownVersion) {
|
|
t.Errorf("version 2 = %v, want ErrUnknownVersion", err)
|
|
}
|
|
if _, err := DecodeComposedDigest([]byte(`{"v":1,"digest":"` + digest + `","judgement":"perhaps"}`)); err == nil {
|
|
t.Errorf("an unknown judgement was read")
|
|
}
|
|
}
|