Merge master into breakout_swarm

Signed-off-by: Alex Ellis <alexellis2@gmail.com>
This commit is contained in:
Alex Ellis
2018-02-01 09:25:39 +00:00
parent afeb7bbce4
commit f954bf0733
1953 changed files with 614131 additions and 175582 deletions

View File

@ -0,0 +1,223 @@
package test
import (
"fmt"
"strings"
"sync/atomic"
"testing"
"time"
"github.com/nats-io/gnatsd/server"
"github.com/nats-io/gnatsd/test"
"github.com/nats-io/go-nats"
)
func TestAuth(t *testing.T) {
opts := test.DefaultTestOptions
opts.Port = 8232
opts.Username = "derek"
opts.Password = "foo"
s := RunServerWithOptions(opts)
defer s.Shutdown()
_, err := nats.Connect("nats://localhost:8232")
if err == nil {
t.Fatal("Should have received an error while trying to connect")
}
// This test may be a bit too strict for the future, but for now makes
// sure that we correctly process the -ERR content on connect.
if err.Error() != nats.ErrAuthorization.Error() {
t.Fatalf("Expected error '%v', got '%v'", nats.ErrAuthorization, err)
}
nc, err := nats.Connect("nats://derek:foo@localhost:8232")
if err != nil {
t.Fatal("Should have connected successfully with a token")
}
nc.Close()
// Use Options
nc, err = nats.Connect("nats://localhost:8232", nats.UserInfo("derek", "foo"))
if err != nil {
t.Fatalf("Should have connected successfully with a token: %v", err)
}
nc.Close()
// Verify that credentials in URL take precedence.
nc, err = nats.Connect("nats://derek:foo@localhost:8232", nats.UserInfo("foo", "bar"))
if err != nil {
t.Fatalf("Should have connected successfully with a token: %v", err)
}
nc.Close()
}
func TestAuthFailNoDisconnectCB(t *testing.T) {
opts := test.DefaultTestOptions
opts.Port = 8232
opts.Username = "derek"
opts.Password = "foo"
s := RunServerWithOptions(opts)
defer s.Shutdown()
copts := nats.GetDefaultOptions()
copts.Url = "nats://localhost:8232"
receivedDisconnectCB := int32(0)
copts.DisconnectedCB = func(nc *nats.Conn) {
atomic.AddInt32(&receivedDisconnectCB, 1)
}
_, err := copts.Connect()
if err == nil {
t.Fatal("Should have received an error while trying to connect")
}
if atomic.LoadInt32(&receivedDisconnectCB) > 0 {
t.Fatal("Should not have received a disconnect callback on auth failure")
}
}
func TestAuthFailAllowReconnect(t *testing.T) {
ts := RunServerOnPort(23232)
defer ts.Shutdown()
var servers = []string{
"nats://localhost:23232",
"nats://localhost:23233",
"nats://localhost:23234",
}
ots2 := test.DefaultTestOptions
ots2.Port = 23233
ots2.Username = "ivan"
ots2.Password = "foo"
ts2 := RunServerWithOptions(ots2)
defer ts2.Shutdown()
ts3 := RunServerOnPort(23234)
defer ts3.Shutdown()
reconnectch := make(chan bool)
opts := nats.GetDefaultOptions()
opts.Servers = servers
opts.AllowReconnect = true
opts.NoRandomize = true
opts.MaxReconnect = 10
opts.ReconnectWait = 100 * time.Millisecond
opts.ReconnectedCB = func(_ *nats.Conn) {
reconnectch <- true
}
// Connect
nc, err := opts.Connect()
if err != nil {
t.Fatalf("Should have connected ok: %v", err)
}
defer nc.Close()
// Stop the server
ts.Shutdown()
// The client will try to connect to the second server, and that
// should fail. It should then try to connect to the third and succeed.
// Wait for the reconnect CB.
if e := Wait(reconnectch); e != nil {
t.Fatal("Reconnect callback should have been triggered")
}
if nc.IsClosed() {
t.Fatal("Should have reconnected")
}
if nc.ConnectedUrl() != servers[2] {
t.Fatalf("Should have reconnected to %s, reconnected to %s instead", servers[2], nc.ConnectedUrl())
}
}
func TestTokenAuth(t *testing.T) {
opts := test.DefaultTestOptions
opts.Port = 8232
secret := "S3Cr3T0k3n!"
opts.Authorization = secret
s := RunServerWithOptions(opts)
defer s.Shutdown()
_, err := nats.Connect("nats://localhost:8232")
if err == nil {
t.Fatal("Should have received an error while trying to connect")
}
tokenURL := fmt.Sprintf("nats://%s@localhost:8232", secret)
nc, err := nats.Connect(tokenURL)
if err != nil {
t.Fatal("Should have connected successfully")
}
nc.Close()
// Use Options
nc, err = nats.Connect("nats://localhost:8232", nats.Token(secret))
if err != nil {
t.Fatalf("Should have connected successfully: %v", err)
}
nc.Close()
// Verify that token in the URL takes precedence.
nc, err = nats.Connect(tokenURL, nats.Token("badtoken"))
if err != nil {
t.Fatalf("Should have connected successfully: %v", err)
}
nc.Close()
}
func TestPermViolation(t *testing.T) {
opts := test.DefaultTestOptions
opts.Port = 8232
opts.Users = []*server.User{
&server.User{
Username: "ivan",
Password: "pwd",
Permissions: &server.Permissions{
Publish: []string{"foo"},
Subscribe: []string{"bar"},
},
},
}
s := RunServerWithOptions(opts)
defer s.Shutdown()
errCh := make(chan error, 2)
errCB := func(_ *nats.Conn, _ *nats.Subscription, err error) {
errCh <- err
}
nc, err := nats.Connect(
fmt.Sprintf("nats://ivan:pwd@localhost:%d", opts.Port),
nats.ErrorHandler(errCB))
if err != nil {
t.Fatalf("Error on connect: %v", err)
}
defer nc.Close()
// Cause a publish error
nc.Publish("bar", []byte("fail"))
// Cause a subscribe error
nc.Subscribe("foo", func(_ *nats.Msg) {})
expectedErrorTypes := []string{"publish", "subscription"}
for _, expectedErr := range expectedErrorTypes {
select {
case e := <-errCh:
if !strings.Contains(e.Error(), nats.PERMISSIONS_ERR) {
t.Fatalf("Did not receive error about permissions")
}
if !strings.Contains(e.Error(), expectedErr) {
t.Fatalf("Did not receive error about %q, got %v", expectedErr, e.Error())
}
case <-time.After(2 * time.Second):
t.Fatalf("Did not get the permission error")
}
}
// Make sure connection has not been closed
if nc.IsClosed() {
t.Fatal("Connection should be not be closed")
}
}

View File

