1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-08-10 22:31:32 +02:00
Files
fp-go/di/provider_test.go

352 lines
8.7 KiB
Go
Raw Permalink Normal View History

// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package di
import (
"fmt"
"testing"
"time"
A "github.com/IBM/fp-go/array"
DIE "github.com/IBM/fp-go/di/erasure"
E "github.com/IBM/fp-go/either"
F "github.com/IBM/fp-go/function"
IOE "github.com/IBM/fp-go/ioeither"
O "github.com/IBM/fp-go/option"
"github.com/stretchr/testify/assert"
)
var (
INJ_KEY2 = MakeToken[string]("INJ_KEY2")
INJ_KEY1 = MakeToken[string]("INJ_KEY1")
INJ_KEY3 = MakeToken[string]("INJ_KEY3")
)
func TestSimpleProvider(t *testing.T) {
var staticCount int
staticValue := func(value string) func() IOE.IOEither[error, string] {
return func() IOE.IOEither[error, string] {
return func() E.Either[error, string] {
staticCount++
return E.Of[error](fmt.Sprintf("Static based on [%s], at [%s]", value, time.Now()))
}
}
}
var dynamicCount int
dynamicValue := func(value string) IOE.IOEither[error, string] {
return func() E.Either[error, string] {
dynamicCount++
return E.Of[error](fmt.Sprintf("Dynamic based on [%s] at [%s]", value, time.Now()))
}
}
p1 := MakeProvider0(INJ_KEY1, staticValue("Carsten"))
p2 := MakeProvider1(INJ_KEY2, INJ_KEY1.Identity(), dynamicValue)
inj := DIE.MakeInjector(A.From(p1, p2))
i1 := Resolve(INJ_KEY1)
i2 := Resolve(INJ_KEY2)
res := IOE.SequenceT4(
i2(inj),
i1(inj),
i2(inj),
i1(inj),
)
r := res()
assert.True(t, E.IsRight(r))
assert.Equal(t, 1, staticCount)
assert.Equal(t, 1, dynamicCount)
}
func TestOptionalProvider(t *testing.T) {
var staticCount int
staticValue := func(value string) func() IOE.IOEither[error, string] {
return func() IOE.IOEither[error, string] {
return func() E.Either[error, string] {
staticCount++
return E.Of[error](fmt.Sprintf("Static based on [%s], at [%s]", value, time.Now()))
}
}
}
var dynamicCount int
dynamicValue := func(value O.Option[string]) IOE.IOEither[error, string] {
return func() E.Either[error, string] {
dynamicCount++
return E.Of[error](fmt.Sprintf("Dynamic based on [%s] at [%s]", value, time.Now()))
}
}
p1 := MakeProvider0(INJ_KEY1, staticValue("Carsten"))
p2 := MakeProvider1(INJ_KEY2, INJ_KEY1.Option(), dynamicValue)
inj := DIE.MakeInjector(A.From(p1, p2))
i1 := Resolve(INJ_KEY1)
i2 := Resolve(INJ_KEY2)
res := IOE.SequenceT4(
i2(inj),
i1(inj),
i2(inj),
i1(inj),
)
r := res()
assert.True(t, E.IsRight(r))
assert.Equal(t, 1, staticCount)
assert.Equal(t, 1, dynamicCount)
}
func TestOptionalProviderMissingDependency(t *testing.T) {
var dynamicCount int
dynamicValue := func(value O.Option[string]) IOE.IOEither[error, string] {
return func() E.Either[error, string] {
dynamicCount++
return E.Of[error](fmt.Sprintf("Dynamic based on [%s] at [%s]", value, time.Now()))
}
}
p2 := MakeProvider1(INJ_KEY2, INJ_KEY1.Option(), dynamicValue)
inj := DIE.MakeInjector(A.From(p2))
i2 := Resolve(INJ_KEY2)
res := IOE.SequenceT2(
i2(inj),
i2(inj),
)
r := res()
assert.True(t, E.IsRight(r))
assert.Equal(t, 1, dynamicCount)
}
func TestProviderMissingDependency(t *testing.T) {
var dynamicCount int
dynamicValue := func(value string) IOE.IOEither[error, string] {
return func() E.Either[error, string] {
dynamicCount++
return E.Of[error](fmt.Sprintf("Dynamic based on [%s] at [%s]", value, time.Now()))
}
}
p2 := MakeProvider1(INJ_KEY2, INJ_KEY1.Identity(), dynamicValue)
inj := DIE.MakeInjector(A.From(p2))
i2 := Resolve(INJ_KEY2)
res := IOE.SequenceT2(
i2(inj),
i2(inj),
)
r := res()
assert.True(t, E.IsLeft(r))
assert.Equal(t, 0, dynamicCount)
}
func TestEagerAndLazyProvider(t *testing.T) {
var staticCount int
staticValue := func(value string) func() IOE.IOEither[error, string] {
return func() IOE.IOEither[error, string] {
return func() E.Either[error, string] {
staticCount++
return E.Of[error](fmt.Sprintf("Static based on [%s], at [%s]", value, time.Now()))
}
}
}
var dynamicCount int
dynamicValue := func(value string) IOE.IOEither[error, string] {
return func() E.Either[error, string] {
dynamicCount++
return E.Of[error](fmt.Sprintf("Dynamic based on [%s] at [%s]", value, time.Now()))
}
}
var lazyEagerCount int
lazyEager := func(laz IOE.IOEither[error, string], eager string) IOE.IOEither[error, string] {
return F.Pipe1(
laz,
IOE.Chain(func(lazValue string) IOE.IOEither[error, string] {
return func() E.Either[error, string] {
lazyEagerCount++
return E.Of[error](fmt.Sprintf("Dynamic based on [%s], [%s] at [%s]", lazValue, eager, time.Now()))
}
}),
)
}
p1 := MakeProvider0(INJ_KEY1, staticValue("Carsten"))
p2 := MakeProvider1(INJ_KEY2, INJ_KEY1.Identity(), dynamicValue)
p3 := MakeProvider2(INJ_KEY3, INJ_KEY2.IOEither(), INJ_KEY1.Identity(), lazyEager)
inj := DIE.MakeInjector(A.From(p1, p2, p3))
i3 := Resolve(INJ_KEY3)
r := i3(inj)()
fmt.Println(r)
assert.True(t, E.IsRight(r))
assert.Equal(t, 1, staticCount)
assert.Equal(t, 1, dynamicCount)
assert.Equal(t, 1, lazyEagerCount)
}
func TestItemProvider(t *testing.T) {
// define a multi token
injMulti := MakeMultiToken[string]("configs")
// provide some values
v1 := ConstProvider(injMulti.Item(), "Value1")
v2 := ConstProvider(injMulti.Item(), "Value2")
// mix in non-multi values
p1 := ConstProvider(INJ_KEY1, "Value3")
p2 := ConstProvider(INJ_KEY2, "Value4")
// populate the injector
inj := DIE.MakeInjector(A.From(p1, v1, p2, v2))
// access the multi value
multi := Resolve(injMulti.Container())
multiInj := multi(inj)
value := multiInj()
assert.Equal(t, E.Of[error](A.From("Value1", "Value2")), value)
}
func TestEmptyItemProvider(t *testing.T) {
// define a multi token
injMulti := MakeMultiToken[string]("configs")
// mix in non-multi values
p1 := ConstProvider(INJ_KEY1, "Value3")
p2 := ConstProvider(INJ_KEY2, "Value4")
// populate the injector
inj := DIE.MakeInjector(A.From(p1, p2))
// access the multi value
multi := Resolve(injMulti.Container())
multiInj := multi(inj)
value := multiInj()
assert.Equal(t, E.Of[error](A.Empty[string]()), value)
}
func TestDependencyOnMultiProvider(t *testing.T) {
// define a multi token
injMulti := MakeMultiToken[string]("configs")
// provide some values
v1 := ConstProvider(injMulti.Item(), "Value1")
v2 := ConstProvider(injMulti.Item(), "Value2")
// mix in non-multi values
p1 := ConstProvider(INJ_KEY1, "Value3")
p2 := ConstProvider(INJ_KEY2, "Value4")
fromMulti := func(val string, multi []string) IOE.IOEither[error, string] {
return IOE.Of[error](fmt.Sprintf("Val: %s, Multi: %s", val, multi))
}
p3 := MakeProvider2(INJ_KEY3, INJ_KEY1.Identity(), injMulti.Container().Identity(), fromMulti)
// populate the injector
inj := DIE.MakeInjector(A.From(p1, p2, v1, v2, p3))
r3 := Resolve(INJ_KEY3)
v := r3(inj)()
assert.Equal(t, E.Of[error]("Val: Value3, Multi: [Value1 Value2]"), v)
}
func TestTokenWithDefaultProvider(t *testing.T) {
// token without a default
injToken1 := MakeToken[string]("Token1")
// token with a default
injToken2 := MakeTokenWithDefault0("Token2", F.Constant(IOE.Of[error]("Carsten")))
// dependency
injToken3 := MakeToken[string]("Token3")
p3 := MakeProvider1(injToken3, injToken2.Identity(), func(data string) IOE.IOEither[error, string] {
return IOE.Of[error](fmt.Sprintf("Token: %s", data))
})
// populate the injector
inj := DIE.MakeInjector(A.From(p3))
// resolving injToken3 should work and use the default provider for injToken2
r1 := Resolve(injToken1)
r3 := Resolve(injToken3)
// inj1 should not be available
assert.True(t, E.IsLeft(r1(inj)()))
// r3 should work
assert.Equal(t, E.Of[error]("Token: Carsten"), r3(inj)())
}
func TestTokenWithDefaultProviderAndOverride(t *testing.T) {
// token with a default
injToken2 := MakeTokenWithDefault0("Token2", F.Constant(IOE.Of[error]("Carsten")))
// dependency
injToken3 := MakeToken[string]("Token3")
p2 := ConstProvider(injToken2, "Override")
p3 := MakeProvider1(injToken3, injToken2.Identity(), func(data string) IOE.IOEither[error, string] {
return IOE.Of[error](fmt.Sprintf("Token: %s", data))
})
// populate the injector
inj := DIE.MakeInjector(A.From(p2, p3))
// resolving injToken3 should work and use the default provider for injToken2
r3 := Resolve(injToken3)
// r3 should work
assert.Equal(t, E.Of[error]("Token: Override"), r3(inj)())
}