Name the pulls buckets of the seat's claimants on the check, so the build agent reads them (issue 1212)
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
package link
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/nats-io/nats.go/jetstream"
|
||||
|
||||
"github.com/novox/mesh-controller/internal/broker"
|
||||
)
|
||||
|
||||
// The build agent reads and watches the pull requests' keys in the buckets the check request names (novox/hq ADR 0437
|
||||
// §3, issue 1212): the controller knows the seat's claimants, the build agent holds no catalogue.
|
||||
|
||||
// 1212/AC4: a request naming no bucket reads mesh-delivery's, as before; the field is optional on the wire.
|
||||
func TestARequestNamingNoPullsBucketUsesMeshDeliverys(t *testing.T) {
|
||||
if got := PullBucketsOf(&CheckRequest{}); !reflect.DeepEqual(got, []string{broker.PullsBucket}) {
|
||||
t.Errorf("buckets %v", got)
|
||||
}
|
||||
if got := PullBucketsOf(nil); !reflect.DeepEqual(got, []string{broker.PullsBucket}) {
|
||||
t.Errorf("buckets of no check %v", got)
|
||||
}
|
||||
named := &CheckRequest{Pulls: []string{"mesh-delivery-gate_pulls"}}
|
||||
if got := PullBucketsOf(named); !reflect.DeepEqual(got, named.Pulls) {
|
||||
t.Errorf("buckets %v", got)
|
||||
}
|
||||
raw := mustJSON(t, CheckRequest{Owner: "o"})
|
||||
if contains(string(raw), "pulls") {
|
||||
t.Errorf("an empty field is written: %s", raw)
|
||||
}
|
||||
}
|
||||
|
||||
// opened is a set of buckets by name, each holding keys; a bucket not in it does not exist.
|
||||
func opened(buckets map[string]map[string]string, opens *[]string) func(context.Context, string) (func(context.Context, string) ([]byte, error), error) {
|
||||
return func(_ context.Context, bucket string) (func(context.Context, string) ([]byte, error), error) {
|
||||
*opens = append(*opens, bucket)
|
||||
keys, ok := buckets[bucket]
|
||||
if !ok {
|
||||
return nil, jetstream.ErrBucketNotFound
|
||||
}
|
||||
return func(_ context.Context, key string) ([]byte, error) {
|
||||
v, ok := keys[key]
|
||||
if !ok {
|
||||
return nil, jetstream.ErrKeyNotFound
|
||||
}
|
||||
return []byte(v), nil
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// 1212/AC3: the buckets are read in turn, the first holding the key answers, a missing bucket is passed over, and
|
||||
// none holding it is no key.
|
||||
func TestThePullKeyIsReadFromTheNamedBucketsInTurn(t *testing.T) {
|
||||
key := PullKeyOf("novox", "a", 7)
|
||||
both := map[string]map[string]string{
|
||||
"mesh-delivery_pulls": {key: `{"head":"old"}`},
|
||||
"mesh-delivery-gate_pulls": {key: `{"head":"new"}`},
|
||||
}
|
||||
var opens []string
|
||||
got, found, err := readPullKeyIn(t.Context(), []string{"mesh-delivery-gate_pulls", "mesh-delivery_pulls"}, opened(both, &opens), "novox", "a", 7)
|
||||
if err != nil || !found || got.Head != "new" || len(opens) != 1 {
|
||||
t.Errorf("first holding answers: %+v %v %v, opened %v", got, found, err, opens)
|
||||
}
|
||||
opens = nil
|
||||
only := map[string]map[string]string{"mesh-delivery_pulls": {key: `{"head":"old"}`}, "mesh-delivery-gate_pulls": {}}
|
||||
got, found, err = readPullKeyIn(t.Context(), []string{"mesh-delivery-gate_pulls", "mesh-delivery_pulls"}, opened(only, &opens), "novox", "a", 7)
|
||||
if err != nil || !found || got.Head != "old" || len(opens) != 2 {
|
||||
t.Errorf("a bucket without the key is passed over: %+v %v %v, opened %v", got, found, err, opens)
|
||||
}
|
||||
opens = nil
|
||||
got, found, err = readPullKeyIn(t.Context(), []string{"gone_pulls", "mesh-delivery_pulls"}, opened(only, &opens), "novox", "a", 7)
|
||||
if err != nil || !found || got.Head != "old" {
|
||||
t.Errorf("a missing bucket is passed over: %+v %v %v", got, found, err)
|
||||
}
|
||||
_, found, err = readPullKeyIn(t.Context(), []string{"gone_pulls", "mesh-delivery-gate_pulls"}, opened(only, &opens), "novox", "a", 8)
|
||||
if err != nil || found {
|
||||
t.Errorf("none holding it: found %v, %v", found, err)
|
||||
}
|
||||
boom := errors.New("bus away")
|
||||
_, _, err = readPullKeyIn(t.Context(), []string{"x"}, func(context.Context, string) (func(context.Context, string) ([]byte, error), error) {
|
||||
return nil, boom
|
||||
}, "novox", "a", 7)
|
||||
if !errors.Is(err, boom) {
|
||||
t.Errorf("a real failure is an error: %v", err)
|
||||
}
|
||||
// The watch takes the first named bucket that exists.
|
||||
opens = nil
|
||||
bucket, err := firstBucketThatExists(t.Context(), []string{"gone_pulls", "mesh-delivery-gate_pulls", "mesh-delivery_pulls"}, func(_ context.Context, b string) error {
|
||||
opens = append(opens, b)
|
||||
if b == "gone_pulls" {
|
||||
return jetstream.ErrBucketNotFound
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil || bucket != "mesh-delivery-gate_pulls" || len(opens) != 2 {
|
||||
t.Errorf("watch bucket %q %v, tried %v", bucket, err, opens)
|
||||
}
|
||||
}
|
||||
|
||||
func mustJSON(t *testing.T, v any) string {
|
||||
t.Helper()
|
||||
raw, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return string(raw)
|
||||
}
|
||||
|
||||
func contains(s, sub string) bool { return strings.Contains(s, sub) }
|
||||
Reference in New Issue
Block a user