-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Extends the registry and resolver modules to accept registrations for factory methods * Reorganizes the whole package structure (adds sub-packages, moves integration tests to the internal package) * Renames test methods * Fixes typo in error message
- Loading branch information
1 parent
ea07b7e
commit 7474ef2
Showing
24 changed files
with
455 additions
and
325 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
package internal | ||
package core | ||
|
||
import ( | ||
"context" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
package internal | ||
package core | ||
|
||
import "sync" | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package tests | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/matzefriedrich/parsley/pkg/registration" | ||
"github.com/matzefriedrich/parsley/pkg/resolving" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func Test_Registry_RegisterTransient_registers_factory_function_to_resolve_dynamic_dependency_at_runtime(t *testing.T) { | ||
|
||
// Arrange | ||
sut := registration.NewServiceRegistry() | ||
|
||
_ = registration.RegisterTransient(sut, NewPersonFactory) | ||
_ = registration.RegisterTransient(sut, NewUserController) | ||
|
||
r := resolving.NewResolver(sut) | ||
|
||
const userId = "123" | ||
|
||
// Act | ||
controller, _ := resolving.ResolveRequiredService[UserController](r, context.Background()) | ||
model := controller.GetUserInfo(userId) | ||
|
||
userServiceFactory, _ := resolving.ResolveRequiredService[func(string) (UserService, error)](r, context.Background()) | ||
service, factoryErr := userServiceFactory(userId) | ||
|
||
// Assert | ||
assert.NoError(t, factoryErr) | ||
assert.NotNil(t, service) | ||
|
||
assert.Equal(t, userId, model.Id) | ||
assert.Equal(t, userId, service.UserId()) | ||
} | ||
|
||
type userService struct { | ||
id string | ||
} | ||
|
||
func (f *userService) UserId() string { | ||
return f.id | ||
} | ||
|
||
type userModel struct { | ||
Id string `json:"id"` | ||
Name string `json:"name"` | ||
} | ||
|
||
func (f *userService) UserName() string { | ||
return "" // retrieve username from data backend | ||
} | ||
|
||
type userController struct { | ||
userServiceFactory func(name string) (UserService, error) | ||
} | ||
|
||
func (f *userController) GetUserInfo(userId string) userModel { | ||
userService, _ := f.userServiceFactory(userId) | ||
return userModel{ | ||
Id: userId, | ||
Name: userService.UserName(), | ||
} | ||
} | ||
|
||
type UserService interface { | ||
UserName() string | ||
UserId() string | ||
} | ||
|
||
type UserController interface { | ||
GetUserInfo(userId string) userModel | ||
} | ||
|
||
func NewPersonFactory() func(string) (UserService, error) { | ||
return func(userId string) (UserService, error) { | ||
return &userService{ | ||
id: userId, | ||
}, nil | ||
} | ||
} | ||
|
||
func NewUserController(userServiceFactory func(string) (UserService, error)) UserController { | ||
return &userController{ | ||
userServiceFactory: userServiceFactory, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
package tests | ||
|
||
import ( | ||
"context" | ||
"reflect" | ||
"testing" | ||
|
||
"github.com/matzefriedrich/parsley/internal/core" | ||
"github.com/matzefriedrich/parsley/pkg/registration" | ||
"github.com/matzefriedrich/parsley/pkg/resolving" | ||
|
||
"github.com/matzefriedrich/parsley/pkg/types" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func Test_ServiceRegistry_register_types_with_different_lifetime_behavior(t *testing.T) { | ||
|
||
// Arrange | ||
sut := registration.NewServiceRegistry() | ||
|
||
// Act | ||
_ = registration.RegisterSingleton(sut, NewFoo) | ||
_ = registration.RegisterTransient(sut, NewFooConsumer) | ||
|
||
fooRegistered := sut.IsRegistered(registration.ServiceType[Foo]()) | ||
fooConsumerRegistered := sut.IsRegistered(registration.ServiceType[FooConsumer]()) | ||
|
||
// Assert | ||
assert.True(t, fooRegistered) | ||
assert.True(t, fooConsumerRegistered) | ||
} | ||
|
||
func Test_Registry_NewResolver_resolve_type_with_dependencies(t *testing.T) { | ||
|
||
// Arrange | ||
sut := registration.NewServiceRegistry() | ||
|
||
// Act | ||
_ = registration.RegisterTransient(sut, NewFoo) | ||
_ = registration.RegisterTransient(sut, NewFooConsumer) | ||
|
||
// Assert | ||
r := resolving.NewResolver(sut) | ||
parsleyContext := core.NewScopedContext(context.Background()) | ||
resolved, _ := r.Resolve(parsleyContext, registration.ServiceType[FooConsumer]()) | ||
assert.NotNil(t, resolved) | ||
|
||
actual, ok := resolved.(FooConsumer) | ||
assert.True(t, ok) | ||
assert.NotNil(t, actual) | ||
} | ||
|
||
func Test_Registry_NewResolver_resolve_scoped_from_same_context_must_be_return_same_instance(t *testing.T) { | ||
|
||
// Arrange | ||
sut := registration.NewServiceRegistry() | ||
|
||
// Act | ||
_ = registration.RegisterSingleton(sut, NewFoo) | ||
_ = registration.RegisterScoped(sut, NewFooConsumer) | ||
|
||
// Assert | ||
r := resolving.NewResolver(sut) | ||
parsleyContext := core.NewScopedContext(context.Background()) | ||
consumer1, _ := r.Resolve(parsleyContext, registration.ServiceType[FooConsumer]()) | ||
assert.NotNil(t, consumer1) | ||
|
||
consumer2, _ := r.Resolve(parsleyContext, registration.ServiceType[FooConsumer]()) | ||
assert.NotNil(t, consumer2) | ||
|
||
actual, ok := consumer1.(FooConsumer) | ||
assert.True(t, ok) | ||
assert.NotNil(t, actual) | ||
assert.Equal(t, reflect.ValueOf(consumer1).Pointer(), reflect.ValueOf(consumer2).Pointer()) | ||
} | ||
|
||
func Test_Registry_NewResolver_resolve_scoped_from_different_context_must_be_return_different_instance(t *testing.T) { | ||
|
||
// Arrange | ||
sut := registration.NewServiceRegistry() | ||
|
||
// Act | ||
_ = registration.RegisterSingleton(sut, NewFoo) | ||
_ = registration.RegisterScoped(sut, NewFooConsumer) | ||
|
||
// Assert | ||
r := resolving.NewResolver(sut) | ||
|
||
parsleyContext1 := core.NewScopedContext(context.Background()) | ||
consumer1, _ := r.Resolve(parsleyContext1, registration.ServiceType[FooConsumer]()) | ||
assert.NotNil(t, consumer1) | ||
|
||
parsleyContext2 := core.NewScopedContext(context.Background()) | ||
consumer2, _ := r.Resolve(parsleyContext2, registration.ServiceType[FooConsumer]()) | ||
assert.NotNil(t, consumer2) | ||
|
||
actual, ok := consumer1.(FooConsumer) | ||
assert.True(t, ok) | ||
assert.NotNil(t, actual) | ||
assert.NotEqual(t, reflect.ValueOf(consumer1).Pointer(), reflect.ValueOf(consumer2).Pointer()) | ||
} | ||
|
||
func Test_Registry_RegisterModule_registers_collection_of_services(t *testing.T) { | ||
|
||
// Arrange | ||
sut := registration.NewServiceRegistry() | ||
|
||
// Act | ||
fooModule := func(r types.ServiceRegistry) error { | ||
_ = registration.RegisterSingleton(r, NewFoo) | ||
_ = registration.RegisterScoped(r, NewFooConsumer) | ||
return nil | ||
} | ||
|
||
_ = sut.RegisterModule(fooModule) | ||
|
||
fooRegistered := sut.IsRegistered(registration.ServiceType[Foo]()) | ||
fooConsumerRegistered := sut.IsRegistered(registration.ServiceType[FooConsumer]()) | ||
|
||
// Assert | ||
assert.True(t, fooRegistered) | ||
assert.True(t, fooConsumerRegistered) | ||
} | ||
|
||
func Test_Registry_RegisterInstance_registers_singleton_service_from_object(t *testing.T) { | ||
|
||
// Arrange | ||
sut := registration.NewServiceRegistry() | ||
|
||
instance := NewFoo() | ||
|
||
// Act | ||
_ = registration.RegisterInstance(sut, instance) | ||
|
||
fooRegistered := sut.IsRegistered(registration.ServiceType[Foo]()) | ||
|
||
r := resolving.NewResolver(sut) | ||
|
||
// Arrange | ||
assert.True(t, fooRegistered) | ||
|
||
resolved, _ := r.Resolve(context.Background(), registration.ServiceType[Foo]()) | ||
assert.NotNil(t, resolved) | ||
|
||
actual, ok := resolved.(Foo) | ||
assert.True(t, ok) | ||
assert.NotNil(t, actual) | ||
assert.Equal(t, reflect.ValueOf(instance).Pointer(), reflect.ValueOf(actual).Pointer()) | ||
} | ||
|
||
type Foo interface { | ||
Bar() | ||
} | ||
|
||
type foo struct{} | ||
|
||
func (f *foo) Bar() {} | ||
|
||
func NewFoo() Foo { | ||
return &foo{} | ||
} | ||
|
||
type FooConsumer interface { | ||
FooBar() | ||
} | ||
|
||
type fooConsumer struct { | ||
foo Foo | ||
} | ||
|
||
func (fb *fooConsumer) FooBar() { | ||
fb.foo.Bar() | ||
} | ||
|
||
func NewFooConsumer(foo Foo) FooConsumer { | ||
return &fooConsumer{ | ||
foo: foo, | ||
} | ||
} |
Oops, something went wrong.