@ -0,0 +1,885 @@
package test
import (
"bytes"
"math"
"regexp"
"runtime"
"sync"
"sync/atomic"
"testing"
"time"
"github.com/nats-io/go-nats"
)
func TestCloseLeakingGoRoutines(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
// Give time for things to settle before capturing the number of
// go routines
time.Sleep(500 * time.Millisecond)
base := runtime.NumGoroutine()
nc := NewDefaultConnection(t)
nc.Flush()
nc.Close()
// Give time for things to settle before capturing the number of
// go routines
time.Sleep(500 * time.Millisecond)
delta := (runtime.NumGoroutine() - base)
if delta > 0 {
t.Fatalf("%d Go routines still exist post Close()", delta)
}
// Make sure we can call Close() multiple times
nc.Close()
}
func TestLeakingGoRoutinesOnFailedConnect(t *testing.T) {
// Give time for things to settle before capturing the number of
// go routines
time.Sleep(500 * time.Millisecond)
base := runtime.NumGoroutine()
nc, err := nats.Connect(nats.DefaultURL)
if err == nil {
nc.Close()
t.Fatalf("Expected failure to connect")
}
// Give time for things to settle before capturing the number of
// go routines
time.Sleep(500 * time.Millisecond)
delta := (runtime.NumGoroutine() - base)
if delta > 0 {
t.Fatalf("%d Go routines still exist post Close()", delta)
}
}
func TestConnectedServer(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
u := nc.ConnectedUrl()
if u == "" || u != nats.DefaultURL {
t.Fatalf("Unexpected connected URL of %s\n", u)
}
srv := nc.ConnectedServerId()
if srv == "" {
t.Fatal("Expected a connected server id")
}
nc.Close()
u = nc.ConnectedUrl()
if u != "" {
t.Fatalf("Expected a nil connected URL, got %s\n", u)
}
srv = nc.ConnectedServerId()
if srv != "" {
t.Fatalf("Expected a nil connect server, got %s\n", srv)
}
}
func TestMultipleClose(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
nc.Close()
wg.Done()
}()
}
wg.Wait()
}
func TestBadOptionTimeoutConnect(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
opts := nats.GetDefaultOptions()
opts.Timeout = -1
opts.Url = "nats://localhost:4222"
_, err := opts.Connect()
if err == nil {
t.Fatal("Expected an error")
}
if err != nats.ErrNoServers {
t.Fatalf("Expected a ErrNoServers error: Got %v\n", err)
}
}
func TestSimplePublish(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
if err := nc.Publish("foo", []byte("Hello World")); err != nil {
t.Fatal("Failed to publish string message: ", err)
}
}
func TestSimplePublishNoData(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
if err := nc.Publish("foo", nil); err != nil {
t.Fatal("Failed to publish empty message: ", err)
}
}
func TestPublishDoesNotFailOnSlowConsumer(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
sub, err := nc.SubscribeSync("foo")
if err != nil {
t.Fatalf("Unable to create subscription: %v", err)
}
if err := sub.SetPendingLimits(1, 1000); err != nil {
t.Fatalf("Unable to set pending limits: %v", err)
}
var pubErr error
msg := []byte("Hello")
for i := 0; i < 10; i++ {
pubErr = nc.Publish("foo", msg)
if pubErr != nil {
break
}
nc.Flush()
}
if pubErr != nil {
t.Fatalf("Publish() should not fail because of slow consumer. Got '%v'", pubErr)
}
}
func TestAsyncSubscribe(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
omsg := []byte("Hello World")
ch := make(chan bool)
// Callback is mandatory
if _, err := nc.Subscribe("foo", nil); err == nil {
t.Fatal("Creating subscription without callback should have failed")
}
_, err := nc.Subscribe("foo", func(m *nats.Msg) {
if !bytes.Equal(m.Data, omsg) {
t.Fatal("Message received does not match")
}
if m.Sub == nil {
t.Fatal("Callback does not have a valid Subscription")
}
ch <- true
})
if err != nil {
t.Fatal("Failed to subscribe: ", err)
}
nc.Publish("foo", omsg)
if e := Wait(ch); e != nil {
t.Fatal("Message not received for subscription")
}
}
func TestAsyncSubscribeRoutineLeakOnUnsubscribe(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
ch := make(chan bool)
// Give time for things to settle before capturing the number of
// go routines
time.Sleep(500 * time.Millisecond)
// Take the base once the connection is established, but before
// the subscriber is created.
base := runtime.NumGoroutine()
sub, err := nc.Subscribe("foo", func(m *nats.Msg) { ch <- true })
if err != nil {
t.Fatal("Failed to subscribe: ", err)
}
// Send to ourself
nc.Publish("foo", []byte("hello"))
// This ensures that the async delivery routine is up and running.
if err := Wait(ch); err != nil {
t.Fatal("Failed to receive message")
}
// Make sure to give it time to go back into wait
time.Sleep(200 * time.Millisecond)
// Explicit unsubscribe
sub.Unsubscribe()
// Give time for things to settle before capturing the number of
// go routines
time.Sleep(500 * time.Millisecond)
delta := (runtime.NumGoroutine() - base)
if delta > 0 {
t.Fatalf("%d Go routines still exist post Unsubscribe()", delta)
}
}
func TestAsyncSubscribeRoutineLeakOnClose(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
ch := make(chan bool)
// Give time for things to settle before capturing the number of
// go routines
time.Sleep(500 * time.Millisecond)
// Take the base before creating the connection, since we are going
// to close it before taking the delta.
base := runtime.NumGoroutine()
nc := NewDefaultConnection(t)
defer nc.Close()
_, err := nc.Subscribe("foo", func(m *nats.Msg) { ch <- true })
if err != nil {
t.Fatal("Failed to subscribe: ", err)
}
// Send to ourself
nc.Publish("foo", []byte("hello"))
// This ensures that the async delivery routine is up and running.
if err := Wait(ch); err != nil {
t.Fatal("Failed to receive message")
}
// Make sure to give it time to go back into wait
time.Sleep(200 * time.Millisecond)
// Close connection without explicit unsubscribe
nc.Close()
// Give time for things to settle before capturing the number of
// go routines
time.Sleep(500 * time.Millisecond)
delta := (runtime.NumGoroutine() - base)
if delta > 0 {
t.Fatalf("%d Go routines still exist post Close()", delta)
}
}
func TestSyncSubscribe(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
sub, err := nc.SubscribeSync("foo")
if err != nil {
t.Fatal("Failed to subscribe: ", err)
}
omsg := []byte("Hello World")
nc.Publish("foo", omsg)
msg, err := sub.NextMsg(1 * time.Second)
if err != nil || !bytes.Equal(msg.Data, omsg) {
t.Fatal("Message received does not match")
}
}
func TestPubSubWithReply(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
sub, err := nc.SubscribeSync("foo")
if err != nil {
t.Fatal("Failed to subscribe: ", err)
}
omsg := []byte("Hello World")
nc.PublishMsg(&nats.Msg{Subject: "foo", Reply: "bar", Data: omsg})
msg, err := sub.NextMsg(10 * time.Second)
if err != nil || !bytes.Equal(msg.Data, omsg) {
t.Fatal("Message received does not match")
}
}
func TestFlush(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
omsg := []byte("Hello World")
for i := 0; i < 10000; i++ {
nc.Publish("flush", omsg)
}
if err := nc.FlushTimeout(0); err == nil {
t.Fatal("Calling FlushTimeout() with invalid timeout should fail")
}
if err := nc.Flush(); err != nil {
t.Fatalf("Received error from flush: %s\n", err)
}
if nb, _ := nc.Buffered(); nb > 0 {
t.Fatalf("Outbound buffer not empty: %d bytes\n", nb)
}
nc.Close()
if _, err := nc.Buffered(); err == nil {
t.Fatal("Calling Buffered() on closed connection should fail")
}
}
func TestQueueSubscriber(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
s1, _ := nc.QueueSubscribeSync("foo", "bar")
s2, _ := nc.QueueSubscribeSync("foo", "bar")
omsg := []byte("Hello World")
nc.Publish("foo", omsg)
nc.Flush()
r1, _ := s1.QueuedMsgs()
r2, _ := s2.QueuedMsgs()
if (r1 + r2) != 1 {
t.Fatal("Received too many messages for multiple queue subscribers")
}
// Drain messages
s1.NextMsg(time.Second)
s2.NextMsg(time.Second)
total := 1000
for i := 0; i < total; i++ {
nc.Publish("foo", omsg)
}
nc.Flush()
v := uint(float32(total) * 0.15)
r1, _ = s1.QueuedMsgs()
r2, _ = s2.QueuedMsgs()
if r1+r2 != total {
t.Fatalf("Incorrect number of messages: %d vs %d", (r1 + r2), total)
}
expected := total / 2
d1 := uint(math.Abs(float64(expected - r1)))
d2 := uint(math.Abs(float64(expected - r2)))
if d1 > v || d2 > v {
t.Fatalf("Too much variance in totals: %d, %d > %d", d1, d2, v)
}
}
func TestReplyArg(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
ch := make(chan bool)
replyExpected := "bar"
nc.Subscribe("foo", func(m *nats.Msg) {
if m.Reply != replyExpected {
t.Fatalf("Did not receive correct reply arg in callback: "+
"('%s' vs '%s')", m.Reply, replyExpected)
}
ch <- true
})
nc.PublishMsg(&nats.Msg{Subject: "foo", Reply: replyExpected, Data: []byte("Hello")})
if e := Wait(ch); e != nil {
t.Fatal("Did not receive callback")
}
}
func TestSyncReplyArg(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
replyExpected := "bar"
sub, _ := nc.SubscribeSync("foo")
nc.PublishMsg(&nats.Msg{Subject: "foo", Reply: replyExpected, Data: []byte("Hello")})
msg, err := sub.NextMsg(1 * time.Second)
if err != nil {
t.Fatal("Received an err on NextMsg()")
}
if msg.Reply != replyExpected {
t.Fatalf("Did not receive correct reply arg in callback: "+
"('%s' vs '%s')", msg.Reply, replyExpected)
}
}
func TestUnsubscribe(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
received := int32(0)
max := int32(10)
ch := make(chan bool)
nc.Subscribe("foo", func(m *nats.Msg) {
atomic.AddInt32(&received, 1)
if received == max {
err := m.Sub.Unsubscribe()
if err != nil {
t.Fatal("Unsubscribe failed with err:", err)
}
ch <- true
}
})
send := 20
for i := 0; i < send; i++ {
nc.Publish("foo", []byte("hello"))
}
nc.Flush()
<-ch
r := atomic.LoadInt32(&received)
if r != max {
t.Fatalf("Received wrong # of messages after unsubscribe: %d vs %d",
r, max)
}
}
func TestDoubleUnsubscribe(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
sub, err := nc.SubscribeSync("foo")
if err != nil {
t.Fatal("Failed to subscribe: ", err)
}
if err = sub.Unsubscribe(); err != nil {
t.Fatal("Unsubscribe failed with err:", err)
}
if err = sub.Unsubscribe(); err == nil {
t.Fatal("Unsubscribe should have reported an error")
}
}
func TestRequestTimeout(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
if _, err := nc.Request("foo", []byte("help"), 10*time.Millisecond); err == nil {
t.Fatalf("Expected to receive a timeout error")
}
}
func TestOldRequest(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc, err := nats.Connect(nats.DefaultURL, nats.UseOldRequestStyle())
if err != nil {
t.Fatalf("Failed to connect: %v", err)
}
defer nc.Close()
response := []byte("I will help you")
nc.Subscribe("foo", func(m *nats.Msg) {
nc.Publish(m.Reply, response)
})
msg, err := nc.Request("foo", []byte("help"), 500*time.Millisecond)
if err != nil {
t.Fatalf("Received an error on Request test: %s", err)
}
if !bytes.Equal(msg.Data, response) {
t.Fatalf("Received invalid response")
}
}
func TestRequest(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
response := []byte("I will help you")
nc.Subscribe("foo", func(m *nats.Msg) {
nc.Publish(m.Reply, response)
})
msg, err := nc.Request("foo", []byte("help"), 500*time.Millisecond)
if err != nil {
t.Fatalf("Received an error on Request test: %s", err)
}
if !bytes.Equal(msg.Data, response) {
t.Fatalf("Received invalid response")
}
}
func TestRequestNoBody(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
response := []byte("I will help you")
nc.Subscribe("foo", func(m *nats.Msg) {
nc.Publish(m.Reply, response)
})
msg, err := nc.Request("foo", nil, 500*time.Millisecond)
if err != nil {
t.Fatalf("Received an error on Request test: %s", err)
}
if !bytes.Equal(msg.Data, response) {
t.Fatalf("Received invalid response")
}
}
func TestSimultaneousRequests(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
response := []byte("I will help you")
nc.Subscribe("foo", func(m *nats.Msg) {
nc.Publish(m.Reply, response)
})
var wg sync.WaitGroup
for i := 0; i < 50; i++ {
wg.Add(1)
go func() {
if _, err := nc.Request("foo", nil, 2*time.Second); err != nil {
t.Fatalf("Expected to receive a timeout error")
} else {
wg.Done()
}
}()
}
wg.Wait()
}
func TestRequestClose(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
wg := &sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
time.Sleep(100 * time.Millisecond)
nc.Close()
}()
if _, err := nc.Request("foo", []byte("help"), 2*time.Second); err != nats.ErrInvalidConnection && err != nats.ErrConnectionClosed {
t.Fatalf("Expected connection error: got %v", err)
}
wg.Wait()
}
func TestRequestCloseTimeout(t *testing.T) {
// Make sure we return a timeout when we close
// the connection even if response is queued.
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
response := []byte("I will help you")
nc.Subscribe("foo", func(m *nats.Msg) {
nc.Publish(m.Reply, response)
nc.Close()
})
if _, err := nc.Request("foo", nil, 1*time.Second); err == nil {
t.Fatalf("Expected to receive a timeout error")
}
}
func TestFlushInCB(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
ch := make(chan bool)
nc.Subscribe("foo", func(_ *nats.Msg) {
nc.Flush()
ch <- true
})
nc.Publish("foo", []byte("Hello"))
if e := Wait(ch); e != nil {
t.Fatal("Flush did not return properly in callback")
}
}
func TestReleaseFlush(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
for i := 0; i < 1000; i++ {
nc.Publish("foo", []byte("Hello"))
}
go nc.Close()
nc.Flush()
}
func TestInbox(t *testing.T) {
inbox := nats.NewInbox()
if matched, _ := regexp.Match(`_INBOX.\S`, []byte(inbox)); !matched {
t.Fatal("Bad INBOX format")
}
}
func TestStats(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
data := []byte("The quick brown fox jumped over the lazy dog")
iter := 10
for i := 0; i < iter; i++ {
nc.Publish("foo", data)
}
if nc.OutMsgs != uint64(iter) {
t.Fatalf("Not properly tracking OutMsgs: received %d, wanted %d\n", nc.OutMsgs, iter)
}
obb := uint64(iter * len(data))
if nc.OutBytes != obb {
t.Fatalf("Not properly tracking OutBytes: received %d, wanted %d\n", nc.OutBytes, obb)
}
// Clear outbound
nc.OutMsgs, nc.OutBytes = 0, 0
// Test both sync and async versions of subscribe.
nc.Subscribe("foo", func(_ *nats.Msg) {})
nc.SubscribeSync("foo")
for i := 0; i < iter; i++ {
nc.Publish("foo", data)
}
nc.Flush()
if nc.InMsgs != uint64(2*iter) {
t.Fatalf("Not properly tracking InMsgs: received %d, wanted %d\n", nc.InMsgs, 2*iter)
}
ibb := 2 * obb
if nc.InBytes != ibb {
t.Fatalf("Not properly tracking InBytes: received %d, wanted %d\n", nc.InBytes, ibb)
}
}
func TestRaceSafeStats(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
go nc.Publish("foo", []byte("Hello World"))
time.Sleep(200 * time.Millisecond)
stats := nc.Stats()
if stats.OutMsgs != uint64(1) {
t.Fatalf("Not properly tracking OutMsgs: received %d, wanted %d\n", nc.OutMsgs, 1)
}
}
func TestBadSubject(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
err := nc.Publish("", []byte("Hello World"))
if err == nil {
t.Fatalf("Expected an error on bad subject to publish")
}
if err != nats.ErrBadSubject {
t.Fatalf("Expected a ErrBadSubject error: Got %v\n", err)
}
}
func TestOptions(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc, err := nats.Connect(nats.DefaultURL, nats.Name("myName"), nats.MaxReconnects(2), nats.ReconnectWait(50*time.Millisecond))
if err != nil {
t.Fatalf("Failed to connect: %v", err)
}
defer nc.Close()
rch := make(chan bool)
cch := make(chan bool)
nc.SetReconnectHandler(func(_ *nats.Conn) { rch <- true })
nc.SetClosedHandler(func(_ *nats.Conn) { cch <- true })
s.Shutdown()
s = RunDefaultServer()
defer s.Shutdown()
if err := Wait(rch); err != nil {
t.Fatal("Failed getting reconnected cb")
}
nc.Close()
if err := Wait(cch); err != nil {
t.Fatal("Failed getting closed cb")
}
nc, err = nats.Connect(nats.DefaultURL, nats.NoReconnect())
if err != nil {
t.Fatalf("Failed to connect: %v", err)
}
defer nc.Close()
nc.SetReconnectHandler(func(_ *nats.Conn) { rch <- true })
nc.SetClosedHandler(func(_ *nats.Conn) { cch <- true })
s.Shutdown()
// We should not get a reconnect cb this time
if err := WaitTime(rch, time.Second); err == nil {
t.Fatal("Unexpected reconnect cb")
}
nc.Close()
if err := Wait(cch); err != nil {
t.Fatal("Failed getting closed cb")
}
}
func TestNilConnection(t *testing.T) {
var nc *nats.Conn
data := []byte("ok")
// Publish
if err := nc.Publish("foo", data); err == nil || err != nats.ErrInvalidConnection {
t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err)
}
if err := nc.PublishMsg(nil); err == nil || err != nats.ErrInvalidMsg {
t.Fatalf("Expected ErrInvalidMsg error, got %v\n", err)
}
if err := nc.PublishMsg(&nats.Msg{}); err == nil || err != nats.ErrInvalidConnection {
t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err)
}
if err := nc.PublishRequest("foo", "reply", data); err == nil || err != nats.ErrInvalidConnection {
t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err)
}
// Subscribe
if _, err := nc.Subscribe("foo", nil); err == nil || err != nats.ErrInvalidConnection {
t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err)
}
if _, err := nc.SubscribeSync("foo"); err == nil || err != nats.ErrInvalidConnection {
t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err)
}
if _, err := nc.QueueSubscribe("foo", "bar", nil); err == nil || err != nats.ErrInvalidConnection {
t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err)
}
ch := make(chan *nats.Msg)
if _, err := nc.ChanSubscribe("foo", ch); err == nil || err != nats.ErrInvalidConnection {
t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err)
}
if _, err := nc.ChanQueueSubscribe("foo", "bar", ch); err == nil || err != nats.ErrInvalidConnection {
t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err)
}
if _, err := nc.QueueSubscribeSyncWithChan("foo", "bar", ch); err == nil || err != nats.ErrInvalidConnection {
t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err)
}
// Flush
if err := nc.Flush(); err == nil || err != nats.ErrInvalidConnection {
t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err)
}
if err := nc.FlushTimeout(time.Millisecond); err == nil || err != nats.ErrInvalidConnection {
t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err)
}
// Nil Subscribers
var sub *nats.Subscription
if sub.Type() != nats.NilSubscription {
t.Fatalf("Got wrong type for nil subscription, %v\n", sub.Type())
}
if sub.IsValid() {
t.Fatalf("Expected IsValid() to return false")
}
if err := sub.Unsubscribe(); err == nil || err != nats.ErrBadSubscription {
t.Fatalf("Expected Unsubscribe to return proper error, got %v\n", err)
}
if err := sub.AutoUnsubscribe(1); err == nil || err != nats.ErrBadSubscription {
t.Fatalf("Expected ErrBadSubscription error, got %v\n", err)
}
if _, err := sub.NextMsg(time.Millisecond); err == nil || err != nats.ErrBadSubscription {
t.Fatalf("Expected ErrBadSubscription error, got %v\n", err)
}
if _, err := sub.QueuedMsgs(); err == nil || err != nats.ErrBadSubscription {
t.Fatalf("Expected ErrBadSubscription error, got %v\n", err)
}
if _, _, err := sub.Pending(); err == nil || err != nats.ErrBadSubscription {
t.Fatalf("Expected ErrBadSubscription error, got %v\n", err)
}
if _, _, err := sub.MaxPending(); err == nil || err != nats.ErrBadSubscription {
t.Fatalf("Expected ErrBadSubscription error, got %v\n", err)
}
if err := sub.ClearMaxPending(); err == nil || err != nats.ErrBadSubscription {
t.Fatalf("Expected ErrBadSubscription error, got %v\n", err)
}
if _, _, err := sub.PendingLimits(); err == nil || err != nats.ErrBadSubscription {
t.Fatalf("Expected ErrBadSubscription error, got %v\n", err)
}
if err := sub.SetPendingLimits(1, 1); err == nil || err != nats.ErrBadSubscription {
t.Fatalf("Expected ErrBadSubscription error, got %v\n", err)
}
if _, err := sub.Delivered(); err == nil || err != nats.ErrBadSubscription {
t.Fatalf("Expected ErrBadSubscription error, got %v\n", err)
}
if _, err := sub.Dropped(); err == nil || err != nats.ErrBadSubscription {
t.Fatalf("Expected ErrBadSubscription error, got %v\n", err)
}
}

View File

@ -0,0 +1,153 @@
package test
import (
"sync/atomic"
"testing"
"time"
"github.com/nats-io/go-nats"
)
func BenchmarkPublishSpeed(b *testing.B) {
b.StopTimer()
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(b)
defer nc.Close()
b.StartTimer()
msg := []byte("Hello World")
for i := 0; i < b.N; i++ {
if err := nc.Publish("foo", msg); err != nil {
b.Fatalf("Error in benchmark during Publish: %v\n", err)
}
}
// Make sure they are all processed.
nc.Flush()
b.StopTimer()
}
func BenchmarkPubSubSpeed(b *testing.B) {
b.StopTimer()
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(b)
defer nc.Close()
ch := make(chan bool)
nc.SetErrorHandler(func(nc *nats.Conn, s *nats.Subscription, err error) {
b.Fatalf("Error : %v\n", err)
})
received := int32(0)
nc.Subscribe("foo", func(m *nats.Msg) {
if nr := atomic.AddInt32(&received, 1); nr >= int32(b.N) {
ch <- true
}
})
msg := []byte("Hello World")
b.StartTimer()
for i := 0; i < b.N; i++ {
if err := nc.Publish("foo", msg); err != nil {
b.Fatalf("Error in benchmark during Publish: %v\n", err)
}
// Don't overrun ourselves and be a slow consumer, server will cut us off
if int32(i)-atomic.LoadInt32(&received) > 32768 {
time.Sleep(100 * time.Nanosecond)
}
}
// Make sure they are all processed.
err := WaitTime(ch, 10*time.Second)
if err != nil {
b.Fatal("Timed out waiting for messages")
} else if atomic.LoadInt32(&received) != int32(b.N) {
b.Fatalf("Received: %d, err:%v", received, nc.LastError())
}
b.StopTimer()
}
func BenchmarkAsyncSubscriptionCreationSpeed(b *testing.B) {
b.StopTimer()
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(b)
defer nc.Close()
b.StartTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
nc.Subscribe("foo", func(m *nats.Msg) {})
}
}
func BenchmarkSyncSubscriptionCreationSpeed(b *testing.B) {
b.StopTimer()
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(b)
defer nc.Close()
b.StartTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
nc.SubscribeSync("foo")
}
}
func BenchmarkInboxCreation(b *testing.B) {
for i := 0; i < b.N; i++ {
nats.NewInbox()
}
}
func BenchmarkRequest(b *testing.B) {
b.StopTimer()
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(b)
defer nc.Close()
ok := []byte("ok")
nc.Subscribe("req", func(m *nats.Msg) {
nc.Publish(m.Reply, ok)
})
b.StartTimer()
b.ReportAllocs()
q := []byte("q")
for i := 0; i < b.N; i++ {
_, err := nc.Request("req", q, 1*time.Second)
if err != nil {
b.Fatalf("Err %v\n", err)
}
}
}
func BenchmarkOldRequest(b *testing.B) {
b.StopTimer()
s := RunDefaultServer()
defer s.Shutdown()
nc, err := nats.Connect(nats.DefaultURL, nats.UseOldRequestStyle())
if err != nil {
b.Fatalf("Failed to connect: %v", err)
}
defer nc.Close()
ok := []byte("ok")
nc.Subscribe("req", func(m *nats.Msg) {
nc.Publish(m.Reply, ok)
})
b.StartTimer()
b.ReportAllocs()
q := []byte("q")
for i := 0; i < b.N; i++ {
_, err := nc.Request("req", q, 1*time.Second)
if err != nil {
b.Fatalf("Err %v\n", err)
}
}
}

View File

