303 lines
7.0 KiB
Go
303 lines
7.0 KiB
Go
package depinject_test
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"cosmossdk.io/depinject"
|
|
)
|
|
|
|
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
|
|
}
|
|
|
|
func IsResolvedInGlobalScope(t *testing.T, pond Pond, typeName string) {
|
|
t.Helper()
|
|
|
|
found := false
|
|
for _, dw := range pond.Ducks {
|
|
if dw.Module == "" {
|
|
require.Contains(t, reflect.TypeOf(dw.Duck).Name(), typeName)
|
|
found = true
|
|
}
|
|
}
|
|
|
|
require.True(t, found)
|
|
}
|
|
|
|
func IsResolvedModuleScope(t *testing.T, pond Pond, module, duckType string) {
|
|
t.Helper()
|
|
|
|
moduleFound := false
|
|
for _, dw := range pond.Ducks {
|
|
if dw.Module == module {
|
|
require.Contains(t, reflect.TypeOf(dw.Duck).Name(), duckType)
|
|
moduleFound = true
|
|
}
|
|
}
|
|
require.True(t, moduleFound)
|
|
}
|
|
|
|
func ProvideMallard() Mallard { return Mallard{} }
|
|
|
|
func ProvideCanvasback() Canvasback { return Canvasback{} }
|
|
|
|
func ProvideMarbled() Marbled { return Marbled{} }
|
|
|
|
func ProvideDuckWrapper(duck Duck) DuckWrapper {
|
|
return DuckWrapper{Module: "", Duck: duck}
|
|
}
|
|
|
|
func ProvideModuleDuck(duck Duck, key depinject.OwnModuleKey) DuckWrapper {
|
|
return DuckWrapper{Module: depinject.ModuleKey(key).Name(), Duck: duck}
|
|
}
|
|
|
|
func ResolvePond(ducks []DuckWrapper) Pond { return Pond{Ducks: ducks} }
|
|
|
|
func fullTypeName(typeName string) string {
|
|
return fmt.Sprintf("cosmossdk.io/depinject_test/depinject_test.%s", typeName)
|
|
}
|
|
|
|
func TestProvideNoBinding(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
configs := depinject.Configs(
|
|
depinject.Provide(
|
|
ProvideMallard,
|
|
ProvideDuckWrapper,
|
|
ResolvePond,
|
|
),
|
|
)
|
|
|
|
var pond Pond
|
|
err := depinject.Inject(configs, &pond)
|
|
require.NoError(t, err)
|
|
|
|
IsResolvedInGlobalScope(t, pond, "Mallard")
|
|
}
|
|
|
|
func TestProvideNoBindingImplementationErrorAmbiguous(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
configs := depinject.Configs(
|
|
depinject.Provide(
|
|
ProvideMallard,
|
|
ProvideCanvasback,
|
|
ProvideDuckWrapper,
|
|
ResolvePond,
|
|
),
|
|
)
|
|
|
|
var pond Pond
|
|
err := depinject.Inject(configs, &pond)
|
|
require.ErrorContains(t, err, "Multiple implementations found")
|
|
}
|
|
|
|
func TestBindInterface(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
configs := depinject.Configs(
|
|
depinject.BindInterface(fullTypeName("Duck"), fullTypeName("Mallard")),
|
|
depinject.Provide(
|
|
ProvideMallard,
|
|
ProvideDuckWrapper,
|
|
ResolvePond,
|
|
),
|
|
)
|
|
|
|
var pond Pond
|
|
err := depinject.Inject(configs, &pond)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestBindInterfaceBoundTypeNotProvided(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
configs := depinject.Configs(
|
|
depinject.BindInterface(fullTypeName("Duck"), fullTypeName("Marbled")),
|
|
depinject.Provide(
|
|
ProvideMallard,
|
|
ProvideDuckWrapper,
|
|
ResolvePond,
|
|
),
|
|
)
|
|
|
|
var pond Pond
|
|
err := depinject.Inject(configs, &pond)
|
|
require.ErrorContains(t, err, "No type for explicit binding")
|
|
}
|
|
|
|
func TestBindInterfaceOverwriteImplicitTypeResolution(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
configs := depinject.Configs(
|
|
depinject.BindInterface(fullTypeName("Duck"), fullTypeName("Marbled")), // overwrite Canvasback
|
|
depinject.Provide(
|
|
ProvideCanvasback,
|
|
ProvideDuckWrapper,
|
|
ResolvePond,
|
|
),
|
|
)
|
|
|
|
var pond Pond
|
|
err := depinject.Inject(configs, &pond)
|
|
require.ErrorContains(t, err, "No type for explicit binding")
|
|
|
|
// same in module scope
|
|
moduleName := "A"
|
|
configs = depinject.Configs(
|
|
depinject.BindInterfaceInModule(moduleName, fullTypeName("Duck"), fullTypeName("Marbled")), // overwrite Canvasback
|
|
depinject.Provide(
|
|
ProvideCanvasback,
|
|
ResolvePond,
|
|
),
|
|
depinject.ProvideInModule(moduleName, ProvideModuleDuck),
|
|
)
|
|
|
|
err = depinject.Inject(configs, &pond)
|
|
require.ErrorContains(t, err, "No type for explicit binding")
|
|
}
|
|
|
|
func TestBindingInterfaceGlobalScopeApplyToGlobalAndModuleScope(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
configs := depinject.Configs(
|
|
depinject.BindInterface(fullTypeName("Duck"), fullTypeName("Mallard")), // order is important
|
|
depinject.Provide(
|
|
ProvideMallard,
|
|
ProvideCanvasback,
|
|
ProvideDuckWrapper,
|
|
ResolvePond,
|
|
),
|
|
)
|
|
|
|
var pond Pond
|
|
err := depinject.Inject(configs, &pond)
|
|
require.NoError(t, err)
|
|
IsResolvedInGlobalScope(t, pond, "Mallard")
|
|
|
|
// same in module scope
|
|
moduleName := "A"
|
|
configs = depinject.Configs(
|
|
depinject.BindInterface(fullTypeName("Duck"), fullTypeName("Mallard")),
|
|
depinject.Provide(
|
|
ProvideMallard,
|
|
ProvideCanvasback,
|
|
ResolvePond,
|
|
),
|
|
depinject.ProvideInModule(moduleName, ProvideModuleDuck),
|
|
)
|
|
|
|
err = depinject.Inject(configs, &pond)
|
|
require.NoError(t, err)
|
|
IsResolvedModuleScope(t, pond, moduleName, "Mallard")
|
|
}
|
|
|
|
func TestBindingInterfaceModuleScopeApplyOnlyModuleScope(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
moduleName := "A"
|
|
configs := depinject.Configs(
|
|
depinject.BindInterfaceInModule(moduleName, fullTypeName("Duck"), fullTypeName("Canvasback")),
|
|
depinject.Provide(
|
|
ProvideMallard,
|
|
ProvideCanvasback,
|
|
ProvideDuckWrapper,
|
|
ResolvePond,
|
|
),
|
|
)
|
|
|
|
var pond Pond
|
|
err := depinject.Inject(configs, &pond)
|
|
require.ErrorContains(t, err, "Multiple implementations found")
|
|
|
|
configs = depinject.Configs(
|
|
depinject.BindInterfaceInModule(moduleName, fullTypeName("Duck"), fullTypeName("Canvasback")),
|
|
depinject.Provide(
|
|
ProvideMallard,
|
|
ProvideCanvasback,
|
|
ResolvePond,
|
|
),
|
|
depinject.ProvideInModule(moduleName, ProvideModuleDuck),
|
|
)
|
|
|
|
err = depinject.Inject(configs, &pond)
|
|
require.NoError(t, err)
|
|
IsResolvedModuleScope(t, pond, moduleName, "Canvasback")
|
|
}
|
|
|
|
func TestBindingInterfaceModuleScopeApplyCorrectModule(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
moduleName := "A"
|
|
configs := depinject.Configs(
|
|
depinject.BindInterfaceInModule(moduleName, fullTypeName("Duck"), fullTypeName("Canvasback")),
|
|
depinject.Provide(
|
|
ProvideMallard,
|
|
ProvideCanvasback,
|
|
ProvideDuckWrapper,
|
|
ResolvePond,
|
|
),
|
|
depinject.ProvideInModule("B", ProvideModuleDuck),
|
|
)
|
|
|
|
var pond Pond
|
|
err := depinject.Inject(configs, &pond)
|
|
require.ErrorContains(t, err, "Multiple implementations found")
|
|
}
|
|
|
|
func TestBindingInterfaceTwoModuleScopedAndGlobalBinding(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
moduleA, moduleB, moduleC := "A", "B", "C"
|
|
|
|
configs := depinject.Configs(
|
|
depinject.BindInterface(fullTypeName("Duck"), fullTypeName("Marbled")),
|
|
depinject.BindInterfaceInModule(moduleA, fullTypeName("Duck"), fullTypeName("Canvasback")),
|
|
depinject.BindInterfaceInModule(moduleB, fullTypeName("Duck"), fullTypeName("Mallard")),
|
|
depinject.Provide(
|
|
ProvideMallard,
|
|
ProvideCanvasback,
|
|
ProvideMarbled,
|
|
ProvideDuckWrapper,
|
|
ResolvePond,
|
|
),
|
|
depinject.ProvideInModule(moduleA, ProvideModuleDuck),
|
|
depinject.ProvideInModule(moduleB, ProvideModuleDuck),
|
|
depinject.ProvideInModule(moduleC, ProvideModuleDuck),
|
|
)
|
|
|
|
var pond Pond
|
|
err := depinject.Inject(configs, &pond)
|
|
require.NoError(t, err)
|
|
|
|
IsResolvedModuleScope(t, pond, moduleA, "Canvasback")
|
|
IsResolvedModuleScope(t, pond, moduleB, "Mallard")
|
|
IsResolvedModuleScope(t, pond, moduleC, "Marbled")
|
|
IsResolvedInGlobalScope(t, pond, "Marbled")
|
|
}
|