diff --git a/state/state.go b/state/chainstate.go similarity index 100% rename from state/state.go rename to state/chainstate.go diff --git a/state/state_test.go b/state/chainstate_test.go similarity index 100% rename from state/state_test.go rename to state/chainstate_test.go diff --git a/state/kvstore.go b/state/kvstore.go index 51aa450c0..4471c8fd2 100644 --- a/state/kvstore.go +++ b/state/kvstore.go @@ -49,12 +49,16 @@ type SimpleDB interface { //---------------------------------------- +// MemKVStore is a simple implementation of SimpleDB. +// It is only intended for quick testing, not to be used +// in production or with large data stores. type MemKVStore struct { m map[string][]byte } var _ SimpleDB = NewMemKVStore() +// NewMemKVStore initializes a MemKVStore func NewMemKVStore() *MemKVStore { return &MemKVStore{ m: make(map[string][]byte, 0), @@ -83,7 +87,12 @@ func (mkv *MemKVStore) Remove(key []byte) (value []byte) { func (mkv *MemKVStore) List(start, end []byte, limit int) []Model { keys := mkv.keysInRange(start, end) sort.Strings(keys) - keys = keys[:limit] + if limit > 0 && len(keys) > 0 { + if limit > len(keys) { + limit = len(keys) + } + keys = keys[:limit] + } res := make([]Model, len(keys)) for i, k := range keys { diff --git a/state/kvstore_test.go b/state/kvstore_test.go deleted file mode 100644 index e4961c360..000000000 --- a/state/kvstore_test.go +++ /dev/null @@ -1,70 +0,0 @@ -package state - -import ( - "bytes" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestKVStore(t *testing.T) { - assert := assert.New(t) - - //stores to be tested - ms := NewMemKVStore() - store := NewMemKVStore() - kvc := NewKVCache(store) - - //key value pairs to be tested within the system - var keyvalue = []struct { - key string - value string - }{ - {"foo", "snake"}, - {"bar", "mouse"}, - } - - //set the kvc to have all the key value pairs - setRecords := func(kv KVStore) { - for _, n := range keyvalue { - kv.Set([]byte(n.key), []byte(n.value)) - } - } - - //store has all the key value pairs - storeHasAll := func(kv KVStore) bool { - for _, n := range keyvalue { - if !bytes.Equal(kv.Get([]byte(n.key)), []byte(n.value)) { - return false - } - } - return true - } - - //test read/write for MemKVStore - setRecords(ms) - assert.True(storeHasAll(ms), "MemKVStore doesn't retrieve after Set") - - //test read/write for KVCache - setRecords(kvc) - assert.True(storeHasAll(kvc), "KVCache doesn't retrieve after Set") - - //test reset - kvc.Reset() - assert.False(storeHasAll(kvc), "KVCache retrieving after reset") - - //test sync - setRecords(kvc) - assert.False(storeHasAll(store), "store retrieving before synced") - kvc.Sync() - assert.True(storeHasAll(store), "store isn't retrieving after synced") - - //test logging - assert.Zero(len(kvc.GetLogLines()), "logging events existed before using SetLogging") - kvc.SetLogging() - setRecords(kvc) - assert.Equal(len(kvc.GetLogLines()), 2, "incorrect number of logging events recorded") - kvc.ClearLogLines() - assert.Zero(len(kvc.GetLogLines()), "logging events still exists after ClearLogLines") - -} diff --git a/state/store_test.go b/state/store_test.go new file mode 100644 index 000000000..de1d8246a --- /dev/null +++ b/state/store_test.go @@ -0,0 +1,136 @@ +package state + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func GetDBs() []SimpleDB { + return []SimpleDB{ + NewMemKVStore(), + } +} + +// TestKVStore makes sure that get/set/remove operations work, +// as well as list +func TestKVStore(t *testing.T) { + assert := assert.New(t) + + type listQuery struct { + // this is the list query + start, end []byte + limit int + // expected result from List, first element also expected for First + expected []Model + // expected result from Last + last Model + } + + cases := []struct { + toSet []Model + toRemove []Model + toGet []Model + toList []listQuery + }{ + // simple add + { + []Model{ + {[]byte{1}, []byte{2}}, + {[]byte{3}, []byte{4}}, + }, + nil, + []Model{{[]byte{1}, []byte{2}}}, + []listQuery{ + { + []byte{1}, []byte{4}, 0, + // all + []Model{ + {[]byte{1}, []byte{2}}, + {[]byte{3}, []byte{4}}, + }, + // last one + Model{[]byte{3}, []byte{4}}, + }, + { + []byte{1}, []byte{3}, 10, + // all + []Model{ + {[]byte{1}, []byte{2}}, + }, + // last one + Model{[]byte{1}, []byte{2}}, + }, + }, + }, + // over-write data, remove + { + []Model{ + {[]byte{1}, []byte{2}}, + {[]byte{2}, []byte{2}}, + {[]byte{3}, []byte{2}}, + {[]byte{2}, []byte{4}}, + }, + []Model{{[]byte{3}, []byte{2}}}, + []Model{ + {[]byte{1}, []byte{2}}, + {[]byte{2}, []byte{4}}, + {[]byte{3}, nil}, + }, + []listQuery{ + { + []byte{0, 5}, []byte{10}, 1, + // all + []Model{ + {[]byte{1}, []byte{2}}, + }, + // last + Model{[]byte{2}, []byte{4}}, + }, + { + []byte{1, 4}, []byte{1, 7}, 10, + []Model{}, + Model{}, + }, + { + []byte{1, 5}, []byte{10}, 0, + []Model{ + {[]byte{2}, []byte{4}}, + }, + Model{[]byte{2}, []byte{4}}, + }, + }, + }, + } + + for i, tc := range cases { + for j, db := range GetDBs() { + for _, s := range tc.toSet { + db.Set(s.Key, s.Value) + } + for k, r := range tc.toRemove { + val := db.Remove(r.Key) + assert.EqualValues(r.Value, val, "%d/%d/%d", i, j, k) + } + for k, g := range tc.toGet { + val := db.Get(g.Key) + assert.EqualValues(g.Value, val, "%d/%d/%d", i, j, k) + has := db.Has(g.Key) + assert.Equal(len(g.Value) != 0, has, "%d/%d/%d", i, j, k) + } + for k, lq := range tc.toList { + list := db.List(lq.start, lq.end, lq.limit) + if assert.EqualValues(lq.expected, list, "%d/%d/%d", i, j, k) { + var first Model + if len(lq.expected) > 0 { + first = lq.expected[0] + } + f := db.First(lq.start, lq.end) + assert.EqualValues(first, f, "%d/%d/%d", i, j, k) + l := db.Last(lq.start, lq.end) + assert.EqualValues(lq.last, l, "%d/%d/%d", i, j, k) + } + } + } + } +}