package mq import ( "context" "errors" "reflect" "strings" "testing" ) func TestNormalizeSubscriptionSetValidDeclaration(t *testing.T) { handler := func(context.Context, Message) {} set := SubscriptionSet{ Owner: " orders ", Provider: " RABBITMQ ", Topics: []TopicSubscription{ {Topic: " orders.created ", QoS: AtMostOnce, Handler: handler}, {Topic: "orders.updated", QoS: AtLeastOnce, Handler: handler}, {Topic: "orders.deleted", QoS: ExactlyOnce, Handler: handler}, }, } got, err := NormalizeSubscriptionSet(set) if err != nil { t.Fatalf("NormalizeSubscriptionSet() error = %v", err) } want := SubscriptionSet{ Owner: "orders", Provider: ProviderRabbitMQ, Topics: []TopicSubscription{ {Topic: "orders.created", QoS: AtMostOnce, Handler: handler}, {Topic: "orders.updated", QoS: AtLeastOnce, Handler: handler}, {Topic: "orders.deleted", QoS: ExactlyOnce, Handler: handler}, }, } if got.Owner != want.Owner || got.Provider != want.Provider { t.Fatalf("normalized identity = %#v, want owner=%q provider=%q", got, want.Owner, want.Provider) } if len(got.Topics) != len(want.Topics) { t.Fatalf("normalized topic count = %d, want %d", len(got.Topics), len(want.Topics)) } for index := range want.Topics { if got.Topics[index].Topic != want.Topics[index].Topic { t.Errorf("topic[%d] = %q, want %q", index, got.Topics[index].Topic, want.Topics[index].Topic) } if got.Topics[index].QoS != want.Topics[index].QoS { t.Errorf("qos[%d] = %d, want %d", index, got.Topics[index].QoS, want.Topics[index].QoS) } if reflect.ValueOf(got.Topics[index].Handler).Pointer() != reflect.ValueOf(want.Topics[index].Handler).Pointer() { t.Errorf("handler[%d] was changed", index) } } } func TestNormalizeSubscriptionSetRejectsInvalidDeclarations(t *testing.T) { handler := func(context.Context, Message) {} base := func() SubscriptionSet { return SubscriptionSet{ Owner: "orders", Provider: ProviderEMQX, Topics: []TopicSubscription{ {Topic: "orders.created", QoS: AtLeastOnce, Handler: handler}, }, } } tests := []struct { name string set SubscriptionSet wantErr string }{ { name: "empty owner", set: func() SubscriptionSet { set := base(); set.Owner = " "; return set }(), wantErr: "owner is empty", }, { name: "empty provider", set: func() SubscriptionSet { set := base(); set.Provider = " "; return set }(), wantErr: "unsupported mq provider", }, { name: "unsupported provider", set: func() SubscriptionSet { set := base(); set.Provider = "kafka"; return set }(), wantErr: "unsupported mq provider", }, { name: "empty topics", set: func() SubscriptionSet { set := base(); set.Topics = nil; return set }(), wantErr: "topics are empty", }, { name: "empty topic", set: func() SubscriptionSet { set := base() set.Topics[0].Topic = " " return set }(), wantErr: "topic at index 0 is empty", }, { name: "duplicate topic", set: func() SubscriptionSet { set := base() set.Topics = append(set.Topics, TopicSubscription{ Topic: " orders.created ", QoS: AtMostOnce, Handler: handler, }) return set }(), wantErr: "duplicate mq subscription topic", }, { name: "nil handler", set: func() SubscriptionSet { set := base() set.Topics[0].Handler = nil return set }(), wantErr: "handler is nil", }, { name: "invalid qos", set: func() SubscriptionSet { set := base() set.Topics[0].QoS = ExactlyOnce + 1 return set }(), wantErr: "invalid mq qos", }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { _, err := NormalizeSubscriptionSet(test.set) if err == nil { t.Fatal("NormalizeSubscriptionSet() error = nil, want error") } if !strings.Contains(err.Error(), test.wantErr) { t.Fatalf("error = %q, want substring %q", err, test.wantErr) } }) } } type subscriptionTestRegistrar struct { sets []SubscriptionSet } func (r *subscriptionTestRegistrar) Register(set SubscriptionSet) error { r.sets = append(r.sets, set) return nil } func (*subscriptionTestRegistrar) Unregister(string) error { return nil } type subscriptionTestContributor struct { name string set SubscriptionSet order *[]string err error } func (c *subscriptionTestContributor) RegisterSubscriptions(registrar SubscriptionRegistrar) error { *c.order = append(*c.order, c.name) if c.err != nil { return c.err } return registrar.Register(c.set) } func TestApplySubscriptionsAppliesMultipleContributorsInOrder(t *testing.T) { order := make([]string, 0, 2) registrar := &subscriptionTestRegistrar{} handler := func(context.Context, Message) {} first := &subscriptionTestContributor{ name: "orders", order: &order, set: SubscriptionSet{ Owner: "orders", Provider: ProviderRabbitMQ, Topics: []TopicSubscription{{Topic: "orders.created", Handler: handler}}, }, } second := &subscriptionTestContributor{ name: "notifications", order: &order, set: SubscriptionSet{ Owner: "notifications", Provider: ProviderEMQX, Topics: []TopicSubscription{{Topic: "notifications.sent", Handler: handler}}, }, } if err := ApplySubscriptions(registrar, first, nil, second); err != nil { t.Fatalf("ApplySubscriptions() error = %v", err) } if !reflect.DeepEqual(order, []string{"orders", "notifications"}) { t.Fatalf("contributor order = %#v, want %#v", order, []string{"orders", "notifications"}) } if len(registrar.sets) != 2 { t.Fatalf("registered sets = %d, want 2", len(registrar.sets)) } if registrar.sets[0].Owner != "orders" || registrar.sets[1].Owner != "notifications" { t.Fatalf("registered owners = %q, %q", registrar.sets[0].Owner, registrar.sets[1].Owner) } } func TestApplySubscriptionsPropagatesContributorError(t *testing.T) { order := make([]string, 0, 2) registrar := &subscriptionTestRegistrar{} wantErr := errors.New("registration failed") failing := &subscriptionTestContributor{name: "failing", order: &order, err: wantErr} following := &subscriptionTestContributor{name: "following", order: &order} err := ApplySubscriptions(registrar, failing, following) if !errors.Is(err, wantErr) { t.Fatalf("ApplySubscriptions() error = %v, want %v", err, wantErr) } if !reflect.DeepEqual(order, []string{"failing"}) { t.Fatalf("contributors called = %#v, want %#v", order, []string{"failing"}) } if len(registrar.sets) != 0 { t.Fatalf("registered sets = %d, want 0", len(registrar.sets)) } } func TestApplySubscriptionsRejectsNilRegistrar(t *testing.T) { if err := ApplySubscriptions(nil); err == nil || !strings.Contains(err.Error(), "registrar is nil") { t.Fatalf("ApplySubscriptions(nil) error = %v, want nil-registrar error", err) } }