Skip to content
Snippets Groups Projects
Commit 30c1337b authored by Benjamin Wenger's avatar Benjamin Wenger
Browse files

Merge remote-tracking branch 'origin/release' into release

parents 0fa85a86 4d9f93b3
No related branches found
No related tags found
1 merge request!23Release
////////////////////////////////////////////////////////////////////////////////
// Copyright © 2020 xx network SEZC //
// //
// Use of this source code is governed by a license that can be found in the //
// LICENSE file //
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Copyright © 2020 xx network SEZC //
// //
// Use of this source code is governed by a license that can be found in the //
// LICENSE file //
////////////////////////////////////////////////////////////////////////////////
package hostList
import (
"bytes"
"github.com/pkg/errors"
"gitlab.com/elixxir/client/storage/versioned"
"gitlab.com/xx_network/primitives/id"
"gitlab.com/xx_network/primitives/netTime"
)
// Storage values.
const (
hostListPrefix = "hostLists"
hostListKey = "hostListIDs"
hostListVersion = 0
)
// Error messages.
const (
getStorageErr = "failed to get host list from storage: %+v"
unmarshallIdErr = "unmarshal host list error: %+v"
unmarshallLenErr = "malformed data: length of data %d incorrect"
)
type Store struct {
kv *versioned.KV
}
// NewStore creates a new Store with a prefixed KV.
func NewStore(kv *versioned.KV) *Store {
return &Store{
kv: kv.Prefix(hostListPrefix),
}
}
// Store saves the list of host IDs to storage.
func (s *Store) Store(list []*id.ID) error {
obj := &versioned.Object{
Version: hostListVersion,
Data: marshalHostList(list),
Timestamp: netTime.Now(),
}
return s.kv.Set(hostListKey, hostListVersion, obj)
}
// Get returns the host list from storage.
func (s *Store) Get() ([]*id.ID, error) {
obj, err := s.kv.Get(hostListKey, hostListVersion)
if err != nil {
return nil, errors.Errorf(getStorageErr, err)
}
return unmarshalHostList(obj.Data)
}
// marshalHostList marshals the list of IDs into a byte slice.
func marshalHostList(list []*id.ID) []byte {
buff := bytes.NewBuffer(nil)
buff.Grow(len(list) * id.ArrIDLen)
for _, hid := range list {
if hid != nil {
buff.Write(hid.Marshal())
} else {
buff.Write((&id.ID{}).Marshal())
}
}
return buff.Bytes()
}
// unmarshalHostList unmarshal the host list data into an ID list. An error is
// returned if an ID cannot be unmarshalled or if the data is not of the correct
// length.
func unmarshalHostList(data []byte) ([]*id.ID, error) {
// Return an error if the data is not of the required length
if len(data)%id.ArrIDLen != 0 {
return nil, errors.Errorf(unmarshallLenErr, len(data))
}
buff := bytes.NewBuffer(data)
list := make([]*id.ID, 0, len(data)/id.ArrIDLen)
// Read each ID from data, unmarshal, and add to list
length := id.ArrIDLen
for n := buff.Next(length); len(n) == length; n = buff.Next(length) {
hid, err := id.Unmarshal(n)
if err != nil {
return nil, errors.Errorf(unmarshallIdErr, err)
}
// If the ID is all zeroes, then treat it as a nil ID.
if *hid == (id.ID{}) {
hid = nil
}
list = append(list, hid)
}
return list, nil
}
////////////////////////////////////////////////////////////////////////////////
// Copyright © 2020 xx network SEZC //
// //
// Use of this source code is governed by a license that can be found in the //
// LICENSE file //
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Copyright © 2020 xx network SEZC //
// //
// Use of this source code is governed by a license that can be found in the //
// LICENSE file //
////////////////////////////////////////////////////////////////////////////////
package hostList
import (
"fmt"
"gitlab.com/elixxir/client/storage/versioned"
"gitlab.com/elixxir/ekv"
"gitlab.com/xx_network/primitives/id"
"reflect"
"strings"
"testing"
)
// Unit test of NewStore.
func TestNewStore(t *testing.T) {
kv := versioned.NewKV(make(ekv.Memstore))
expected := &Store{kv: kv.Prefix(hostListPrefix)}
s := NewStore(kv)
if !reflect.DeepEqual(expected, s) {
t.Errorf("NewStore did not return the expected object."+
"\nexpected: %+v\nreceived: %+v", expected, s)
}
}
// Tests that a host list saved by Store.Store matches the host list returned
// by Store.Get.
func TestStore_Store_Get(t *testing.T) {
s := NewStore(versioned.NewKV(make(ekv.Memstore)))
list := []*id.ID{
id.NewIdFromString("histID_1", id.Node, t),
nil,
id.NewIdFromString("histID_2", id.Node, t),
id.NewIdFromString("histID_3", id.Node, t),
}
err := s.Store(list)
if err != nil {
t.Errorf("Store returned an error: %+v", err)
}
newList, err := s.Get()
if err != nil {
t.Errorf("Get returned an error: %+v", err)
}
if !reflect.DeepEqual(list, newList) {
t.Errorf("Failed to save and load host list."+
"\nexpected: %+v\nreceived: %+v", list, newList)
}
}
// Error path: tests that Store.Get returns an error if not host list is
// saved in storage.
func TestStore_Get_StorageError(t *testing.T) {
s := NewStore(versioned.NewKV(make(ekv.Memstore)))
expectedErr := strings.SplitN(getStorageErr, "%", 2)[0]
_, err := s.Get()
if err == nil || !strings.Contains(err.Error(), expectedErr) {
t.Errorf("Get failed to return the expected error."+
"\nexpected: %s\nreceived: %+v", expectedErr, err)
}
}
// Tests that a list of IDs that is marshalled using marshalHostList and
// unmarshalled using unmarshalHostList matches the original.
func Test_marshalHostList_unmarshalHostList(t *testing.T) {
list := []*id.ID{
id.NewIdFromString("histID_1", id.Node, t),
nil,
id.NewIdFromString("histID_2", id.Node, t),
id.NewIdFromString("histID_3", id.Node, t),
}
data := marshalHostList(list)
newList, err := unmarshalHostList(data)
if err != nil {
t.Errorf("unmarshalHostList produced an error: %+v", err)
}
if !reflect.DeepEqual(list, newList) {
t.Errorf("Failed to marshal and unmarshal ID list."+
"\nexpected: %+v\nreceived: %+v", list, newList)
}
}
// Error path: tests that unmarshalHostList returns an error if the data is not
// of the correct length.
func Test_unmarshalHostList_InvalidDataErr(t *testing.T) {
data := []byte("Invalid Data")
expectedErr := fmt.Sprintf(unmarshallLenErr, len(data))
_, err := unmarshalHostList(data)
if err == nil || err.Error() != expectedErr {
t.Errorf("unmarshalHostList failed to return the expected error."+
"\nexpected: %s\nreceived: %+v", expectedErr, err)
}
}
......@@ -10,6 +10,7 @@
package storage
import (
"gitlab.com/elixxir/client/storage/hostList"
"gitlab.com/elixxir/client/storage/rounds"
"sync"
"testing"
......@@ -65,6 +66,7 @@ type Session struct {
reception *reception.Store
clientVersion *clientVersion.Store
uncheckedRounds *rounds.UncheckedRoundStore
hostList *hostList.Store
}
// Initialize a new Session object
......@@ -148,6 +150,9 @@ func New(baseDir, password string, u userInterface.User, currentVersion version.
if err != nil {
return nil, errors.WithMessage(err, "Failed to create unchecked round store")
}
s.hostList = hostList.NewStore(s.kv)
return s, nil
}
......@@ -224,6 +229,8 @@ func Load(baseDir, password string, currentVersion version.Version,
return nil, errors.WithMessage(err, "Failed to load unchecked round store")
}
s.hostList = hostList.NewStore(s.kv)
return s, nil
}
......@@ -300,6 +307,12 @@ func (s *Session) UncheckedRounds() *rounds.UncheckedRoundStore {
return s.uncheckedRounds
}
func (s *Session) HostList() *hostList.Store {
s.mux.RLock()
defer s.mux.RUnlock()
return s.hostList
}
// Get an object from the session
func (s *Session) Get(key string) (*versioned.Object, error) {
return s.kv.Get(key, currentSessionVersion)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment