This commit is contained in:
Кобелев Андрей Андреевич
2022-08-15 23:06:20 +03:00
commit 4ea1a3802b
532 changed files with 211522 additions and 0 deletions

View File

@ -0,0 +1,266 @@
package bolt
import (
"fmt"
"time"
sgob "github.com/asdine/storm/codec/gob"
"github.com/asdine/storm/v3"
"go.etcd.io/bbolt"
"github.com/mochi-co/mqtt/server/persistence"
)
const (
// defaultPath is the default file to use to store the data.
defaultPath = "mochi.db"
// defaultTimeout is the default timeout of the file lock.
defaultTimeout = 250 * time.Millisecond
)
var (
// ErrDBNotOpen indicates the bolt db file is not open for reading.
ErrDBNotOpen = fmt.Errorf("boltdb not opened")
)
// Store is a backend for writing and reading to bolt persistent storage.
type Store struct {
path string // the path on which to store the db file.
opts *bbolt.Options // options for configuring the boltdb instance.
db *storm.DB // the boltdb instance.
}
// New returns a configured instance of the boltdb store.
func New(path string, opts *bbolt.Options) *Store {
if path == "" || path == "." {
path = defaultPath
}
if opts == nil {
opts = &bbolt.Options{
Timeout: defaultTimeout,
}
}
return &Store{
path: path,
opts: opts,
}
}
// Open opens the boltdb instance.
func (s *Store) Open() error {
var err error
s.db, err = storm.Open(s.path, storm.BoltOptions(0600, s.opts), storm.Codec(sgob.Codec))
if err != nil {
return err
}
return nil
}
// Close closes the boltdb instance.
func (s *Store) Close() {
s.db.Close()
}
// WriteServerInfo writes the server info to the boltdb instance.
func (s *Store) WriteServerInfo(v persistence.ServerInfo) error {
if s.db == nil {
return ErrDBNotOpen
}
err := s.db.Save(&v)
if err != nil {
return err
}
return nil
}
// WriteSubscription writes a single subscription to the boltdb instance.
func (s *Store) WriteSubscription(v persistence.Subscription) error {
if s.db == nil {
return ErrDBNotOpen
}
err := s.db.Save(&v)
if err != nil {
return err
}
return nil
}
// WriteInflight writes a single inflight message to the boltdb instance.
func (s *Store) WriteInflight(v persistence.Message) error {
if s.db == nil {
return ErrDBNotOpen
}
err := s.db.Save(&v)
if err != nil {
return err
}
return nil
}
// WriteRetained writes a single retained message to the boltdb instance.
func (s *Store) WriteRetained(v persistence.Message) error {
if s.db == nil {
return ErrDBNotOpen
}
err := s.db.Save(&v)
if err != nil {
return err
}
return nil
}
// WriteClient writes a single client to the boltdb instance.
func (s *Store) WriteClient(v persistence.Client) error {
if s.db == nil {
return ErrDBNotOpen
}
err := s.db.Save(&v)
if err != nil {
return err
}
return nil
}
// DeleteSubscription deletes a subscription from the boltdb instance.
func (s *Store) DeleteSubscription(id string) error {
if s.db == nil {
return ErrDBNotOpen
}
err := s.db.DeleteStruct(&persistence.Subscription{
ID: id,
})
if err != nil {
return err
}
return nil
}
// DeleteClient deletes a client from the boltdb instance.
func (s *Store) DeleteClient(id string) error {
if s.db == nil {
return ErrDBNotOpen
}
err := s.db.DeleteStruct(&persistence.Client{
ID: id,
})
if err != nil {
return err
}
return nil
}
// DeleteInflight deletes an inflight message from the boltdb instance.
func (s *Store) DeleteInflight(id string) error {
if s.db == nil {
return ErrDBNotOpen
}
err := s.db.DeleteStruct(&persistence.Message{
ID: id,
})
if err != nil {
return err
}
return nil
}
// DeleteRetained deletes a retained message from the boltdb instance.
func (s *Store) DeleteRetained(id string) error {
if s.db == nil {
return ErrDBNotOpen
}
err := s.db.DeleteStruct(&persistence.Message{
ID: id,
})
if err != nil {
return err
}
return nil
}
// ReadSubscriptions loads all the subscriptions from the boltdb instance.
func (s *Store) ReadSubscriptions() (v []persistence.Subscription, err error) {
if s.db == nil {
return v, ErrDBNotOpen
}
err = s.db.Find("T", persistence.KSubscription, &v)
if err != nil && err != storm.ErrNotFound {
return
}
return v, nil
}
// ReadClients loads all the clients from the boltdb instance.
func (s *Store) ReadClients() (v []persistence.Client, err error) {
if s.db == nil {
return v, ErrDBNotOpen
}
err = s.db.Find("T", persistence.KClient, &v)
if err != nil && err != storm.ErrNotFound {
return
}
return v, nil
}
// ReadInflight loads all the inflight messages from the boltdb instance.
func (s *Store) ReadInflight() (v []persistence.Message, err error) {
if s.db == nil {
return v, ErrDBNotOpen
}
err = s.db.Find("T", persistence.KInflight, &v)
if err != nil && err != storm.ErrNotFound {
return
}
return v, nil
}
// ReadRetained loads all the retained messages from the boltdb instance.
func (s *Store) ReadRetained() (v []persistence.Message, err error) {
if s.db == nil {
return v, ErrDBNotOpen
}
err = s.db.Find("T", persistence.KRetained, &v)
if err != nil && err != storm.ErrNotFound {
return
}
return v, nil
}
//ReadServerInfo loads the server info from the boltdb instance.
func (s *Store) ReadServerInfo() (v persistence.ServerInfo, err error) {
if s.db == nil {
return v, ErrDBNotOpen
}
err = s.db.One("ID", persistence.KServerInfo, &v)
if err != nil && err != storm.ErrNotFound {
return
}
return v, nil
}

