Skip to content

Commit

Permalink
feat(orm): add mock hooks (cosmos#11135)
Browse files Browse the repository at this point in the history
* feat(orm): add mock hooks

* add hooks

* add tests

* update docs

* Update orm/testing/ormmocks/docs.go

Co-authored-by: Tyler <[email protected]>

* add Backend.WithHooks method

Co-authored-by: Tyler <[email protected]>
  • Loading branch information
aaronc and technicallyty authored Feb 8, 2022
1 parent 4addb73 commit 77ac8fa
Show file tree
Hide file tree
Showing 8 changed files with 190 additions and 6 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ mocks: $(MOCKS_DIR)
$(mockgen_cmd) -source=types/router.go -package mocks -destination tests/mocks/types_router.go
$(mockgen_cmd) -package mocks -destination tests/mocks/grpc_server.go github.com/gogo/protobuf/grpc Server
$(mockgen_cmd) -package mocks -destination tests/mocks/tendermint_tendermint_libs_log_DB.go github.com/tendermint/tendermint/libs/log Logger
$(mockgen_cmd) -source=orm/model/ormtable/hooks.go -package ormmocks -destination orm/testing/ormmocks/hooks.go
.PHONY: mocks

$(MOCKS_DIR):
Expand Down
1 change: 1 addition & 0 deletions orm/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/cosmos/cosmos-proto v1.0.0-alpha7
github.com/cosmos/cosmos-sdk/api v0.1.0-alpha4
github.com/cosmos/cosmos-sdk/errors v1.0.0-beta.2
github.com/golang/mock v1.6.0
github.com/iancoleman/strcase v0.2.0
github.com/stretchr/testify v1.7.0
github.com/tendermint/tm-db v0.6.6
Expand Down
7 changes: 7 additions & 0 deletions orm/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,10 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
Expand Down Expand Up @@ -154,6 +157,7 @@ github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljT
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
Expand All @@ -167,6 +171,7 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
Expand All @@ -191,6 +196,7 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
Expand Down Expand Up @@ -228,6 +234,7 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
60 changes: 54 additions & 6 deletions orm/model/ormdb/module_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import (
"strings"
"testing"

"github.com/golang/mock/gomock"

"github.com/cosmos/cosmos-sdk/orm/testing/ormmocks"

"google.golang.org/protobuf/reflect/protoreflect"
"gotest.tools/v3/assert"
"gotest.tools/v3/golden"
Expand All @@ -34,6 +38,19 @@ type keeper struct {
store testpb.BankStore
}

func NewKeeper(db ormdb.ModuleDB) (Keeper, error) {
store, err := testpb.NewBankStore(db)
return keeper{store}, err
}

type Keeper interface {
Send(ctx context.Context, from, to, denom string, amount uint64) error
Mint(ctx context.Context, acct, denom string, amount uint64) error
Burn(ctx context.Context, acct, denom string, amount uint64) error
Balance(ctx context.Context, acct, denom string) (uint64, error)
Supply(ctx context.Context, denom string) (uint64, error)
}

func (k keeper) Send(ctx context.Context, from, to, denom string, amount uint64) error {
err := k.safeSubBalance(ctx, from, denom, amount)
if err != nil {
Expand Down Expand Up @@ -151,11 +168,6 @@ func (k keeper) safeSubBalance(ctx context.Context, acct, denom string, amount u
}
}

func newKeeper(db ormdb.ModuleDB) (keeper, error) {
store, err := testpb.NewBankStore(db)
return keeper{store}, err
}

func TestModuleDB(t *testing.T) {
// create db & debug context
db, err := ormdb.NewModuleDB(TestBankSchema, ormdb.ModuleDBOptions{})
Expand All @@ -171,7 +183,7 @@ func TestModuleDB(t *testing.T) {
))

// create keeper
k, err := newKeeper(db)
k, err := NewKeeper(db)
assert.NilError(t, err)

// mint coins
Expand Down Expand Up @@ -250,3 +262,39 @@ func TestModuleDB(t *testing.T) {
assert.NilError(t, db.ImportJSON(ctx2, source))
testkv.AssertBackendsEqual(t, backend, backend2)
}

func TestHooks(t *testing.T) {
ctrl := gomock.NewController(t)
db, err := ormdb.NewModuleDB(TestBankSchema, ormdb.ModuleDBOptions{})
assert.NilError(t, err)
hooks := ormmocks.NewMockHooks(ctrl)
ctx := ormtable.WrapContextDefault(ormtest.NewMemoryBackend().WithHooks(hooks))
k, err := NewKeeper(db)
assert.NilError(t, err)

denom := "foo"
acct1 := "bob"
acct2 := "sally"

hooks.EXPECT().OnInsert(ormmocks.Eq(&testpb.Balance{Address: acct1, Denom: denom, Amount: 10}))
hooks.EXPECT().OnInsert(ormmocks.Eq(&testpb.Supply{Denom: denom, Amount: 10}))
assert.NilError(t, k.Mint(ctx, acct1, denom, 10))

hooks.EXPECT().OnUpdate(
ormmocks.Eq(&testpb.Balance{Address: acct1, Denom: denom, Amount: 10}),
ormmocks.Eq(&testpb.Balance{Address: acct1, Denom: denom, Amount: 5}),
)
hooks.EXPECT().OnInsert(
ormmocks.Eq(&testpb.Balance{Address: acct2, Denom: denom, Amount: 5}),
)
assert.NilError(t, k.Send(ctx, acct1, acct2, denom, 5))

hooks.EXPECT().OnUpdate(
ormmocks.Eq(&testpb.Supply{Denom: denom, Amount: 10}),
ormmocks.Eq(&testpb.Supply{Denom: denom, Amount: 5}),
)
hooks.EXPECT().OnDelete(
ormmocks.Eq(&testpb.Balance{Address: acct1, Denom: denom, Amount: 5}),
)
assert.NilError(t, k.Burn(ctx, acct1, denom, 5))
}
8 changes: 8 additions & 0 deletions orm/model/ormtable/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ type Backend interface {

// Hooks returns a Hooks instance or nil.
Hooks() Hooks

// WithHooks returns a copy of this backend with the provided hooks.
WithHooks(Hooks) Backend
}

// ReadBackendOptions defines options for creating a ReadBackend.
Expand Down Expand Up @@ -82,6 +85,11 @@ type backend struct {
hooks Hooks
}

func (c backend) WithHooks(hooks Hooks) Backend {
c.hooks = hooks
return c
}

func (backend) private() {}

func (c backend) CommitmentStoreReader() kv.ReadonlyStore {
Expand Down
13 changes: 13 additions & 0 deletions orm/testing/ormmocks/docs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Package ormmocks contains generated mocks for orm types that can be used
// in testing. Right now, this package only contains a mock for ormtable.Hooks
// as this useful way for unit testing using an in-memory database. Rather
// than attempting to mock a whole table or database instance, instead
// a mock Hook instance can be passed in to verify that the expected
// insert/update/delete operations are happening in the database.
//
// The Eq function gomock.Matcher that compares protobuf messages can
// be used in gomock EXPECT functions.
//
// See TestHooks in ormdb/module_test.go for examples of how to use
// mock Hooks in a real-world scenario.
package ormmocks
77 changes: 77 additions & 0 deletions orm/testing/ormmocks/hooks.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 29 additions & 0 deletions orm/testing/ormmocks/match.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package ormmocks

import (
"github.com/golang/mock/gomock"
"github.com/google/go-cmp/cmp"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/testing/protocmp"
)

// Code adapted from MIT-licensed https://github.com/budougumi0617/cmpmock/blob/master/diffmatcher.go

// Eq returns a gomock.Matcher which uses go-cmp to compare protobuf messages.
func Eq(message proto.Message) gomock.Matcher {
return &protoEq{message: message}
}

type protoEq struct {
message interface{}
diff string
}

func (p protoEq) Matches(x interface{}) bool {
p.diff = cmp.Diff(x, p.message, protocmp.Transform())
return len(p.diff) == 0
}

func (p protoEq) String() string {
return p.diff
}

0 comments on commit 77ac8fa

Please sign in to comment.