cosmos-sdk/core/appconfig/config_test.go

226 lines
5.3 KiB
Go

package appconfig_test
import (
"bytes"
"fmt"
"io"
"reflect"
"sort"
"testing"
"gotest.tools/v3/assert"
"cosmossdk.io/depinject"
"cosmossdk.io/core/appconfig"
"cosmossdk.io/core/appmodule"
"cosmossdk.io/core/internal"
"cosmossdk.io/core/internal/testpb"
_ "cosmossdk.io/core/internal/testpb"
)
func expectContainerErrorContains(t *testing.T, option depinject.Config, contains string) {
t.Helper()
err := depinject.Inject(option)
assert.ErrorContains(t, err, contains)
}
func TestCompose(t *testing.T) {
opt := appconfig.LoadJSON([]byte(`{"modules":[{}]}`))
expectContainerErrorContains(t, opt, "module is missing name")
opt = appconfig.LoadJSON([]byte(`{"modules":[{"name": "a"}]}`))
expectContainerErrorContains(t, opt, `module "a" is missing a config object`)
opt = appconfig.LoadYAML([]byte(`
modules:
- name: a
config:
"@type": testpb.ModuleFoo
`))
expectContainerErrorContains(t, opt, `unable to resolve`)
opt = appconfig.LoadYAML([]byte(`
modules:
- name: a
config:
"@type": cosmos.app.v1alpha1.Config # this is not actually a module config type!
`))
expectContainerErrorContains(t, opt, "does not have the option cosmos.app.v1alpha1.module")
expectContainerErrorContains(t, opt, "registered modules are")
expectContainerErrorContains(t, opt, "testpb.TestModuleA")
opt = appconfig.LoadYAML([]byte(`
modules:
- name: a
config:
"@type": testpb.TestUnregisteredModule
`))
expectContainerErrorContains(t, opt, "did you forget to import cosmossdk.io/core/internal/testpb")
expectContainerErrorContains(t, opt, "registered modules are")
expectContainerErrorContains(t, opt, "testpb.TestModuleA")
var app App
opt = appconfig.LoadYAML([]byte(`
modules:
- name: runtime
config:
"@type": testpb.TestRuntimeModule
- name: a
config:
"@type": testpb.TestModuleA
- name: b
config:
"@type": testpb.TestModuleB
`))
assert.NilError(t, depinject.Inject(opt, &app))
buf := &bytes.Buffer{}
app(buf)
const expected = `got store key a
got store key b
running module handler a
result: hello
running module handler b
result: goodbye
`
assert.Equal(t, expected, buf.String())
opt = appconfig.LoadYAML([]byte(`
golang_bindings:
- interfaceType: interfaceType/package.name
implementation: implementationType/package.name
- interfaceType: interfaceType/package.nameTwo
implementation: implementationType/package.nameTwo
modules:
- name: a
config:
"@type": testpb.TestModuleA
golang_bindings:
- interfaceType: interfaceType/package.name
implementation: implementationType/package.name
- interfaceType: interfaceType/package.nameTwo
implementation: implementationType/package.nameTwo
`))
assert.NilError(t, depinject.Inject(opt))
// module registration failures:
appmodule.Register(&testpb.TestNoModuleOptionModule{})
opt = appconfig.LoadYAML([]byte(`
modules:
- name: a
config:
"@type": testpb.TestNoGoImportModule
`))
expectContainerErrorContains(t, opt, "module should have the option cosmos.app.v1alpha1.module")
internal.ModuleRegistry = map[reflect.Type]*internal.ModuleInitializer{} // reset module registry
appmodule.Register(&testpb.TestNoGoImportModule{})
opt = appconfig.LoadYAML([]byte(`
modules:
- name: a
config:
"@type": testpb.TestNoGoImportModule
`))
expectContainerErrorContains(t, opt, "module should have ModuleDescriptor.go_import specified")
}
//
// Test Module Initialization Logic
//
func init() {
appmodule.Register(&testpb.TestRuntimeModule{},
appmodule.Provide(ProvideRuntimeState, ProvideStoreKey, ProvideApp),
)
appmodule.Register(&testpb.TestModuleA{},
appmodule.Provide(ProvideModuleA),
)
appmodule.Register(&testpb.TestModuleB{},
appmodule.Provide(ProvideModuleB),
)
}
func ProvideRuntimeState() *RuntimeState {
return &RuntimeState{}
}
func ProvideStoreKey(key depinject.ModuleKey, state *RuntimeState) StoreKey {
sk := StoreKey{name: key.Name()}
state.storeKeys = append(state.storeKeys, sk)
return sk
}
func ProvideApp(state *RuntimeState, handlers map[string]Handler) App {
return func(w io.Writer) {
sort.Slice(state.storeKeys, func(i, j int) bool {
return state.storeKeys[i].name < state.storeKeys[j].name
})
for _, key := range state.storeKeys {
_, _ = fmt.Fprintf(w, "got store key %s\n", key.name)
}
var modNames []string
for modName := range handlers {
modNames = append(modNames, modName)
}
sort.Strings(modNames)
for _, name := range modNames {
_, _ = fmt.Fprintf(w, "running module handler %s\n", name)
_, _ = fmt.Fprintf(w, "result: %s\n", handlers[name].DoSomething())
}
}
}
type App func(writer io.Writer)
type RuntimeState struct {
storeKeys []StoreKey
}
type StoreKey struct{ name string }
type Handler struct {
DoSomething func() string
}
func (h Handler) IsOnePerModuleType() {}
func ProvideModuleA(key StoreKey) (KeeperA, Handler) {
return keeperA{key: key}, Handler{DoSomething: func() string {
return "hello"
}}
}
type keeperA struct {
key StoreKey
}
type KeeperA interface {
Foo()
}
func (k keeperA) Foo() {}
func ProvideModuleB(key StoreKey, a KeeperA) (KeeperB, Handler) {
return keeperB{key: key, a: a}, Handler{
DoSomething: func() string {
return "goodbye"
},
}
}
type keeperB struct {
key StoreKey
a KeeperA
}
type KeeperB interface {
isKeeperB()
}
func (k keeperB) isKeeperB() {}