diff --git a/core/testing/context.go b/core/testing/context.go index 7cda34ab7043..99881c32684e 100644 --- a/core/testing/context.go +++ b/core/testing/context.go @@ -3,6 +3,9 @@ package coretesting import ( "context" + "google.golang.org/protobuf/runtime/protoiface" + + "cosmossdk.io/core/event" "cosmossdk.io/core/store" ) @@ -10,7 +13,9 @@ type dummyKey struct{} func Context() context.Context { dummy := &dummyCtx{ - stores: map[string]store.KVStore{}, + stores: map[string]store.KVStore{}, + events: map[string][]event.Event{}, + protoEvents: map[string][]protoiface.MessageV1{}, } ctx := context.WithValue(context.Background(), dummyKey{}, dummy) @@ -18,7 +23,12 @@ func Context() context.Context { } type dummyCtx struct { + // maps store by the actor. stores map[string]store.KVStore + // maps event emitted by the actor. + events map[string][]event.Event + // maps proto events emitted by the actor. + protoEvents map[string][]protoiface.MessageV1 } func unwrap(ctx context.Context) *dummyCtx { diff --git a/core/testing/event.go b/core/testing/event.go new file mode 100644 index 000000000000..e4d113a4886c --- /dev/null +++ b/core/testing/event.go @@ -0,0 +1,50 @@ +package coretesting + +import ( + "context" + + "google.golang.org/protobuf/runtime/protoiface" + + "cosmossdk.io/core/event" +) + +var _ event.Service = (*MemEventsService)(nil) + +// EventsService attaches an event service to the context. +// Adding an existing module will reset the events. +func EventsService(ctx context.Context, moduleName string) MemEventsService { + unwrap(ctx).events[moduleName] = nil + unwrap(ctx).protoEvents[moduleName] = nil + return MemEventsService{moduleName: moduleName} +} + +type MemEventsService struct { + moduleName string +} + +func (e MemEventsService) EventManager(ctx context.Context) event.Manager { + return eventManager{moduleName: e.moduleName, ctx: unwrap(ctx)} +} + +func (e MemEventsService) GetEvents(ctx context.Context) []event.Event { + return unwrap(ctx).events[e.moduleName] +} + +func (e MemEventsService) GetProtoEvents(ctx context.Context) []protoiface.MessageV1 { + return unwrap(ctx).protoEvents[e.moduleName] +} + +type eventManager struct { + moduleName string + ctx *dummyCtx +} + +func (e eventManager) Emit(event protoiface.MessageV1) error { + e.ctx.protoEvents[e.moduleName] = append(e.ctx.protoEvents[e.moduleName], event) + return nil +} + +func (e eventManager) EmitKV(eventType string, attrs ...event.Attribute) error { + e.ctx.events[e.moduleName] = append(e.ctx.events[e.moduleName], event.NewEvent(eventType, attrs...)) + return nil +} diff --git a/core/testing/event_test.go b/core/testing/event_test.go new file mode 100644 index 000000000000..b94e39709cf1 --- /dev/null +++ b/core/testing/event_test.go @@ -0,0 +1,38 @@ +package coretesting + +import ( + "testing" + + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/runtime/protoiface" + "google.golang.org/protobuf/types/known/wrapperspb" + + "cosmossdk.io/core/event" +) + +func TestEventsService(t *testing.T) { + ctx := Context() + es := EventsService(ctx, "auth") + + wantProtoEvent := &wrapperspb.BoolValue{Value: true} + err := es.EventManager(ctx).Emit(wantProtoEvent) + require.NoError(t, err) + + wantEvent := event.NewEvent("new-account", event.Attribute{ + Key: "number", + Value: "1", + }) + err = es.EventManager(ctx).EmitKV(wantEvent.Type, wantEvent.Attributes...) + require.NoError(t, err) + + gotProtoEvents := es.GetProtoEvents(ctx) + require.Equal(t, []protoiface.MessageV1{wantProtoEvent}, gotProtoEvents) + + gotEvents := es.GetEvents(ctx) + require.Equal(t, []event.Event{wantEvent}, gotEvents) + + // test reset + es = EventsService(ctx, "auth") + require.Nil(t, es.GetEvents(ctx)) + require.Nil(t, es.GetProtoEvents(ctx)) +} diff --git a/core/testing/go.mod b/core/testing/go.mod index 8dace714ae8b..7f5d44dfe3f5 100644 --- a/core/testing/go.mod +++ b/core/testing/go.mod @@ -8,6 +8,7 @@ require ( cosmossdk.io/core v0.12.0 github.com/stretchr/testify v1.9.0 github.com/tidwall/btree v1.7.0 + google.golang.org/protobuf v1.34.2 ) require ( diff --git a/core/testing/go.sum b/core/testing/go.sum index 47b87a45f871..498794a52841 100644 --- a/core/testing/go.sum +++ b/core/testing/go.sum @@ -1,6 +1,7 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= @@ -11,6 +12,8 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI= github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/core/testing/memdb.go b/core/testing/memdb.go index b3f257127583..b137319752af 100644 --- a/core/testing/memdb.go +++ b/core/testing/memdb.go @@ -17,16 +17,16 @@ const ( var errKeyEmpty = errors.New("key cannot be empty") -var _ store.KVStore = (*memDB)(nil) +var _ store.KVStore = (*MemKV)(nil) -// memDB a lightweight memory db -type memDB struct { +// MemKV a lightweight memory db +type MemKV struct { tree *btree.BTreeG[item] } -// newMemDB creates a wrapper around `btree.BTreeG`. -func newMemDB() memDB { - return memDB{ +// NewMemKV creates a wrapper around `btree.BTreeG`. +func NewMemKV() MemKV { + return MemKV{ tree: btree.NewBTreeGOptions(byKeys, btree.Options{ Degree: bTreeDegree, NoLocks: true, @@ -35,25 +35,25 @@ func newMemDB() memDB { } // set adds a new key-value pair to the change set's tree. -func (bt memDB) set(key, value []byte) { +func (bt MemKV) set(key, value []byte) { bt.tree.Set(newItem(key, value)) } -// get retrieves the value associated with the given key from the memDB's tree. -func (bt memDB) get(key []byte) (value []byte, found bool) { +// get retrieves the value associated with the given key from the MemKV's tree. +func (bt MemKV) get(key []byte) (value []byte, found bool) { it, found := bt.tree.Get(item{key: key}) return it.value, found } // delete removes the value associated with the given key from the change set. // If the key does not exist in the change set, this method does nothing. -func (bt memDB) delete(key []byte) { +func (bt MemKV) delete(key []byte) { bt.tree.Delete(item{key: key}) } -// iterator returns a new iterator over the key-value pairs in the memDB +// iterator returns a new iterator over the key-value pairs in the MemKV // that have keys greater than or equal to the start key and less than the end key. -func (bt memDB) iterator(start, end []byte) (store.Iterator, error) { +func (bt MemKV) iterator(start, end []byte) (store.Iterator, error) { if (start != nil && len(start) == 0) || (end != nil && len(end) == 0) { return nil, errKeyEmpty } @@ -61,9 +61,9 @@ func (bt memDB) iterator(start, end []byte) (store.Iterator, error) { } // reverseIterator returns a new iterator that iterates over the key-value pairs in reverse order -// within the specified range [start, end) in the memDB's tree. +// within the specified range [start, end) in the MemKV's tree. // If start or end is an empty byte slice, it returns an error indicating that the key is empty. -func (bt memDB) reverseIterator(start, end []byte) (store.Iterator, error) { +func (bt MemKV) reverseIterator(start, end []byte) (store.Iterator, error) { if (start != nil && len(start) == 0) || (end != nil && len(end) == 0) { return nil, errKeyEmpty } @@ -72,31 +72,31 @@ func (bt memDB) reverseIterator(start, end []byte) (store.Iterator, error) { // KV impl -func (bt memDB) Get(key []byte) ([]byte, error) { +func (bt MemKV) Get(key []byte) ([]byte, error) { value, _ := bt.get(key) return value, nil } -func (bt memDB) Has(key []byte) (bool, error) { +func (bt MemKV) Has(key []byte) (bool, error) { _, found := bt.get(key) return found, nil } -func (bt memDB) Set(key, value []byte) error { +func (bt MemKV) Set(key, value []byte) error { bt.set(key, value) return nil } -func (bt memDB) Delete(key []byte) error { +func (bt MemKV) Delete(key []byte) error { bt.delete(key) return nil } -func (bt memDB) Iterator(start, end []byte) (store.Iterator, error) { +func (bt MemKV) Iterator(start, end []byte) (store.Iterator, error) { return bt.iterator(start, end) } -func (bt memDB) ReverseIterator(start, end []byte) (store.Iterator, error) { +func (bt MemKV) ReverseIterator(start, end []byte) (store.Iterator, error) { return bt.reverseIterator(start, end) } diff --git a/core/testing/memdb_test.go b/core/testing/memdb_test.go index 263efaa0ae47..d1c753505002 100644 --- a/core/testing/memdb_test.go +++ b/core/testing/memdb_test.go @@ -10,7 +10,7 @@ import ( ) func TestMemDB(t *testing.T) { - var db store.KVStore = newMemDB() + var db store.KVStore = NewMemKV() key, value := []byte("key"), []byte("value") require.NoError(t, db.Set(key, value)) diff --git a/core/testing/store.go b/core/testing/store.go index 8461e8789120..1b8257417b73 100644 --- a/core/testing/store.go +++ b/core/testing/store.go @@ -7,8 +7,10 @@ import ( "cosmossdk.io/core/store" ) +var _ store.KVStoreService = (*kvStoreService)(nil) + func KVStoreService(ctx context.Context, moduleName string) store.KVStoreService { - unwrap(ctx).stores[moduleName] = newMemDB() + unwrap(ctx).stores[moduleName] = NewMemKV() return kvStoreService{ moduleName: moduleName, }