View File

@ -0,0 +1,486 @@
package bolt
import (
"os"
"testing"
"time"
"github.com/stretchr/testify/require"
"go.etcd.io/bbolt"
"github.com/mochi-co/mqtt/server/persistence"
"github.com/mochi-co/mqtt/server/system"
)
const tmpPath = "testbolt.db"
func teardown(s *Store, t *testing.T) {
s.Close()
err := os.Remove(tmpPath)
require.NoError(t, err)
}
func TestSatsifies(t *testing.T) {
var x persistence.Store
x = New(tmpPath, &bbolt.Options{
Timeout: 500 * time.Millisecond,
})
require.NotNil(t, x)
}
func TestNew(t *testing.T) {
s := New(tmpPath, &bbolt.Options{
Timeout: 500 * time.Millisecond,
})
require.NotNil(t, s)
require.Equal(t, tmpPath, s.path)
require.Equal(t, 500*time.Millisecond, s.opts.Timeout)
}
func TestNewNoPath(t *testing.T) {
s := New("", nil)
require.NotNil(t, s)
require.Equal(t, defaultPath, s.path)
}
func TestNewNoOpts(t *testing.T) {
s := New("", nil)
require.NotNil(t, s)
require.Equal(t, defaultTimeout, s.opts.Timeout)
}
func TestOpen(t *testing.T) {
s := New(tmpPath, nil)
err := s.Open()
require.NoError(t, err)
defer teardown(s, t)
require.NotNil(t, s.db)
}
func TestOpenFailure(t *testing.T) {
s := New("..", nil)
err := s.Open()
require.Error(t, err)
}
func TestWriteAndRetrieveServerInfo(t *testing.T) {
s := New(tmpPath, nil)
err := s.Open()
require.NoError(t, err)
defer teardown(s, t)
v := system.Info{
Version: "test",
Started: 100,
}
err = s.WriteServerInfo(persistence.ServerInfo{
Info: v,
ID: persistence.KServerInfo,
})
require.NoError(t, err)
r, err := s.ReadServerInfo()
require.NoError(t, err)
require.NotNil(t, r)
require.Equal(t, v.Version, r.Version)
require.Equal(t, v.Started, r.Started)
}
func TestWriteServerInfoNoDB(t *testing.T) {
s := New(tmpPath, nil)
err := s.WriteServerInfo(persistence.ServerInfo{})
require.Error(t, err)
}
func TestWriteServerInfoFail(t *testing.T) {
s := New(tmpPath, nil)
err := s.Open()
require.NoError(t, err)
err = os.Remove(tmpPath)
require.NoError(t, err)
err = s.WriteServerInfo(persistence.ServerInfo{})
require.Error(t, err)
}
func TestReadServerInfoNoDB(t *testing.T) {
s := New(tmpPath, nil)
_, err := s.ReadServerInfo()
require.Error(t, err)
}
func TestReadServerInfoFail(t *testing.T) {
s := New(tmpPath, nil)
err := s.Open()
require.NoError(t, err)
s.Close()
_, err = s.ReadServerInfo()
require.Error(t, err)
}
func TestWriteRetrieveDeleteSubscription(t *testing.T) {
s := New(tmpPath, nil)
err := s.Open()
require.NoError(t, err)
defer teardown(s, t)
v := persistence.Subscription{
ID: "test:a/b/c",
Client: "test",
Filter: "a/b/c",
QoS: 1,
T: persistence.KSubscription,
}
err = s.WriteSubscription(v)
require.NoError(t, err)
v2 := persistence.Subscription{
ID: "test:d/e/f",
Client: "test",
Filter: "d/e/f",
QoS: 2,
T: persistence.KSubscription,
}
err = s.WriteSubscription(v2)
require.NoError(t, err)
subs, err := s.ReadSubscriptions()
require.NoError(t, err)
require.Equal(t, persistence.KSubscription, subs[0].T)
require.Equal(t, 2, len(subs))
err = s.DeleteSubscription("test:d/e/f")
require.NoError(t, err)
subs, err = s.ReadSubscriptions()
require.NoError(t, err)
require.Equal(t, 1, len(subs))
}
func TestWriteSubscriptionNoDB(t *testing.T) {
s := New(tmpPath, nil)
err := s.WriteSubscription(persistence.Subscription{})
require.Error(t, err)
}
func TestWriteSubscriptionFail(t *testing.T) {
s := New(tmpPath, nil)
err := s.Open()
require.NoError(t, err)
s.Close()
err = s.WriteSubscription(persistence.Subscription{})
require.Error(t, err)
}
func TestReadSubscriptionNoDB(t *testing.T) {
s := New(tmpPath, nil)
_, err := s.ReadSubscriptions()
require.Error(t, err)
}
func TestReadSubscriptionFail(t *testing.T) {
s := New(tmpPath, nil)
err := s.Open()
require.NoError(t, err)
s.Close()
_, err = s.ReadSubscriptions()
require.Error(t, err)
}
func TestWriteRetrieveDeleteInflight(t *testing.T) {
s := New(tmpPath, nil)
err := s.Open()
require.NoError(t, err)
defer teardown(s, t)
v := persistence.Message{
ID: "client1_if_0",
T: persistence.KInflight,
PacketID: 0,
TopicName: "a/b/c",
Payload: []byte{'h', 'e', 'l', 'l', 'o'},
Sent: 100,
Resends: 0,
}
err = s.WriteInflight(v)
require.NoError(t, err)
v2 := persistence.Message{
ID: "client1_if_100",
T: persistence.KInflight,
PacketID: 100,
TopicName: "d/e/f",
Payload: []byte{'y', 'e', 's'},
Sent: 200,
Resends: 1,
}
err = s.WriteInflight(v2)
require.NoError(t, err)
msgs, err := s.ReadInflight()
require.NoError(t, err)
require.Equal(t, persistence.KInflight, msgs[0].T)
require.Equal(t, 2, len(msgs))
err = s.DeleteInflight("client1_if_100")
require.NoError(t, err)
msgs, err = s.ReadInflight()
require.NoError(t, err)
require.Equal(t, 1, len(msgs))
}
func TestWriteInflightNoDB(t *testing.T) {
s := New(tmpPath, nil)
err := s.WriteInflight(persistence.Message{})
require.Error(t, err)
}
func TestWriteInflightFail(t *testing.T) {
s := New(tmpPath, nil)
err := s.Open()
require.NoError(t, err)
s.Close()
err = s.WriteInflight(persistence.Message{})
require.Error(t, err)
}
func TestReadInflightNoDB(t *testing.T) {
s := New(tmpPath, nil)
_, err := s.ReadInflight()
require.Error(t, err)
}
func TestReadInflightFail(t *testing.T) {
s := New(tmpPath, nil)
err := s.Open()
require.NoError(t, err)
s.Close()
_, err = s.ReadInflight()
require.Error(t, err)
}
func TestWriteRetrieveDeleteRetained(t *testing.T) {
s := New(tmpPath, nil)
err := s.Open()
require.NoError(t, err)
defer teardown(s, t)
v := persistence.Message{
ID: "client1_ret_200",
T: persistence.KRetained,
FixedHeader: persistence.FixedHeader{
Retain: true,
},
PacketID: 200,
TopicName: "a/b/c",
Payload: []byte{'h', 'e', 'l', 'l', 'o'},
Sent: 100,
Resends: 0,
}
err = s.WriteRetained(v)
require.NoError(t, err)
v2 := persistence.Message{
ID: "client1_ret_300",
T: persistence.KRetained,
FixedHeader: persistence.FixedHeader{
Retain: true,
},
PacketID: 100,
TopicName: "d/e/f",
Payload: []byte{'y', 'e', 's'},
Sent: 200,
Resends: 1,
}
err = s.WriteRetained(v2)
require.NoError(t, err)
msgs, err := s.ReadRetained()
require.NoError(t, err)
require.Equal(t, persistence.KRetained, msgs[0].T)
require.Equal(t, true, msgs[0].FixedHeader.Retain)
require.Equal(t, 2, len(msgs))
err = s.DeleteRetained("client1_ret_300")
require.NoError(t, err)
msgs, err = s.ReadRetained()
require.NoError(t, err)
require.Equal(t, 1, len(msgs))
}
func TestWriteRetainedNoDB(t *testing.T) {
s := New(tmpPath, nil)
err := s.WriteRetained(persistence.Message{})
require.Error(t, err)
}
func TestWriteRetainedFail(t *testing.T) {
s := New(tmpPath, nil)
err := s.Open()
require.NoError(t, err)
err = os.Remove(tmpPath)
require.NoError(t, err)
err = s.WriteRetained(persistence.Message{})
require.Error(t, err)
}
func TestReadRetainedNoDB(t *testing.T) {
s := New(tmpPath, nil)
_, err := s.ReadRetained()
require.Error(t, err)
}
func TestReadRetainedFail(t *testing.T) {
s := New(tmpPath, nil)
err := s.Open()
require.NoError(t, err)
s.Close()
_, err = s.ReadRetained()
require.Error(t, err)
}
func TestWriteRetrieveDeleteClients(t *testing.T) {
s := New(tmpPath, nil)
err := s.Open()
require.NoError(t, err)
defer teardown(s, t)
v := persistence.Client{
ID: "cl_client1",
ClientID: "client1",
T: persistence.KClient,
Listener: "tcp1",
Username: []byte{'m', 'o', 'c', 'h', 'i'},
LWT: persistence.LWT{
Topic: "a/b/c",
Message: []byte{'h', 'e', 'l', 'l', 'o'},
Qos: 1,
Retain: true,
},
}
err = s.WriteClient(v)
require.NoError(t, err)
clients, err := s.ReadClients()
require.NoError(t, err)
require.Equal(t, []byte{'m', 'o', 'c', 'h', 'i'}, clients[0].Username)
require.Equal(t, "a/b/c", clients[0].LWT.Topic)
v2 := persistence.Client{
ID: "cl_client2",
ClientID: "client2",
T: persistence.KClient,
Listener: "tcp1",
}
err = s.WriteClient(v2)
require.NoError(t, err)
clients, err = s.ReadClients()
require.NoError(t, err)
require.Equal(t, persistence.KClient, clients[0].T)
require.Equal(t, 2, len(clients))
err = s.DeleteClient("cl_client2")
require.NoError(t, err)
clients, err = s.ReadClients()
require.NoError(t, err)
require.Equal(t, 1, len(clients))
}
func TestWriteClientNoDB(t *testing.T) {
s := New(tmpPath, nil)
err := s.WriteClient(persistence.Client{})
require.Error(t, err)
}
func TestWriteClientFail(t *testing.T) {
s := New(tmpPath, nil)
err := s.Open()
require.NoError(t, err)
s.Close()
err = s.WriteClient(persistence.Client{})
require.Error(t, err)
}
func TestReadClientNoDB(t *testing.T) {
s := New(tmpPath, nil)
_, err := s.ReadClients()
require.Error(t, err)
}
func TestReadClientFail(t *testing.T) {
s := New(tmpPath, nil)
err := s.Open()
require.NoError(t, err)
s.Close()
_, err = s.ReadClients()
require.Error(t, err)
}
func TestDeleteSubscriptionNoDB(t *testing.T) {
s := New(tmpPath, nil)
err := s.DeleteSubscription("a")
require.Error(t, err)
}
func TestDeleteSubscriptionFail(t *testing.T) {
s := New(tmpPath, nil)
err := s.Open()
require.NoError(t, err)
s.Close()
err = s.DeleteSubscription("a")
require.Error(t, err)
}
func TestDeleteClientNoDB(t *testing.T) {
s := New(tmpPath, nil)
err := s.DeleteClient("a")
require.Error(t, err)
}
func TestDeleteClientFail(t *testing.T) {
s := New(tmpPath, nil)
err := s.Open()
require.NoError(t, err)
s.Close()
err = s.DeleteClient("a")
require.Error(t, err)
}
func TestDeleteInflightNoDB(t *testing.T) {
s := New(tmpPath, nil)
err := s.DeleteInflight("a")
require.Error(t, err)
}
func TestDeleteInflightFail(t *testing.T) {
s := New(tmpPath, nil)
err := s.Open()
require.NoError(t, err)
s.Close()
err = s.DeleteInflight("a")
require.Error(t, err)
}
func TestDeleteRetainedNoDB(t *testing.T) {
s := New(tmpPath, nil)
err := s.DeleteRetained("a")
require.Error(t, err)
}
func TestDeleteRetainedFail(t *testing.T) {
s := New(tmpPath, nil)
err := s.Open()
require.NoError(t, err)
s.Close()
err = s.DeleteRetained("a")
require.Error(t, err)
}