@ -0,0 +1,606 @@
package test
import (
"math"
"regexp"
"runtime"
"strings"
"sync"
"testing"
"time"
"github.com/nats-io/gnatsd/test"
"github.com/nats-io/go-nats"
)
var testServers = []string{
"nats://localhost:1222",
"nats://localhost:1223",
"nats://localhost:1224",
"nats://localhost:1225",
"nats://localhost:1226",
"nats://localhost:1227",
"nats://localhost:1228",
}
var servers = strings.Join(testServers, ",")
func TestServersOption(t *testing.T) {
opts := nats.GetDefaultOptions()
opts.NoRandomize = true
_, err := opts.Connect()
if err != nats.ErrNoServers {
t.Fatalf("Wrong error: '%v'\n", err)
}
opts.Servers = testServers
_, err = opts.Connect()
if err == nil || err != nats.ErrNoServers {
t.Fatalf("Did not receive proper error: %v\n", err)
}
// Make sure we can connect to first server if running
s1 := RunServerOnPort(1222)
// Do this in case some failure occurs before explicit shutdown
defer s1.Shutdown()
nc, err := opts.Connect()
if err != nil {
t.Fatalf("Could not connect: %v\n", err)
}
if nc.ConnectedUrl() != "nats://localhost:1222" {
nc.Close()
t.Fatalf("Does not report correct connection: %s\n",
nc.ConnectedUrl())
}
nc.Close()
s1.Shutdown()
// Make sure we can connect to a non first server if running
s2 := RunServerOnPort(1223)
// Do this in case some failure occurs before explicit shutdown
defer s2.Shutdown()
nc, err = opts.Connect()
if err != nil {
t.Fatalf("Could not connect: %v\n", err)
}
defer nc.Close()
if nc.ConnectedUrl() != "nats://localhost:1223" {
t.Fatalf("Does not report correct connection: %s\n",
nc.ConnectedUrl())
}
}
func TestNewStyleServersOption(t *testing.T) {
_, err := nats.Connect(nats.DefaultURL, nats.DontRandomize())
if err != nats.ErrNoServers {
t.Fatalf("Wrong error: '%v'\n", err)
}
servers := strings.Join(testServers, ",")
_, err = nats.Connect(servers, nats.DontRandomize())
if err == nil || err != nats.ErrNoServers {
t.Fatalf("Did not receive proper error: %v\n", err)
}
// Make sure we can connect to first server if running
s1 := RunServerOnPort(1222)
// Do this in case some failure occurs before explicit shutdown
defer s1.Shutdown()
nc, err := nats.Connect(servers, nats.DontRandomize())
if err != nil {
t.Fatalf("Could not connect: %v\n", err)
}
if nc.ConnectedUrl() != "nats://localhost:1222" {
nc.Close()
t.Fatalf("Does not report correct connection: %s\n",
nc.ConnectedUrl())
}
nc.Close()
s1.Shutdown()
// Make sure we can connect to a non-first server if running
s2 := RunServerOnPort(1223)
// Do this in case some failure occurs before explicit shutdown
defer s2.Shutdown()
nc, err = nats.Connect(servers, nats.DontRandomize())
if err != nil {
t.Fatalf("Could not connect: %v\n", err)
}
defer nc.Close()
if nc.ConnectedUrl() != "nats://localhost:1223" {
t.Fatalf("Does not report correct connection: %s\n",
nc.ConnectedUrl())
}
}
func TestAuthServers(t *testing.T) {
var plainServers = []string{
"nats://localhost:1222",
"nats://localhost:1224",
}
opts := test.DefaultTestOptions
opts.Username = "derek"
opts.Password = "foo"
opts.Port = 1222
as1 := RunServerWithOptions(opts)
defer as1.Shutdown()
opts.Port = 1224
as2 := RunServerWithOptions(opts)
defer as2.Shutdown()
pservers := strings.Join(plainServers, ",")
nc, err := nats.Connect(pservers, nats.DontRandomize(), nats.Timeout(5*time.Second))
if err == nil {
nc.Close()
t.Fatalf("Expect Auth failure, got no error\n")
}
if matched, _ := regexp.Match(`authorization`, []byte(err.Error())); !matched {
t.Fatalf("Wrong error, wanted Auth failure, got '%s'\n", err)
}
// Test that we can connect to a subsequent correct server.
var authServers = []string{
"nats://localhost:1222",
"nats://derek:foo@localhost:1224",
}
aservers := strings.Join(authServers, ",")
nc, err = nats.Connect(aservers, nats.DontRandomize(), nats.Timeout(5*time.Second))
if err != nil {
t.Fatalf("Expected to connect properly: %v\n", err)
}
defer nc.Close()
if nc.ConnectedUrl() != authServers[1] {
t.Fatalf("Does not report correct connection: %s\n",
nc.ConnectedUrl())
}
}
func TestBasicClusterReconnect(t *testing.T) {
s1 := RunServerOnPort(1222)
defer s1.Shutdown()
s2 := RunServerOnPort(1224)
defer s2.Shutdown()
dch := make(chan bool)
rch := make(chan bool)
dcbCalled := false
opts := []nats.Option{nats.DontRandomize(),
nats.DisconnectHandler(func(nc *nats.Conn) {
// Suppress any additional callbacks
if dcbCalled {
return
}
dcbCalled = true
dch <- true
}),
nats.ReconnectHandler(func(_ *nats.Conn) { rch <- true }),
}
nc, err := nats.Connect(servers, opts...)
if err != nil {
t.Fatalf("Expected to connect, got err: %v\n", err)
}
defer nc.Close()
s1.Shutdown()
// wait for disconnect
if e := WaitTime(dch, 2*time.Second); e != nil {
t.Fatal("Did not receive a disconnect callback message")
}
reconnectTimeStart := time.Now()
// wait for reconnect
if e := WaitTime(rch, 2*time.Second); e != nil {
t.Fatal("Did not receive a reconnect callback message")
}
if nc.ConnectedUrl() != testServers[2] {
t.Fatalf("Does not report correct connection: %s\n",
nc.ConnectedUrl())
}
// Make sure we did not wait on reconnect for default time.
// Reconnect should be fast since it will be a switch to the
// second server and not be dependent on server restart time.
// On Windows, a failed connect takes more than a second, so
// account for that.
maxDuration := 100 * time.Millisecond
if runtime.GOOS == "windows" {
maxDuration = 1100 * time.Millisecond
}
reconnectTime := time.Since(reconnectTimeStart)
if reconnectTime > maxDuration {
t.Fatalf("Took longer than expected to reconnect: %v\n", reconnectTime)
}
}
func TestHotSpotReconnect(t *testing.T) {
s1 := RunServerOnPort(1222)
defer s1.Shutdown()
var srvrs string
if runtime.GOOS == "windows" {
srvrs = strings.Join(testServers[:5], ",")
} else {
srvrs = servers
}
numClients := 32
clients := []*nats.Conn{}
wg := &sync.WaitGroup{}
wg.Add(numClients)
opts := []nats.Option{
nats.ReconnectWait(50 * time.Millisecond),
nats.ReconnectHandler(func(_ *nats.Conn) { wg.Done() }),
}
for i := 0; i < numClients; i++ {
nc, err := nats.Connect(srvrs, opts...)
if err != nil {
t.Fatalf("Expected to connect, got err: %v\n", err)
}
defer nc.Close()
if nc.ConnectedUrl() != testServers[0] {
t.Fatalf("Connected to incorrect server: %v\n", nc.ConnectedUrl())
}
clients = append(clients, nc)
}
s2 := RunServerOnPort(1224)
defer s2.Shutdown()
s3 := RunServerOnPort(1226)
defer s3.Shutdown()
s1.Shutdown()
numServers := 2
// Wait on all reconnects
wg.Wait()
// Walk the clients and calculate how many of each..
cs := make(map[string]int)
for _, nc := range clients {
cs[nc.ConnectedUrl()]++
nc.Close()
}
if len(cs) != numServers {
t.Fatalf("Wrong number of reported servers: %d vs %d\n", len(cs), numServers)
}
expected := numClients / numServers
v := uint(float32(expected) * 0.40)
// Check that each item is within acceptable range
for s, total := range cs {
delta := uint(math.Abs(float64(expected - total)))
if delta > v {
t.Fatalf("Connected clients to server: %s out of range: %d\n", s, total)
}
}
}
func TestProperReconnectDelay(t *testing.T) {
s1 := RunServerOnPort(1222)
defer s1.Shutdown()
var srvs string
opts := nats.GetDefaultOptions()
if runtime.GOOS == "windows" {
srvs = strings.Join(testServers[:2], ",")
} else {
srvs = strings.Join(testServers, ",")
}
opts.NoRandomize = true
dcbCalled := false
closedCbCalled := false
dch := make(chan bool)
dcb := func(nc *nats.Conn) {
// Suppress any additional calls
if dcbCalled {
return
}
dcbCalled = true
dch <- true
}
ccb := func(_ *nats.Conn) {
closedCbCalled = true
}
nc, err := nats.Connect(srvs, nats.DontRandomize(), nats.DisconnectHandler(dcb), nats.ClosedHandler(ccb))
if err != nil {
t.Fatalf("Expected to connect, got err: %v\n", err)
}
defer nc.Close()
s1.Shutdown()
// wait for disconnect
if e := WaitTime(dch, 2*time.Second); e != nil {
t.Fatal("Did not receive a disconnect callback message")
}
// Wait, want to make sure we don't spin on reconnect to non-existent servers.
time.Sleep(1 * time.Second)
// Make sure we are still reconnecting..
if closedCbCalled {
t.Fatal("Closed CB was triggered, should not have been.")
}
if status := nc.Status(); status != nats.RECONNECTING {
t.Fatalf("Wrong status: %d\n", status)
}
}
func TestProperFalloutAfterMaxAttempts(t *testing.T) {
s1 := RunServerOnPort(1222)
defer s1.Shutdown()
opts := nats.GetDefaultOptions()
// Reduce the list of servers for Windows tests
if runtime.GOOS == "windows" {
opts.Servers = testServers[:2]
opts.MaxReconnect = 2
} else {
opts.Servers = testServers
opts.MaxReconnect = 5
}
opts.NoRandomize = true
opts.ReconnectWait = (25 * time.Millisecond)
dch := make(chan bool)
opts.DisconnectedCB = func(_ *nats.Conn) {
dch <- true
}
closedCbCalled := false
cch := make(chan bool)
opts.ClosedCB = func(_ *nats.Conn) {
closedCbCalled = true
cch <- true
}
nc, err := opts.Connect()
if err != nil {
t.Fatalf("Expected to connect, got err: %v\n", err)
}
defer nc.Close()
s1.Shutdown()
// On Windows, creating a TCP connection to a server not running takes more than
// a second. So be generous with the WaitTime.
// wait for disconnect
if e := WaitTime(dch, 5*time.Second); e != nil {
t.Fatal("Did not receive a disconnect callback message")
}
// Wait for ClosedCB
if e := WaitTime(cch, 5*time.Second); e != nil {
t.Fatal("Did not receive a closed callback message")
}
// Make sure we are not still reconnecting..
if !closedCbCalled {
t.Logf("%+v\n", nc)
t.Fatal("Closed CB was not triggered, should have been.")
}
// Expect connection to be closed...
if !nc.IsClosed() {
t.Fatalf("Wrong status: %d\n", nc.Status())
}
}
func TestProperFalloutAfterMaxAttemptsWithAuthMismatch(t *testing.T) {
var myServers = []string{
"nats://localhost:1222",
"nats://localhost:4443",
}
s1 := RunServerOnPort(1222)
defer s1.Shutdown()
s2, _ := RunServerWithConfig("./configs/tlsverify.conf")
defer s2.Shutdown()
opts := nats.GetDefaultOptions()
opts.Servers = myServers
opts.NoRandomize = true
if runtime.GOOS == "windows" {
opts.MaxReconnect = 2
} else {
opts.MaxReconnect = 5
}
opts.ReconnectWait = (25 * time.Millisecond)
dch := make(chan bool)
opts.DisconnectedCB = func(_ *nats.Conn) {
dch <- true
}
closedCbCalled := false
cch := make(chan bool)
opts.ClosedCB = func(_ *nats.Conn) {
closedCbCalled = true
cch <- true
}
nc, err := opts.Connect()
if err != nil {
t.Fatalf("Expected to connect, got err: %v\n", err)
}
defer nc.Close()
s1.Shutdown()
// On Windows, creating a TCP connection to a server not running takes more than
// a second. So be generous with the WaitTime.
// wait for disconnect
if e := WaitTime(dch, 5*time.Second); e != nil {
t.Fatal("Did not receive a disconnect callback message")
}
// Wait for ClosedCB
if e := WaitTime(cch, 5*time.Second); e != nil {
reconnects := nc.Stats().Reconnects
t.Fatalf("Did not receive a closed callback message, #reconnects: %v", reconnects)
}
// Make sure we have not exceeded MaxReconnect
reconnects := nc.Stats().Reconnects
if reconnects != uint64(opts.MaxReconnect) {
t.Fatalf("Num reconnects was %v, expected %v", reconnects, opts.MaxReconnect)
}
// Make sure we are not still reconnecting..
if !closedCbCalled {
t.Logf("%+v\n", nc)
t.Fatal("Closed CB was not triggered, should have been.")
}
// Expect connection to be closed...
if !nc.IsClosed() {
t.Fatalf("Wrong status: %d\n", nc.Status())
}
}
func TestTimeoutOnNoServers(t *testing.T) {
s1 := RunServerOnPort(1222)
defer s1.Shutdown()
opts := nats.GetDefaultOptions()
if runtime.GOOS == "windows" {
opts.Servers = testServers[:2]
opts.MaxReconnect = 2
opts.ReconnectWait = (100 * time.Millisecond)
} else {
opts.Servers = testServers
// 1 second total time wait
opts.MaxReconnect = 10
opts.ReconnectWait = (100 * time.Millisecond)
}
opts.NoRandomize = true
dch := make(chan bool)
opts.DisconnectedCB = func(nc *nats.Conn) {
// Suppress any additional calls
nc.SetDisconnectHandler(nil)
dch <- true
}
cch := make(chan bool)
opts.ClosedCB = func(_ *nats.Conn) {
cch <- true
}
nc, err := opts.Connect()
if err != nil {
t.Fatalf("Expected to connect, got err: %v\n", err)
}
defer nc.Close()
s1.Shutdown()
// On Windows, creating a connection to a non-running server takes
// more than a second. So be generous with WaitTime
// wait for disconnect
if e := WaitTime(dch, 5*time.Second); e != nil {
t.Fatal("Did not receive a disconnect callback message")
}
startWait := time.Now()
// Wait for ClosedCB
if e := WaitTime(cch, 5*time.Second); e != nil {
t.Fatal("Did not receive a closed callback message")
}
if runtime.GOOS != "windows" {
timeWait := time.Since(startWait)
// Use 500ms as variable time delta
variable := (500 * time.Millisecond)
expected := (time.Duration(opts.MaxReconnect) * opts.ReconnectWait)
if timeWait > (expected + variable) {
t.Fatalf("Waited too long for Closed state: %d\n", timeWait/time.Millisecond)
}
}
}
func TestPingReconnect(t *testing.T) {
RECONNECTS := 4
s1 := RunServerOnPort(1222)
defer s1.Shutdown()
opts := nats.GetDefaultOptions()
opts.Servers = testServers
opts.NoRandomize = true
opts.ReconnectWait = 200 * time.Millisecond
opts.PingInterval = 50 * time.Millisecond
opts.MaxPingsOut = -1
var wg sync.WaitGroup
wg.Add(1)
rch := make(chan time.Time, RECONNECTS)
dch := make(chan time.Time, RECONNECTS)
opts.DisconnectedCB = func(_ *nats.Conn) {
d := dch
select {
case d <- time.Now():
default:
d = nil
}
}
opts.ReconnectedCB = func(c *nats.Conn) {
r := rch
select {
case r <- time.Now():
default:
r = nil
wg.Done()
}
}
nc, err := opts.Connect()
if err != nil {
t.Fatalf("Expected to connect, got err: %v\n", err)
}
defer nc.Close()
wg.Wait()
s1.Shutdown()
<-dch
for i := 0; i < RECONNECTS-1; i++ {
disconnectedAt := <-dch
reconnectAt := <-rch
pingCycle := disconnectedAt.Sub(reconnectAt)
if pingCycle > 2*opts.PingInterval {
t.Fatalf("Reconnect due to ping took %s", pingCycle.String())
}
}
}

View File

@ -0,0 +1,38 @@
-----BEGIN CERTIFICATE-----
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
+Svp
-----END CERTIFICATE-----

View File

