cosmos-sdk/depinject/binding_test.go

151 lines
3.7 KiB
Go

package depinject_test
import (
"fmt"
"reflect"
"testing"
"github.com/stretchr/testify/require"
"github.com/regen-network/gocuke"
"github.com/stretchr/testify/assert"
"github.com/cosmos/cosmos-sdk/depinject"
)
func TestBindInterface(t *testing.T) {
gocuke.NewRunner(t, &bindingSuite{}).
Path("features/bindings.feature").
Step(`we try to resolve a "Duck" in global scope`, (*bindingSuite).WeTryToResolveADuckInGlobalScope).
Step(`module "(\w+)" wants a "Duck"`, (*bindingSuite).ModuleWantsADuck).
Run()
}
type Duck interface {
quack()
}
type (
Mallard struct{}
Canvasback struct{}
Marbled struct{}
)
func (duck Mallard) quack() {}
func (duck Canvasback) quack() {}
func (duck Marbled) quack() {}
type DuckWrapper struct {
Module string
Duck Duck
}
func (d DuckWrapper) IsManyPerContainerType() {}
type Pond struct {
Ducks []DuckWrapper
}
type bindingSuite struct {
gocuke.TestingT // this gets injected by gocuke
configs []depinject.Config
pond *Pond
err error
}
func (s bindingSuite) AnInterfaceDuck() {
// we don't need to do anything because this is defined at the type level
}
func (s bindingSuite) TwoImplementationsMallardAndCanvasback() {
// we don't need to do anything because this is defined at the type level
}
func (s *bindingSuite) IsProvided(a string) {
switch a {
case "Mallard":
s.addConfig(depinject.Provide(func() Mallard { return Mallard{} }))
case "Canvasback":
s.addConfig(depinject.Provide(func() Canvasback { return Canvasback{} }))
case "Marbled":
s.addConfig(depinject.Provide(func() Marbled { return Marbled{} }))
default:
s.Fatalf("unexpected duck type %s", a)
}
}
func (s *bindingSuite) addConfig(config depinject.Config) {
s.configs = append(s.configs, config)
}
func (s *bindingSuite) WeTryToResolveADuckInGlobalScope() {
s.addConfig(depinject.Provide(func(duck Duck) DuckWrapper {
return DuckWrapper{Module: "", Duck: duck}
}))
}
func (s *bindingSuite) resolvePond() *Pond {
if s.pond != nil {
return s.pond
}
s.addConfig(depinject.Provide(func(ducks []DuckWrapper) Pond { return Pond{Ducks: ducks} }))
var pond Pond
s.err = depinject.Inject(depinject.Configs(s.configs...), &pond)
s.pond = &pond
return s.pond
}
func (s *bindingSuite) IsResolvedInGlobalScope(typeName string) {
pond := s.resolvePond()
found := false
for _, dw := range pond.Ducks {
if dw.Module == "" {
require.Contains(s, reflect.TypeOf(dw.Duck).Name(), typeName)
found = true
}
}
assert.True(s, found)
}
func (s *bindingSuite) ThereIsAError(expectedErrorMsg string) {
s.resolvePond()
assert.ErrorContains(s, s.err, expectedErrorMsg)
}
func (s *bindingSuite) ThereIsNoError() {
s.resolvePond()
assert.NoError(s, s.err)
}
func fullTypeName(typeName string) string {
return fmt.Sprintf("github.com/cosmos/cosmos-sdk/depinject_test/depinject_test.%s", typeName)
}
func (s *bindingSuite) ThereIsAGlobalBindingForA(preferredType string, interfaceType string) {
s.addConfig(depinject.BindInterface(fullTypeName(interfaceType), fullTypeName(preferredType)))
}
func (s *bindingSuite) ThereIsABindingForAInModule(preferredType string, interfaceType string, moduleName string) {
s.addConfig(depinject.BindInterfaceInModule(moduleName, fullTypeName(interfaceType), fullTypeName(preferredType)))
}
func (s *bindingSuite) ModuleWantsADuck(module string) {
s.addConfig(depinject.ProvideInModule(module, func(duck Duck) DuckWrapper {
return DuckWrapper{Module: module, Duck: duck}
}))
}
func (s *bindingSuite) ModuleResolvesA(module string, duckType string) {
pond := s.resolvePond()
moduleFound := false
for _, dw := range pond.Ducks {
if dw.Module == module {
assert.Contains(s, reflect.TypeOf(dw.Duck).Name(), duckType)
moduleFound = true
}
}
assert.True(s, moduleFound)
}