With no plan left to take a done key in, a write of the builder's keys registers the new fingerprint, composes the module's nodes and the bus's user list, and writes its grant, so the assigner's rules can name it (ADR 0539 clauses 5 and 7). The new controller composes every node at its start, and the controller it replaced at its own fingerprint, the operator's way back (clause 10); the merge window's, silent sends' and building plans' buckets are no longer asserted and are deleted once, each said as the event bucket-deleted (clause 13). The assigner may read the compositions' digests, and a node-engine hears its backup holder's backed-up.
164 lines
5.5 KiB
Go
164 lines
5.5 KiB
Go
//go:build integration
|
|
|
|
package broker
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/nats-io/nats.go"
|
|
"github.com/nats-io/nats.go/jetstream"
|
|
"github.com/novox/mesh-controller/internal/testbus"
|
|
)
|
|
|
|
func TestWhatTheControllerAskedIsBounded(t *testing.T) {
|
|
js, err := Dial(testbus.URL(t))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer js.Close()
|
|
if err := js.EnsureControllerBuckets(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
kv, err := jetstream.New(js.Conn())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
bucket, err := kv.KeyValue(ctx, AskedBucket)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
status, err := bucket.Status(ctx)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
info := status.(*jetstream.KeyValueBucketStatus).StreamInfo()
|
|
if status.History() != 1 || status.TTL() != AskedKeptFor || info.Config.MaxBytes <= 0 || info.Config.MaxBytes > 64<<20 {
|
|
t.Errorf("history %d, age %s, bytes %d", status.History(), status.TTL(), info.Config.MaxBytes)
|
|
}
|
|
}
|
|
|
|
func TestAResetOfTheEventsConsumerLeavesTheMergesAndTheirConsumerAlone(t *testing.T) {
|
|
js, err := Dial(testbus.URL(t))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer js.Close()
|
|
if err := AssertMeshStreams(js); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := AssertMeshConsumers(js); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, c := range MeshConsumers() {
|
|
if c.Stream == EventsStream && c.Name == MergesConsumerName {
|
|
if c.Resettable != "" || c.MaxDeliver != 0 || len(c.Filters) != 1 || c.Filters[0] != ControllerFollows[3] {
|
|
t.Errorf("the merges consumer is %+v; want one filter, no max-deliver, not resettable", c)
|
|
}
|
|
}
|
|
if c.Stream == EventsStream && c.Name == ControllerName {
|
|
for _, f := range c.Filters {
|
|
if f == ControllerFollows[3] {
|
|
t.Errorf("the events consumer still follows the merges, which the merges consumer takes")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if _, err := js.Context().Publish(ControllerFollows[3], []byte(`{"commit":"c1"}`)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := js.Context().Publish(ControllerFollows[0], []byte(`{}`)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
merges, err := js.Context().ConsumerInfo(EventsStream, MergesConsumerName)
|
|
if err != nil || merges.NumPending != 1 {
|
|
t.Fatalf("the merges consumer holds %v (%v); want the merge", merges, err)
|
|
}
|
|
if _, _, err := js.ResetConsumer(EventsStream, ControllerName); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
merges, err = js.Context().ConsumerInfo(EventsStream, MergesConsumerName)
|
|
if err != nil || merges.NumPending != 1 || merges.Delivered.Stream != 0 {
|
|
t.Fatalf("after the reset the merges consumer holds %+v (%v); want its merge left alone", merges, err)
|
|
}
|
|
}
|
|
|
|
func TestAMergeTheOldConsumerHadNotAcknowledgedIsTheMergesConsumers(t *testing.T) {
|
|
js, err := Dial(testbus.URL(t))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer js.Close()
|
|
if err := AssertMeshStreams(js); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
old := &nats.ConsumerConfig{Durable: ControllerName, AckPolicy: nats.AckExplicitPolicy, AckWait: 30 * time.Second,
|
|
MaxDeliver: 5, MaxAckPending: 1, DeliverSubject: "_DELIVER.controller.EVENTS", DeliverPolicy: nats.DeliverNewPolicy,
|
|
FilterSubjects: append(append([]string(nil), ControllerFollows...), AgainFilter(ControllerName))}
|
|
if _, err := js.Context().AddConsumer(EventsStream, old); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := js.Context().Publish(ControllerFollows[3], []byte(`{"commit":"c1"}`)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := AssertMeshConsumers(js); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
merges, err := js.Context().ConsumerInfo(EventsStream, MergesConsumerName)
|
|
if err != nil || merges.NumPending != 1 {
|
|
t.Fatalf("the merges consumer holds %v (%v); want the merge the old consumer never acknowledged", merges, err)
|
|
}
|
|
}
|
|
|
|
// **The chain's buckets keep sixty-four revisions, the assignments' that were a mirror's included** (ADR 0408 §3 to §5,
|
|
// issue 1089): a bucket made as a mirror — one revision, a marker age — is brought to the chain's options without the
|
|
// server refusing the update, and a key written three times keeps three values.
|
|
func TestTheChainBucketsKeepSixtyFourRevisionsOnTheBus(t *testing.T) {
|
|
js, err := Dial(testbus.URL(t))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer js.Close()
|
|
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
|
defer cancel()
|
|
api, err := jetstream.New(js.Conn())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// The bucket as the mirror made it.
|
|
if _, err := api.CreateOrUpdateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: AssignmentsBucket, History: 1,
|
|
MaxValueSize: MirrorValueBytes, MaxBytes: MirrorBytes, Storage: jetstream.FileStorage,
|
|
LimitMarkerTTL: MirrorMarkerTTL}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := js.EnsureControllerBuckets(); err != nil {
|
|
t.Fatalf("a mirror's bucket is not brought to the chain's options: %v", err)
|
|
}
|
|
for _, name := range ChainBuckets() {
|
|
kv, err := api.KeyValue(ctx, name)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
status, err := kv.Status(ctx)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cfg := status.(*jetstream.KeyValueBucketStatus).StreamInfo().Config
|
|
if status.History() != ChainHistory || status.TTL() != 0 || cfg.MaxMsgSize < 0 || cfg.MaxMsgSize != ChainValueBytes {
|
|
t.Errorf("%s keeps %d revisions, age %s, a value of %d bytes", name, status.History(), status.TTL(), cfg.MaxMsgSize)
|
|
}
|
|
for _, value := range []string{"one", "two", "three"} {
|
|
if _, err := kv.Put(ctx, "k", []byte(value)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
history, err := kv.History(ctx, "k")
|
|
if err != nil || len(history) != 3 {
|
|
t.Errorf("%s keeps %d values of a key written three times: %v", name, len(history), err)
|
|
}
|
|
}
|
|
}
|