@ -0,0 +1,30 @@
-----BEGIN CERTIFICATE-----
MIIFPDCCAySgAwIBAgIJAO+k4G7bNTypMA0GCSqGSIb3DQEBCwUAMIGLMQswCQYD
VQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzAR
BgNVBAoTCkFwY2VyYSBJbmMxEDAOBgNVBAsTB25hdHMuaW8xEjAQBgNVBAMTCWxv
Y2FsaG9zdDEcMBoGCSqGSIb3DQEJARYNZGVyZWtAbmF0cy5pbzAeFw0xNTExMDUy
MzEwNDdaFw0xOTExMDQyMzEwNDdaMBYxFDASBgNVBAMTC25hdHMtY2xpZW50MIIC
IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEArgLxszD5/vDrDUwwIEgQx9I0
J/H6MXPO0Tj9D2BnR+nwjCe9M03fsq4Il96BVzoaAiAQD1r4NyAX2adKydlnE3/m
bUFiSVHErJceEi9aSs+WlLdmKEgU2qrsIal9KzthlI786qtjb7OFSCxP14R4xYA5
dlZXhJ9oUuFhVTdaVmRMzWuWj8RbBx8VptSZ0f7Q+Uv8GuB0kyiVkv6GYcH/IWuI
7jnM0QcVWBmxJfWmqd0yx/FLlX/LRXqdiyoFSIlMaP0VOwto3uEhAoBk83Z+/zrZ
Brymx1Nnz3qzTCf8/mdMjPuWibXDTLbo0/Kf6neHs6wxx8irb1ZfIwhn8grXTcgd
rg9bfcyyUOBey7QXiedpU0xFqoH26E+Aq+CV4R56i1sJKsSYEGu8O69H8zu5dgan
LZRhcCHcZhMe7Nbiu5BcuOW4r3rGDMTLXSugEX91iy5jJaYmRjtPN5imQIJtf+GK
Vq7YLv4MQV6R3xRiZXaocCae1qzIMc4kxCKvZTmxuJsvIUPjNnGumwbjV/a2fLFX
9tMqUKyEmiPtFtqNH/kmkHCQ5FGYIIj3wGuD5yWfK5Tr3iHOdNJoNNPgPBg9tMRw
j3+W8+uyBxc+FUEb8a9m3R4VmAYyiqgzCA0DWZBF1fOYLWfRnwS5OBKiP4OUlUEb
YZUEzfvDbLOwQrb123cCAwEAAaMXMBUwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDQYJ
KoZIhvcNAQELBQADggIBACNKPbvaXwl5rRTqFw37Am1r6e+LkUg9dFogSwXDnuT/
RRZJi5MHsC5MUOkHB28lTmPwkAogs+LBmKrM0Npzk6OPkT/LCgKqpVoz2Tc1nGMI
Jy8jxPYogMmDCOhoEoC7zsWABMLiX5KDAuKommk61w7AwKu4kK198ngwbfF2fzdH
1DUGID7iV4fyPGI+pCU3Ullv51c5xkhqjVy1JYdYc0+s6rFyVTibSABa7PfHE2ML
A+cNFWoKQhugVHQU7qYvuWvnEqZro2T6nmSmpK3oOaUgVnDuY2q4JwiMbZAtuyD7
8LFwCim49WzgYcfs/BwKlUrTV/QBYurruHWjElZzwA39/ZlbnOjJJ85j/YqxR+4S
fK/KktegyrPJU3fxdl2+77zVlfgzxaQ//58vx5LgXWhl2KeHyakeD0jQFVn1R7GD
bynAlHlSOr+nGkwP2WVqXKf+l/gb/gUEY7bC8fCVRCctkcK+smEl+sIKH3O9JY8l
rBWjOXkMY91ZDh77hfTNni/s2/DGAoNrEft8rgu3/NPxhCTfQH3ranCryth9mF6I
qsOFr5/81WGKqU+Kec8st/RSU2vBjBp41HILAEEhUiB6prhc9B3+exwkvQSPz22W
PIvhkzqeOYRoEDE2bWGC1ukd818qvQp618eLBmJSvwGh4YfUcmgqHaEk2NjoPIMV
-----END CERTIFICATE-----

View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,31 @@
-----BEGIN CERTIFICATE-----
MIIFQTCCAymgAwIBAgIJAO+k4G7bNTyoMA0GCSqGSIb3DQEBCwUAMIGLMQswCQYD
VQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzAR
BgNVBAoTCkFwY2VyYSBJbmMxEDAOBgNVBAsTB25hdHMuaW8xEjAQBgNVBAMTCWxv
Y2FsaG9zdDEcMBoGCSqGSIb3DQEJARYNZGVyZWtAbmF0cy5pbzAeFw0xNTExMDUy
MzA2MzRaFw0xOTExMDQyMzA2MzRaMBQxEjAQBgNVBAMTCWxvY2FsaG9zdDCCAiIw
DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALYBy3IEY3kqlf5h2vEtk9CB4Vnt
AD+eaVc3Y9xuFHJ4k0ScsjwrYH3YcwQW0fDpOQCI0102YoQT7tPCBT+rC0w1mM82
0ZSKS/y2SIK9cM6LHWkUbQcWfeaL+uz2HB3jTEm8tmEEFTLBJFMbMpUsvjA5GLqG
URswsNjYEl8M9wS1BETw2e+eCFa4wxq9oGHp/Dgh0vZglHzQL5osEpRct1aaQo6O
jWzZc1Cgx4SxmMOoMWF8BQzlO7aikbZEJxk03TIFNph/azJt7mviMseW72mP+bX9
sm/8bsINiYgJMM2HAyjIgFVMXX8AYfEFC4wozYloLqn0yy9TdjhyGbsUjg0yTd4l
A9LkGKroBdY1drPSek5Nj9br27UGGGfU2ddAD5xYBIeeFY+3nqST868oIXB/m1P7
1p8tpkgujx/RqKr3nvOqBHizmoQaWZsPC3X/Jc4NvVHihpuNzN/u1D5mxGhxsx+R
qnrIkhS0IqNrokggPZazugmHntd95HgTb3JpjY3RGEYXAQNr+mZGUCc+CVu0mhFX
xAMZcfVp5nDg4hKHiaRv0KcaqBmnn8AB5w5FiTppzUbRP0zz7GkwrdulwR6c2Eb5
75+/022TbgCx8B9SH4zJRTj5mtrK56eFgTcnuXB+YnWaP7/7qmKIZzxrd3UDvnza
bhnMiiIK7vL8qiOTAgMBAAGjHjAcMBoGA1UdEQQTMBGCCWxvY2FsaG9zdIcEfwAA
ATANBgkqhkiG9w0BAQsFAAOCAgEAOrh8XfW6quwBAcCxHf6/uvu/iNq4yHCg2qH6
VtWs/x38I2t3BRSNsLsJGieh6yLlZDzOus+XYui4uDE50XmcwaIsY0VcXnvdyZVZ
w9+lMyfp00kRF1o3B6eVxq0pRE5VB0cai7XI7tyfpRwGzA+oNLF4vBvxAHm9Ony5
Q57DC/HFzyUogdkMYciO/kd9oa4HosDEXwaE8UvZUL8OVl/dptMXLL/GGwzZsUAE
1sLAbgm044YChLUDzgBAtDTkB/HNkcPzSKwULuskhe7ndoaEQNXVZuP7quGiZ/W1
1lE59gnmnyG8ySFCL05jHrKLtFAJe88gQjgDK65ZJv4W/k7ocmT+HhCxWyQWcX6v
abJ0EssqeSQuzRMuZebMJJ8s46d6RcYuMdIX3RDXq+1moJDFopE7lgNrlRhWgaky
Og8f/u8s1j75tk1YaYcY9uBKjKk7f681R9wMumkd6IEmEvkUwHNFsctxi4fGI7h1
PRdKL0DlhVmnpHlKs6Kvm2sJ3twSAGSrC4u0LuxACeR3XbiBfyhFV/291LSuw/y1
JtWOW5koh0g1k9xtkiu3/ePVdG/CLp796IyRhdB1jP/vD7W5RLLG/VAlomfjsPsB
AnwFYbVZ8KrmMKYUpTJOH31CRzFdOB6nWqXu5tk3nOtLKo1nIOuVtmp9XLz3VtHe
NiZPnqA=
-----END CERTIFICATE-----

View File

@ -0,0 +1,17 @@
# Simple TLS config file
port: 4443
net: localhost # net interface
tls {
cert_file: "./configs/certs/server.pem"
key_file: "./configs/certs/key.pem"
timeout: 2
}
authorization {
user: derek
password: buckley
timeout: 1
}

View File

