266 lines
10 KiB
Go
266 lines
10 KiB
Go
package delivery
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// 1297/B1a: a want key round-trips through its parser; a held key, a have key and a node's own key are refused,
|
|
// naming the key.
|
|
func TestWantKey(t *testing.T) {
|
|
key, err := WantKey("ace", "mesh-mail")
|
|
if err != nil || key != "want.ace.mesh-mail" {
|
|
t.Fatalf("WantKey = %q, %v", key, err)
|
|
}
|
|
node, module, err := ParseWantKey(key)
|
|
if err != nil || node != "ace" || module != "mesh-mail" {
|
|
t.Errorf("ParseWantKey = %q, %q, %v", node, module, err)
|
|
}
|
|
for _, n := range [][2]string{{"ace", "node"}, {"a.b", "m"}, {"ace", ""}, {"", "m"}, {"ace", "m*"}} {
|
|
if _, err := WantKey(n[0], n[1]); err == nil {
|
|
t.Errorf("WantKey(%q, %q) accepted", n[0], n[1])
|
|
}
|
|
}
|
|
held, _ := HeldKey("mesh-mail", fp2of('a'))
|
|
for _, bad := range []string{"ace.mesh-mail", "want.ace.node", "want.ace", "want.ace.m.x", held, "wants.ace.m", ""} {
|
|
if _, _, err := ParseWantKey(bad); err == nil || !strings.Contains(err.Error(), "\""+bad+"\"") {
|
|
t.Errorf("ParseWantKey(%q) = %v, want a refusal naming it", bad, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 1297/B1a: a held key round-trips; a want key, a builder build key and a fingerprint not of the new form are refused.
|
|
func TestHeldKey(t *testing.T) {
|
|
fp := fp2of('b')
|
|
key, err := HeldKey("mesh-mail", fp)
|
|
if err != nil || key != "held.mesh-mail."+strings.Repeat("b", 64) {
|
|
t.Fatalf("HeldKey = %q, %v", key, err)
|
|
}
|
|
module, back, err := ParseHeldKey(key)
|
|
if err != nil || module != "mesh-mail" || back != fp {
|
|
t.Errorf("ParseHeldKey = %q, %q, %v", module, back, err)
|
|
}
|
|
src1, _ := SourceFingerprint(SourceInputs{Module: "m", Tree: "t"})
|
|
if _, err := HeldKey("mesh-mail", src1); err == nil {
|
|
t.Errorf("a src1 fingerprint was held")
|
|
}
|
|
build, _ := BuilderBuildKey("mesh-mail", fp)
|
|
for _, bad := range []string{build, "want.ace.mesh-mail", "held.mesh-mail.abc", "held." + strings.Repeat("b", 64), ""} {
|
|
if _, _, err := ParseHeldKey(bad); err == nil || !strings.Contains(err.Error(), "\""+bad+"\"") {
|
|
t.Errorf("ParseHeldKey(%q) = %v, want a refusal naming it", bad, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 1297/B1a: a difference key round-trips; a want or held key and a node's own key are refused.
|
|
func TestDifferenceKey(t *testing.T) {
|
|
key, err := DifferenceKey("g14", "mesh-mail")
|
|
if err != nil || key != "g14.mesh-mail" {
|
|
t.Fatalf("DifferenceKey = %q, %v", key, err)
|
|
}
|
|
node, module, err := ParseDifferenceKey(key)
|
|
if err != nil || node != "g14" || module != "mesh-mail" {
|
|
t.Errorf("ParseDifferenceKey = %q, %q, %v", node, module, err)
|
|
}
|
|
held, _ := HeldKey("mesh-mail", fp2of('a'))
|
|
for _, bad := range []string{"want.g14.mesh-mail", held, "g14.node", "g14", ""} {
|
|
if _, _, err := ParseDifferenceKey(bad); err == nil || !strings.Contains(err.Error(), "\""+bad+"\"") {
|
|
t.Errorf("ParseDifferenceKey(%q) = %v, want a refusal naming it", bad, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 1297/B1a: wants, holds and differences round-trip through their encoders unchanged, at Version.
|
|
func TestAssignerValuesRoundTrip(t *testing.T) {
|
|
wants := []Want{
|
|
{FP: fp2of('a')},
|
|
{FP: fp2of('a'), Newest: fp2of('b'), Rule: RuleCanary, Words: "the canary ace does not run it healthy yet"},
|
|
{Removed: true, Words: "the module left g14"},
|
|
}
|
|
for _, w := range wants {
|
|
data, err := EncodeWant(w)
|
|
if err != nil {
|
|
t.Fatalf("EncodeWant(%+v): %v", w, err)
|
|
}
|
|
got, err := DecodeWant(data)
|
|
w.V = Version
|
|
if err != nil || !SameWant(got, w) {
|
|
t.Errorf("want %+v came back %+v, %v", w, got, err)
|
|
}
|
|
}
|
|
h := Held{Node: "ace", Words: "its health check failed: connection refused"}
|
|
data, err := EncodeHeld(h)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
gotH, err := DecodeHeld(data)
|
|
h.V = Version
|
|
if err != nil || !SameHeld(gotH, h) {
|
|
t.Errorf("held came back %+v, %v", gotH, err)
|
|
}
|
|
diffs := []Difference{
|
|
{Want: fp2of('a'), Sent: fp2of('b'), Rule: RuleCanary},
|
|
{Sent: fp2of('b'), Rule: RuleBuildDone},
|
|
{WantRemoved: true, Sent: fp2of('b'), Unexplained: true},
|
|
{SentBuild: "build-17", Rule: RuleGrant},
|
|
}
|
|
for _, d := range diffs {
|
|
data, err := EncodeDifference(d)
|
|
if err != nil {
|
|
t.Fatalf("EncodeDifference(%+v): %v", d, err)
|
|
}
|
|
got, err := DecodeDifference(data)
|
|
d.V = Version
|
|
if err != nil || !SameDifference(got, d) {
|
|
t.Errorf("difference %+v came back %+v, %v", d, got, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 1297/B1a: what a want, a hold and a difference may not say is refused on encode and on decode.
|
|
func TestAssignerValuesRefused(t *testing.T) {
|
|
src1, _ := SourceFingerprint(SourceInputs{Module: "m", Tree: "t"})
|
|
for _, w := range []Want{
|
|
{},
|
|
{FP: src1},
|
|
{Removed: true, FP: fp2of('a')},
|
|
{FP: fp2of('a'), Newest: fp2of('b')},
|
|
{FP: fp2of('a'), Newest: fp2of('a'), Rule: 3},
|
|
{FP: fp2of('a'), Newest: fp2of('b'), Rule: 7},
|
|
} {
|
|
if _, err := EncodeWant(w); err == nil {
|
|
t.Errorf("EncodeWant(%+v) accepted", w)
|
|
}
|
|
}
|
|
if _, err := EncodeHeld(Held{Node: "a.b"}); err == nil {
|
|
t.Errorf("a held naming a dotted node was accepted")
|
|
}
|
|
for _, d := range []Difference{
|
|
{Want: fp2of('a'), Sent: fp2of('b')},
|
|
{Want: fp2of('a'), Sent: fp2of('b'), Rule: 3, Unexplained: true},
|
|
{Want: fp2of('a'), Sent: fp2of('a'), Rule: 3},
|
|
{},
|
|
{Want: fp2of('a'), Sent: fp2of('b'), Rule: 9},
|
|
{WantRemoved: true, Want: fp2of('a'), Rule: 1},
|
|
} {
|
|
if _, err := EncodeDifference(d); err == nil {
|
|
t.Errorf("EncodeDifference(%+v) accepted", d)
|
|
}
|
|
}
|
|
if _, err := DecodeWant([]byte(`{"v":2,"fp":"` + fp2of('a') + `"}`)); !errors.Is(err, ErrUnknownVersion) {
|
|
t.Errorf("a want of version 2 was read: %v", err)
|
|
}
|
|
if _, err := DecodeDifference([]byte(`{"v":1,"want":"` + fp2of('a') + `","sent":"` + fp2of('b') + `"}`)); err == nil {
|
|
t.Errorf("a difference naming no rule was read")
|
|
}
|
|
}
|
|
|
|
// 1297/B1b: a composition key is the checker's verdict key of the same check, and round-trips; a want, held or
|
|
// difference key is refused, naming it.
|
|
func TestCompositionKey(t *testing.T) {
|
|
key, err := CompositionKey("feat/r4g", "0123456789ab")
|
|
verdict, _ := CheckerVerdictKey("feat/r4g", "0123456789ab")
|
|
if err != nil || key != verdict {
|
|
t.Fatalf("CompositionKey = %q, %v; the checker's key is %q", key, err, verdict)
|
|
}
|
|
group, heads, err := ParseCompositionKey(key)
|
|
if err != nil || group != "feat/r4g" || heads != "0123456789ab" {
|
|
t.Errorf("ParseCompositionKey = %q, %q, %v", group, heads, err)
|
|
}
|
|
held, _ := HeldKey("mesh-mail", fp2of('a'))
|
|
for _, bad := range []string{"want.ace.mesh-mail", held, "ace.mesh-mail", "g.0123456789AB", ""} {
|
|
if _, _, err := ParseCompositionKey(bad); err == nil || !strings.Contains(err.Error(), "\""+bad+"\"") {
|
|
t.Errorf("ParseCompositionKey(%q) = %v, want a refusal naming it", bad, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 1297/B1b: a composition in each state round-trips; the checker's composition of a pass or fail carries its state,
|
|
// words, mesh-sdk commit and machines, and a wait gives none.
|
|
func TestCompositionVerdictRoundTrip(t *testing.T) {
|
|
for _, c := range []CompositionVerdict{
|
|
{Group: "g", Heads: "0123456789ab", State: CompositionPass, SDK: "7aacbc1", Machines: []string{"ace"}, Took: 120},
|
|
{Group: "g", Heads: "0123456789ab", State: CompositionFail, SDK: "7aacbc1", Machines: []string{"ace", "g14"}, Machine: "g14",
|
|
Words: "g14 runs mesh-mail, which the change removes from its source", Warnings: []string{"the merge rebuilds 13 modules"}},
|
|
{Group: "g", Heads: "0123456789ab", State: CompositionWaits, Words: "the indexer holds no candidates for the group yet"},
|
|
} {
|
|
data, err := EncodeCompositionVerdict(c)
|
|
if err != nil {
|
|
t.Fatalf("EncodeCompositionVerdict(%+v): %v", c, err)
|
|
}
|
|
got, err := DecodeCompositionVerdict(data)
|
|
c.V = Version
|
|
if err != nil || !SameCompositionVerdict(got, c) {
|
|
t.Errorf("composition %+v came back %+v, %v", c, got, err)
|
|
}
|
|
comp := got.Composition()
|
|
if c.State == CompositionWaits {
|
|
if comp != nil {
|
|
t.Errorf("a wait gave the checker a composition %+v", comp)
|
|
}
|
|
continue
|
|
}
|
|
if comp == nil || comp.State != c.State || comp.SDK != c.SDK || comp.Words != c.Words || len(comp.Machines) != len(c.Machines) {
|
|
t.Errorf("the checker's composition of %+v is %+v", c, comp)
|
|
}
|
|
if err := comp.check(); err != nil {
|
|
t.Errorf("the checker's composition of %+v is refused by the verdict: %v", c, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 1297/B1b: what a composition may not say is refused.
|
|
func TestCompositionVerdictRefused(t *testing.T) {
|
|
for _, c := range []CompositionVerdict{
|
|
{Heads: "0123456789ab", State: CompositionPass, SDK: "x"},
|
|
{Group: "g", Heads: "0123", State: CompositionPass, SDK: "x"},
|
|
{Group: "g", Heads: "0123456789ab", State: CompositionPass},
|
|
{Group: "g", Heads: "0123456789ab", State: CompositionPass, SDK: "x", Machine: "ace"},
|
|
{Group: "g", Heads: "0123456789ab", State: CompositionFail, SDK: "x"},
|
|
{Group: "g", Heads: "0123456789ab", State: CompositionFail, Words: "w"},
|
|
{Group: "g", Heads: "0123456789ab", State: CompositionWaits},
|
|
{Group: "g", Heads: "0123456789ab", State: CompositionWaits, Words: "w", Machines: []string{"ace"}},
|
|
{Group: "g", Heads: "0123456789ab", State: CompositionNotRun},
|
|
{Group: "g", Heads: "0123456789ab", State: CompositionPass, SDK: "x", Took: -1},
|
|
} {
|
|
if _, err := EncodeCompositionVerdict(c); err == nil {
|
|
t.Errorf("EncodeCompositionVerdict(%+v) accepted", c)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 1394/B1b: a grant key round-trips; a want or held key is refused, naming it; a grant round-trips and one naming an
|
|
// empty account is refused.
|
|
func TestControllerGrant(t *testing.T) {
|
|
fp := fp2of('c')
|
|
key, err := ControllerGrantKey("mesh-delivery-assigner", fp)
|
|
if err != nil || key != "mesh-delivery-assigner."+strings.Repeat("c", 64) {
|
|
t.Fatalf("ControllerGrantKey = %q, %v", key, err)
|
|
}
|
|
module, back, err := ParseControllerGrantKey(key)
|
|
if err != nil || module != "mesh-delivery-assigner" || back != fp {
|
|
t.Errorf("ParseControllerGrantKey = %q, %q, %v", module, back, err)
|
|
}
|
|
held, _ := HeldKey("mesh-mail", fp)
|
|
for _, bad := range []string{"want.ace.mesh-mail", held, "ace.mesh-mail", ""} {
|
|
if _, _, err := ParseControllerGrantKey(bad); err == nil || !strings.Contains(err.Error(), "\""+bad+"\"") {
|
|
t.Errorf("ParseControllerGrantKey(%q) = %v, want a refusal naming it", bad, err)
|
|
}
|
|
}
|
|
for _, g := range []ControllerGrant{{}, {Accounts: []string{"UABC", "UDEF"}}} {
|
|
data, err := EncodeControllerGrant(g)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, err := DecodeControllerGrant(data)
|
|
g.V = Version
|
|
if err != nil || !SameControllerGrant(got, g) {
|
|
t.Errorf("grant %+v came back %+v, %v", g, got, err)
|
|
}
|
|
}
|
|
if _, err := EncodeControllerGrant(ControllerGrant{Accounts: []string{""}}); err == nil {
|
|
t.Errorf("a grant naming an empty account was accepted")
|
|
}
|
|
}
|