-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
test: v2 services helpers and demo using x/bank
#23057
base: main
Are you sure you want to change the base?
Changes from 23 commits
dab4f61
ccdbad4
ef438eb
9dfae05
779bd84
2e7ca6d
6161b6a
7355259
46a30d3
89e5b26
05a8577
f827e73
6c7dc2a
ed63aca
c8b7460
4956083
b5703d4
c7bc69d
1442dc2
9d98953
d29ed65
d42ac1f
a6ffa04
fb55664
87fb4f9
e18d809
4f56126
f67b33f
997bf85
ed61ace
47a681e
9f54324
0cea6dd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package coretesting | ||
|
||
import ( | ||
"context" | ||
|
||
appmodulev2 "cosmossdk.io/core/appmodule/v2" | ||
corecontext "cosmossdk.io/core/context" | ||
corelog "cosmossdk.io/core/log" | ||
"cosmossdk.io/core/router" | ||
"cosmossdk.io/core/store" | ||
) | ||
|
||
type TestEnvironmentConfig struct { | ||
ModuleName string | ||
Logger corelog.Logger | ||
MsgRouter router.Service | ||
QueryRouter router.Service | ||
} | ||
|
||
type TestEnvironment struct { | ||
appmodulev2.Environment | ||
|
||
memEventsService MemEventsService | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Include the concrete types of our |
||
memHeaderService MemHeaderService | ||
} | ||
|
||
func NewTestEnvironment(cfg TestEnvironmentConfig) (TestContext, TestEnvironment) { | ||
ctx := Context() | ||
|
||
memEventService := EventsService(ctx, cfg.ModuleName) | ||
memHeaderService := MemHeaderService{} | ||
|
||
env := TestEnvironment{ | ||
Environment: appmodulev2.Environment{ | ||
Logger: cfg.Logger, | ||
BranchService: nil, | ||
EventService: memEventService, | ||
GasService: MemGasService{}, | ||
HeaderService: memHeaderService, | ||
QueryRouterService: cfg.QueryRouter, | ||
MsgRouterService: cfg.MsgRouter, | ||
TransactionService: MemTransactionService{}, | ||
KVStoreService: KVStoreService(ctx, cfg.ModuleName), | ||
MemStoreService: nil, | ||
}, | ||
memEventsService: memEventService, | ||
memHeaderService: memHeaderService, | ||
} | ||
|
||
// set internal context to point to environment | ||
ctx.Context = context.WithValue(ctx.Context, corecontext.EnvironmentContextKey, env.Environment) | ||
return ctx, env | ||
} | ||
|
||
func (env TestEnvironment) MemEventsService() MemEventsService { | ||
return env.memEventsService | ||
} | ||
|
||
func (env TestEnvironment) KVStoreService() store.KVStoreService { | ||
return env.Environment.KVStoreService | ||
} | ||
|
||
func (env TestEnvironment) HeaderService() MemHeaderService { | ||
return env.memHeaderService | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package coretesting | ||
|
||
import ( | ||
"context" | ||
|
||
"cosmossdk.io/core/gas" | ||
) | ||
|
||
var _ gas.Service = &MemGasService{} | ||
|
||
type MemGasService struct{} | ||
technicallyty marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
func (m MemGasService) GasMeter(ctx context.Context) gas.Meter { | ||
dummy := unwrap(ctx) | ||
|
||
return dummy.gasMeter | ||
} | ||
|
||
func (m MemGasService) GasConfig(ctx context.Context) gas.GasConfig { | ||
dummy := unwrap(ctx) | ||
|
||
return dummy.gasConfig | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package coretesting | ||
|
||
import ( | ||
"context" | ||
|
||
"cosmossdk.io/core/header" | ||
) | ||
|
||
var _ header.Service = &MemHeaderService{} | ||
|
||
type MemHeaderService struct{} | ||
technicallyty marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
func (e MemHeaderService) HeaderInfo(ctx context.Context) header.Info { | ||
return unwrap(ctx).header | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
package coretesting | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"reflect" | ||
|
||
|
||
appmodulev2 "cosmossdk.io/core/appmodule/v2" | ||
"cosmossdk.io/core/router" | ||
"cosmossdk.io/core/transaction" | ||
) | ||
|
||
var ErrNoHandler = errors.New("no handler") | ||
|
||
// NewMsgRouterBuilder is a router that routes messages to their respective handlers. | ||
func NewMsgRouterBuilder() *ReflectionRouterBuilder { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Core-only router builder that uses There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. my IDE says there are no usages of this function - is there an example usage of this somewhere? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would be used for an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. adding comment here from our conversation so we don't forget: resolution here is to add a test that utilizes this code in some way |
||
return &ReflectionRouterBuilder{ | ||
handlers: make(map[string]appmodulev2.HandlerFunc), | ||
preHandlers: make(map[string][]appmodulev2.PreMsgHandler), | ||
postHandlers: make(map[string][]appmodulev2.PostMsgHandler), | ||
} | ||
} | ||
|
||
type ReflectionRouterBuilder struct { | ||
handlers map[string]appmodulev2.HandlerFunc | ||
globalPreHandlers []appmodulev2.PreMsgHandler | ||
preHandlers map[string][]appmodulev2.PreMsgHandler | ||
postHandlers map[string][]appmodulev2.PostMsgHandler | ||
globalPostHandlers []appmodulev2.PostMsgHandler | ||
} | ||
|
||
func (b *ReflectionRouterBuilder) RegisterHandler(msgType string, handler appmodulev2.HandlerFunc) error { | ||
// panic on override | ||
technicallyty marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if _, ok := b.handlers[msgType]; ok { | ||
return fmt.Errorf("handler already registered: %s", msgType) | ||
} | ||
b.handlers[msgType] = handler | ||
return nil | ||
} | ||
|
||
func (b *ReflectionRouterBuilder) RegisterGlobalPreMsgHandler(handler appmodulev2.PreMsgHandler) { | ||
b.globalPreHandlers = append(b.globalPreHandlers, handler) | ||
} | ||
|
||
func (b *ReflectionRouterBuilder) RegisterPreMsgHandler(msgType string, handler appmodulev2.PreMsgHandler) { | ||
b.preHandlers[msgType] = append(b.preHandlers[msgType], handler) | ||
} | ||
|
||
func (b *ReflectionRouterBuilder) RegisterPostMsgHandler(msgType string, handler appmodulev2.PostMsgHandler) { | ||
b.postHandlers[msgType] = append(b.postHandlers[msgType], handler) | ||
} | ||
|
||
func (b *ReflectionRouterBuilder) RegisterGlobalPostMsgHandler(handler appmodulev2.PostMsgHandler) { | ||
b.globalPostHandlers = append(b.globalPostHandlers, handler) | ||
} | ||
|
||
func (b *ReflectionRouterBuilder) HandlerExists(msgType string) bool { | ||
_, ok := b.handlers[msgType] | ||
return ok | ||
} | ||
|
||
func (b *ReflectionRouterBuilder) Build() (ReflectionRouter, error) { | ||
handlers := make(map[string]appmodulev2.HandlerFunc) | ||
|
||
globalPreHandler := func(ctx context.Context, msg transaction.Msg) error { | ||
for _, h := range b.globalPreHandlers { | ||
err := h(ctx, msg) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
globalPostHandler := func(ctx context.Context, msg, msgResp transaction.Msg) error { | ||
for _, h := range b.globalPostHandlers { | ||
err := h(ctx, msg, msgResp) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
for msgType, handler := range b.handlers { | ||
// find pre handler | ||
preHandlers := b.preHandlers[msgType] | ||
// find post handler | ||
postHandlers := b.postHandlers[msgType] | ||
// build the handler | ||
handlers[msgType] = buildHandler(handler, preHandlers, globalPreHandler, postHandlers, globalPostHandler) | ||
} | ||
|
||
|
||
return ReflectionRouter{ | ||
handlers: handlers, | ||
}, nil | ||
} | ||
|
||
func buildHandler( | ||
handler appmodulev2.HandlerFunc, | ||
preHandlers []appmodulev2.PreMsgHandler, | ||
globalPreHandler appmodulev2.PreMsgHandler, | ||
postHandlers []appmodulev2.PostMsgHandler, | ||
globalPostHandler appmodulev2.PostMsgHandler, | ||
) appmodulev2.HandlerFunc { | ||
return func(ctx context.Context, msg transaction.Msg) (msgResp transaction.Msg, err error) { | ||
if len(preHandlers) != 0 { | ||
for _, preHandler := range preHandlers { | ||
if err := preHandler(ctx, msg); err != nil { | ||
return nil, err | ||
} | ||
} | ||
} | ||
err = globalPreHandler(ctx, msg) | ||
if err != nil { | ||
return nil, err | ||
} | ||
msgResp, err = handler(ctx, msg) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if len(postHandlers) != 0 { | ||
for _, postHandler := range postHandlers { | ||
if err := postHandler(ctx, msg, msgResp); err != nil { | ||
return nil, err | ||
} | ||
} | ||
} | ||
err = globalPostHandler(ctx, msg, msgResp) | ||
return msgResp, err | ||
} | ||
} | ||
|
||
var _ router.Service = (*ReflectionRouter)(nil) | ||
|
||
// ReflectionRouter implements the STF router for msg and query handlers. | ||
type ReflectionRouter struct { | ||
handlers map[string]appmodulev2.HandlerFunc | ||
} | ||
|
||
func (r ReflectionRouter) CanInvoke(_ context.Context, typeURL string) error { | ||
_, exists := r.handlers[typeURL] | ||
if !exists { | ||
return fmt.Errorf("%w: %s", ErrNoHandler, typeURL) | ||
} | ||
return nil | ||
} | ||
|
||
func (r ReflectionRouter) Invoke(ctx context.Context, req transaction.Msg) (res transaction.Msg, err error) { | ||
typeName := reflect.TypeOf(req).String() | ||
handler, exists := r.handlers[typeName] | ||
if !exists { | ||
return nil, fmt.Errorf("%w: %s", ErrNoHandler, typeName) | ||
} | ||
|
||
return handler(ctx, req) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package coretesting | ||
|
||
import ( | ||
"context" | ||
|
||
"cosmossdk.io/core/transaction" | ||
) | ||
|
||
var _ transaction.Service = &MemTransactionService{} | ||
|
||
type MemTransactionService struct{} | ||
technicallyty marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
func (m MemTransactionService) ExecMode(ctx context.Context) transaction.ExecMode { | ||
dummy := unwrap(ctx) | ||
|
||
return dummy.execMode | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Created the TestContext wrapper so that we can extend with methods to modify it like the ones here