View File

@ -0,0 +1,291 @@
package persistence
import (
"errors"
"github.com/mochi-co/mqtt/server/system"
)
const (
// KSubscription is the key for subscription data.
KSubscription = "sub"
// KServerInfo is the key for server info data.
KServerInfo = "srv"
// KRetained is the key for retained messages data.
KRetained = "ret"
// KInflight is the key for inflight messages data.
KInflight = "ifm"
// KClient is the key for client data.
KClient = "cl"
)
// Store is an interface which details a persistent storage connector.
type Store interface {
Open() error
Close()
WriteSubscription(v Subscription) error
WriteClient(v Client) error
WriteInflight(v Message) error
WriteServerInfo(v ServerInfo) error
WriteRetained(v Message) error
DeleteSubscription(id string) error
DeleteClient(id string) error
DeleteInflight(id string) error
DeleteRetained(id string) error
ReadSubscriptions() (v []Subscription, err error)
ReadInflight() (v []Message, err error)
ReadRetained() (v []Message, err error)
ReadClients() (v []Client, err error)
ReadServerInfo() (v ServerInfo, err error)
}
// ServerInfo contains information and statistics about the server.
type ServerInfo struct {
system.Info // embed the system info struct.
ID string // the storage key.
}
// Subscription contains the details of a topic filter subscription.
type Subscription struct {
ID string // the storage key.
T string // the type of the stored data.
Client string // the id of the client who the subscription belongs to.
Filter string // the topic filter being subscribed to.
QoS byte // the desired QoS byte.
}
// Message contains the details of a retained or inflight message.
type Message struct {
Payload []byte // the message payload (if retained).
FixedHeader FixedHeader // the header properties of the message.
T string // the type of the stored data.
ID string // the storage key.
Client string // the id of the client who sent the message (if inflight).
TopicName string // the topic the message was sent to (if retained).
Sent int64 // the last time the message was sent (for retries) in unixtime (if inflight).
Resends int // the number of times the message was attempted to be sent (if inflight).
PacketID uint16 // the unique id of the packet (if inflight).
}
// FixedHeader contains the fixed header properties of a message.
type FixedHeader struct {
Remaining int // the number of remaining bytes in the payload.
Type byte // the type of the packet (PUBLISH, SUBSCRIBE, etc) from bits 7 - 4 (byte 1).
Qos byte // indicates the quality of service expected.
Dup bool // indicates if the packet was already sent at an earlier time.
Retain bool // whether the message should be retained.
}
// Client contains client data that can be persistently stored.
type Client struct {
LWT LWT // the last-will-and-testament message for the client.
Username []byte // the username the client authenticated with.
ID string // the storage key.
ClientID string // the id of the client.
T string // the type of the stored data.
Listener string // the last known listener id for the client
}
// LWT contains details about a clients LWT payload.
type LWT struct {
Message []byte // the message that shall be sent when the client disconnects.
Topic string // the topic the will message shall be sent to.
Qos byte // the quality of service desired.
Retain bool // indicates whether the will message should be retained
}
// MockStore is a mock storage backend for testing.
type MockStore struct {
Fail map[string]bool // issue errors for different methods.
FailOpen bool // error on open.
Closed bool // indicate mock store is closed.
Opened bool // indicate mock store is open.
}
// Open opens the storage instance.
func (s *MockStore) Open() error {
if s.FailOpen {
return errors.New("test")
}
s.Opened = true
return nil
}
// Close closes the storage instance.
func (s *MockStore) Close() {
s.Closed = true
}
// WriteSubscription writes a single subscription to the storage instance.
func (s *MockStore) WriteSubscription(v Subscription) error {
if _, ok := s.Fail["write_subs"]; ok {
return errors.New("test")
}
return nil
}
// WriteClient writes a single client to the storage instance.
func (s *MockStore) WriteClient(v Client) error {
if _, ok := s.Fail["write_clients"]; ok {
return errors.New("test")
}
return nil
}
// WriteInFlight writes a single InFlight message to the storage instance.
func (s *MockStore) WriteInflight(v Message) error {
if _, ok := s.Fail["write_inflight"]; ok {
return errors.New("test")
}
return nil
}
// WriteRetained writes a single retained message to the storage instance.
func (s *MockStore) WriteRetained(v Message) error {
if _, ok := s.Fail["write_retained"]; ok {
return errors.New("test")
}
return nil
}
// WriteServerInfo writes server info to the storage instance.
func (s *MockStore) WriteServerInfo(v ServerInfo) error {
if _, ok := s.Fail["write_info"]; ok {
return errors.New("test")
}
return nil
}
// DeleteSubscription deletes a subscription from the persistent store.
func (s *MockStore) DeleteSubscription(id string) error {
if _, ok := s.Fail["delete_subs"]; ok {
return errors.New("test")
}
return nil
}
// DeleteClient deletes a client from the persistent store.
func (s *MockStore) DeleteClient(id string) error {
if _, ok := s.Fail["delete_clients"]; ok {
return errors.New("test")
}
return nil
}
// DeleteInflight deletes an inflight message from the persistent store.
func (s *MockStore) DeleteInflight(id string) error {
if _, ok := s.Fail["delete_inflight"]; ok {
return errors.New("test")
}
return nil
}
// DeleteRetained deletes a retained message from the persistent store.
func (s *MockStore) DeleteRetained(id string) error {
if _, ok := s.Fail["delete_retained"]; ok {
return errors.New("test")
}
return nil
}
// ReadSubscriptions loads the subscriptions from the storage instance.
func (s *MockStore) ReadSubscriptions() (v []Subscription, err error) {
if _, ok := s.Fail["read_subs"]; ok {
return v, errors.New("test_subs")
}
return []Subscription{
{
ID: "test:a/b/c",
Client: "test",
Filter: "a/b/c",
QoS: 1,
T: KSubscription,
},
}, nil
}
// ReadClients loads the clients from the storage instance.
func (s *MockStore) ReadClients() (v []Client, err error) {
if _, ok := s.Fail["read_clients"]; ok {
return v, errors.New("test_clients")
}
return []Client{
{
ID: "cl_client1",
ClientID: "client1",
T: KClient,
Listener: "tcp1",
},
}, nil
}
// ReadInflight loads the inflight messages from the storage instance.
func (s *MockStore) ReadInflight() (v []Message, err error) {
if _, ok := s.Fail["read_inflight"]; ok {
return v, errors.New("test_inflight")
}
return []Message{
{
ID: "client1_if_100",
T: KInflight,
Client: "client1",
PacketID: 100,
TopicName: "d/e/f",
Payload: []byte{'y', 'e', 's'},
Sent: 200,
Resends: 1,
},
}, nil
}
// ReadRetained loads the retained messages from the storage instance.
func (s *MockStore) ReadRetained() (v []Message, err error) {
if _, ok := s.Fail["read_retained"]; ok {
return v, errors.New("test_retained")
}
return []Message{
{
ID: "client1_ret_200",
T: KRetained,
FixedHeader: FixedHeader{
Retain: true,
},
PacketID: 200,
TopicName: "a/b/c",
Payload: []byte{'h', 'e', 'l', 'l', 'o'},
Sent: 100,
Resends: 0,
},
}, nil
}
//ReadServerInfo loads the server info from the storage instance.
func (s *MockStore) ReadServerInfo() (v ServerInfo, err error) {
if _, ok := s.Fail["read_info"]; ok {
return v, errors.New("test_info")
}
return ServerInfo{
system.Info{
Version: "test",
Started: 100,
},
KServerInfo,
}, nil
}

