mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-12-25 07:29:38 -05:00
Ref/store ectd (#45)
Improved unit & integration tests for store, by making them independent and test a single thing.
This commit is contained in:
parent
dfee5910b3
commit
4abb483902
@ -147,7 +147,7 @@ type EtcdTransaction struct {
|
||||
|
||||
func (t *EtcdTransaction) Get(request string) ([]byte, error) {
|
||||
if !t.ongoingTransaction {
|
||||
return nil, fmt.Errorf("EtcdTransaction Pointer is nil, but Get function is called")
|
||||
return nil, &TransactionAlreadyCommittedError{op: "Get"}
|
||||
}
|
||||
if value, ok := t.dataInsert[request]; ok {
|
||||
return value, nil
|
||||
@ -161,7 +161,7 @@ func (t *EtcdTransaction) Get(request string) ([]byte, error) {
|
||||
// Put saves a value.
|
||||
func (t *EtcdTransaction) Put(request string, requestData []byte) error {
|
||||
if !t.ongoingTransaction {
|
||||
return fmt.Errorf("EtcdTransaction Pointer is nil, but Put function is called")
|
||||
return &TransactionAlreadyCommittedError{op: "Put"}
|
||||
}
|
||||
t.dataInsert[request] = requestData
|
||||
return nil
|
||||
@ -170,7 +170,7 @@ func (t *EtcdTransaction) Put(request string, requestData []byte) error {
|
||||
// Delete deletes the key if it exists. Only errors if there is no ongoing Transaction.
|
||||
func (t *EtcdTransaction) Delete(key string) error {
|
||||
if !t.ongoingTransaction {
|
||||
return fmt.Errorf("EtcdTransaction Pointer is nil, but Delete function is called")
|
||||
return &TransactionAlreadyCommittedError{op: "Delete"}
|
||||
}
|
||||
delete(t.dataInsert, key)
|
||||
t.dataDelete[key] = struct{}{}
|
||||
|
@ -16,7 +16,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
etcdImageName = "bitnami/etcd:3.5.1"
|
||||
etcdImageName = "bitnami/etcd:3.5.2"
|
||||
)
|
||||
|
||||
func TestEtcdStore(t *testing.T) {
|
||||
@ -72,9 +72,21 @@ func TestEtcdStore(t *testing.T) {
|
||||
|
||||
// TODO: since the etcd store does network, it should be canceled with a timeout.
|
||||
testStore(t, func() (Store, error) {
|
||||
clearStore(require, store)
|
||||
return store, nil
|
||||
})
|
||||
|
||||
// Usually call it with a defer statement. However this causes problems with the construct above
|
||||
require.NoError(dockerClient.ContainerStop(ctx, createResp.ID, nil))
|
||||
}
|
||||
|
||||
func clearStore(require *require.Assertions, store Store) {
|
||||
iter, err := store.Iterator("")
|
||||
require.NoError(err)
|
||||
for iter.HasNext() {
|
||||
key, err := iter.GetNext()
|
||||
require.NoError(err)
|
||||
err = store.Delete(key)
|
||||
require.NoError(err)
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +74,11 @@ func (s *StdStore) Iterator(prefix string) (Iterator, error) {
|
||||
|
||||
// BeginTransaction starts a new transaction.
|
||||
func (s *StdStore) BeginTransaction() (Transaction, error) {
|
||||
tx := stdTransaction{store: s, data: map[string]string{}}
|
||||
tx := stdTransaction{
|
||||
store: s,
|
||||
data: map[string]string{},
|
||||
ongoingTransaction: true,
|
||||
}
|
||||
s.txmut.Lock()
|
||||
|
||||
s.mut.Lock()
|
||||
@ -109,12 +113,16 @@ func (s *StdStore) Transfer(newstore Store) error {
|
||||
}
|
||||
|
||||
type stdTransaction struct {
|
||||
store *StdStore
|
||||
data map[string]string
|
||||
store *StdStore
|
||||
data map[string]string
|
||||
ongoingTransaction bool
|
||||
}
|
||||
|
||||
// Get retrieves a value.
|
||||
func (t *stdTransaction) Get(request string) ([]byte, error) {
|
||||
if !t.ongoingTransaction {
|
||||
return nil, &TransactionAlreadyCommittedError{op: "Get"}
|
||||
}
|
||||
if value, ok := t.data[request]; ok {
|
||||
return []byte(value), nil
|
||||
}
|
||||
@ -123,11 +131,17 @@ func (t *stdTransaction) Get(request string) ([]byte, error) {
|
||||
|
||||
// Put saves a value.
|
||||
func (t *stdTransaction) Put(request string, requestData []byte) error {
|
||||
if !t.ongoingTransaction {
|
||||
return &TransactionAlreadyCommittedError{op: "Put"}
|
||||
}
|
||||
t.data[request] = string(requestData)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *stdTransaction) Delete(key string) error {
|
||||
if !t.ongoingTransaction {
|
||||
return &TransactionAlreadyCommittedError{op: "Delete"}
|
||||
}
|
||||
delete(t.data, key)
|
||||
return nil
|
||||
}
|
||||
@ -150,6 +164,7 @@ func (t *stdTransaction) Commit() error {
|
||||
return err
|
||||
}
|
||||
t.store = nil
|
||||
t.ongoingTransaction = false
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -6,18 +6,18 @@ import (
|
||||
|
||||
// Store is the interface for persistence.
|
||||
type Store interface {
|
||||
// BeginTransaction starts a new transaction.
|
||||
BeginTransaction() (Transaction, error)
|
||||
// Get returns a value from store by key.
|
||||
Get(string) ([]byte, error)
|
||||
// Put saves a value to store by key.
|
||||
Put(string, []byte) error
|
||||
// Iterator returns an Iterator for a given prefix.
|
||||
Iterator(string) (Iterator, error)
|
||||
// Transfer copies the whole store Database.
|
||||
Transfer(Store) error
|
||||
// Delete deletes the key.
|
||||
Delete(string) error
|
||||
// Iterator returns an Iterator for a given prefix.
|
||||
Iterator(string) (Iterator, error)
|
||||
// BeginTransaction starts a new transaction.
|
||||
BeginTransaction() (Transaction, error)
|
||||
// Transfer copies the whole store Database.
|
||||
Transfer(Store) error
|
||||
}
|
||||
|
||||
// Transaction is a Store transaction.
|
||||
@ -64,3 +64,13 @@ type NoElementsLeftError struct {
|
||||
func (n *NoElementsLeftError) Error() string {
|
||||
return fmt.Sprintf("index out of range [%d]", n.idx)
|
||||
}
|
||||
|
||||
// TransactionAlreadyCommittedError occurs when further operations:
|
||||
// Get, Put, Delete or Iterate are called on a committed transaction.
|
||||
type TransactionAlreadyCommittedError struct {
|
||||
op string
|
||||
}
|
||||
|
||||
func (t *TransactionAlreadyCommittedError) Error() string {
|
||||
return fmt.Sprintf("transaction is already committed, but %s is called", t.op)
|
||||
}
|
||||
|
@ -17,170 +17,446 @@ var newStore func() (Store, error)
|
||||
|
||||
func testStore(t *testing.T, storeFactory func() (Store, error)) {
|
||||
newStore = storeFactory
|
||||
t.Run("basic", testBasic)
|
||||
t.Run("iterator", testIterator)
|
||||
t.Run("rollback", testRollback)
|
||||
t.Run("testConcurrency", testConcurrency)
|
||||
t.Run("testTransaction", testTransaction)
|
||||
t.Run("testTransactionGet", testTransactionGet)
|
||||
t.Run("testTransactionDelete_1", testTransactionDelete1)
|
||||
t.Run("testTransactionDelete_2", testTransactionDelete2)
|
||||
t.Run("testIteratorKey", testIteratorKey)
|
||||
t.Run("testTransactionIterator", testTransactionIterator)
|
||||
t.Run("testTransactionDeleteIterator", testTransactionDeleteIterator)
|
||||
t.Run("testEmptyIterator", testEmptyIterator)
|
||||
t.Run("testIteratorRace", testIteratorRace)
|
||||
t.Run("testTransactionIteratorPutPrefix", testTransactionIteratorPutPrefix)
|
||||
t.Run("testStoreByValue", testStoreByValue)
|
||||
|
||||
t.Run("NewStore", testNewStore)
|
||||
t.Run("NewStoreIsEmpty", testNewStoreIsEmpty)
|
||||
t.Run("NewStoreClearsStore", testNewStoreClearsStore)
|
||||
t.Run("Put", testPut)
|
||||
t.Run("PutTwice", testPutTwice)
|
||||
t.Run("Get", testGet)
|
||||
t.Run("GetNonExisting", testGetNonExisting)
|
||||
t.Run("Delete", testDelete)
|
||||
t.Run("DeleteNonExisting", testDeleteNonExisting)
|
||||
t.Run("Iterator", testIterator)
|
||||
t.Run("IteratorSingleKey", testIteratorSingleKey)
|
||||
t.Run("IteratorNoValues", testIteratorNoValues)
|
||||
t.Run("IteratorRace", testIteratorRace)
|
||||
t.Run("Transaction", testTransaction)
|
||||
t.Run("TransactionInternalChangesVisible", testTransactionInternalChangesVisible)
|
||||
t.Run("TransactionInternalChangesNotVisibleOutside", testTransactionInternalChangesNotVisibleOutside)
|
||||
t.Run("TransactionNoop", testTransactionNoop)
|
||||
t.Run("TransactionDeleteThenPut", testTransactionDeleteThenPut)
|
||||
t.Run("TransactionDelete", testTransactionDelete)
|
||||
t.Run("TransactionIterator", testTransactionIterator)
|
||||
t.Run("TransactionIterateNotSeeDeleted", testTransactionIterateNotSeeDeleted)
|
||||
t.Run("TransactionGetAfterCommit", testTransactionGetAfterCommit)
|
||||
t.Run("TransactionPutAfterCommit", testTransactionPutAfterCommit)
|
||||
t.Run("TransactionDeleteAfterCommit", testTransactionDeleteAfterCommit)
|
||||
t.Run("RollbackPut", testRollbackPut)
|
||||
t.Run("RollbackDelete", testRollbackDelete)
|
||||
t.Run("Concurrency", testConcurrency)
|
||||
t.Run("StoreByValue", testStoreByValue)
|
||||
t.Run("IndependentTest", testIndependentTest)
|
||||
t.Run("IndependentTestReader", testIndependentTestReader)
|
||||
}
|
||||
|
||||
func testBasic(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
func testNewStore(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
_, err := newStore()
|
||||
|
||||
require.NoError(err)
|
||||
}
|
||||
|
||||
func testNewStoreIsEmpty(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
|
||||
testData1 := []byte("test data")
|
||||
testData2 := []byte("more test data")
|
||||
|
||||
// request unset value
|
||||
_, err = store.Get("test:input")
|
||||
assert.Error(err)
|
||||
|
||||
// test Put method
|
||||
tx, err := store.BeginTransaction()
|
||||
iter, err := store.Iterator("")
|
||||
require.NoError(err)
|
||||
assert.NoError(tx.Put("test:input", testData1))
|
||||
assert.NoError(tx.Put("another:input", testData2))
|
||||
assert.NoError(tx.Commit())
|
||||
require.False(iter.HasNext())
|
||||
}
|
||||
|
||||
// make sure values have been set
|
||||
val, err := store.Get("test:input")
|
||||
func testNewStoreClearsStore(t *testing.T) {
|
||||
require := require.New(t)
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
assert.Equal(testData1, val)
|
||||
val, err = store.Get("another:input")
|
||||
require.NoError(store.Put("key", []byte("value")))
|
||||
|
||||
store, err = newStore()
|
||||
require.NoError(err)
|
||||
assert.Equal(testData2, val)
|
||||
|
||||
_, err = store.Get("invalid:key")
|
||||
assert.Error(err)
|
||||
var unsetErr *ValueUnsetError
|
||||
assert.ErrorAs(err, &unsetErr)
|
||||
assert.NoError(store.Delete("test:input"))
|
||||
assert.NoError(store.Delete("another:input"))
|
||||
iter, err := store.Iterator("")
|
||||
require.NoError(err)
|
||||
require.False(iter.HasNext())
|
||||
}
|
||||
|
||||
_, err = store.Get("test:input")
|
||||
assert.Error(err)
|
||||
func testPut(t *testing.T) {
|
||||
require := require.New(t)
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
|
||||
err = store.Put("key", []byte("value"))
|
||||
|
||||
require.NoError(err)
|
||||
}
|
||||
|
||||
func testPutTwice(t *testing.T) {
|
||||
require := require.New(t)
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
err = store.Put("key", []byte("value"))
|
||||
require.NoError(err)
|
||||
|
||||
err = store.Put("key", []byte("newValue"))
|
||||
require.NoError(err)
|
||||
|
||||
fetchedValue, err := store.Get("key")
|
||||
require.NoError(err)
|
||||
require.Equal([]byte("newValue"), fetchedValue)
|
||||
}
|
||||
|
||||
func testGet(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
err = store.Put("key", []byte("value"))
|
||||
require.NoError(err)
|
||||
|
||||
fetchedValue, err := store.Get("key")
|
||||
|
||||
require.NoError(err)
|
||||
require.Equal([]byte("value"), fetchedValue)
|
||||
}
|
||||
|
||||
func testGetNonExisting(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
|
||||
_, err = store.Get("key")
|
||||
|
||||
var unsetError *ValueUnsetError
|
||||
require.ErrorAs(err, &unsetError)
|
||||
}
|
||||
|
||||
func testDelete(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
err = store.Put("key", []byte("value"))
|
||||
require.NoError(err)
|
||||
|
||||
err = store.Delete("key")
|
||||
require.NoError(err)
|
||||
|
||||
_, err = store.Get("key")
|
||||
var unsetError *ValueUnsetError
|
||||
require.ErrorAs(err, &unsetError)
|
||||
}
|
||||
|
||||
// Deleting a non-existing key is fine, and should not result in error.
|
||||
func testDeleteNonExisting(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
|
||||
err = store.Delete("key")
|
||||
|
||||
require.NoError(err)
|
||||
}
|
||||
|
||||
func testIterator(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
assert := assert.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
require.NoError(store.Put("iterate:1", []byte("one")))
|
||||
require.NoError(store.Put("iterate:2", []byte("two")))
|
||||
require.NoError(store.Put("iterate:3", []byte("three")))
|
||||
|
||||
require.NoError(store.Put("test:1", []byte{0x00, 0x11}))
|
||||
require.NoError(store.Put("test:2", []byte{0x00, 0x11}))
|
||||
require.NoError(store.Put("test:3", []byte{0x00, 0x11}))
|
||||
require.NoError(store.Put("value:1", []byte{0x00, 0x11}))
|
||||
require.NoError(store.Put("something:1", []byte{0x00}))
|
||||
|
||||
iter, err := store.Iterator("test")
|
||||
iter, err := store.Iterator("iterate")
|
||||
require.NoError(err)
|
||||
idx := 0
|
||||
for iter.HasNext() {
|
||||
idx++
|
||||
val, err := iter.GetNext()
|
||||
assert.NoError(err)
|
||||
assert.Contains(val, "test:")
|
||||
assert.Contains(val, "iterate:")
|
||||
}
|
||||
assert.EqualValues(3, idx)
|
||||
|
||||
iter, err = store.Iterator("value")
|
||||
require.NoError(err)
|
||||
idx = 0
|
||||
for iter.HasNext() {
|
||||
idx++
|
||||
val, err := iter.GetNext()
|
||||
assert.NoError(err)
|
||||
assert.Contains(val, "value:")
|
||||
}
|
||||
assert.EqualValues(1, idx)
|
||||
|
||||
iter, err = store.Iterator("")
|
||||
require.NoError(err)
|
||||
idx = 0
|
||||
for iter.HasNext() {
|
||||
idx++
|
||||
_, err = iter.GetNext()
|
||||
assert.NoError(err)
|
||||
}
|
||||
assert.EqualValues(5, idx)
|
||||
|
||||
iter, err = store.Iterator("empty")
|
||||
require.NoError(err)
|
||||
assert.False(iter.HasNext())
|
||||
|
||||
_, err = iter.GetNext()
|
||||
assert.Error(err)
|
||||
|
||||
require.NoError(store.Delete("test:2"))
|
||||
require.NoError(store.Delete("test:1"))
|
||||
require.NoError(store.Delete("test:3"))
|
||||
require.NoError(store.Delete("value:1"))
|
||||
require.NoError(store.Delete("something:1"))
|
||||
assert.Equal(3, idx)
|
||||
}
|
||||
|
||||
func testRollback(t *testing.T) {
|
||||
func testIteratorSingleKey(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
require.NoError(store.Put("key", []byte("value")))
|
||||
|
||||
iter, err := store.Iterator("key")
|
||||
require.NoError(err)
|
||||
key, err := iter.GetNext()
|
||||
require.NoError(err)
|
||||
assert.Equal("key", key)
|
||||
require.False(iter.HasNext())
|
||||
}
|
||||
|
||||
func testIteratorNoValues(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
|
||||
iter, err := store.Iterator("iterate")
|
||||
require.NoError(err)
|
||||
require.False(iter.HasNext())
|
||||
|
||||
_, err = iter.GetNext()
|
||||
var noElementsLeftError *NoElementsLeftError
|
||||
require.ErrorAs(err, &noElementsLeftError)
|
||||
}
|
||||
|
||||
// Test the race condition in the stdStore.
|
||||
// This must be specified in the test through [-race].
|
||||
func testIteratorRace(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
tx, err := store.BeginTransaction()
|
||||
require.NoError(err)
|
||||
require.NoError(tx.Put("key", []byte("value")))
|
||||
go func() {
|
||||
_, err = store.Iterator("key")
|
||||
require.NoError(err)
|
||||
}()
|
||||
require.NoError(tx.Commit())
|
||||
}
|
||||
|
||||
func testTransaction(t *testing.T) {
|
||||
require := require.New(t)
|
||||
assert := assert.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
require.NoError(store.Put("toBeDeleted", []byte{0x00, 0x00}))
|
||||
|
||||
tx1, err := store.BeginTransaction()
|
||||
require.NoError(err)
|
||||
require.NoError(tx1.Put("newKey", []byte{0x11, 0x11}))
|
||||
require.NoError(tx1.Delete("toBeDeleted"))
|
||||
require.NoError(tx1.Commit())
|
||||
|
||||
result, err := store.Get("newKey")
|
||||
require.NoError(err)
|
||||
assert.Equal(result, []byte{0x11, 0x11})
|
||||
_, err = store.Get("toBeDeleted")
|
||||
require.Error(err)
|
||||
}
|
||||
|
||||
func testTransactionInternalChangesVisible(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
|
||||
tx, err := store.BeginTransaction()
|
||||
require.NoError(err)
|
||||
require.NoError(tx.Put(("key"), []byte("value")))
|
||||
|
||||
fetchedValue, err := tx.Get("key")
|
||||
require.NoError(err)
|
||||
require.Equal([]byte("value"), fetchedValue)
|
||||
}
|
||||
|
||||
func testTransactionInternalChangesNotVisibleOutside(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
|
||||
tx, err := store.BeginTransaction()
|
||||
require.NoError(err)
|
||||
require.NoError(tx.Put(("key"), []byte("value")))
|
||||
|
||||
_, err = store.Get("key")
|
||||
var valueUnsetError *ValueUnsetError
|
||||
require.ErrorAs(err, &valueUnsetError)
|
||||
}
|
||||
|
||||
func testTransactionNoop(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
|
||||
tx, err := store.BeginTransaction()
|
||||
require.NoError(err)
|
||||
require.NotNil(tx)
|
||||
|
||||
err = tx.Commit()
|
||||
require.NoError(err)
|
||||
}
|
||||
|
||||
func testTransactionDeleteThenPut(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
require.NoError(store.Put("key", []byte{0x00, 0x00}))
|
||||
|
||||
tx1, err := store.BeginTransaction()
|
||||
require.NoError(err)
|
||||
|
||||
require.NoError(tx1.Put("key", []byte{0x00, 0x11}))
|
||||
assert.NoError(tx1.Delete("key"))
|
||||
require.NoError(tx1.Put("key", []byte{0x7, 0x8}))
|
||||
assert.NoError(tx1.Commit())
|
||||
|
||||
result, err := store.Get("key")
|
||||
assert.NoError(err)
|
||||
assert.Equal([]byte{0x7, 0x8}, result)
|
||||
}
|
||||
|
||||
func testTransactionDelete(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
require.NoError(store.Put("key", []byte{0x00, 0x00}))
|
||||
|
||||
tx1, err := store.BeginTransaction()
|
||||
require.NoError(err)
|
||||
assert.NoError(tx1.Delete("key"))
|
||||
_, err = tx1.Get("key")
|
||||
assert.Error(err)
|
||||
assert.NoError(tx1.Commit())
|
||||
}
|
||||
|
||||
func testTransactionIterator(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
require.NoError(store.Put("key", []byte("value")))
|
||||
|
||||
tx, err := store.BeginTransaction()
|
||||
require.NoError(err)
|
||||
iter, err := tx.Iterator("key")
|
||||
require.NoError(err)
|
||||
key, err := iter.GetNext()
|
||||
require.NoError(err)
|
||||
assert.Equal("key", key)
|
||||
require.NoError(tx.Commit())
|
||||
require.False(iter.HasNext())
|
||||
}
|
||||
|
||||
func testTransactionIterateNotSeeDeleted(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
require.NoError(store.Put("key:1", []byte("value")))
|
||||
require.NoError(store.Put("key:2", []byte("otherValue")))
|
||||
|
||||
tx, err := store.BeginTransaction()
|
||||
require.NoError(err)
|
||||
require.NoError(tx.Delete("key:1"))
|
||||
iter, err := tx.Iterator("key")
|
||||
require.NoError(err)
|
||||
key, err := iter.GetNext()
|
||||
require.NoError(err)
|
||||
assert.Equal("key:2", key)
|
||||
require.NoError(tx.Commit())
|
||||
require.False(iter.HasNext())
|
||||
}
|
||||
|
||||
func testTransactionGetAfterCommit(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
|
||||
tx, err := store.BeginTransaction()
|
||||
require.NoError(err)
|
||||
require.NoError(tx.Put("key", []byte("value")))
|
||||
require.NoError(tx.Commit())
|
||||
|
||||
_, err = tx.Get("key")
|
||||
var alreadyCommittedError *TransactionAlreadyCommittedError
|
||||
require.ErrorAs(err, &alreadyCommittedError)
|
||||
}
|
||||
|
||||
func testTransactionPutAfterCommit(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
|
||||
tx, err := store.BeginTransaction()
|
||||
require.NoError(err)
|
||||
require.NoError(tx.Put("key", []byte("value")))
|
||||
require.NoError(tx.Commit())
|
||||
|
||||
err = tx.Put("key", []byte("newValue"))
|
||||
var alreadyCommittedError *TransactionAlreadyCommittedError
|
||||
require.ErrorAs(err, &alreadyCommittedError)
|
||||
}
|
||||
|
||||
func testTransactionDeleteAfterCommit(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
|
||||
tx, err := store.BeginTransaction()
|
||||
require.NoError(err)
|
||||
require.NoError(tx.Put("key", []byte("value")))
|
||||
require.NoError(tx.Commit())
|
||||
|
||||
err = tx.Delete("key")
|
||||
var alreadyCommittedError *TransactionAlreadyCommittedError
|
||||
require.ErrorAs(err, &alreadyCommittedError)
|
||||
}
|
||||
|
||||
func testRollbackPut(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
|
||||
testData1 := []byte("test data")
|
||||
testData2 := []byte("more test data")
|
||||
testData3 := []byte("and even more data")
|
||||
|
||||
// save data to store and seal
|
||||
tx, err := store.BeginTransaction()
|
||||
require.NoError(err)
|
||||
assert.NoError(tx.Put("test:input", testData1))
|
||||
assert.NoError(tx.Commit())
|
||||
|
||||
// save more data to store
|
||||
tx, err = store.BeginTransaction()
|
||||
err = tx.Put("key", []byte("value"))
|
||||
require.NoError(err)
|
||||
assert.NoError(tx.Put("another:input", testData2))
|
||||
|
||||
// rollback and verify only testData1 exists
|
||||
tx.Rollback()
|
||||
val, err := store.Get("test:input")
|
||||
require.NoError(err)
|
||||
assert.Equal(testData1, val)
|
||||
_, err = store.Get("another:input")
|
||||
assert.Error(err)
|
||||
|
||||
// save something new
|
||||
tx, err = store.BeginTransaction()
|
||||
require.NoError(err)
|
||||
assert.NoError(tx.Put("last:input", testData3))
|
||||
assert.NoError(tx.Commit())
|
||||
_, err = store.Get("key")
|
||||
var valueUnsetError *ValueUnsetError
|
||||
assert.ErrorAs(err, &valueUnsetError)
|
||||
}
|
||||
|
||||
// verify values
|
||||
val, err = store.Get("test:input")
|
||||
require.NoError(err)
|
||||
assert.Equal(testData1, val)
|
||||
val, err = store.Get("last:input")
|
||||
require.NoError(err)
|
||||
assert.Equal(testData3, val)
|
||||
_, err = store.Get("another:input")
|
||||
assert.Error(err)
|
||||
func testRollbackDelete(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
assert.NoError(store.Delete("test:input"))
|
||||
assert.NoError(store.Delete("last:input"))
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
require.NoError(store.Put("key", []byte("value")))
|
||||
|
||||
tx, err := store.BeginTransaction()
|
||||
require.NoError(err)
|
||||
err = tx.Delete("key")
|
||||
require.NoError(err)
|
||||
tx.Rollback()
|
||||
|
||||
fetchedValue, err := store.Get("key")
|
||||
require.NoError(err)
|
||||
assert.Equal([]byte("value"), fetchedValue)
|
||||
}
|
||||
|
||||
// Test explicitly the storeLocking mechanism.
|
||||
@ -193,7 +469,7 @@ func testConcurrency(t *testing.T) {
|
||||
require.NoError(err)
|
||||
tx1, err := store.BeginTransaction()
|
||||
require.NoError(err)
|
||||
assert.NoError(tx1.Put("concurrent", []byte{0x00, 0x00}))
|
||||
assert.NoError(tx1.Put("key", []byte("one")))
|
||||
|
||||
go func() {
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
@ -201,202 +477,14 @@ func testConcurrency(t *testing.T) {
|
||||
}()
|
||||
tx2, err := store.BeginTransaction()
|
||||
require.NoError(err)
|
||||
result, err := tx2.Get("concurrent")
|
||||
result, err := tx2.Get("key")
|
||||
require.NoError(err)
|
||||
assert.Equal(result, []byte{0x00, 0x00})
|
||||
assert.NoError(tx2.Put("concurrent", []byte{0x11, 0x11}))
|
||||
assert.Equal(result, []byte("one"))
|
||||
assert.NoError(tx2.Put("key", []byte("two")))
|
||||
assert.NoError(tx2.Commit())
|
||||
result, err = store.Get("concurrent")
|
||||
result, err = store.Get("key")
|
||||
require.NoError(err)
|
||||
assert.Equal(result, []byte{0x11, 0x11})
|
||||
|
||||
assert.NoError(store.Delete("concurrent"))
|
||||
}
|
||||
|
||||
func testTransaction(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
assert.NoError(store.Put("transactionDelete", []byte{0x00, 0x00}))
|
||||
|
||||
tx1, err := store.BeginTransaction()
|
||||
require.NoError(err)
|
||||
assert.NoError(tx1.Put("transaction", []byte{0x11, 0x11}))
|
||||
assert.NoError(tx1.Delete("transactionDelete"))
|
||||
assert.NoError(tx1.Commit())
|
||||
|
||||
result, err := store.Get("transaction")
|
||||
require.NoError(err)
|
||||
assert.Equal(result, []byte{0x11, 0x11})
|
||||
_, err = store.Get("transactionDelete")
|
||||
assert.Error(err)
|
||||
}
|
||||
|
||||
func testTransactionGet(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
require.NoError(store.Put("transactionGet", []byte{0x00, 0x00}))
|
||||
|
||||
tx1, err := store.BeginTransaction()
|
||||
require.NoError(err)
|
||||
result, err := tx1.Get("transactionGet")
|
||||
require.NoError(err)
|
||||
assert.Equal(result, []byte{0x00, 0x00})
|
||||
assert.NoError(tx1.Put("transactionGet", []byte{0x11, 0x11}))
|
||||
result, err = tx1.Get("transactionGet")
|
||||
require.NoError(err)
|
||||
assert.Equal(result, []byte{0x11, 0x11})
|
||||
assert.NoError(tx1.Commit())
|
||||
|
||||
result, err = store.Get("transactionGet")
|
||||
require.Equal(result, []byte{0x11, 0x11})
|
||||
assert.NoError(err)
|
||||
}
|
||||
|
||||
func testTransactionDelete1(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
require.NoError(store.Put("transactionDelete", []byte{0x00, 0x00}))
|
||||
|
||||
tx1, err := store.BeginTransaction()
|
||||
require.NoError(err)
|
||||
|
||||
require.NoError(tx1.Put("transactionDelete", []byte{0x00, 0x11}))
|
||||
assert.NoError(tx1.Delete("transactionDelete"))
|
||||
require.NoError(tx1.Put("transactionDelete", []byte{0x7, 0x8}))
|
||||
assert.NoError(tx1.Commit())
|
||||
|
||||
result, err := store.Get("transactionDelete")
|
||||
assert.NoError(err)
|
||||
assert.Equal([]byte{0x7, 0x8}, result)
|
||||
}
|
||||
|
||||
func testTransactionDelete2(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
require.NoError(store.Put("transactionDelete", []byte{0x00, 0x00}))
|
||||
|
||||
tx1, err := store.BeginTransaction()
|
||||
require.NoError(err)
|
||||
assert.NoError(tx1.Delete("transactionDelete"))
|
||||
_, err = tx1.Get("transactionDelete")
|
||||
assert.Error(err)
|
||||
assert.NoError(tx1.Commit())
|
||||
}
|
||||
|
||||
func testIteratorKey(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
require.NoError(store.Put("testIteratorKey", []byte{0xF1, 0xFF}))
|
||||
iter, err := store.Iterator("testIteratorKey")
|
||||
require.NoError(err)
|
||||
key, err := iter.GetNext()
|
||||
require.NoError(err)
|
||||
assert.Equal("testIteratorKey", key)
|
||||
}
|
||||
|
||||
func testTransactionIterator(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
require.NoError(store.Put("testTransactionIterator", []byte{0xF1, 0xFF}))
|
||||
txdata, err := store.BeginTransaction()
|
||||
require.NoError(err)
|
||||
iter, err := txdata.Iterator("testTransactionIterator")
|
||||
require.NoError(err)
|
||||
key, err := iter.GetNext()
|
||||
require.NoError(err)
|
||||
assert.Equal("testTransactionIterator", key)
|
||||
require.NoError(txdata.Commit())
|
||||
}
|
||||
|
||||
func testTransactionDeleteIterator(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
require.NoError(store.Put("testTransactionDeleteIterator", []byte{0xF1, 0xFF}))
|
||||
require.NoError(store.Put("testTransactionDeleteIterator15", []byte{0x5, 0xFF}))
|
||||
txdata, err := store.BeginTransaction()
|
||||
require.NoError(err)
|
||||
require.NoError(txdata.Delete("testTransactionDeleteIterator"))
|
||||
iter, err := txdata.Iterator("testTransactionDeleteIterator")
|
||||
require.NoError(err)
|
||||
key, err := iter.GetNext()
|
||||
require.NoError(err)
|
||||
assert.Equal("testTransactionDeleteIterator15", key)
|
||||
require.NoError(txdata.Commit())
|
||||
}
|
||||
|
||||
func testEmptyIterator(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
txdata, err := store.BeginTransaction()
|
||||
require.NoError(err)
|
||||
iter, err := txdata.Iterator("ThisKeyTestsAnEmptyIterator")
|
||||
require.NoError(err)
|
||||
assert.False(iter.HasNext())
|
||||
_, err = iter.GetNext()
|
||||
assert.Error(err)
|
||||
require.NoError(txdata.Commit())
|
||||
}
|
||||
|
||||
// Test the race condition in the stdStore.
|
||||
// This must be specified in the test through [-race].
|
||||
func testIteratorRace(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
tx, err := store.BeginTransaction()
|
||||
require.NoError(err)
|
||||
require.NoError(tx.Put("testIteratorRace", []byte{1, 2, 3, 4, 5}))
|
||||
go func() {
|
||||
_, err = store.Iterator("testIteratorRace")
|
||||
require.NoError(err)
|
||||
}()
|
||||
require.NoError(tx.Commit())
|
||||
}
|
||||
|
||||
func testTransactionIteratorPutPrefix(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
expectedKey := "testTransactionIteratorPutPrefix"
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
txdata, err := store.BeginTransaction()
|
||||
require.NoError(err)
|
||||
require.NoError(txdata.Put("IteratorNotInIterator", []byte{0xF2, 0xFF}))
|
||||
require.NoError(txdata.Put(expectedKey, []byte{0xF1, 0xFF}))
|
||||
iter, err := txdata.Iterator(expectedKey)
|
||||
require.NoError(err)
|
||||
require.True(iter.HasNext())
|
||||
key, err := iter.GetNext()
|
||||
require.NoError(err)
|
||||
assert.Equal(expectedKey, key)
|
||||
assert.False(iter.HasNext())
|
||||
assert.NoError(txdata.Commit())
|
||||
assert.Equal(result, []byte("two"))
|
||||
}
|
||||
|
||||
func testStoreByValue(t *testing.T) {
|
||||
@ -407,8 +495,38 @@ func testStoreByValue(t *testing.T) {
|
||||
require.NoError(err)
|
||||
testValue := []byte{0xF1, 0xFF}
|
||||
require.NoError(store.Put("StoreByValue", testValue))
|
||||
|
||||
testValue[0] = 0x00
|
||||
storeValue, err := store.Get("StoreByValue")
|
||||
|
||||
require.NoError(err)
|
||||
assert.NotEqual(storeValue[0], testValue[0])
|
||||
}
|
||||
|
||||
func testIndependentTest(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
|
||||
err = store.Put("uniqueTestKey253", []byte("value"))
|
||||
require.NoError(err)
|
||||
|
||||
value, err := store.Get("uniqueTestKey253")
|
||||
require.NoError(err)
|
||||
assert.Equal([]byte("value"), value)
|
||||
}
|
||||
|
||||
func testIndependentTestReader(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
store, err := newStore()
|
||||
require.NoError(err)
|
||||
|
||||
// This test should not see & depend on the key `testIndependentTest` sets.
|
||||
_, err = store.Get("uniqueTestKey253")
|
||||
var unsetErr *ValueUnsetError
|
||||
assert.ErrorAs(err, &unsetErr)
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ for the termination. However, to keep the code clean, we accept this tradeoff an
|
||||
const (
|
||||
publicgRPCPort = "9000"
|
||||
constellationImageName = "constellation:latest"
|
||||
etcdImageName = "bitnami/etcd:3.5.1"
|
||||
etcdImageName = "bitnami/etcd:3.5.2"
|
||||
etcdOverlayNetwork = "constellationIntegrationTest"
|
||||
masterSecret = "ConstellationIntegrationTest"
|
||||
numberFirstActivation = 3
|
||||
|
Loading…
Reference in New Issue
Block a user