mirror of
https://github.com/nats-io/nats.go.git
synced 2025-10-28 02:42:14 +08:00
1919 lines
50 KiB
Go
1919 lines
50 KiB
Go
// Copyright 2020-2023 The NATS Authors
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package jetstream
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/nats-io/nats.go"
|
|
)
|
|
|
|
func TestPullConsumerFetch(t *testing.T) {
|
|
testSubject := "FOO.123"
|
|
testMsgs := []string{"m1", "m2", "m3", "m4", "m5"}
|
|
publishTestMsgs := func(t *testing.T, nc *nats.Conn) {
|
|
for _, msg := range testMsgs {
|
|
if err := nc.Publish(testSubject, []byte(msg)); err != nil {
|
|
t.Fatalf("Unexpected error during publish: %s", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
t.Run("no options", func(t *testing.T) {
|
|
srv := RunBasicJetStreamServer()
|
|
defer shutdownJSServerAndRemoveStorage(t, srv)
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
s, err := js.CreateStream(ctx, StreamConfig{Name: "foo", Subjects: []string{"FOO.*"}})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
publishTestMsgs(t, nc)
|
|
msgs, err := c.Fetch(5)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
var i int
|
|
for msg := range msgs.Messages() {
|
|
if string(msg.Data()) != testMsgs[i] {
|
|
t.Fatalf("Invalid msg on index %d; expected: %s; got: %s", i, testMsgs[i], string(msg.Data()))
|
|
}
|
|
i++
|
|
}
|
|
if len(testMsgs) != i {
|
|
t.Fatalf("Invalid number of messages received; want: %d; got: %d", len(testMsgs), i)
|
|
}
|
|
if msgs.Error() != nil {
|
|
t.Fatalf("Unexpected error during fetch: %v", msgs.Error())
|
|
}
|
|
})
|
|
|
|
t.Run("no options, fetch single messages one by one", func(t *testing.T) {
|
|
srv := RunBasicJetStreamServer()
|
|
defer shutdownJSServerAndRemoveStorage(t, srv)
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
s, err := js.CreateStream(ctx, StreamConfig{Name: "foo", Subjects: []string{"FOO.*"}})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
res := make([]Msg, 0)
|
|
errs := make(chan error)
|
|
done := make(chan struct{})
|
|
go func() {
|
|
for {
|
|
if len(res) == len(testMsgs) {
|
|
close(done)
|
|
return
|
|
}
|
|
msgs, err := c.Fetch(1)
|
|
if err != nil {
|
|
errs <- err
|
|
return
|
|
}
|
|
|
|
msg := <-msgs.Messages()
|
|
if msg != nil {
|
|
res = append(res, msg)
|
|
}
|
|
if err := msgs.Error(); err != nil {
|
|
errs <- err
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
|
|
time.Sleep(10 * time.Millisecond)
|
|
publishTestMsgs(t, nc)
|
|
select {
|
|
case err := <-errs:
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
case <-done:
|
|
if len(res) != len(testMsgs) {
|
|
t.Fatalf("Unexpected received message count; want %d; got %d", len(testMsgs), len(res))
|
|
}
|
|
}
|
|
for i, msg := range res {
|
|
if string(msg.Data()) != testMsgs[i] {
|
|
t.Fatalf("Invalid msg on index %d; expected: %s; got: %s", i, testMsgs[i], string(msg.Data()))
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("with no wait, no messages at the time of request", func(t *testing.T) {
|
|
srv := RunBasicJetStreamServer()
|
|
defer shutdownJSServerAndRemoveStorage(t, srv)
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
s, err := js.CreateStream(ctx, StreamConfig{Name: "foo", Subjects: []string{"FOO.*"}})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
msgs, err := c.FetchNoWait(5)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
publishTestMsgs(t, nc)
|
|
|
|
msg := <-msgs.Messages()
|
|
if msg != nil {
|
|
t.Fatalf("Expected no messages; got: %s", string(msg.Data()))
|
|
}
|
|
})
|
|
|
|
t.Run("with no wait, some messages available", func(t *testing.T) {
|
|
srv := RunBasicJetStreamServer()
|
|
defer shutdownJSServerAndRemoveStorage(t, srv)
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
s, err := js.CreateStream(ctx, StreamConfig{Name: "foo", Subjects: []string{"FOO.*"}})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
publishTestMsgs(t, nc)
|
|
time.Sleep(50 * time.Millisecond)
|
|
msgs, err := c.FetchNoWait(10)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
time.Sleep(100 * time.Millisecond)
|
|
publishTestMsgs(t, nc)
|
|
|
|
var msgsNum int
|
|
for range msgs.Messages() {
|
|
msgsNum++
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error during fetch: %v", err)
|
|
}
|
|
|
|
if msgsNum != len(testMsgs) {
|
|
t.Fatalf("Expected 5 messages, got: %d", msgsNum)
|
|
}
|
|
})
|
|
|
|
t.Run("with timeout", func(t *testing.T) {
|
|
srv := RunBasicJetStreamServer()
|
|
defer shutdownJSServerAndRemoveStorage(t, srv)
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
s, err := js.CreateStream(ctx, StreamConfig{Name: "foo", Subjects: []string{"FOO.*"}})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
msgs, err := c.Fetch(5, FetchMaxWait(50*time.Millisecond))
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
msg := <-msgs.Messages()
|
|
if msg != nil {
|
|
t.Fatalf("Expected no messages; got: %s", string(msg.Data()))
|
|
}
|
|
})
|
|
|
|
t.Run("with invalid timeout value", func(t *testing.T) {
|
|
srv := RunBasicJetStreamServer()
|
|
defer shutdownJSServerAndRemoveStorage(t, srv)
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
s, err := js.CreateStream(ctx, StreamConfig{Name: "foo", Subjects: []string{"FOO.*"}})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
_, err = c.Fetch(5, FetchMaxWait(-50*time.Millisecond))
|
|
if !errors.Is(err, ErrInvalidOption) {
|
|
t.Fatalf("Expected error: %v; got: %v", ErrInvalidOption, err)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestPullConsumerFetchBytes(t *testing.T) {
|
|
testSubject := "FOO.123"
|
|
msg := [10]byte{}
|
|
publishTestMsgs := func(t *testing.T, nc *nats.Conn, count int) {
|
|
for i := 0; i < count; i++ {
|
|
if err := nc.Publish(testSubject, msg[:]); err != nil {
|
|
t.Fatalf("Unexpected error during publish: %s", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
t.Run("no options, exact byte count received", func(t *testing.T) {
|
|
srv := RunBasicJetStreamServer()
|
|
defer shutdownJSServerAndRemoveStorage(t, srv)
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
s, err := js.CreateStream(ctx, StreamConfig{Name: "foo", Subjects: []string{"FOO.*"}})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy, Name: "con"})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
publishTestMsgs(t, nc, 5)
|
|
// actual received msg size will be 60 (payload=10 + Subject=7 + Reply=43)
|
|
msgs, err := c.FetchBytes(300)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
var i int
|
|
for msg := range msgs.Messages() {
|
|
msg.Ack()
|
|
i++
|
|
}
|
|
if i != 5 {
|
|
t.Fatalf("Expected 5 messages; got: %d", i)
|
|
}
|
|
if msgs.Error() != nil {
|
|
t.Fatalf("Unexpected error during fetch: %v", msgs.Error())
|
|
}
|
|
})
|
|
|
|
t.Run("no options, last msg does not fit max bytes", func(t *testing.T) {
|
|
srv := RunBasicJetStreamServer()
|
|
defer shutdownJSServerAndRemoveStorage(t, srv)
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
s, err := js.CreateStream(ctx, StreamConfig{Name: "foo", Subjects: []string{"FOO.*"}})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy, Name: "con"})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
publishTestMsgs(t, nc, 5)
|
|
// actual received msg size will be 60 (payload=10 + Subject=7 + Reply=43)
|
|
msgs, err := c.FetchBytes(250)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
var i int
|
|
for msg := range msgs.Messages() {
|
|
msg.Ack()
|
|
i++
|
|
}
|
|
if i != 4 {
|
|
t.Fatalf("Expected 5 messages; got: %d", i)
|
|
}
|
|
if msgs.Error() != nil {
|
|
t.Fatalf("Unexpected error during fetch: %v", msgs.Error())
|
|
}
|
|
})
|
|
t.Run("no options, single msg is too large", func(t *testing.T) {
|
|
srv := RunBasicJetStreamServer()
|
|
defer shutdownJSServerAndRemoveStorage(t, srv)
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
s, err := js.CreateStream(ctx, StreamConfig{Name: "foo", Subjects: []string{"FOO.*"}})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy, Name: "con"})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
publishTestMsgs(t, nc, 5)
|
|
// actual received msg size will be 60 (payload=10 + Subject=7 + Reply=43)
|
|
msgs, err := c.FetchBytes(30)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
var i int
|
|
for msg := range msgs.Messages() {
|
|
msg.Ack()
|
|
i++
|
|
}
|
|
if i != 0 {
|
|
t.Fatalf("Expected 5 messages; got: %d", i)
|
|
}
|
|
if msgs.Error() != nil {
|
|
t.Fatalf("Unexpected error during fetch: %v", msgs.Error())
|
|
}
|
|
})
|
|
|
|
t.Run("timeout waiting for messages", func(t *testing.T) {
|
|
srv := RunBasicJetStreamServer()
|
|
defer shutdownJSServerAndRemoveStorage(t, srv)
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
s, err := js.CreateStream(ctx, StreamConfig{Name: "foo", Subjects: []string{"FOO.*"}})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy, Name: "con"})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
publishTestMsgs(t, nc, 5)
|
|
// actual received msg size will be 60 (payload=10 + Subject=7 + Reply=43)
|
|
msgs, err := c.FetchBytes(1000, FetchMaxWait(50*time.Millisecond))
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
var i int
|
|
for msg := range msgs.Messages() {
|
|
msg.Ack()
|
|
i++
|
|
}
|
|
if i != 5 {
|
|
t.Fatalf("Expected 5 messages; got: %d", i)
|
|
}
|
|
if msgs.Error() != nil {
|
|
t.Fatalf("Unexpected error during fetch: %v", msgs.Error())
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestPullConsumerFetch_WithCluster(t *testing.T) {
|
|
testSubject := "FOO.123"
|
|
testMsgs := []string{"m1", "m2", "m3", "m4", "m5"}
|
|
publishTestMsgs := func(t *testing.T, nc *nats.Conn) {
|
|
for _, msg := range testMsgs {
|
|
if err := nc.Publish(testSubject, []byte(msg)); err != nil {
|
|
t.Fatalf("Unexpected error during publish: %s", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
name := "cluster"
|
|
stream := StreamConfig{
|
|
Name: name,
|
|
Replicas: 1,
|
|
Subjects: []string{"FOO.*"},
|
|
}
|
|
t.Run("no options", func(t *testing.T) {
|
|
withJSClusterAndStream(t, name, 3, stream, func(t *testing.T, subject string, srvs ...*jsServer) {
|
|
srv := srvs[0]
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
s, err := js.Stream(ctx, stream.Name)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
publishTestMsgs(t, nc)
|
|
msgs, err := c.Fetch(5)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
var i int
|
|
for msg := range msgs.Messages() {
|
|
if string(msg.Data()) != testMsgs[i] {
|
|
t.Fatalf("Invalid msg on index %d; expected: %s; got: %s", i, testMsgs[i], string(msg.Data()))
|
|
}
|
|
i++
|
|
}
|
|
if msgs.Error() != nil {
|
|
t.Fatalf("Unexpected error during fetch: %v", msgs.Error())
|
|
}
|
|
})
|
|
})
|
|
|
|
t.Run("with no wait, no messages at the time of request", func(t *testing.T) {
|
|
withJSClusterAndStream(t, name, 3, stream, func(t *testing.T, subject string, srvs ...*jsServer) {
|
|
nc, err := nats.Connect(srvs[0].ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
s, err := js.Stream(ctx, stream.Name)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
msgs, err := c.FetchNoWait(5)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
publishTestMsgs(t, nc)
|
|
|
|
msg := <-msgs.Messages()
|
|
if msg != nil {
|
|
t.Fatalf("Expected no messages; got: %s", string(msg.Data()))
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestPullConsumerMessages(t *testing.T) {
|
|
testSubject := "FOO.123"
|
|
testMsgs := []string{"m1", "m2", "m3", "m4", "m5"}
|
|
publishTestMsgs := func(t *testing.T, nc *nats.Conn) {
|
|
for _, msg := range testMsgs {
|
|
if err := nc.Publish(testSubject, []byte(msg)); err != nil {
|
|
t.Fatalf("Unexpected error during publish: %s", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
t.Run("no options", func(t *testing.T) {
|
|
srv := RunBasicJetStreamServer()
|
|
defer shutdownJSServerAndRemoveStorage(t, srv)
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
|
defer cancel()
|
|
s, err := js.CreateStream(ctx, StreamConfig{Name: "foo", Subjects: []string{"FOO.*"}})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
msgs := make([]Msg, 0)
|
|
it, err := c.Messages()
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
publishTestMsgs(t, nc)
|
|
for i := 0; i < len(testMsgs); i++ {
|
|
msg, err := it.Next()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if msg == nil {
|
|
break
|
|
}
|
|
msg.Ack()
|
|
msgs = append(msgs, msg)
|
|
|
|
}
|
|
it.Stop()
|
|
|
|
// calling Stop() multiple times should have no effect
|
|
it.Stop()
|
|
if len(msgs) != len(testMsgs) {
|
|
t.Fatalf("Unexpected received message count; want %d; got %d", len(testMsgs), len(msgs))
|
|
}
|
|
for i, msg := range msgs {
|
|
if string(msg.Data()) != testMsgs[i] {
|
|
t.Fatalf("Invalid msg on index %d; expected: %s; got: %s", i, testMsgs[i], string(msg.Data()))
|
|
}
|
|
}
|
|
_, err = it.Next()
|
|
if err == nil || !errors.Is(err, ErrMsgIteratorClosed) {
|
|
t.Fatalf("Expected error: %v; got: %v", ErrMsgIteratorClosed, err)
|
|
}
|
|
})
|
|
|
|
t.Run("with custom batch size", func(t *testing.T) {
|
|
srv := RunBasicJetStreamServer()
|
|
defer shutdownJSServerAndRemoveStorage(t, srv)
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
s, err := js.CreateStream(ctx, StreamConfig{Name: "foo", Subjects: []string{"FOO.*"}})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
msgs := make([]Msg, 0)
|
|
it, err := c.Messages(PullMaxMessages(3))
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
publishTestMsgs(t, nc)
|
|
for i := 0; i < len(testMsgs); i++ {
|
|
msg, err := it.Next()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if msg == nil {
|
|
break
|
|
}
|
|
msg.Ack()
|
|
msgs = append(msgs, msg)
|
|
|
|
}
|
|
it.Stop()
|
|
time.Sleep(10 * time.Millisecond)
|
|
if len(msgs) != len(testMsgs) {
|
|
t.Fatalf("Unexpected received message count; want %d; got %d", len(testMsgs), len(msgs))
|
|
}
|
|
for i, msg := range msgs {
|
|
if string(msg.Data()) != testMsgs[i] {
|
|
t.Fatalf("Invalid msg on index %d; expected: %s; got: %s", i, testMsgs[i], string(msg.Data()))
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("with max fitting 1 message", func(t *testing.T) {
|
|
srv := RunBasicJetStreamServer()
|
|
defer shutdownJSServerAndRemoveStorage(t, srv)
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
s, err := js.CreateStream(ctx, StreamConfig{Name: "foo", Subjects: []string{"FOO.*"}})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
// subscribe to next request subject to verify how many next requests were sent
|
|
sub, err := nc.SubscribeSync(fmt.Sprintf("$JS.API.CONSUMER.MSG.NEXT.foo.%s", c.CachedInfo().Name))
|
|
if err != nil {
|
|
t.Fatalf("Error on subscribe: %v", err)
|
|
}
|
|
|
|
msgs := make([]Msg, 0)
|
|
it, err := c.Messages(PullMaxBytes(60))
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
publishTestMsgs(t, nc)
|
|
for i := 0; i < len(testMsgs); i++ {
|
|
msg, err := it.Next()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if msg == nil {
|
|
break
|
|
}
|
|
msg.Ack()
|
|
msgs = append(msgs, msg)
|
|
|
|
}
|
|
it.Stop()
|
|
time.Sleep(10 * time.Millisecond)
|
|
requestsNum, _, err := sub.Pending()
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
// with batch size set to 1, and 5 messages published on subject, there should be a total of 5 requests sent
|
|
if requestsNum < 5 {
|
|
t.Fatalf("Unexpected number of requests sent; want at least 5; got %d", requestsNum)
|
|
}
|
|
|
|
if len(msgs) != len(testMsgs) {
|
|
t.Fatalf("Unexpected received message count; want %d; got %d", len(testMsgs), len(msgs))
|
|
}
|
|
for i, msg := range msgs {
|
|
if string(msg.Data()) != testMsgs[i] {
|
|
t.Fatalf("Invalid msg on index %d; expected: %s; got: %s", i, testMsgs[i], string(msg.Data()))
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("with custom max bytes", func(t *testing.T) {
|
|
srv := RunBasicJetStreamServer()
|
|
defer shutdownJSServerAndRemoveStorage(t, srv)
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
s, err := js.CreateStream(ctx, StreamConfig{Name: "foo", Subjects: []string{"FOO.*"}})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
// subscribe to next request subject to verify how many next requests were sent
|
|
sub, err := nc.SubscribeSync(fmt.Sprintf("$JS.API.CONSUMER.MSG.NEXT.foo.%s", c.CachedInfo().Name))
|
|
if err != nil {
|
|
t.Fatalf("Error on subscribe: %v", err)
|
|
}
|
|
|
|
msgs := make([]Msg, 0)
|
|
it, err := c.Messages(PullMaxBytes(150))
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
publishTestMsgs(t, nc)
|
|
for i := 0; i < len(testMsgs); i++ {
|
|
msg, err := it.Next()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if msg == nil {
|
|
break
|
|
}
|
|
msg.Ack()
|
|
msgs = append(msgs, msg)
|
|
|
|
}
|
|
it.Stop()
|
|
time.Sleep(10 * time.Millisecond)
|
|
requestsNum, _, err := sub.Pending()
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
if requestsNum < 3 {
|
|
t.Fatalf("Unexpected number of requests sent; want at least 3; got %d", requestsNum)
|
|
}
|
|
|
|
if len(msgs) != len(testMsgs) {
|
|
t.Fatalf("Unexpected received message count; want %d; got %d", len(testMsgs), len(msgs))
|
|
}
|
|
for i, msg := range msgs {
|
|
if string(msg.Data()) != testMsgs[i] {
|
|
t.Fatalf("Invalid msg on index %d; expected: %s; got: %s", i, testMsgs[i], string(msg.Data()))
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("with batch size set to 1", func(t *testing.T) {
|
|
srv := RunBasicJetStreamServer()
|
|
defer shutdownJSServerAndRemoveStorage(t, srv)
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
s, err := js.CreateStream(ctx, StreamConfig{Name: "foo", Subjects: []string{"FOO.*"}})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
// subscribe to next request subject to verify how many next requests were sent
|
|
sub, err := nc.SubscribeSync(fmt.Sprintf("$JS.API.CONSUMER.MSG.NEXT.foo.%s", c.CachedInfo().Name))
|
|
if err != nil {
|
|
t.Fatalf("Error on subscribe: %v", err)
|
|
}
|
|
|
|
msgs := make([]Msg, 0)
|
|
it, err := c.Messages(PullMaxMessages(1))
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
publishTestMsgs(t, nc)
|
|
for i := 0; i < len(testMsgs); i++ {
|
|
msg, err := it.Next()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if msg == nil {
|
|
break
|
|
}
|
|
msg.Ack()
|
|
msgs = append(msgs, msg)
|
|
|
|
}
|
|
it.Stop()
|
|
time.Sleep(10 * time.Millisecond)
|
|
requestsNum, _, err := sub.Pending()
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
// with batch size set to 1, and 5 messages published on subject, there should be a total of 5 requests sent
|
|
if requestsNum != 5 {
|
|
t.Fatalf("Unexpected number of requests sent; want 5; got %d", requestsNum)
|
|
}
|
|
|
|
if len(msgs) != len(testMsgs) {
|
|
t.Fatalf("Unexpected received message count; want %d; got %d", len(testMsgs), len(msgs))
|
|
}
|
|
for i, msg := range msgs {
|
|
if string(msg.Data()) != testMsgs[i] {
|
|
t.Fatalf("Invalid msg on index %d; expected: %s; got: %s", i, testMsgs[i], string(msg.Data()))
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("create iterator, stop, then create again", func(t *testing.T) {
|
|
srv := RunBasicJetStreamServer()
|
|
defer shutdownJSServerAndRemoveStorage(t, srv)
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
|
defer cancel()
|
|
s, err := js.CreateStream(ctx, StreamConfig{Name: "foo", Subjects: []string{"FOO.*"}})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
msgs := make([]Msg, 0)
|
|
it, err := c.Messages()
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
publishTestMsgs(t, nc)
|
|
for i := 0; i < len(testMsgs); i++ {
|
|
msg, err := it.Next()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if msg == nil {
|
|
break
|
|
}
|
|
msg.Ack()
|
|
msgs = append(msgs, msg)
|
|
|
|
}
|
|
it.Stop()
|
|
time.Sleep(10 * time.Millisecond)
|
|
|
|
publishTestMsgs(t, nc)
|
|
it, err = c.Messages()
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
for i := 0; i < len(testMsgs); i++ {
|
|
msg, err := it.Next()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if msg == nil {
|
|
break
|
|
}
|
|
msg.Ack()
|
|
msgs = append(msgs, msg)
|
|
|
|
}
|
|
it.Stop()
|
|
if len(msgs) != 2*len(testMsgs) {
|
|
t.Fatalf("Unexpected received message count; want %d; got %d", len(testMsgs), len(msgs))
|
|
}
|
|
expectedMsgs := append(testMsgs, testMsgs...)
|
|
for i, msg := range msgs {
|
|
if string(msg.Data()) != expectedMsgs[i] {
|
|
t.Fatalf("Invalid msg on index %d; expected: %s; got: %s", i, testMsgs[i], string(msg.Data()))
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("with invalid batch size", func(t *testing.T) {
|
|
srv := RunBasicJetStreamServer()
|
|
defer shutdownJSServerAndRemoveStorage(t, srv)
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
s, err := js.CreateStream(ctx, StreamConfig{Name: "foo", Subjects: []string{"FOO.*"}})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
_, err = c.Messages(PullMaxMessages(-1))
|
|
if err == nil || !errors.Is(err, ErrInvalidOption) {
|
|
t.Fatalf("Expected error: %v; got: %v", ErrInvalidOption, err)
|
|
}
|
|
})
|
|
|
|
t.Run("with idle heartbeat", func(t *testing.T) {
|
|
srv := RunBasicJetStreamServer()
|
|
defer shutdownJSServerAndRemoveStorage(t, srv)
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
s, err := js.CreateStream(ctx, StreamConfig{Name: "foo", Subjects: []string{"FOO.*"}})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
msgs := make([]Msg, 0)
|
|
// use custom function to bypass validation in test
|
|
it, err := c.Messages(pullOptFunc(func(o *consumeOpts) error {
|
|
o.Heartbeat = 10 * time.Millisecond
|
|
return nil
|
|
}))
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
publishTestMsgs(t, nc)
|
|
for i := 0; i < len(testMsgs); i++ {
|
|
msg, err := it.Next()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if msg == nil {
|
|
break
|
|
}
|
|
msg.Ack()
|
|
msgs = append(msgs, msg)
|
|
|
|
}
|
|
it.Stop()
|
|
|
|
if len(msgs) != len(testMsgs) {
|
|
t.Fatalf("Unexpected received message count; want %d; got %d", len(testMsgs), len(msgs))
|
|
}
|
|
for i, msg := range msgs {
|
|
if string(msg.Data()) != testMsgs[i] {
|
|
t.Fatalf("Invalid msg on index %d; expected: %s; got: %s", i, testMsgs[i], string(msg.Data()))
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("with server restart", func(t *testing.T) {
|
|
srv := RunBasicJetStreamServer()
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
s, err := js.CreateStream(ctx, StreamConfig{Name: "foo", Subjects: []string{"FOO.*"}})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
msgs := make([]Msg, 0)
|
|
it, err := c.Messages()
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
done := make(chan struct{})
|
|
errs := make(chan error)
|
|
publishTestMsgs(t, nc)
|
|
go func() {
|
|
for i := 0; i < 2*len(testMsgs); i++ {
|
|
msg, err := it.Next()
|
|
if err != nil {
|
|
errs <- err
|
|
return
|
|
}
|
|
msg.Ack()
|
|
msgs = append(msgs, msg)
|
|
}
|
|
done <- struct{}{}
|
|
}()
|
|
time.Sleep(10 * time.Millisecond)
|
|
// restart the server
|
|
srv = restartBasicJSServer(t, srv)
|
|
defer shutdownJSServerAndRemoveStorage(t, srv)
|
|
time.Sleep(10 * time.Millisecond)
|
|
publishTestMsgs(t, nc)
|
|
|
|
select {
|
|
case <-done:
|
|
if len(msgs) != 2*len(testMsgs) {
|
|
t.Fatalf("Unexpected received message count; want %d; got %d", len(testMsgs), len(msgs))
|
|
}
|
|
case err := <-errs:
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestPullConsumerConsume(t *testing.T) {
|
|
testSubject := "FOO.123"
|
|
testMsgs := []string{"m1", "m2", "m3", "m4", "m5"}
|
|
publishTestMsgs := func(t *testing.T, nc *nats.Conn) {
|
|
for _, msg := range testMsgs {
|
|
if err := nc.Publish(testSubject, []byte(msg)); err != nil {
|
|
t.Fatalf("Unexpected error during publish: %s", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
t.Run("no options", func(t *testing.T) {
|
|
srv := RunBasicJetStreamServer()
|
|
defer shutdownJSServerAndRemoveStorage(t, srv)
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
|
defer cancel()
|
|
s, err := js.CreateStream(ctx, StreamConfig{Name: "foo", Subjects: []string{"FOO.*"}})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
msgs := make([]Msg, 0)
|
|
wg := &sync.WaitGroup{}
|
|
wg.Add(len(testMsgs))
|
|
l, err := c.Consume(func(msg Msg) {
|
|
msgs = append(msgs, msg)
|
|
wg.Done()
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer l.Stop()
|
|
|
|
publishTestMsgs(t, nc)
|
|
wg.Wait()
|
|
if len(msgs) != len(testMsgs) {
|
|
t.Fatalf("Unexpected received message count; want %d; got %d", len(testMsgs), len(msgs))
|
|
}
|
|
for i, msg := range msgs {
|
|
if string(msg.Data()) != testMsgs[i] {
|
|
t.Fatalf("Invalid msg on index %d; expected: %s; got: %s", i, testMsgs[i], string(msg.Data()))
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("subscribe twice on the same consumer", func(t *testing.T) {
|
|
srv := RunBasicJetStreamServer()
|
|
defer shutdownJSServerAndRemoveStorage(t, srv)
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
s, err := js.CreateStream(ctx, StreamConfig{Name: "foo", Subjects: []string{"FOO.*"}})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
wg := sync.WaitGroup{}
|
|
msgs1, msgs2 := make([]Msg, 0), make([]Msg, 0)
|
|
l1, err := c.Consume(func(msg Msg) {
|
|
msgs1 = append(msgs1, msg)
|
|
wg.Done()
|
|
msg.Ack()
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer l1.Stop()
|
|
l2, err := c.Consume(func(msg Msg) {
|
|
msgs2 = append(msgs2, msg)
|
|
wg.Done()
|
|
msg.Ack()
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer l2.Stop()
|
|
|
|
wg.Add(len(testMsgs))
|
|
publishTestMsgs(t, nc)
|
|
wg.Wait()
|
|
|
|
if len(msgs1)+len(msgs2) != len(testMsgs) {
|
|
t.Fatalf("Unexpected received message count; want %d; got %d", len(testMsgs), len(msgs1)+len(msgs2))
|
|
}
|
|
if len(msgs1) == 0 || len(msgs2) == 0 {
|
|
t.Fatalf("Received no messages on one of the subscriptions")
|
|
}
|
|
})
|
|
|
|
t.Run("subscribe, cancel subscription, then subscribe again", func(t *testing.T) {
|
|
srv := RunBasicJetStreamServer()
|
|
defer shutdownJSServerAndRemoveStorage(t, srv)
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
|
defer cancel()
|
|
s, err := js.CreateStream(ctx, StreamConfig{Name: "foo", Subjects: []string{"FOO.*"}})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
wg := sync.WaitGroup{}
|
|
wg.Add(len(testMsgs))
|
|
msgs := make([]Msg, 0)
|
|
l, err := c.Consume(func(msg Msg) {
|
|
if err := msg.Ack(); err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
msgs = append(msgs, msg)
|
|
wg.Done()
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
publishTestMsgs(t, nc)
|
|
wg.Wait()
|
|
l.Stop()
|
|
|
|
time.Sleep(10 * time.Millisecond)
|
|
wg.Add(len(testMsgs))
|
|
l, err = c.Consume(func(msg Msg) {
|
|
if err := msg.Ack(); err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
msgs = append(msgs, msg)
|
|
wg.Done()
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer l.Stop()
|
|
publishTestMsgs(t, nc)
|
|
wg.Wait()
|
|
if len(msgs) != 2*len(testMsgs) {
|
|
t.Fatalf("Unexpected received message count; want %d; got %d", len(testMsgs), len(msgs))
|
|
}
|
|
expectedMsgs := append(testMsgs, testMsgs...)
|
|
for i, msg := range msgs {
|
|
if string(msg.Data()) != expectedMsgs[i] {
|
|
t.Fatalf("Invalid msg on index %d; expected: %s; got: %s", i, testMsgs[i], string(msg.Data()))
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("with custom batch size", func(t *testing.T) {
|
|
srv := RunBasicJetStreamServer()
|
|
defer shutdownJSServerAndRemoveStorage(t, srv)
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
s, err := js.CreateStream(ctx, StreamConfig{Name: "foo", Subjects: []string{"FOO.*"}})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
msgs := make([]Msg, 0)
|
|
wg := &sync.WaitGroup{}
|
|
wg.Add(len(testMsgs))
|
|
l, err := c.Consume(func(msg Msg) {
|
|
msgs = append(msgs, msg)
|
|
wg.Done()
|
|
}, PullMaxMessages(4))
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer l.Stop()
|
|
|
|
publishTestMsgs(t, nc)
|
|
wg.Wait()
|
|
|
|
if len(msgs) != len(testMsgs) {
|
|
t.Fatalf("Unexpected received message count; want %d; got %d", len(testMsgs), len(msgs))
|
|
}
|
|
for i, msg := range msgs {
|
|
if string(msg.Data()) != testMsgs[i] {
|
|
t.Fatalf("Invalid msg on index %d; expected: %s; got: %s", i, testMsgs[i], string(msg.Data()))
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("fetch messages one by one", func(t *testing.T) {
|
|
srv := RunBasicJetStreamServer()
|
|
defer shutdownJSServerAndRemoveStorage(t, srv)
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
s, err := js.CreateStream(ctx, StreamConfig{Name: "foo", Subjects: []string{"FOO.*"}})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
msgs := make([]Msg, 0)
|
|
wg := &sync.WaitGroup{}
|
|
wg.Add(len(testMsgs))
|
|
l, err := c.Consume(func(msg Msg) {
|
|
msgs = append(msgs, msg)
|
|
wg.Done()
|
|
}, PullMaxMessages(1))
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer l.Stop()
|
|
|
|
publishTestMsgs(t, nc)
|
|
wg.Wait()
|
|
|
|
if len(msgs) != len(testMsgs) {
|
|
t.Fatalf("Unexpected received message count; want %d; got %d", len(testMsgs), len(msgs))
|
|
}
|
|
for i, msg := range msgs {
|
|
if string(msg.Data()) != testMsgs[i] {
|
|
t.Fatalf("Invalid msg on index %d; expected: %s; got: %s", i, testMsgs[i], string(msg.Data()))
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("with custom max bytes", func(t *testing.T) {
|
|
srv := RunBasicJetStreamServer()
|
|
defer shutdownJSServerAndRemoveStorage(t, srv)
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
s, err := js.CreateStream(ctx, StreamConfig{Name: "foo", Subjects: []string{"FOO.*"}})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
// subscribe to next request subject to verify how many next requests were sent
|
|
sub, err := nc.SubscribeSync(fmt.Sprintf("$JS.API.CONSUMER.MSG.NEXT.foo.%s", c.CachedInfo().Name))
|
|
if err != nil {
|
|
t.Fatalf("Error on subscribe: %v", err)
|
|
}
|
|
|
|
publishTestMsgs(t, nc)
|
|
msgs := make([]Msg, 0)
|
|
wg := &sync.WaitGroup{}
|
|
wg.Add(len(testMsgs))
|
|
l, err := c.Consume(func(msg Msg) {
|
|
msgs = append(msgs, msg)
|
|
wg.Done()
|
|
}, PullMaxBytes(150))
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer l.Stop()
|
|
|
|
wg.Wait()
|
|
requestsNum, _, err := sub.Pending()
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
// new request should be sent after each consumed message (msg size is 57)
|
|
if requestsNum < 3 {
|
|
t.Fatalf("Unexpected number of requests sent; want at least 5; got %d", requestsNum)
|
|
}
|
|
|
|
if len(msgs) != len(testMsgs) {
|
|
t.Fatalf("Unexpected received message count; want %d; got %d", len(testMsgs), len(msgs))
|
|
}
|
|
for i, msg := range msgs {
|
|
if string(msg.Data()) != testMsgs[i] {
|
|
t.Fatalf("Invalid msg on index %d; expected: %s; got: %s", i, testMsgs[i], string(msg.Data()))
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("with invalid batch size", func(t *testing.T) {
|
|
srv := RunBasicJetStreamServer()
|
|
defer shutdownJSServerAndRemoveStorage(t, srv)
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
s, err := js.CreateStream(ctx, StreamConfig{Name: "foo", Subjects: []string{"FOO.*"}})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
_, err = c.Consume(func(_ Msg) {
|
|
}, PullMaxMessages(-1))
|
|
if err == nil || !errors.Is(err, ErrInvalidOption) {
|
|
t.Fatalf("Expected error: %v; got: %v", ErrInvalidOption, err)
|
|
}
|
|
})
|
|
|
|
t.Run("with custom expiry", func(t *testing.T) {
|
|
srv := RunBasicJetStreamServer()
|
|
defer shutdownJSServerAndRemoveStorage(t, srv)
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
s, err := js.CreateStream(ctx, StreamConfig{Name: "foo", Subjects: []string{"FOO.*"}})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
msgs := make([]Msg, 0)
|
|
wg := &sync.WaitGroup{}
|
|
wg.Add(len(testMsgs))
|
|
l, err := c.Consume(func(msg Msg) {
|
|
msgs = append(msgs, msg)
|
|
wg.Done()
|
|
}, PullExpiry(2*time.Second))
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer l.Stop()
|
|
|
|
publishTestMsgs(t, nc)
|
|
wg.Wait()
|
|
|
|
if len(msgs) != len(testMsgs) {
|
|
t.Fatalf("Unexpected received message count; want %d; got %d", len(testMsgs), len(msgs))
|
|
}
|
|
for i, msg := range msgs {
|
|
if string(msg.Data()) != testMsgs[i] {
|
|
t.Fatalf("Invalid msg on index %d; expected: %s; got: %s", i, testMsgs[i], string(msg.Data()))
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("with timeout on pull request, pending messages left", func(t *testing.T) {
|
|
srv := RunBasicJetStreamServer()
|
|
defer shutdownJSServerAndRemoveStorage(t, srv)
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
s, err := js.CreateStream(ctx, StreamConfig{Name: "foo", Subjects: []string{"FOO.*"}})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
msgs := make([]Msg, 0)
|
|
wg := &sync.WaitGroup{}
|
|
wg.Add(4 * len(testMsgs))
|
|
l, err := c.Consume(func(msg Msg) {
|
|
msgs = append(msgs, msg)
|
|
wg.Done()
|
|
}, pullOptFunc(func(o *consumeOpts) error {
|
|
o.Expires = 50 * time.Millisecond
|
|
return nil
|
|
}))
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer l.Stop()
|
|
|
|
time.Sleep(60 * time.Millisecond)
|
|
|
|
// publish messages in 4 batches, with pause between each batch
|
|
// on the second pull request, we should get timeout error, which triggers another batch
|
|
publishTestMsgs(t, nc)
|
|
time.Sleep(40 * time.Millisecond)
|
|
publishTestMsgs(t, nc)
|
|
time.Sleep(40 * time.Millisecond)
|
|
publishTestMsgs(t, nc)
|
|
time.Sleep(40 * time.Millisecond)
|
|
publishTestMsgs(t, nc)
|
|
wg.Wait()
|
|
if len(msgs) != 4*len(testMsgs) {
|
|
t.Fatalf("Unexpected received message count; want %d; got %d", len(testMsgs), len(msgs))
|
|
}
|
|
})
|
|
|
|
t.Run("with invalid expiry", func(t *testing.T) {
|
|
srv := RunBasicJetStreamServer()
|
|
defer shutdownJSServerAndRemoveStorage(t, srv)
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
s, err := js.CreateStream(ctx, StreamConfig{Name: "foo", Subjects: []string{"FOO.*"}})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
_, err = c.Consume(func(_ Msg) {
|
|
}, PullExpiry(-1))
|
|
if err == nil || !errors.Is(err, ErrInvalidOption) {
|
|
t.Fatalf("Expected error: %v; got: %v", ErrInvalidOption, err)
|
|
}
|
|
})
|
|
|
|
t.Run("with idle heartbeat", func(t *testing.T) {
|
|
srv := RunBasicJetStreamServer()
|
|
defer shutdownJSServerAndRemoveStorage(t, srv)
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
s, err := js.CreateStream(ctx, StreamConfig{Name: "foo", Subjects: []string{"FOO.*"}})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
msgs := make([]Msg, 0)
|
|
wg := &sync.WaitGroup{}
|
|
wg.Add(len(testMsgs))
|
|
l, err := c.Consume(func(msg Msg) {
|
|
msgs = append(msgs, msg)
|
|
wg.Done()
|
|
}, PullMaxBytes(1*time.Second))
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer l.Stop()
|
|
|
|
publishTestMsgs(t, nc)
|
|
wg.Wait()
|
|
if len(msgs) != len(testMsgs) {
|
|
t.Fatalf("Unexpected received message count; want %d; got %d", len(testMsgs), len(msgs))
|
|
}
|
|
for i, msg := range msgs {
|
|
if string(msg.Data()) != testMsgs[i] {
|
|
t.Fatalf("Invalid msg on index %d; expected: %s; got: %s", i, testMsgs[i], string(msg.Data()))
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("with server restart", func(t *testing.T) {
|
|
srv := RunBasicJetStreamServer()
|
|
nc, err := nats.Connect(srv.ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
s, err := js.CreateStream(ctx, StreamConfig{Name: "foo", Subjects: []string{"FOO.*"}})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
wg := &sync.WaitGroup{}
|
|
wg.Add(2 * len(testMsgs))
|
|
msgs := make([]Msg, 0)
|
|
publishTestMsgs(t, nc)
|
|
l, err := c.Consume(func(msg Msg) {
|
|
msgs = append(msgs, msg)
|
|
wg.Done()
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer l.Stop()
|
|
time.Sleep(10 * time.Millisecond)
|
|
// restart the server
|
|
srv = restartBasicJSServer(t, srv)
|
|
defer shutdownJSServerAndRemoveStorage(t, srv)
|
|
time.Sleep(10 * time.Millisecond)
|
|
publishTestMsgs(t, nc)
|
|
wg.Wait()
|
|
})
|
|
}
|
|
|
|
func TestPullConsumerConsume_WithCluster(t *testing.T) {
|
|
testSubject := "FOO.123"
|
|
testMsgs := []string{"m1", "m2", "m3", "m4", "m5"}
|
|
publishTestMsgs := func(t *testing.T, nc *nats.Conn) {
|
|
for _, msg := range testMsgs {
|
|
if err := nc.Publish(testSubject, []byte(msg)); err != nil {
|
|
t.Fatalf("Unexpected error during publish: %s", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
name := "cluster"
|
|
stream := StreamConfig{
|
|
Name: name,
|
|
Replicas: 1,
|
|
Subjects: []string{"FOO.*"},
|
|
}
|
|
|
|
t.Run("no options", func(t *testing.T) {
|
|
withJSClusterAndStream(t, name, 3, stream, func(t *testing.T, subject string, srvs ...*jsServer) {
|
|
nc, err := nats.Connect(srvs[0].ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
s, err := js.Stream(ctx, stream.Name)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
msgs := make([]Msg, 0)
|
|
wg := &sync.WaitGroup{}
|
|
wg.Add(len(testMsgs))
|
|
l, err := c.Consume(func(msg Msg) {
|
|
msgs = append(msgs, msg)
|
|
wg.Done()
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer l.Stop()
|
|
|
|
publishTestMsgs(t, nc)
|
|
wg.Wait()
|
|
if len(msgs) != len(testMsgs) {
|
|
t.Fatalf("Unexpected received message count; want %d; got %d", len(testMsgs), len(msgs))
|
|
}
|
|
for i, msg := range msgs {
|
|
if string(msg.Data()) != testMsgs[i] {
|
|
t.Fatalf("Invalid msg on index %d; expected: %s; got: %s", i, testMsgs[i], string(msg.Data()))
|
|
}
|
|
}
|
|
})
|
|
})
|
|
|
|
t.Run("subscribe, cancel subscription, then subscribe again", func(t *testing.T) {
|
|
withJSClusterAndStream(t, name, 3, stream, func(t *testing.T, subject string, srvs ...*jsServer) {
|
|
nc, err := nats.Connect(srvs[0].ClientURL())
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
js, err := New(nc)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
|
s, err := js.Stream(ctx, stream.Name)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
c, err := s.AddConsumer(ctx, ConsumerConfig{AckPolicy: AckExplicitPolicy})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
wg := sync.WaitGroup{}
|
|
wg.Add(len(testMsgs))
|
|
msgs := make([]Msg, 0)
|
|
l, err := c.Consume(func(msg Msg) {
|
|
if err := msg.Ack(); err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
msgs = append(msgs, msg)
|
|
if len(msgs) == 5 {
|
|
cancel()
|
|
}
|
|
wg.Done()
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
publishTestMsgs(t, nc)
|
|
wg.Wait()
|
|
l.Stop()
|
|
|
|
time.Sleep(10 * time.Millisecond)
|
|
wg.Add(len(testMsgs))
|
|
defer cancel()
|
|
l, err = c.Consume(func(msg Msg) {
|
|
if err := msg.Ack(); err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
msgs = append(msgs, msg)
|
|
wg.Done()
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
defer l.Stop()
|
|
publishTestMsgs(t, nc)
|
|
wg.Wait()
|
|
if len(msgs) != 2*len(testMsgs) {
|
|
t.Fatalf("Unexpected received message count; want %d; got %d", len(testMsgs), len(msgs))
|
|
}
|
|
expectedMsgs := append(testMsgs, testMsgs...)
|
|
for i, msg := range msgs {
|
|
if string(msg.Data()) != expectedMsgs[i] {
|
|
t.Fatalf("Invalid msg on index %d; expected: %s; got: %s", i, testMsgs[i], string(msg.Data()))
|
|
}
|
|
}
|
|
})
|
|
})
|
|
}
|