View File

@ -0,0 +1,251 @@
package persistence
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestMockStoreOpen(t *testing.T) {
s := new(MockStore)
err := s.Open()
require.NoError(t, err)
require.Equal(t, true, s.Opened)
}
func TestMockStoreOpenFail(t *testing.T) {
s := new(MockStore)
s.FailOpen = true
err := s.Open()
require.Error(t, err)
}
func TestMockStoreClose(t *testing.T) {
s := new(MockStore)
s.Close()
require.Equal(t, true, s.Closed)
}
func TestMockStoreWriteSubscription(t *testing.T) {
s := new(MockStore)
err := s.WriteSubscription(Subscription{})
require.NoError(t, err)
}
func TestMockStoreWriteSubscriptionFail(t *testing.T) {
s := &MockStore{
Fail: map[string]bool{
"write_subs": true,
},
}
err := s.WriteSubscription(Subscription{})
require.Error(t, err)
}
func TestMockStoreWriteClient(t *testing.T) {
s := new(MockStore)
err := s.WriteClient(Client{})
require.NoError(t, err)
}
func TestMockStoreWriteClientFail(t *testing.T) {
s := &MockStore{
Fail: map[string]bool{
"write_clients": true,
},
}
err := s.WriteClient(Client{})
require.Error(t, err)
}
func TestMockStoreWriteInflight(t *testing.T) {
s := new(MockStore)
err := s.WriteInflight(Message{})
require.NoError(t, err)
}
func TestMockStoreWriteInflightFail(t *testing.T) {
s := &MockStore{
Fail: map[string]bool{
"write_inflight": true,
},
}
err := s.WriteInflight(Message{})
require.Error(t, err)
}
func TestMockStoreWriteRetained(t *testing.T) {
s := new(MockStore)
err := s.WriteRetained(Message{})
require.NoError(t, err)
}
func TestMockStoreWriteRetainedFail(t *testing.T) {
s := &MockStore{
Fail: map[string]bool{
"write_retained": true,
},
}
err := s.WriteRetained(Message{})
require.Error(t, err)
}
func TestMockStoreWriteServerInfo(t *testing.T) {
s := new(MockStore)
err := s.WriteServerInfo(ServerInfo{})
require.NoError(t, err)
}
func TestMockStoreWriteServerInfoFail(t *testing.T) {
s := &MockStore{
Fail: map[string]bool{
"write_info": true,
},
}
err := s.WriteServerInfo(ServerInfo{})
require.Error(t, err)
}
func TestMockStoreDeleteSubscription(t *testing.T) {
s := new(MockStore)
err := s.DeleteSubscription("client1:d/e/f")
require.NoError(t, err)
}
func TestMockStoreDeleteSubscriptionFail(t *testing.T) {
s := &MockStore{
Fail: map[string]bool{
"delete_subs": true,
},
}
err := s.DeleteSubscription("client1:a/b/c")
require.Error(t, err)
}
func TestMockStoreDeleteClient(t *testing.T) {
s := new(MockStore)
err := s.DeleteClient("client1")
require.NoError(t, err)
}
func TestMockStoreDeleteClientFail(t *testing.T) {
s := &MockStore{
Fail: map[string]bool{
"delete_clients": true,
},
}
err := s.DeleteClient("client1")
require.Error(t, err)
}
func TestMockStoreDeleteInflight(t *testing.T) {
s := new(MockStore)
err := s.DeleteInflight("client1-if-100")
require.NoError(t, err)
}
func TestMockStoreDeleteInflightFail(t *testing.T) {
s := &MockStore{
Fail: map[string]bool{
"delete_inflight": true,
},
}
err := s.DeleteInflight("client1-if-100")
require.Error(t, err)
}
func TestMockStoreDeleteRetained(t *testing.T) {
s := new(MockStore)
err := s.DeleteRetained("client1-ret-100")
require.NoError(t, err)
}
func TestMockStoreDeleteRetainedFail(t *testing.T) {
s := &MockStore{
Fail: map[string]bool{
"delete_retained": true,
},
}
err := s.DeleteRetained("client1-ret-100")
require.Error(t, err)
}
func TestMockStorReadServerInfo(t *testing.T) {
s := new(MockStore)
_, err := s.ReadServerInfo()
require.NoError(t, err)
}
func TestMockStorReadServerInfoFail(t *testing.T) {
s := &MockStore{
Fail: map[string]bool{
"read_info": true,
},
}
_, err := s.ReadServerInfo()
require.Error(t, err)
}
func TestMockStoreReadSubscriptions(t *testing.T) {
s := new(MockStore)
_, err := s.ReadSubscriptions()
require.NoError(t, err)
}
func TestMockStoreReadSubscriptionsFail(t *testing.T) {
s := &MockStore{
Fail: map[string]bool{
"read_subs": true,
},
}
_, err := s.ReadSubscriptions()
require.Error(t, err)
}
func TestMockStoreReadClients(t *testing.T) {
s := new(MockStore)
_, err := s.ReadClients()
require.NoError(t, err)
}
func TestMockStoreReadClientsFail(t *testing.T) {
s := &MockStore{
Fail: map[string]bool{
"read_clients": true,
},
}
_, err := s.ReadClients()
require.Error(t, err)
}
func TestMockStoreReadInflight(t *testing.T) {
s := new(MockStore)
_, err := s.ReadInflight()
require.NoError(t, err)
}
func TestMockStoreReadInflightFail(t *testing.T) {
s := &MockStore{
Fail: map[string]bool{
"read_inflight": true,
},
}
_, err := s.ReadInflight()
require.Error(t, err)
}
func TestMockStoreReadRetained(t *testing.T) {
s := new(MockStore)
_, err := s.ReadRetained()
require.NoError(t, err)
}
func TestMockStoreReadRetainedFail(t *testing.T) {
s := &MockStore{
Fail: map[string]bool{
"read_retained": true,
},
}
_, err := s.ReadRetained()
require.Error(t, err)
}