@ -0,0 +1,17 @@
# Simple TLS config file
port: 4443
net: localhost
tls {
cert_file: "./configs/certs/server.pem"
key_file: "./configs/certs/key.pem"
timeout: 2
# Optional certificate authority for clients
ca_file: "./configs/certs/ca.pem"
# Require a client certificate
verify: true
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,999 @@
// Copyright 2012-2017 Apcera Inc. All rights reserved.
// +build go1.7
package test
import (
"context"
"strings"
"testing"
"time"
"github.com/nats-io/go-nats"
)
func TestContextRequestWithNilConnection(t *testing.T) {
var nc *nats.Conn
ctx, cancelCB := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancelCB() // should always be called, not discarded, to prevent context leak
_, err := nc.RequestWithContext(ctx, "fast", []byte(""))
if err == nil {
t.Fatalf("Expected request with context and nil connection to fail\n")
}
if err != nats.ErrInvalidConnection {
t.Fatalf("Expected nats.ErrInvalidConnection, got %v\n", err)
}
}
func testContextRequestWithTimeout(t *testing.T, nc *nats.Conn) {
nc.Subscribe("slow", func(m *nats.Msg) {
// Simulates latency into the client so that timeout is hit.
time.Sleep(200 * time.Millisecond)
nc.Publish(m.Reply, []byte("NG"))
})
nc.Subscribe("fast", func(m *nats.Msg) {
nc.Publish(m.Reply, []byte("OK"))
})
ctx, cancelCB := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancelCB() // should always be called, not discarded, to prevent context leak
// Fast request should not fail at this point.
resp, err := nc.RequestWithContext(ctx, "fast", []byte(""))
if err != nil {
t.Fatalf("Expected request with context to not fail on fast response: %s", err)
}
got := string(resp.Data)
expected := "OK"
if got != expected {
t.Errorf("Expected to receive %s, got: %s", expected, got)
}
// Slow request hits timeout so expected to fail.
_, err = nc.RequestWithContext(ctx, "slow", []byte("world"))
if err == nil {
t.Fatalf("Expected request with timeout context to fail: %s", err)
}
// Reported error is "context deadline exceeded" from Context package,
// which implements net.Error interface.
type timeoutError interface {
Timeout() bool
}
timeoutErr, ok := err.(timeoutError)
if !ok || !timeoutErr.Timeout() {
t.Errorf("Expected to have a timeout error")
}
expected = `context deadline exceeded`
if !strings.Contains(err.Error(), expected) {
t.Errorf("Expected %q error, got: %q", expected, err.Error())
}
// 2nd request should fail again even if they would be fast because context
// has already timed out.
_, err = nc.RequestWithContext(ctx, "fast", []byte("world"))
if err == nil {
t.Fatalf("Expected request with context to fail: %s", err)
}
}
func TestContextRequestWithTimeout(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
testContextRequestWithTimeout(t, nc)
}
func TestOldContextRequestWithTimeout(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc, err := nats.Connect(nats.DefaultURL, nats.UseOldRequestStyle())
if err != nil {
t.Fatalf("Failed to connect: %v", err)
}
defer nc.Close()
testContextRequestWithTimeout(t, nc)
}
func testContextRequestWithTimeoutCanceled(t *testing.T, nc *nats.Conn) {
ctx, cancelCB := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancelCB()
nc.Subscribe("fast", func(m *nats.Msg) {
nc.Publish(m.Reply, []byte("OK"))
})
// Fast request should not fail
resp, err := nc.RequestWithContext(ctx, "fast", []byte(""))
if err != nil {
t.Fatalf("Expected request with context to not fail on fast response: %s", err)
}
got := string(resp.Data)
expected := "OK"
if got != expected {
t.Errorf("Expected to receive %s, got: %s", expected, got)
}
// Cancel the context already so that rest of requests fail.
cancelCB()
_, err = nc.RequestWithContext(ctx, "fast", []byte("world"))
if err == nil {
t.Fatalf("Expected request with timeout context to fail: %s", err)
}
// Reported error is "context canceled" from Context package,
// which is not a timeout error.
type timeoutError interface {
Timeout() bool
}
if _, ok := err.(timeoutError); ok {
t.Errorf("Expected to not have a timeout error")
}
expected = `context canceled`
if !strings.Contains(err.Error(), expected) {
t.Errorf("Expected %q error, got: %q", expected, err.Error())
}
// 2nd request should fail again even if fast because context has already been canceled
_, err = nc.RequestWithContext(ctx, "fast", []byte("world"))
if err == nil {
t.Fatalf("Expected request with context to fail: %s", err)
}
}
func TestContextRequestWithTimeoutCanceled(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
testContextRequestWithTimeoutCanceled(t, nc)
}
func TestOldContextRequestWithTimeoutCanceled(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc, err := nats.Connect(nats.DefaultURL, nats.UseOldRequestStyle())
if err != nil {
t.Fatalf("Failed to connect: %v", err)
}
defer nc.Close()
testContextRequestWithTimeoutCanceled(t, nc)
}
func testContextRequestWithCancel(t *testing.T, nc *nats.Conn) {
ctx, cancelCB := context.WithCancel(context.Background())
defer cancelCB() // should always be called, not discarded, to prevent context leak
// timer which cancels the context though can also be arbitrarily extended
expirationTimer := time.AfterFunc(100*time.Millisecond, func() {
cancelCB()
})
nc.Subscribe("slow", func(m *nats.Msg) {
// simulates latency into the client so that timeout is hit.
time.Sleep(40 * time.Millisecond)
nc.Publish(m.Reply, []byte("OK"))
})
nc.Subscribe("slower", func(m *nats.Msg) {
// we know this request will take longer so extend the timeout
expirationTimer.Reset(100 * time.Millisecond)
// slower reply which would have hit original timeout
time.Sleep(90 * time.Millisecond)
nc.Publish(m.Reply, []byte("Also OK"))
})
for i := 0; i < 2; i++ {
resp, err := nc.RequestWithContext(ctx, "slow", []byte(""))
if err != nil {
t.Fatalf("Expected request with context to not fail: %s", err)
}
got := string(resp.Data)
expected := "OK"
if got != expected {
t.Errorf("Expected to receive %s, got: %s", expected, got)
}
}
// A third request with latency would make the context
// get canceled, but these reset the timer so deadline
// gets extended:
for i := 0; i < 10; i++ {
resp, err := nc.RequestWithContext(ctx, "slower", []byte(""))
if err != nil {
t.Fatalf("Expected request with context to not fail: %s", err)
}
got := string(resp.Data)
expected := "Also OK"
if got != expected {
t.Errorf("Expected to receive %s, got: %s", expected, got)
}
}
// One more slow request will expire the timer and cause an error...
_, err := nc.RequestWithContext(ctx, "slow", []byte(""))
if err == nil {
t.Fatalf("Expected request with cancellation context to fail: %s", err)
}
// ...though reported error is "context canceled" from Context package,
// which is not a timeout error.
type timeoutError interface {
Timeout() bool
}
if _, ok := err.(timeoutError); ok {
t.Errorf("Expected to not have a timeout error")
}
expected := `context canceled`
if !strings.Contains(err.Error(), expected) {
t.Errorf("Expected %q error, got: %q", expected, err.Error())
}
}
func TestContextRequestWithCancel(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
testContextRequestWithCancel(t, nc)
}
func TestOldContextRequestWithCancel(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc, err := nats.Connect(nats.DefaultURL, nats.UseOldRequestStyle())
if err != nil {
t.Fatalf("Failed to connect: %v", err)
}
defer nc.Close()
testContextRequestWithCancel(t, nc)
}
func testContextRequestWithDeadline(t *testing.T, nc *nats.Conn) {
deadline := time.Now().Add(100 * time.Millisecond)
ctx, cancelCB := context.WithDeadline(context.Background(), deadline)
defer cancelCB() // should always be called, not discarded, to prevent context leak
nc.Subscribe("slow", func(m *nats.Msg) {
// simulates latency into the client so that timeout is hit.
time.Sleep(40 * time.Millisecond)
nc.Publish(m.Reply, []byte("OK"))
})
for i := 0; i < 2; i++ {
resp, err := nc.RequestWithContext(ctx, "slow", []byte(""))
if err != nil {
t.Fatalf("Expected request with context to not fail: %s", err)
}
got := string(resp.Data)
expected := "OK"
if got != expected {
t.Errorf("Expected to receive %s, got: %s", expected, got)
}
}
// A third request with latency would make the context
// reach the deadline.
_, err := nc.RequestWithContext(ctx, "slow", []byte(""))
if err == nil {
t.Fatalf("Expected request with context to reach deadline: %s", err)
}
// Reported error is "context deadline exceeded" from Context package,
// which implements net.Error Timeout interface.
type timeoutError interface {
Timeout() bool
}
timeoutErr, ok := err.(timeoutError)
if !ok || !timeoutErr.Timeout() {
t.Errorf("Expected to have a timeout error")
}
expected := `context deadline exceeded`
if !strings.Contains(err.Error(), expected) {
t.Errorf("Expected %q error, got: %q", expected, err.Error())
}
}
func TestContextRequestWithDeadline(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
testContextRequestWithDeadline(t, nc)
}
func TestOldContextRequestWithDeadline(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc, err := nats.Connect(nats.DefaultURL, nats.UseOldRequestStyle())
if err != nil {
t.Fatalf("Failed to connect: %v", err)
}
defer nc.Close()
testContextRequestWithDeadline(t, nc)
}
func TestContextSubNextMsgWithTimeout(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
ctx, cancelCB := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancelCB() // should always be called, not discarded, to prevent context leak
sub, err := nc.SubscribeSync("slow")
if err != nil {
t.Fatalf("Expected to be able to subscribe: %s", err)
}
for i := 0; i < 2; i++ {
err := nc.Publish("slow", []byte("OK"))
if err != nil {
t.Fatalf("Expected publish to not fail: %s", err)
}
// Enough time to get a couple of messages
time.Sleep(40 * time.Millisecond)
msg, err := sub.NextMsgWithContext(ctx)
if err != nil {
t.Fatalf("Expected to receive message: %s", err)
}
got := string(msg.Data)
expected := "OK"
if got != expected {
t.Errorf("Expected to receive %s, got: %s", expected, got)
}
}
// Third message will fail because the context will be canceled by now
_, err = sub.NextMsgWithContext(ctx)
if err == nil {
t.Fatalf("Expected to fail receiving a message: %s", err)
}
// Reported error is "context deadline exceeded" from Context package,
// which implements net.Error Timeout interface.
type timeoutError interface {
Timeout() bool
}
timeoutErr, ok := err.(timeoutError)
if !ok || !timeoutErr.Timeout() {
t.Errorf("Expected to have a timeout error")
}
expected := `context deadline exceeded`
if !strings.Contains(err.Error(), expected) {
t.Errorf("Expected %q error, got: %q", expected, err.Error())
}
}
func TestContextSubNextMsgWithTimeoutCanceled(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
ctx, cancelCB := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancelCB() // should always be called, not discarded, to prevent context leak
sub, err := nc.SubscribeSync("fast")
if err != nil {
t.Fatalf("Expected to be able to subscribe: %s", err)
}
for i := 0; i < 2; i++ {
err := nc.Publish("fast", []byte("OK"))
if err != nil {
t.Fatalf("Expected publish to not fail: %s", err)
}
// Enough time to get a couple of messages
time.Sleep(40 * time.Millisecond)
msg, err := sub.NextMsgWithContext(ctx)
if err != nil {
t.Fatalf("Expected to receive message: %s", err)
}
got := string(msg.Data)
expected := "OK"
if got != expected {
t.Errorf("Expected to receive %s, got: %s", expected, got)
}
}
// Cancel the context already so that rest of NextMsg calls fail.
cancelCB()
_, err = sub.NextMsgWithContext(ctx)
if err == nil {
t.Fatalf("Expected request with timeout context to fail: %s", err)
}
// Reported error is "context canceled" from Context package,
// which is not a timeout error.
type timeoutError interface {
Timeout() bool
}
if _, ok := err.(timeoutError); ok {
t.Errorf("Expected to not have a timeout error")
}
expected := `context canceled`
if !strings.Contains(err.Error(), expected) {
t.Errorf("Expected %q error, got: %q", expected, err.Error())
}
}
func TestContextSubNextMsgWithCancel(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
ctx, cancelCB := context.WithCancel(context.Background())
defer cancelCB() // should always be called, not discarded, to prevent context leak
// timer which cancels the context though can also be arbitrarily extended
time.AfterFunc(100*time.Millisecond, func() {
cancelCB()
})
sub1, err := nc.SubscribeSync("foo")
if err != nil {
t.Fatalf("Expected to be able to subscribe: %s", err)
}
sub2, err := nc.SubscribeSync("bar")
if err != nil {
t.Fatalf("Expected to be able to subscribe: %s", err)
}
for i := 0; i < 2; i++ {
err := nc.Publish("foo", []byte("OK"))
if err != nil {
t.Fatalf("Expected publish to not fail: %s", err)
}
resp, err := sub1.NextMsgWithContext(ctx)
if err != nil {
t.Fatalf("Expected request with context to not fail: %s", err)
}
got := string(resp.Data)
expected := "OK"
if got != expected {
t.Errorf("Expected to receive %s, got: %s", expected, got)
}
}
err = nc.Publish("bar", []byte("Also OK"))
if err != nil {
t.Fatalf("Expected publish to not fail: %s", err)
}
resp, err := sub2.NextMsgWithContext(ctx)
if err != nil {
t.Fatalf("Expected request with context to not fail: %s", err)
}
got := string(resp.Data)
expected := "Also OK"
if got != expected {
t.Errorf("Expected to receive %s, got: %s", expected, got)
}
// We do not have another message pending so timer will
// cancel the context.
_, err = sub2.NextMsgWithContext(ctx)
if err == nil {
t.Fatalf("Expected request with context to fail: %s", err)
}
// Reported error is "context canceled" from Context package,
// which is not a timeout error.
type timeoutError interface {
Timeout() bool
}
if _, ok := err.(timeoutError); ok {
t.Errorf("Expected to not have a timeout error")
}
expected = `context canceled`
if !strings.Contains(err.Error(), expected) {
t.Errorf("Expected %q error, got: %q", expected, err.Error())
}
}
func TestContextSubNextMsgWithDeadline(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
deadline := time.Now().Add(100 * time.Millisecond)
ctx, cancelCB := context.WithDeadline(context.Background(), deadline)
defer cancelCB() // should always be called, not discarded, to prevent context leak
sub, err := nc.SubscribeSync("slow")
if err != nil {
t.Fatalf("Expected to be able to subscribe: %s", err)
}
for i := 0; i < 2; i++ {
err := nc.Publish("slow", []byte("OK"))
if err != nil {
t.Fatalf("Expected publish to not fail: %s", err)
}
// Enough time to get a couple of messages
time.Sleep(40 * time.Millisecond)
msg, err := sub.NextMsgWithContext(ctx)
if err != nil {
t.Fatalf("Expected to receive message: %s", err)
}
got := string(msg.Data)
expected := "OK"
if got != expected {
t.Errorf("Expected to receive %s, got: %s", expected, got)
}
}
// Third message will fail because the context will be canceled by now
_, err = sub.NextMsgWithContext(ctx)
if err == nil {
t.Fatalf("Expected to fail receiving a message: %s", err)
}
// Reported error is "context deadline exceeded" from Context package,
// which implements net.Error Timeout interface.
type timeoutError interface {
Timeout() bool
}
timeoutErr, ok := err.(timeoutError)
if !ok || !timeoutErr.Timeout() {
t.Errorf("Expected to have a timeout error")
}
expected := `context deadline exceeded`
if !strings.Contains(err.Error(), expected) {
t.Errorf("Expected %q error, got: %q", expected, err.Error())
}
}
func TestContextEncodedRequestWithTimeout(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
c, err := nats.NewEncodedConn(nc, nats.JSON_ENCODER)
if err != nil {
t.Fatalf("Unable to create encoded connection: %v", err)
}
defer c.Close()
deadline := time.Now().Add(100 * time.Millisecond)
ctx, cancelCB := context.WithDeadline(context.Background(), deadline)
defer cancelCB() // should always be called, not discarded, to prevent context leak
type request struct {
Message string `json:"message"`
}
type response struct {
Code int `json:"code"`
}
c.Subscribe("slow", func(_, reply string, req *request) {
got := req.Message
expected := "Hello"
if got != expected {
t.Errorf("Expected to receive request with %q, got %q", got, expected)
}
// simulates latency into the client so that timeout is hit.
time.Sleep(40 * time.Millisecond)
c.Publish(reply, &response{Code: 200})
})
for i := 0; i < 2; i++ {
req := &request{Message: "Hello"}
resp := &response{}
err := c.RequestWithContext(ctx, "slow", req, resp)
if err != nil {
t.Fatalf("Expected encoded request with context to not fail: %s", err)
}
got := resp.Code
expected := 200
if got != expected {
t.Errorf("Expected to receive %v, got: %v", expected, got)
}
}
// A third request with latency would make the context
// reach the deadline.
req := &request{Message: "Hello"}
resp := &response{}
err = c.RequestWithContext(ctx, "slow", req, resp)
if err == nil {
t.Fatalf("Expected request with context to reach deadline: %s", err)
}
// Reported error is "context deadline exceeded" from Context package,
// which implements net.Error Timeout interface.
type timeoutError interface {
Timeout() bool
}
timeoutErr, ok := err.(timeoutError)
if !ok || !timeoutErr.Timeout() {
t.Errorf("Expected to have a timeout error")
}
expected := `context deadline exceeded`
if !strings.Contains(err.Error(), expected) {
t.Errorf("Expected %q error, got: %q", expected, err.Error())
}
}
func TestContextEncodedRequestWithTimeoutCanceled(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
c, err := nats.NewEncodedConn(nc, nats.JSON_ENCODER)
if err != nil {
t.Fatalf("Unable to create encoded connection: %v", err)
}
defer c.Close()
ctx, cancelCB := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancelCB() // should always be called, not discarded, to prevent context leak
type request struct {
Message string `json:"message"`
}
type response struct {
Code int `json:"code"`
}
c.Subscribe("fast", func(_, reply string, req *request) {
got := req.Message
expected := "Hello"
if got != expected {
t.Errorf("Expected to receive request with %q, got %q", got, expected)
}
// simulates latency into the client so that timeout is hit.
time.Sleep(40 * time.Millisecond)
c.Publish(reply, &response{Code: 200})
})
// Fast request should not fail
req := &request{Message: "Hello"}
resp := &response{}
c.RequestWithContext(ctx, "fast", req, resp)
expectedCode := 200
if resp.Code != expectedCode {
t.Errorf("Expected to receive %d, got: %d", expectedCode, resp.Code)
}
// Cancel the context already so that rest of requests fail.
cancelCB()
err = c.RequestWithContext(ctx, "fast", req, resp)
if err == nil {
t.Fatalf("Expected request with timeout context to fail: %s", err)
}
// Reported error is "context canceled" from Context package,
// which is not a timeout error.
type timeoutError interface {
Timeout() bool
}
if _, ok := err.(timeoutError); ok {
t.Errorf("Expected to not have a timeout error")
}
expected := `context canceled`
if !strings.Contains(err.Error(), expected) {
t.Errorf("Expected %q error, got: %q", expected, err.Error())
}
// 2nd request should fail again even if fast because context has already been canceled
err = c.RequestWithContext(ctx, "fast", req, resp)
if err == nil {
t.Fatalf("Expected request with timeout context to fail: %s", err)
}
}
func TestContextEncodedRequestWithCancel(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
c, err := nats.NewEncodedConn(nc, nats.JSON_ENCODER)
if err != nil {
t.Fatalf("Unable to create encoded connection: %v", err)
}
defer c.Close()
ctx, cancelCB := context.WithCancel(context.Background())
defer cancelCB() // should always be called, not discarded, to prevent context leak
// timer which cancels the context though can also be arbitrarily extended
expirationTimer := time.AfterFunc(100*time.Millisecond, func() {
cancelCB()
})
type request struct {
Message string `json:"message"`
}
type response struct {
Code int `json:"code"`
}
c.Subscribe("slow", func(_, reply string, req *request) {
got := req.Message
expected := "Hello"
if got != expected {
t.Errorf("Expected to receive request with %q, got %q", got, expected)
}
// simulates latency into the client so that timeout is hit.
time.Sleep(40 * time.Millisecond)
c.Publish(reply, &response{Code: 200})
})
c.Subscribe("slower", func(_, reply string, req *request) {
got := req.Message
expected := "World"
if got != expected {
t.Errorf("Expected to receive request with %q, got %q", got, expected)
}
// we know this request will take longer so extend the timeout
expirationTimer.Reset(100 * time.Millisecond)
// slower reply which would have hit original timeout
time.Sleep(90 * time.Millisecond)
c.Publish(reply, &response{Code: 200})
})
for i := 0; i < 2; i++ {
req := &request{Message: "Hello"}
resp := &response{}
err := c.RequestWithContext(ctx, "slow", req, resp)
if err != nil {
t.Fatalf("Expected encoded request with context to not fail: %s", err)
}
got := resp.Code
expected := 200
if got != expected {
t.Errorf("Expected to receive %v, got: %v", expected, got)
}
}
// A third request with latency would make the context
// get canceled, but these reset the timer so deadline
// gets extended:
for i := 0; i < 10; i++ {
req := &request{Message: "World"}
resp := &response{}
err := c.RequestWithContext(ctx, "slower", req, resp)
if err != nil {
t.Fatalf("Expected request with context to not fail: %s", err)
}
got := resp.Code
expected := 200
if got != expected {
t.Errorf("Expected to receive %d, got: %d", expected, got)
}
}
req := &request{Message: "Hello"}
resp := &response{}
// One more slow request will expire the timer and cause an error...
err = c.RequestWithContext(ctx, "slow", req, resp)
if err == nil {
t.Fatalf("Expected request with cancellation context to fail: %s", err)
}
// ...though reported error is "context canceled" from Context package,
// which is not a timeout error.
type timeoutError interface {
Timeout() bool
}
if _, ok := err.(timeoutError); ok {
t.Errorf("Expected to not have a timeout error")
}
expected := `context canceled`
if !strings.Contains(err.Error(), expected) {
t.Errorf("Expected %q error, got: %q", expected, err.Error())
}
}
func TestContextEncodedRequestWithDeadline(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
c, err := nats.NewEncodedConn(nc, nats.JSON_ENCODER)
if err != nil {
t.Fatalf("Unable to create encoded connection: %v", err)
}
defer c.Close()
deadline := time.Now().Add(100 * time.Millisecond)
ctx, cancelCB := context.WithDeadline(context.Background(), deadline)
defer cancelCB() // should always be called, not discarded, to prevent context leak
type request struct {
Message string `json:"message"`
}
type response struct {
Code int `json:"code"`
}
c.Subscribe("slow", func(_, reply string, req *request) {
got := req.Message
expected := "Hello"
if got != expected {
t.Errorf("Expected to receive request with %q, got %q", got, expected)
}
// simulates latency into the client so that timeout is hit.
time.Sleep(40 * time.Millisecond)
c.Publish(reply, &response{Code: 200})
})
for i := 0; i < 2; i++ {
req := &request{Message: "Hello"}
resp := &response{}
err := c.RequestWithContext(ctx, "slow", req, resp)
if err != nil {
t.Fatalf("Expected encoded request with context to not fail: %s", err)
}
got := resp.Code
expected := 200
if got != expected {
t.Errorf("Expected to receive %v, got: %v", expected, got)
}
}
// A third request with latency would make the context
// reach the deadline.
req := &request{Message: "Hello"}
resp := &response{}
err = c.RequestWithContext(ctx, "slow", req, resp)
if err == nil {
t.Fatalf("Expected request with context to reach deadline: %s", err)
}
// Reported error is "context deadline exceeded" from Context package,
// which implements net.Error Timeout interface.
type timeoutError interface {
Timeout() bool
}
timeoutErr, ok := err.(timeoutError)
if !ok || !timeoutErr.Timeout() {
t.Errorf("Expected to have a timeout error")
}
expected := `context deadline exceeded`
if !strings.Contains(err.Error(), expected) {
t.Errorf("Expected %q error, got: %q", expected, err.Error())
}
}
func TestContextRequestConnClosed(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
ctx, cancelCB := context.WithCancel(context.Background())
defer cancelCB()
time.AfterFunc(100*time.Millisecond, func() {
cancelCB()
})
nc.Close()
_, err := nc.RequestWithContext(ctx, "foo", []byte(""))
if err == nil {
t.Fatalf("Expected request to fail with error")
}
if err != nats.ErrConnectionClosed {
t.Errorf("Expected request to fail with connection closed error: %s", err)
}
}
func TestContextBadSubscription(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
ctx, cancelCB := context.WithCancel(context.Background())
defer cancelCB()
time.AfterFunc(100*time.Millisecond, func() {
cancelCB()
})
sub, err := nc.Subscribe("foo", func(_ *nats.Msg) {})
if err != nil {
t.Fatalf("Expected to be able to subscribe: %s", err)
}
err = sub.Unsubscribe()
if err != nil {
t.Fatalf("Expected to be able to unsubscribe: %s", err)
}
_, err = sub.NextMsgWithContext(ctx)
if err == nil {
t.Fatalf("Expected to fail getting next message with context")
}
if err != nats.ErrBadSubscription {
t.Errorf("Expected request to fail with connection closed error: %s", err)
}
}
func TestContextInvalid(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
c, err := nats.NewEncodedConn(nc, nats.JSON_ENCODER)
if err != nil {
t.Fatalf("Unable to create encoded connection: %v", err)
}
defer c.Close()
_, err = nc.RequestWithContext(nil, "foo", []byte(""))
if err == nil {
t.Fatalf("Expected request to fail with error")
}
if err != nats.ErrInvalidContext {
t.Errorf("Expected request to fail with connection closed error: %s", err)
}
sub, err := nc.Subscribe("foo", func(_ *nats.Msg) {})
if err != nil {
t.Fatalf("Expected to be able to subscribe: %s", err)
}
_, err = sub.NextMsgWithContext(nil)
if err == nil {
t.Fatalf("Expected request to fail with error")
}
if err != nats.ErrInvalidContext {
t.Errorf("Expected request to fail with connection closed error: %s", err)
}
type request struct {
Message string `json:"message"`
}
type response struct {
Code int `json:"code"`
}
req := &request{Message: "Hello"}
resp := &response{}
err = c.RequestWithContext(nil, "slow", req, resp)
if err == nil {
t.Fatalf("Expected request with context to reach deadline: %s", err)
}
if err != nats.ErrInvalidContext {
t.Errorf("Expected request to fail with connection closed error: %s", err)
}
}

View File

@ -0,0 +1,448 @@
// Copyright 2012-2017 Apcera Inc. All rights reserved.
package test
import (
"bytes"
"testing"
"time"
"github.com/nats-io/go-nats"
"github.com/nats-io/go-nats/encoders/builtin"
)
const TEST_PORT = 8168
func NewDefaultEConn(t *testing.T) *nats.EncodedConn {
ec, err := nats.NewEncodedConn(NewConnection(t, TEST_PORT), nats.DEFAULT_ENCODER)
if err != nil {
t.Fatalf("Failed to create an encoded connection: %v\n", err)
}
return ec
}
func TestEncBuiltinConstructorErrs(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
c := NewConnection(t, TEST_PORT)
_, err := nats.NewEncodedConn(nil, "default")
if err == nil {
t.Fatal("Expected err for nil connection")
}
_, err = nats.NewEncodedConn(c, "foo22")
if err == nil {
t.Fatal("Expected err for bad encoder")
}
c.Close()
_, err = nats.NewEncodedConn(c, "default")
if err == nil {
t.Fatal("Expected err for closed connection")
}
}
func TestEncBuiltinMarshalString(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewDefaultEConn(t)
defer ec.Close()
ch := make(chan bool)
testString := "Hello World!"
ec.Subscribe("enc_string", func(s string) {
if s != testString {
t.Fatalf("Received test string of '%s', wanted '%s'\n", s, testString)
}
ch <- true
})
ec.Publish("enc_string", testString)
if e := Wait(ch); e != nil {
if ec.LastError() != nil {
e = ec.LastError()
}
t.Fatalf("Did not receive the message: %s", e)
}
}
func TestEncBuiltinMarshalBytes(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewDefaultEConn(t)
defer ec.Close()
ch := make(chan bool)
testBytes := []byte("Hello World!")
ec.Subscribe("enc_bytes", func(b []byte) {
if !bytes.Equal(b, testBytes) {
t.Fatalf("Received test bytes of '%s', wanted '%s'\n", b, testBytes)
}
ch <- true
})
ec.Publish("enc_bytes", testBytes)
if e := Wait(ch); e != nil {
if ec.LastError() != nil {
e = ec.LastError()
}
t.Fatalf("Did not receive the message: %s", e)
}
}
func TestEncBuiltinMarshalInt(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewDefaultEConn(t)
defer ec.Close()
ch := make(chan bool)
testN := 22
ec.Subscribe("enc_int", func(n int) {
if n != testN {
t.Fatalf("Received test number of %d, wanted %d\n", n, testN)
}
ch <- true
})
ec.Publish("enc_int", testN)
if e := Wait(ch); e != nil {
if ec.LastError() != nil {
e = ec.LastError()
}
t.Fatalf("Did not receive the message: %s", e)
}
}
func TestEncBuiltinMarshalInt32(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewDefaultEConn(t)
defer ec.Close()
ch := make(chan bool)
testN := 22
ec.Subscribe("enc_int", func(n int32) {
if n != int32(testN) {
t.Fatalf("Received test number of %d, wanted %d\n", n, testN)
}
ch <- true
})
ec.Publish("enc_int", testN)
if e := Wait(ch); e != nil {
if ec.LastError() != nil {
e = ec.LastError()
}
t.Fatalf("Did not receive the message: %s", e)
}
}
func TestEncBuiltinMarshalInt64(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewDefaultEConn(t)
defer ec.Close()
ch := make(chan bool)
testN := 22
ec.Subscribe("enc_int", func(n int64) {
if n != int64(testN) {
t.Fatalf("Received test number of %d, wanted %d\n", n, testN)
}
ch <- true
})
ec.Publish("enc_int", testN)
if e := Wait(ch); e != nil {
if ec.LastError() != nil {
e = ec.LastError()
}
t.Fatalf("Did not receive the message: %s", e)
}
}
func TestEncBuiltinMarshalFloat32(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewDefaultEConn(t)
defer ec.Close()
ch := make(chan bool)
testN := float32(22)
ec.Subscribe("enc_float", func(n float32) {
if n != testN {
t.Fatalf("Received test number of %f, wanted %f\n", n, testN)
}
ch <- true
})
ec.Publish("enc_float", testN)
if e := Wait(ch); e != nil {
if ec.LastError() != nil {
e = ec.LastError()
}
t.Fatalf("Did not receive the message: %s", e)
}
}
func TestEncBuiltinMarshalFloat64(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewDefaultEConn(t)
defer ec.Close()
ch := make(chan bool)
testN := float64(22.22)
ec.Subscribe("enc_float", func(n float64) {
if n != testN {
t.Fatalf("Received test number of %f, wanted %f\n", n, testN)
}
ch <- true
})
ec.Publish("enc_float", testN)
if e := Wait(ch); e != nil {
if ec.LastError() != nil {
e = ec.LastError()
}
t.Fatalf("Did not receive the message: %s", e)
}
}
func TestEncBuiltinMarshalBool(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewDefaultEConn(t)
defer ec.Close()
ch := make(chan bool)
expected := make(chan bool, 1)
ec.Subscribe("enc_bool", func(b bool) {
val := <-expected
if b != val {
t.Fatal("Boolean values did not match")
}
ch <- true
})
expected <- false
ec.Publish("enc_bool", false)
if e := Wait(ch); e != nil {
if ec.LastError() != nil {
e = ec.LastError()
}
t.Fatalf("Did not receive the message: %s", e)
}
expected <- true
ec.Publish("enc_bool", true)
if e := Wait(ch); e != nil {
if ec.LastError() != nil {
e = ec.LastError()
}
t.Fatalf("Did not receive the message: %s", e)
}
}
func TestEncBuiltinExtendedSubscribeCB(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewDefaultEConn(t)
defer ec.Close()
ch := make(chan bool)
testString := "Hello World!"
subject := "cb_args"
ec.Subscribe(subject, func(subj, s string) {
if s != testString {
t.Fatalf("Received test string of '%s', wanted '%s'\n", s, testString)
}
if subj != subject {
t.Fatalf("Received subject of '%s', wanted '%s'\n", subj, subject)
}
ch <- true
})
ec.Publish(subject, testString)
if e := Wait(ch); e != nil {
if ec.LastError() != nil {
e = ec.LastError()
}
t.Fatalf("Did not receive the message: %s", e)
}
}
func TestEncBuiltinExtendedSubscribeCB2(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewDefaultEConn(t)
defer ec.Close()
ch := make(chan bool)
testString := "Hello World!"
oSubj := "cb_args"
oReply := "foobar"
ec.Subscribe(oSubj, func(subj, reply, s string) {
if s != testString {
t.Fatalf("Received test string of '%s', wanted '%s'\n", s, testString)
}
if subj != oSubj {
t.Fatalf("Received subject of '%s', wanted '%s'\n", subj, oSubj)
}
if reply != oReply {
t.Fatalf("Received reply of '%s', wanted '%s'\n", reply, oReply)
}
ch <- true
})
ec.PublishRequest(oSubj, oReply, testString)
if e := Wait(ch); e != nil {
if ec.LastError() != nil {
e = ec.LastError()
}
t.Fatalf("Did not receive the message: %s", e)
}
}
func TestEncBuiltinRawMsgSubscribeCB(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewDefaultEConn(t)
defer ec.Close()
ch := make(chan bool)
testString := "Hello World!"
oSubj := "cb_args"
oReply := "foobar"
ec.Subscribe(oSubj, func(m *nats.Msg) {
s := string(m.Data)
if s != testString {
t.Fatalf("Received test string of '%s', wanted '%s'\n", s, testString)
}
if m.Subject != oSubj {
t.Fatalf("Received subject of '%s', wanted '%s'\n", m.Subject, oSubj)
}
if m.Reply != oReply {
t.Fatalf("Received reply of '%s', wanted '%s'\n", m.Reply, oReply)
}
ch <- true
})
ec.PublishRequest(oSubj, oReply, testString)
if e := Wait(ch); e != nil {
if ec.LastError() != nil {
e = ec.LastError()
}
t.Fatalf("Did not receive the message: %s", e)
}
}
func TestEncBuiltinRequest(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewDefaultEConn(t)
defer ec.Close()
expectedResp := "I can help!"
ec.Subscribe("help", func(subj, reply, req string) {
ec.Publish(reply, expectedResp)
})
var resp string
err := ec.Request("help", "help me", &resp, 1*time.Second)
if err != nil {
t.Fatalf("Failed at receiving proper response: %v\n", err)
}
if resp != expectedResp {
t.Fatalf("Received reply '%s', wanted '%s'\n", resp, expectedResp)
}
}
func TestEncBuiltinRequestReceivesMsg(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewDefaultEConn(t)
defer ec.Close()
expectedResp := "I can help!"
ec.Subscribe("help", func(subj, reply, req string) {
ec.Publish(reply, expectedResp)
})
var resp nats.Msg
err := ec.Request("help", "help me", &resp, 1*time.Second)
if err != nil {
t.Fatalf("Failed at receiving proper response: %v\n", err)
}
if string(resp.Data) != expectedResp {
t.Fatalf("Received reply '%s', wanted '%s'\n", string(resp.Data), expectedResp)
}
}
func TestEncBuiltinAsyncMarshalErr(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewDefaultEConn(t)
defer ec.Close()
ch := make(chan bool)
testString := "Hello World!"
subject := "err_marshall"
ec.Subscribe(subject, func(subj, num int) {
// This will never get called.
})
ec.Conn.Opts.AsyncErrorCB = func(c *nats.Conn, s *nats.Subscription, err error) {
ch <- true
}
ec.Publish(subject, testString)
if e := Wait(ch); e != nil {
t.Fatalf("Did not receive the message: %s", e)
}
}
func TestEncBuiltinEncodeNil(t *testing.T) {
de := &builtin.DefaultEncoder{}
_, err := de.Encode("foo", nil)
if err != nil {
t.Fatalf("Expected no error encoding nil: %v", err)
}
}
func TestEncBuiltinDecodeDefault(t *testing.T) {
de := &builtin.DefaultEncoder{}
b, err := de.Encode("foo", 22)
if err != nil {
t.Fatalf("Expected no error encoding number: %v", err)
}
var c chan bool
err = de.Decode("foo", b, &c)
if err == nil {
t.Fatalf("Expected an error decoding")
}
}

View File

@ -0,0 +1,128 @@
// Copyright 2012-2017 Apcera Inc. All rights reserved.
package test
import (
"reflect"
"testing"
"github.com/nats-io/go-nats"
)
func NewGobEncodedConn(tl TestLogger) *nats.EncodedConn {
ec, err := nats.NewEncodedConn(NewConnection(tl, TEST_PORT), nats.GOB_ENCODER)
if err != nil {
tl.Fatalf("Failed to create an encoded connection: %v\n", err)
}
return ec
}
func TestEncBuiltinGobMarshalString(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewGobEncodedConn(t)
defer ec.Close()
ch := make(chan bool)
testString := "Hello World!"
ec.Subscribe("gob_string", func(s string) {
if s != testString {
t.Fatalf("Received test string of '%s', wanted '%s'\n", s, testString)
}
ch <- true
})
ec.Publish("gob_string", testString)
if e := Wait(ch); e != nil {
t.Fatal("Did not receive the message")
}
}
func TestEncBuiltinGobMarshalInt(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewGobEncodedConn(t)
defer ec.Close()
ch := make(chan bool)
testN := 22
ec.Subscribe("gob_int", func(n int) {
if n != testN {
t.Fatalf("Received test int of '%d', wanted '%d'\n", n, testN)
}
ch <- true
})
ec.Publish("gob_int", testN)
if e := Wait(ch); e != nil {
t.Fatal("Did not receive the message")
}
}
func TestEncBuiltinGobMarshalStruct(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewGobEncodedConn(t)
defer ec.Close()
ch := make(chan bool)
me := &person{Name: "derek", Age: 22, Address: "140 New Montgomery St"}
me.Children = make(map[string]*person)
me.Children["sam"] = &person{Name: "sam", Age: 19, Address: "140 New Montgomery St"}
me.Children["meg"] = &person{Name: "meg", Age: 17, Address: "140 New Montgomery St"}
me.Assets = make(map[string]uint)
me.Assets["house"] = 1000
me.Assets["car"] = 100
ec.Subscribe("gob_struct", func(p *person) {
if !reflect.DeepEqual(p, me) {
t.Fatalf("Did not receive the correct struct response")
}
ch <- true
})
ec.Publish("gob_struct", me)
if e := Wait(ch); e != nil {
t.Fatal("Did not receive the message")
}
}
func BenchmarkPublishGobStruct(b *testing.B) {
// stop benchmark for set-up
b.StopTimer()
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewGobEncodedConn(b)
defer ec.Close()
ch := make(chan bool)
me := &person{Name: "derek", Age: 22, Address: "140 New Montgomery St"}
me.Children = make(map[string]*person)
me.Children["sam"] = &person{Name: "sam", Age: 19, Address: "140 New Montgomery St"}
me.Children["meg"] = &person{Name: "meg", Age: 17, Address: "140 New Montgomery St"}
ec.Subscribe("gob_struct", func(p *person) {
if !reflect.DeepEqual(p, me) {
b.Fatalf("Did not receive the correct struct response")
}
ch <- true
})
// resume benchmark
b.StartTimer()
for n := 0; n < b.N; n++ {
ec.Publish("gob_struct", me)
if e := Wait(ch); e != nil {
b.Fatal("Did not receive the message")
}
}
}

View File

@ -0,0 +1,93 @@
// Copyright 2015 Apcera Inc. All rights reserved.
package test
import (
"errors"
"fmt"
"time"
"github.com/nats-io/gnatsd/server"
"github.com/nats-io/go-nats"
gnatsd "github.com/nats-io/gnatsd/test"
)
// So that we can pass tests and benchmarks...
type tLogger interface {
Fatalf(format string, args ...interface{})
Errorf(format string, args ...interface{})
}
// TestLogger
type TestLogger tLogger
// Dumb wait program to sync on callbacks, etc... Will timeout
func Wait(ch chan bool) error {
return WaitTime(ch, 5*time.Second)
}
// Wait for a chan with a timeout.
func WaitTime(ch chan bool, timeout time.Duration) error {
select {
case <-ch:
return nil
case <-time.After(timeout):
}
return errors.New("timeout")
}
////////////////////////////////////////////////////////////////////////////////
// Creating client connections
////////////////////////////////////////////////////////////////////////////////
// NewDefaultConnection
func NewDefaultConnection(t tLogger) *nats.Conn {
return NewConnection(t, nats.DefaultPort)
}
// NewConnection forms connection on a given port.
func NewConnection(t tLogger, port int) *nats.Conn {
url := fmt.Sprintf("nats://localhost:%d", port)
nc, err := nats.Connect(url)
if err != nil {
t.Fatalf("Failed to create default connection: %v\n", err)
return nil
}
return nc
}
// NewEConn
func NewEConn(t tLogger) *nats.EncodedConn {
ec, err := nats.NewEncodedConn(NewDefaultConnection(t), nats.DEFAULT_ENCODER)
if err != nil {
t.Fatalf("Failed to create an encoded connection: %v\n", err)
}
return ec
}
////////////////////////////////////////////////////////////////////////////////
// Running gnatsd server in separate Go routines
////////////////////////////////////////////////////////////////////////////////
// RunDefaultServer will run a server on the default port.
func RunDefaultServer() *server.Server {
return RunServerOnPort(nats.DefaultPort)
}
// RunServerOnPort will run a server on the given port.
func RunServerOnPort(port int) *server.Server {
opts := gnatsd.DefaultTestOptions
opts.Port = port
return RunServerWithOptions(opts)
}
// RunServerWithOptions will run a server with the given options.
func RunServerWithOptions(opts server.Options) *server.Server {
return gnatsd.RunServer(&opts)
}
// RunServerWithConfig will run a server with the given configuration file.
func RunServerWithConfig(configFile string) (*server.Server, *server.Options) {
return gnatsd.RunServerWithConfig(configFile)
}

View File

@ -0,0 +1,209 @@
// Copyright 2012-2017 Apcera Inc. All rights reserved.
package test
import (
"reflect"
"testing"
"time"
"github.com/nats-io/go-nats"
"github.com/nats-io/go-nats/encoders/builtin"
)
func NewJsonEncodedConn(tl TestLogger) *nats.EncodedConn {
ec, err := nats.NewEncodedConn(NewConnection(tl, TEST_PORT), nats.JSON_ENCODER)
if err != nil {
tl.Fatalf("Failed to create an encoded connection: %v\n", err)
}
return ec
}
func TestEncBuiltinJsonMarshalString(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewJsonEncodedConn(t)
defer ec.Close()
ch := make(chan bool)
testString := "Hello World!"
ec.Subscribe("json_string", func(s string) {
if s != testString {
t.Fatalf("Received test string of '%s', wanted '%s'\n", s, testString)
}
ch <- true
})
ec.Publish("json_string", testString)
if e := Wait(ch); e != nil {
t.Fatal("Did not receive the message")
}
}
func TestEncBuiltinJsonMarshalInt(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewJsonEncodedConn(t)
defer ec.Close()
ch := make(chan bool)
testN := 22
ec.Subscribe("json_int", func(n int) {
if n != testN {
t.Fatalf("Received test int of '%d', wanted '%d'\n", n, testN)
}
ch <- true
})
ec.Publish("json_int", testN)
if e := Wait(ch); e != nil {
t.Fatal("Did not receive the message")
}
}
type person struct {
Name string
Address string
Age int
Children map[string]*person
Assets map[string]uint
}
func TestEncBuiltinJsonMarshalStruct(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewJsonEncodedConn(t)
defer ec.Close()
ch := make(chan bool)
me := &person{Name: "derek", Age: 22, Address: "140 New Montgomery St"}
me.Children = make(map[string]*person)
me.Children["sam"] = &person{Name: "sam", Age: 19, Address: "140 New Montgomery St"}
me.Children["meg"] = &person{Name: "meg", Age: 17, Address: "140 New Montgomery St"}
me.Assets = make(map[string]uint)
me.Assets["house"] = 1000
me.Assets["car"] = 100
ec.Subscribe("json_struct", func(p *person) {
if !reflect.DeepEqual(p, me) {
t.Fatal("Did not receive the correct struct response")
}
ch <- true
})
ec.Publish("json_struct", me)
if e := Wait(ch); e != nil {
t.Fatal("Did not receive the message")
}
}
func BenchmarkJsonMarshalStruct(b *testing.B) {
me := &person{Name: "derek", Age: 22, Address: "140 New Montgomery St"}
me.Children = make(map[string]*person)
me.Children["sam"] = &person{Name: "sam", Age: 19, Address: "140 New Montgomery St"}
me.Children["meg"] = &person{Name: "meg", Age: 17, Address: "140 New Montgomery St"}
encoder := &builtin.JsonEncoder{}
for n := 0; n < b.N; n++ {
if _, err := encoder.Encode("protobuf_test", me); err != nil {
b.Fatal("Couldn't serialize object", err)
}
}
}
func BenchmarkPublishJsonStruct(b *testing.B) {
// stop benchmark for set-up
b.StopTimer()
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewJsonEncodedConn(b)
defer ec.Close()
ch := make(chan bool)
me := &person{Name: "derek", Age: 22, Address: "140 New Montgomery St"}
me.Children = make(map[string]*person)
me.Children["sam"] = &person{Name: "sam", Age: 19, Address: "140 New Montgomery St"}
me.Children["meg"] = &person{Name: "meg", Age: 17, Address: "140 New Montgomery St"}
ec.Subscribe("json_struct", func(p *person) {
if !reflect.DeepEqual(p, me) {
b.Fatalf("Did not receive the correct struct response")
}
ch <- true
})
// resume benchmark
b.StartTimer()
for n := 0; n < b.N; n++ {
ec.Publish("json_struct", me)
if e := Wait(ch); e != nil {
b.Fatal("Did not receive the message")
}
}
}
func TestEncBuiltinNotMarshableToJson(t *testing.T) {
je := &builtin.JsonEncoder{}
ch := make(chan bool)
_, err := je.Encode("foo", ch)
if err == nil {
t.Fatal("Expected an error when failing encoding")
}
}
func TestEncBuiltinFailedEncodedPublish(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewJsonEncodedConn(t)
defer ec.Close()
ch := make(chan bool)
err := ec.Publish("foo", ch)
if err == nil {
t.Fatal("Expected an error trying to publish a channel")
}
err = ec.PublishRequest("foo", "bar", ch)
if err == nil {
t.Fatal("Expected an error trying to publish a channel")
}
var cr chan bool
err = ec.Request("foo", ch, &cr, 1*time.Second)
if err == nil {
t.Fatal("Expected an error trying to publish a channel")
}
err = ec.LastError()
if err != nil {
t.Fatalf("Expected LastError to be nil: %q ", err)
}
}
func TestEncBuiltinDecodeConditionals(t *testing.T) {
je := &builtin.JsonEncoder{}
b, err := je.Encode("foo", 22)
if err != nil {
t.Fatalf("Expected no error when encoding, got %v\n", err)
}
var foo string
var bar []byte
err = je.Decode("foo", b, &foo)
if err != nil {
t.Fatalf("Expected no error when decoding, got %v\n", err)
}
err = je.Decode("foo", b, &bar)
if err != nil {
t.Fatalf("Expected no error when decoding, got %v\n", err)
}
}

14
gateway/vendor/github.com/nats-io/go-nats/test/main.go generated vendored Normal file
View File

@ -0,0 +1,14 @@
// Copyright 2017 Apcera Inc. All rights reserved.
package test
import (
"os"
"testing"
)
// TestMain runs all tests. Added since tests were moved to a separate package.
func TestMain(m *testing.M) {
// call flag.Parse() here if TestMain uses flags
os.Exit(m.Run())
}

View File

@ -0,0 +1,355 @@
package test
import (
"runtime"
"testing"
"time"
"github.com/nats-io/go-nats"
)
func TestBadChan(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
ec := NewEConn(t)
defer ec.Close()
if err := ec.BindSendChan("foo", "not a chan"); err == nil {
t.Fatalf("Expected an Error when sending a non-channel\n")
}
if _, err := ec.BindRecvChan("foo", "not a chan"); err == nil {
t.Fatalf("Expected an Error when sending a non-channel\n")
}
if err := ec.BindSendChan("foo", "not a chan"); err != nats.ErrChanArg {
t.Fatalf("Expected an ErrChanArg when sending a non-channel\n")
}
if _, err := ec.BindRecvChan("foo", "not a chan"); err != nats.ErrChanArg {
t.Fatalf("Expected an ErrChanArg when sending a non-channel\n")
}
}
func TestSimpleSendChan(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
ec := NewEConn(t)
defer ec.Close()
recv := make(chan bool)
numSent := int32(22)
ch := make(chan int32)
if err := ec.BindSendChan("foo", ch); err != nil {
t.Fatalf("Failed to bind to a send channel: %v\n", err)
}
ec.Subscribe("foo", func(num int32) {
if num != numSent {
t.Fatalf("Failed to receive correct value: %d vs %d\n", num, numSent)
}
recv <- true
})
// Send to 'foo'
ch <- numSent
if e := Wait(recv); e != nil {
if ec.LastError() != nil {
e = ec.LastError()
}
t.Fatalf("Did not receive the message: %s", e)
}
close(ch)
}
func TestFailedChannelSend(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
ec := NewEConn(t)
defer ec.Close()
nc := ec.Conn
ch := make(chan bool)
wch := make(chan bool)
nc.Opts.AsyncErrorCB = func(c *nats.Conn, s *nats.Subscription, e error) {
wch <- true
}
if err := ec.BindSendChan("foo", ch); err != nil {
t.Fatalf("Failed to bind to a receive channel: %v\n", err)
}
nc.Flush()
go func() {
time.Sleep(100 * time.Millisecond)
nc.Close()
}()
func() {
for {
select {
case ch <- true:
case <-wch:
return
case <-time.After(time.Second):
t.Fatal("Failed to get async error cb")
}
}
}()
ec = NewEConn(t)
defer ec.Close()
nc = ec.Conn
bch := make(chan []byte)
nc.Opts.AsyncErrorCB = func(c *nats.Conn, s *nats.Subscription, e error) {
wch <- true
}
if err := ec.BindSendChan("foo", bch); err != nil {
t.Fatalf("Failed to bind to a receive channel: %v\n", err)
}
buf := make([]byte, 2*1024*1024)
bch <- buf
if e := Wait(wch); e != nil {
t.Fatal("Failed to call async err handler")
}
}
func TestSimpleRecvChan(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
ec := NewEConn(t)
defer ec.Close()
numSent := int32(22)
ch := make(chan int32)
if _, err := ec.BindRecvChan("foo", ch); err != nil {
t.Fatalf("Failed to bind to a receive channel: %v\n", err)
}
ec.Publish("foo", numSent)
// Receive from 'foo'
select {
case num := <-ch:
if num != numSent {
t.Fatalf("Failed to receive correct value: %d vs %d\n", num, numSent)
}
case <-time.After(1 * time.Second):
t.Fatalf("Failed to receive a value, timed-out\n")
}
close(ch)
}
func TestQueueRecvChan(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
ec := NewEConn(t)
defer ec.Close()
numSent := int32(22)
ch := make(chan int32)
if _, err := ec.BindRecvQueueChan("foo", "bar", ch); err != nil {
t.Fatalf("Failed to bind to a queue receive channel: %v\n", err)
}
ec.Publish("foo", numSent)
// Receive from 'foo'
select {
case num := <-ch:
if num != numSent {
t.Fatalf("Failed to receive correct value: %d vs %d\n", num, numSent)
}
case <-time.After(1 * time.Second):
t.Fatalf("Failed to receive a value, timed-out\n")
}
close(ch)
}
func TestDecoderErrRecvChan(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
ec := NewEConn(t)
defer ec.Close()
nc := ec.Conn
wch := make(chan bool)
nc.Opts.AsyncErrorCB = func(c *nats.Conn, s *nats.Subscription, e error) {
wch <- true
}
ch := make(chan *int32)
if _, err := ec.BindRecvChan("foo", ch); err != nil {
t.Fatalf("Failed to bind to a send channel: %v\n", err)
}
ec.Publish("foo", "Hello World")
if e := Wait(wch); e != nil {
t.Fatal("Failed to call async err handler")
}
}
func TestRecvChanPanicOnClosedChan(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
ec := NewEConn(t)
defer ec.Close()
ch := make(chan int)
if _, err := ec.BindRecvChan("foo", ch); err != nil {
t.Fatalf("Failed to bind to a send channel: %v\n", err)
}
close(ch)
ec.Publish("foo", 22)
ec.Flush()
}
func TestRecvChanAsyncLeakGoRoutines(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
ec := NewEConn(t)
defer ec.Close()
// Call this to make sure that we have everything setup connection wise
ec.Flush()
before := runtime.NumGoroutine()
ch := make(chan int)
if _, err := ec.BindRecvChan("foo", ch); err != nil {
t.Fatalf("Failed to bind to a send channel: %v\n", err)
}
// Close the receive Channel
close(ch)
// The publish will trigger the close and shutdown of the Go routines
ec.Publish("foo", 22)
ec.Flush()
time.Sleep(100 * time.Millisecond)
delta := (runtime.NumGoroutine() - before)
if delta > 0 {
t.Fatalf("Leaked Go routine(s) : %d, closing channel should have closed them\n", delta)
}
}
func TestRecvChanLeakGoRoutines(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
ec := NewEConn(t)
defer ec.Close()
// Call this to make sure that we have everything setup connection wise
ec.Flush()
before := runtime.NumGoroutine()
ch := make(chan int)
sub, err := ec.BindRecvChan("foo", ch)
if err != nil {
t.Fatalf("Failed to bind to a send channel: %v\n", err)
}
sub.Unsubscribe()
// Sleep a bit to wait for the Go routine to exit.
time.Sleep(500 * time.Millisecond)
delta := (runtime.NumGoroutine() - before)
if delta > 0 {
t.Fatalf("Leaked Go routine(s) : %d, closing channel should have closed them\n", delta)
}
}
func TestRecvChanMultipleMessages(t *testing.T) {
// Make sure we can receive more than one message.
// In response to #25, which is a bug from fixing #22.
s := RunDefaultServer()
defer s.Shutdown()
ec := NewEConn(t)
defer ec.Close()
// Num to send, should == len of messages queued.
size := 10
ch := make(chan int, size)
if _, err := ec.BindRecvChan("foo", ch); err != nil {
t.Fatalf("Failed to bind to a send channel: %v\n", err)
}
for i := 0; i < size; i++ {
ec.Publish("foo", 22)
}
ec.Flush()
time.Sleep(10 * time.Millisecond)
if lch := len(ch); lch != size {
t.Fatalf("Expected %d messages queued, got %d.", size, lch)
}
}
func BenchmarkPublishSpeedViaChan(b *testing.B) {
b.StopTimer()
s := RunDefaultServer()
defer s.Shutdown()
nc, err := nats.Connect(nats.DefaultURL)
if err != nil {
b.Fatalf("Could not connect: %v\n", err)
}
ec, err := nats.NewEncodedConn(nc, nats.DEFAULT_ENCODER)
if err != nil {
b.Fatalf("Failed creating encoded connection: %v\n", err)
}
defer ec.Close()
ch := make(chan int32, 1024)
if err := ec.BindSendChan("foo", ch); err != nil {
b.Fatalf("Failed to bind to a send channel: %v\n", err)
}
b.StartTimer()
num := int32(22)
for i := 0; i < b.N; i++ {
ch <- num
}
// Make sure they are all processed.
nc.Flush()
b.StopTimer()
}

View File

@ -0,0 +1,126 @@
package test
import (
"reflect"
"testing"
"time"
"github.com/nats-io/go-nats"
"github.com/nats-io/go-nats/encoders/protobuf"
pb "github.com/nats-io/go-nats/encoders/protobuf/testdata"
)
func NewProtoEncodedConn(tl TestLogger) *nats.EncodedConn {
ec, err := nats.NewEncodedConn(NewConnection(tl, TEST_PORT), protobuf.PROTOBUF_ENCODER)
if err != nil {
tl.Fatalf("Failed to create an encoded connection: %v\n", err)
}
return ec
}
func TestEncProtoMarshalStruct(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewProtoEncodedConn(t)
defer ec.Close()
ch := make(chan bool)
me := &pb.Person{Name: "derek", Age: 22, Address: "140 New Montgomery St"}
me.Children = make(map[string]*pb.Person)
me.Children["sam"] = &pb.Person{Name: "sam", Age: 19, Address: "140 New Montgomery St"}
me.Children["meg"] = &pb.Person{Name: "meg", Age: 17, Address: "140 New Montgomery St"}
ec.Subscribe("protobuf_test", func(p *pb.Person) {
if !reflect.DeepEqual(p, me) {
t.Fatal("Did not receive the correct protobuf response")
}
ch <- true
})
ec.Publish("protobuf_test", me)
if e := Wait(ch); e != nil {
t.Fatal("Did not receive the message")
}
}
func TestEncProtoNilRequest(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewProtoEncodedConn(t)
defer ec.Close()
testPerson := &pb.Person{Name: "Anatolii", Age: 25, Address: "Ukraine, Nikolaev"}
//Subscribe with empty interface shouldn't failed on empty message
ec.Subscribe("nil_test", func(_, reply string, _ interface{}) {
ec.Publish(reply, testPerson)
})
resp := new(pb.Person)
//Request with nil argument shouldn't failed with nil argument
err := ec.Request("nil_test", nil, resp, 100*time.Millisecond)
ec.Flush()
if err != nil {
t.Error("Fail to send empty message via encoded proto connection")
}
if !reflect.DeepEqual(testPerson, resp) {
t.Error("Fail to receive encoded response")
}
}
func BenchmarkProtobufMarshalStruct(b *testing.B) {
me := &pb.Person{Name: "derek", Age: 22, Address: "140 New Montgomery St"}
me.Children = make(map[string]*pb.Person)
me.Children["sam"] = &pb.Person{Name: "sam", Age: 19, Address: "140 New Montgomery St"}
me.Children["meg"] = &pb.Person{Name: "meg", Age: 17, Address: "140 New Montgomery St"}
encoder := &protobuf.ProtobufEncoder{}
for n := 0; n < b.N; n++ {
if _, err := encoder.Encode("protobuf_test", me); err != nil {
b.Fatal("Couldn't serialize object", err)
}
}
}
func BenchmarkPublishProtobufStruct(b *testing.B) {
// stop benchmark for set-up
b.StopTimer()
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewProtoEncodedConn(b)
defer ec.Close()
ch := make(chan bool)
me := &pb.Person{Name: "derek", Age: 22, Address: "140 New Montgomery St"}
me.Children = make(map[string]*pb.Person)
me.Children["sam"] = &pb.Person{Name: "sam", Age: 19, Address: "140 New Montgomery St"}
me.Children["meg"] = &pb.Person{Name: "meg", Age: 17, Address: "140 New Montgomery St"}
ec.Subscribe("protobuf_test", func(p *pb.Person) {
if !reflect.DeepEqual(p, me) {
b.Fatalf("Did not receive the correct protobuf response")
}
ch <- true
})
// resume benchmark
b.StartTimer()
for n := 0; n < b.N; n++ {
ec.Publish("protobuf_test", me)
if e := Wait(ch); e != nil {
b.Fatal("Did not receive the message")
}
}
}

View File

@ -0,0 +1,623 @@
package test
import (
"sync"
"sync/atomic"
"testing"
"time"
"github.com/nats-io/gnatsd/server"
"github.com/nats-io/go-nats"
)
func startReconnectServer(t *testing.T) *server.Server {
return RunServerOnPort(22222)
}
func TestReconnectTotalTime(t *testing.T) {
opts := nats.GetDefaultOptions()
totalReconnectTime := time.Duration(opts.MaxReconnect) * opts.ReconnectWait
if totalReconnectTime < (2 * time.Minute) {
t.Fatalf("Total reconnect time should be at least 2 mins: Currently %v\n",
totalReconnectTime)
}
}
func TestReconnectDisallowedFlags(t *testing.T) {
ts := startReconnectServer(t)
defer ts.Shutdown()
ch := make(chan bool)
opts := nats.GetDefaultOptions()
opts.Url = "nats://localhost:22222"
opts.AllowReconnect = false
opts.ClosedCB = func(_ *nats.Conn) {
ch <- true
}
nc, err := opts.Connect()
if err != nil {
t.Fatalf("Should have connected ok: %v", err)
}
defer nc.Close()
ts.Shutdown()
if e := Wait(ch); e != nil {
t.Fatal("Did not trigger ClosedCB correctly")
}
}
func TestReconnectAllowedFlags(t *testing.T) {
ts := startReconnectServer(t)
defer ts.Shutdown()
ch := make(chan bool)
dch := make(chan bool)
opts := nats.GetDefaultOptions()
opts.Url = "nats://localhost:22222"
opts.AllowReconnect = true
opts.MaxReconnect = 2
opts.ReconnectWait = 1 * time.Second
opts.ClosedCB = func(_ *nats.Conn) {
ch <- true
}
opts.DisconnectedCB = func(_ *nats.Conn) {
dch <- true
}
nc, err := opts.Connect()
if err != nil {
t.Fatalf("Should have connected ok: %v", err)
}
defer nc.Close()
ts.Shutdown()
// We want wait to timeout here, and the connection
// should not trigger the Close CB.
if e := WaitTime(ch, 500*time.Millisecond); e == nil {
t.Fatal("Triggered ClosedCB incorrectly")
}
// We should wait to get the disconnected callback to ensure
// that we are in the process of reconnecting.
if e := Wait(dch); e != nil {
t.Fatal("DisconnectedCB should have been triggered")
}
if !nc.IsReconnecting() {
t.Fatal("Expected to be in a reconnecting state")
}
// clear the CloseCB since ch will block
nc.Opts.ClosedCB = nil
}
var reconnectOpts = nats.Options{
Url: "nats://localhost:22222",
AllowReconnect: true,
MaxReconnect: 10,
ReconnectWait: 100 * time.Millisecond,
Timeout: nats.DefaultTimeout,
}
func TestConnCloseBreaksReconnectLoop(t *testing.T) {
ts := startReconnectServer(t)
defer ts.Shutdown()
cch := make(chan bool)
opts := reconnectOpts
// Bump the max reconnect attempts
opts.MaxReconnect = 100
opts.ClosedCB = func(_ *nats.Conn) {
cch <- true
}
nc, err := opts.Connect()
if err != nil {
t.Fatalf("Should have connected ok: %v", err)
}
defer nc.Close()
nc.Flush()
// Shutdown the server
ts.Shutdown()
// Wait a second, then close the connection
time.Sleep(time.Second)
// Close the connection, this should break the reconnect loop.
// Do this in a go routine since the issue was that Close()
// would block until the reconnect loop is done.
go nc.Close()
// Even on Windows (where a createConn takes more than a second)
// we should be able to break the reconnect loop with the following
// timeout.
if err := WaitTime(cch, 3*time.Second); err != nil {
t.Fatal("Did not get a closed callback")
}
}
func TestBasicReconnectFunctionality(t *testing.T) {
ts := startReconnectServer(t)
defer ts.Shutdown()
ch := make(chan bool)
dch := make(chan bool)
opts := reconnectOpts
opts.DisconnectedCB = func(_ *nats.Conn) {
dch <- true
}
nc, err := opts.Connect()
if err != nil {
t.Fatalf("Should have connected ok: %v\n", err)
}
defer nc.Close()
ec, err := nats.NewEncodedConn(nc, nats.DEFAULT_ENCODER)
if err != nil {
t.Fatalf("Failed to create an encoded connection: %v\n", err)
}
testString := "bar"
ec.Subscribe("foo", func(s string) {
if s != testString {
t.Fatal("String doesn't match")
}
ch <- true
})
ec.Flush()
ts.Shutdown()
// server is stopped here...
if err := Wait(dch); err != nil {
t.Fatalf("Did not get the disconnected callback on time\n")
}
if err := ec.Publish("foo", testString); err != nil {
t.Fatalf("Failed to publish message: %v\n", err)
}
ts = startReconnectServer(t)
defer ts.Shutdown()
if err := ec.FlushTimeout(5 * time.Second); err != nil {
t.Fatalf("Error on Flush: %v", err)
}
if e := Wait(ch); e != nil {
t.Fatal("Did not receive our message")
}
expectedReconnectCount := uint64(1)
reconnectCount := ec.Conn.Stats().Reconnects
if reconnectCount != expectedReconnectCount {
t.Fatalf("Reconnect count incorrect: %d vs %d\n",
reconnectCount, expectedReconnectCount)
}
}
func TestExtendedReconnectFunctionality(t *testing.T) {
ts := startReconnectServer(t)
defer ts.Shutdown()
opts := reconnectOpts
dch := make(chan bool)
opts.DisconnectedCB = func(_ *nats.Conn) {
dch <- true
}
rch := make(chan bool)
opts.ReconnectedCB = func(_ *nats.Conn) {
rch <- true
}
nc, err := opts.Connect()
if err != nil {
t.Fatalf("Should have connected ok: %v", err)
}
defer nc.Close()
ec, err := nats.NewEncodedConn(nc, nats.DEFAULT_ENCODER)
if err != nil {
t.Fatalf("Failed to create an encoded connection: %v\n", err)
}
testString := "bar"
received := int32(0)
ec.Subscribe("foo", func(s string) {
atomic.AddInt32(&received, 1)
})
sub, _ := ec.Subscribe("foobar", func(s string) {
atomic.AddInt32(&received, 1)
})
ec.Publish("foo", testString)
ec.Flush()
ts.Shutdown()
// server is stopped here..
// wait for disconnect
if e := WaitTime(dch, 2*time.Second); e != nil {
t.Fatal("Did not receive a disconnect callback message")
}
// Sub while disconnected
ec.Subscribe("bar", func(s string) {
atomic.AddInt32(&received, 1)
})
// Unsub foobar while disconnected
sub.Unsubscribe()
if err = ec.Publish("foo", testString); err != nil {
t.Fatalf("Received an error after disconnect: %v\n", err)
}
if err = ec.Publish("bar", testString); err != nil {
t.Fatalf("Received an error after disconnect: %v\n", err)
}
ts = startReconnectServer(t)
defer ts.Shutdown()
// server is restarted here..
// wait for reconnect
if e := WaitTime(rch, 2*time.Second); e != nil {
t.Fatal("Did not receive a reconnect callback message")
}
if err = ec.Publish("foobar", testString); err != nil {
t.Fatalf("Received an error after server restarted: %v\n", err)
}
if err = ec.Publish("foo", testString); err != nil {
t.Fatalf("Received an error after server restarted: %v\n", err)
}
ch := make(chan bool)
ec.Subscribe("done", func(b bool) {
ch <- true
})
ec.Publish("done", true)
if e := Wait(ch); e != nil {
t.Fatal("Did not receive our message")
}
// Sleep a bit to guarantee scheduler runs and process all subs.
time.Sleep(50 * time.Millisecond)
if atomic.LoadInt32(&received) != 4 {
t.Fatalf("Received != %d, equals %d\n", 4, received)
}
}
func TestQueueSubsOnReconnect(t *testing.T) {
ts := startReconnectServer(t)
defer ts.Shutdown()
opts := reconnectOpts
// Allow us to block on reconnect complete.
reconnectsDone := make(chan bool)
opts.ReconnectedCB = func(nc *nats.Conn) {
reconnectsDone <- true
}
// Create connection
nc, err := opts.Connect()
if err != nil {
t.Fatalf("Should have connected ok: %v\n", err)
}
defer nc.Close()
ec, err := nats.NewEncodedConn(nc, nats.JSON_ENCODER)
if err != nil {
t.Fatalf("Failed to create an encoded connection: %v\n", err)
}
// To hold results.
results := make(map[int]int)
var mu sync.Mutex
// Make sure we got what we needed, 1 msg only and all seqnos accounted for..
checkResults := func(numSent int) {
mu.Lock()
defer mu.Unlock()
for i := 0; i < numSent; i++ {
if results[i] != 1 {
t.Fatalf("Received incorrect number of messages, [%d] for seq: %d\n", results[i], i)
}
}
// Auto reset results map
results = make(map[int]int)
}
subj := "foo.bar"
qgroup := "workers"
cb := func(seqno int) {
mu.Lock()
defer mu.Unlock()
results[seqno] = results[seqno] + 1
}
// Create Queue Subscribers
ec.QueueSubscribe(subj, qgroup, cb)
ec.QueueSubscribe(subj, qgroup, cb)
ec.Flush()
// Helper function to send messages and check results.
sendAndCheckMsgs := func(numToSend int) {
for i := 0; i < numToSend; i++ {
ec.Publish(subj, i)
}
// Wait for processing.
ec.Flush()
time.Sleep(50 * time.Millisecond)
// Check Results
checkResults(numToSend)
}
// Base Test
sendAndCheckMsgs(10)
// Stop and restart server
ts.Shutdown()
ts = startReconnectServer(t)
defer ts.Shutdown()
if err := Wait(reconnectsDone); err != nil {
t.Fatal("Did not get the ReconnectedCB!")
}
// Reconnect Base Test
sendAndCheckMsgs(10)
}
func TestIsClosed(t *testing.T) {
ts := startReconnectServer(t)
defer ts.Shutdown()
nc := NewConnection(t, 22222)
defer nc.Close()
if nc.IsClosed() {
t.Fatalf("IsClosed returned true when the connection is still open.")
}
ts.Shutdown()
if nc.IsClosed() {
t.Fatalf("IsClosed returned true when the connection is still open.")
}
ts = startReconnectServer(t)
defer ts.Shutdown()
if nc.IsClosed() {
t.Fatalf("IsClosed returned true when the connection is still open.")
}
nc.Close()
if !nc.IsClosed() {
t.Fatalf("IsClosed returned false after Close() was called.")
}
}
func TestIsReconnectingAndStatus(t *testing.T) {
ts := startReconnectServer(t)
defer ts.Shutdown()
disconnectedch := make(chan bool)
reconnectch := make(chan bool)
opts := nats.GetDefaultOptions()
opts.Url = "nats://localhost:22222"
opts.AllowReconnect = true
opts.MaxReconnect = 10000
opts.ReconnectWait = 100 * time.Millisecond
opts.DisconnectedCB = func(_ *nats.Conn) {
disconnectedch <- true
}
opts.ReconnectedCB = func(_ *nats.Conn) {
reconnectch <- true
}
// Connect, verify initial reconnecting state check, then stop the server
nc, err := opts.Connect()
if err != nil {
t.Fatalf("Should have connected ok: %v", err)
}
defer nc.Close()
if nc.IsReconnecting() {
t.Fatalf("IsReconnecting returned true when the connection is still open.")
}
if status := nc.Status(); status != nats.CONNECTED {
t.Fatalf("Status returned %d when connected instead of CONNECTED", status)
}
ts.Shutdown()
// Wait until we get the disconnected callback
if e := Wait(disconnectedch); e != nil {
t.Fatalf("Disconnect callback wasn't triggered: %v", e)
}
if !nc.IsReconnecting() {
t.Fatalf("IsReconnecting returned false when the client is reconnecting.")
}
if status := nc.Status(); status != nats.RECONNECTING {
t.Fatalf("Status returned %d when reconnecting instead of CONNECTED", status)
}
ts = startReconnectServer(t)
defer ts.Shutdown()
// Wait until we get the reconnect callback
if e := Wait(reconnectch); e != nil {
t.Fatalf("Reconnect callback wasn't triggered: %v", e)
}
if nc.IsReconnecting() {
t.Fatalf("IsReconnecting returned true after the connection was reconnected.")
}
if status := nc.Status(); status != nats.CONNECTED {
t.Fatalf("Status returned %d when reconnected instead of CONNECTED", status)
}
// Close the connection, reconnecting should still be false
nc.Close()
if nc.IsReconnecting() {
t.Fatalf("IsReconnecting returned true after Close() was called.")
}
if status := nc.Status(); status != nats.CLOSED {
t.Fatalf("Status returned %d after Close() was called instead of CLOSED", status)
}
}
func TestFullFlushChanDuringReconnect(t *testing.T) {
ts := startReconnectServer(t)
defer ts.Shutdown()
reconnectch := make(chan bool)
opts := nats.GetDefaultOptions()
opts.Url = "nats://localhost:22222"
opts.AllowReconnect = true
opts.MaxReconnect = 10000
opts.ReconnectWait = 100 * time.Millisecond
opts.ReconnectedCB = func(_ *nats.Conn) {
reconnectch <- true
}
// Connect
nc, err := opts.Connect()
if err != nil {
t.Fatalf("Should have connected ok: %v", err)
}
defer nc.Close()
// Channel used to make the go routine sending messages to stop.
stop := make(chan bool)
// While connected, publish as fast as we can
go func() {
for i := 0; ; i++ {
_ = nc.Publish("foo", []byte("hello"))
// Make sure we are sending at least flushChanSize (1024) messages
// before potentially pausing.
if i%2000 == 0 {
select {
case <-stop:
return
default:
time.Sleep(100 * time.Millisecond)
}
}
}
}()
// Send a bit...
time.Sleep(500 * time.Millisecond)
// Shut down the server
ts.Shutdown()
// Continue sending while we are disconnected
time.Sleep(time.Second)
// Restart the server
ts = startReconnectServer(t)
defer ts.Shutdown()
// Wait for the reconnect CB to be invoked (but not for too long)
if e := WaitTime(reconnectch, 5*time.Second); e != nil {
t.Fatalf("Reconnect callback wasn't triggered: %v", e)
}
}
func TestReconnectVerbose(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
o := nats.GetDefaultOptions()
o.Verbose = true
rch := make(chan bool)
o.ReconnectedCB = func(_ *nats.Conn) {
rch <- true
}
nc, err := o.Connect()
if err != nil {
t.Fatalf("Should have connected ok: %v", err)
}
defer nc.Close()
err = nc.Flush()
if err != nil {
t.Fatalf("Error during flush: %v", err)
}
s.Shutdown()
s = RunDefaultServer()
defer s.Shutdown()
if e := Wait(rch); e != nil {
t.Fatal("Should have reconnected ok")
}
err = nc.Flush()
if err != nil {
t.Fatalf("Error during flush: %v", err)
}
}
func TestReconnectBufSize(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
o := nats.GetDefaultOptions()
o.ReconnectBufSize = 32 // 32 bytes
dch := make(chan bool)
o.DisconnectedCB = func(_ *nats.Conn) {
dch <- true
}
nc, err := o.Connect()
if err != nil {
t.Fatalf("Should have connected ok: %v", err)
}
defer nc.Close()
err = nc.Flush()
if err != nil {
t.Fatalf("Error during flush: %v", err)
}
// Force disconnected state.
s.Shutdown()
if e := Wait(dch); e != nil {
t.Fatal("DisconnectedCB should have been triggered")
}
msg := []byte("food") // 4 bytes paylaod, total proto is 16 bytes
// These should work, 2X16 = 32
if err := nc.Publish("foo", msg); err != nil {
t.Fatalf("Failed to publish message: %v\n", err)
}
if err := nc.Publish("foo", msg); err != nil {
t.Fatalf("Failed to publish message: %v\n", err)
}
// This should fail since we have exhausted the backing buffer.
if err := nc.Publish("foo", msg); err == nil {
t.Fatalf("Expected to fail to publish message: got no error\n")
}
nc.Buffered()
}

File diff suppressed because it is too large Load Diff