Skip to content

Commit 77ac8fa

Browse files
feat(orm): add mock hooks (cosmos#11135)
* feat(orm): add mock hooks * add hooks * add tests * update docs * Update orm/testing/ormmocks/docs.go Co-authored-by: Tyler <48813565+technicallyty@users.noreply.github.com> * add Backend.WithHooks method Co-authored-by: Tyler <48813565+technicallyty@users.noreply.github.com>
1 parent 4addb73 commit 77ac8fa

File tree

8 files changed

+190
-6
lines changed

8 files changed

+190
-6
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ mocks: $(MOCKS_DIR)
151151
$(mockgen_cmd) -source=types/router.go -package mocks -destination tests/mocks/types_router.go
152152
$(mockgen_cmd) -package mocks -destination tests/mocks/grpc_server.go github.com/gogo/protobuf/grpc Server
153153
$(mockgen_cmd) -package mocks -destination tests/mocks/tendermint_tendermint_libs_log_DB.go github.com/tendermint/tendermint/libs/log Logger
154+
$(mockgen_cmd) -source=orm/model/ormtable/hooks.go -package ormmocks -destination orm/testing/ormmocks/hooks.go
154155
.PHONY: mocks
155156

156157
$(MOCKS_DIR):

orm/go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require (
66
github.com/cosmos/cosmos-proto v1.0.0-alpha7
77
github.com/cosmos/cosmos-sdk/api v0.1.0-alpha4
88
github.com/cosmos/cosmos-sdk/errors v1.0.0-beta.2
9+
github.com/golang/mock v1.6.0
910
github.com/iancoleman/strcase v0.2.0
1011
github.com/stretchr/testify v1.7.0
1112
github.com/tendermint/tm-db v0.6.6

orm/go.sum

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,10 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
6363
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
6464
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
6565
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
66+
github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8=
6667
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
68+
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
69+
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
6770
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
6871
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
6972
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -154,6 +157,7 @@ github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljT
154157
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
155158
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
156159
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
160+
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
157161
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
158162
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
159163
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
@@ -167,6 +171,7 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx
167171
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
168172
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
169173
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
174+
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
170175
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
171176
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
172177
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -191,6 +196,7 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
191196
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
192197
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
193198
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
199+
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
194200
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
195201
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
196202
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -228,6 +234,7 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
228234
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
229235
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
230236
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
237+
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
231238
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
232239
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
233240
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

orm/model/ormdb/module_test.go

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ import (
88
"strings"
99
"testing"
1010

11+
"github.com/golang/mock/gomock"
12+
13+
"github.com/cosmos/cosmos-sdk/orm/testing/ormmocks"
14+
1115
"google.golang.org/protobuf/reflect/protoreflect"
1216
"gotest.tools/v3/assert"
1317
"gotest.tools/v3/golden"
@@ -34,6 +38,19 @@ type keeper struct {
3438
store testpb.BankStore
3539
}
3640

41+
func NewKeeper(db ormdb.ModuleDB) (Keeper, error) {
42+
store, err := testpb.NewBankStore(db)
43+
return keeper{store}, err
44+
}
45+
46+
type Keeper interface {
47+
Send(ctx context.Context, from, to, denom string, amount uint64) error
48+
Mint(ctx context.Context, acct, denom string, amount uint64) error
49+
Burn(ctx context.Context, acct, denom string, amount uint64) error
50+
Balance(ctx context.Context, acct, denom string) (uint64, error)
51+
Supply(ctx context.Context, denom string) (uint64, error)
52+
}
53+
3754
func (k keeper) Send(ctx context.Context, from, to, denom string, amount uint64) error {
3855
err := k.safeSubBalance(ctx, from, denom, amount)
3956
if err != nil {
@@ -151,11 +168,6 @@ func (k keeper) safeSubBalance(ctx context.Context, acct, denom string, amount u
151168
}
152169
}
153170

154-
func newKeeper(db ormdb.ModuleDB) (keeper, error) {
155-
store, err := testpb.NewBankStore(db)
156-
return keeper{store}, err
157-
}
158-
159171
func TestModuleDB(t *testing.T) {
160172
// create db & debug context
161173
db, err := ormdb.NewModuleDB(TestBankSchema, ormdb.ModuleDBOptions{})
@@ -171,7 +183,7 @@ func TestModuleDB(t *testing.T) {
171183
))
172184

173185
// create keeper
174-
k, err := newKeeper(db)
186+
k, err := NewKeeper(db)
175187
assert.NilError(t, err)
176188

177189
// mint coins
@@ -250,3 +262,39 @@ func TestModuleDB(t *testing.T) {
250262
assert.NilError(t, db.ImportJSON(ctx2, source))
251263
testkv.AssertBackendsEqual(t, backend, backend2)
252264
}
265+
266+
func TestHooks(t *testing.T) {
267+
ctrl := gomock.NewController(t)
268+
db, err := ormdb.NewModuleDB(TestBankSchema, ormdb.ModuleDBOptions{})
269+
assert.NilError(t, err)
270+
hooks := ormmocks.NewMockHooks(ctrl)
271+
ctx := ormtable.WrapContextDefault(ormtest.NewMemoryBackend().WithHooks(hooks))
272+
k, err := NewKeeper(db)
273+
assert.NilError(t, err)
274+
275+
denom := "foo"
276+
acct1 := "bob"
277+
acct2 := "sally"
278+
279+
hooks.EXPECT().OnInsert(ormmocks.Eq(&testpb.Balance{Address: acct1, Denom: denom, Amount: 10}))
280+
hooks.EXPECT().OnInsert(ormmocks.Eq(&testpb.Supply{Denom: denom, Amount: 10}))
281+
assert.NilError(t, k.Mint(ctx, acct1, denom, 10))
282+
283+
hooks.EXPECT().OnUpdate(
284+
ormmocks.Eq(&testpb.Balance{Address: acct1, Denom: denom, Amount: 10}),
285+
ormmocks.Eq(&testpb.Balance{Address: acct1, Denom: denom, Amount: 5}),
286+
)
287+
hooks.EXPECT().OnInsert(
288+
ormmocks.Eq(&testpb.Balance{Address: acct2, Denom: denom, Amount: 5}),
289+
)
290+
assert.NilError(t, k.Send(ctx, acct1, acct2, denom, 5))
291+
292+
hooks.EXPECT().OnUpdate(
293+
ormmocks.Eq(&testpb.Supply{Denom: denom, Amount: 10}),
294+
ormmocks.Eq(&testpb.Supply{Denom: denom, Amount: 5}),
295+
)
296+
hooks.EXPECT().OnDelete(
297+
ormmocks.Eq(&testpb.Balance{Address: acct1, Denom: denom, Amount: 5}),
298+
)
299+
assert.NilError(t, k.Burn(ctx, acct1, denom, 5))
300+
}

orm/model/ormtable/backend.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ type Backend interface {
3232

3333
// Hooks returns a Hooks instance or nil.
3434
Hooks() Hooks
35+
36+
// WithHooks returns a copy of this backend with the provided hooks.
37+
WithHooks(Hooks) Backend
3538
}
3639

3740
// ReadBackendOptions defines options for creating a ReadBackend.
@@ -82,6 +85,11 @@ type backend struct {
8285
hooks Hooks
8386
}
8487

88+
func (c backend) WithHooks(hooks Hooks) Backend {
89+
c.hooks = hooks
90+
return c
91+
}
92+
8593
func (backend) private() {}
8694

8795
func (c backend) CommitmentStoreReader() kv.ReadonlyStore {

orm/testing/ormmocks/docs.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Package ormmocks contains generated mocks for orm types that can be used
2+
// in testing. Right now, this package only contains a mock for ormtable.Hooks
3+
// as this useful way for unit testing using an in-memory database. Rather
4+
// than attempting to mock a whole table or database instance, instead
5+
// a mock Hook instance can be passed in to verify that the expected
6+
// insert/update/delete operations are happening in the database.
7+
//
8+
// The Eq function gomock.Matcher that compares protobuf messages can
9+
// be used in gomock EXPECT functions.
10+
//
11+
// See TestHooks in ormdb/module_test.go for examples of how to use
12+
// mock Hooks in a real-world scenario.
13+
package ormmocks

orm/testing/ormmocks/hooks.go

Lines changed: 77 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

orm/testing/ormmocks/match.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package ormmocks
2+
3+
import (
4+
"github.com/golang/mock/gomock"
5+
"github.com/google/go-cmp/cmp"
6+
"google.golang.org/protobuf/proto"
7+
"google.golang.org/protobuf/testing/protocmp"
8+
)
9+
10+
// Code adapted from MIT-licensed https://github.com/budougumi0617/cmpmock/blob/master/diffmatcher.go
11+
12+
// Eq returns a gomock.Matcher which uses go-cmp to compare protobuf messages.
13+
func Eq(message proto.Message) gomock.Matcher {
14+
return &protoEq{message: message}
15+
}
16+
17+
type protoEq struct {
18+
message interface{}
19+
diff string
20+
}
21+
22+
func (p protoEq) Matches(x interface{}) bool {
23+
p.diff = cmp.Diff(x, p.message, protocmp.Transform())
24+
return len(p.diff) == 0
25+
}
26+
27+
func (p protoEq) String() string {
28+
return p.diff
29+
}

0 commit comments

Comments
 (0)