Files
nats.go/jetstream/errors_test.go
Piotr Piotrowski e7ab93ecb8 Add ordered consumer, FetchBytes and Next, rework options
Signed-off-by: Piotr Piotrowski <piotr@synadia.com>
2023-05-23 12:03:02 +02:00

211 lines
6.0 KiB
Go

// Copyright 2020-2023 The NATS Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package jetstream
import (
"context"
"errors"
"fmt"
"os"
"testing"
"time"
"github.com/nats-io/nats.go"
)
func TestJetStreamErrors(t *testing.T) {
t.Run("API error", func(t *testing.T) {
conf := createConfFile(t, []byte(`
listen: 127.0.0.1:-1
no_auth_user: rip
jetstream: {max_mem_store: 64GB, max_file_store: 10TB}
accounts: {
JS: {
jetstream: enabled
users: [ {user: dlc, password: foo} ]
},
IU: {
users: [ {user: rip, password: bar} ]
},
}
`))
defer os.Remove(conf)
s, _ := RunServerWithConfig(conf)
defer shutdownJSServerAndRemoveStorage(t, s)
nc, err := nats.Connect(s.ClientURL())
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
js, err := New(nc)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
defer nc.Close()
_, err = js.AccountInfo(ctx)
// check directly to var (backwards compatible)
if err != ErrJetStreamNotEnabledForAccount {
t.Fatalf("Did not get the proper error, got %v", err)
}
// matching via errors.Is
if ok := errors.Is(err, ErrJetStreamNotEnabledForAccount); !ok {
t.Fatal("Expected ErrJetStreamNotEnabledForAccount")
}
// matching wrapped via error.Is
err2 := fmt.Errorf("custom error: %w", ErrJetStreamNotEnabledForAccount)
if ok := errors.Is(err2, ErrJetStreamNotEnabledForAccount); !ok {
t.Fatal("Expected wrapped ErrJetStreamNotEnabled")
}
// via classic type assertion.
jserr, ok := err.(JetStreamError)
if !ok {
t.Fatal("Expected a JetStreamError")
}
expected := JSErrCodeJetStreamNotEnabledForAccount
if jserr.APIError().ErrorCode != expected {
t.Fatalf("Expected: %v, got: %v", expected, jserr.APIError().ErrorCode)
}
if jserr.APIError() == nil {
t.Fatal("Expected APIError")
}
// matching to interface via errors.As(...)
var apierr JetStreamError
ok = errors.As(err, &apierr)
if !ok {
t.Fatal("Expected a JetStreamError")
}
if apierr.APIError() == nil {
t.Fatal("Expected APIError")
}
if apierr.APIError().ErrorCode != expected {
t.Fatalf("Expected: %v, got: %v", expected, apierr.APIError().ErrorCode)
}
expectedMessage := "nats: API error: code=503 err_code=10039 description=jetstream not enabled for account"
if apierr.Error() != expectedMessage {
t.Fatalf("Expected: %v, got: %v", expectedMessage, apierr.Error())
}
// an APIError also implements the JetStreamError interface.
var _ JetStreamError = &APIError{}
// matching arbitrary custom error via errors.Is(...)
customErr := &APIError{ErrorCode: expected}
if ok := errors.Is(customErr, ErrJetStreamNotEnabledForAccount); !ok {
t.Fatal("Expected wrapped ErrJetStreamNotEnabledForAccount")
}
customErr = &APIError{ErrorCode: 1}
if ok := errors.Is(customErr, ErrJetStreamNotEnabledForAccount); ok {
t.Fatal("Expected to not match ErrJetStreamNotEnabled")
}
var cerr JetStreamError
if ok := errors.As(customErr, &cerr); !ok {
t.Fatal("Expected custom error to be a JetStreamError")
}
// matching to concrete type via errors.As(...)
var aerr *APIError
ok = errors.As(err, &aerr)
if !ok {
t.Fatal("Expected an APIError")
}
if aerr.ErrorCode != expected {
t.Fatalf("Expected: %v, got: %v", expected, aerr.ErrorCode)
}
expectedMessage = "nats: API error: code=503 err_code=10039 description=jetstream not enabled for account"
if aerr.Error() != expectedMessage {
t.Fatalf("Expected: %v, got: %v", expectedMessage, apierr.Error())
}
})
t.Run("test non-api error", func(t *testing.T) {
s := RunBasicJetStreamServer()
defer shutdownJSServerAndRemoveStorage(t, s)
nc, err := nats.Connect(s.ClientURL())
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
js, err := New(nc)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
defer nc.Close()
// stream with empty name
_, err = js.CreateStream(ctx, StreamConfig{})
if err == nil {
t.Fatalf("Expected error, got nil")
}
// check directly to var (backwards compatible)
if err != ErrStreamNameRequired {
t.Fatalf("Expected: %v; got: %v", ErrInvalidStreamName, err)
}
// matching via errors.Is
if ok := errors.Is(err, ErrStreamNameRequired); !ok {
t.Fatalf("Expected: %v; got: %v", ErrStreamNameRequired, err)
}
// matching wrapped via error.Is
err2 := fmt.Errorf("custom error: %w", ErrStreamNameRequired)
if ok := errors.Is(err2, ErrStreamNameRequired); !ok {
t.Fatal("Expected wrapped ErrStreamNameRequired")
}
// via classic type assertion.
jserr, ok := err.(JetStreamError)
if !ok {
t.Fatal("Expected a JetStreamError")
}
if jserr.APIError() != nil {
t.Fatalf("Expected: empty APIError; got: %v", jserr.APIError())
}
// matching to interface via errors.As(...)
var jserr2 JetStreamError
ok = errors.As(err, &jserr2)
if !ok {
t.Fatal("Expected a JetStreamError")
}
if jserr2.APIError() != nil {
t.Fatalf("Expected: empty APIError; got: %v", jserr2.APIError())
}
expectedMessage := "nats: stream name is required"
if jserr2.Error() != expectedMessage {
t.Fatalf("Expected: %v, got: %v", expectedMessage, jserr2.Error())
}
// matching to concrete type via errors.As(...)
var aerr *APIError
ok = errors.As(err, &aerr)
if ok {
t.Fatal("Expected ErrStreamNameRequired not to map to APIError")
}
})
}