mirror of
https://github.com/IBM/fp-go.git
synced 2025-08-10 22:31:32 +02:00
347 lines
8.5 KiB
Go
347 lines
8.5 KiB
Go
// 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) 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) 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) 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", 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", 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)())
|
|
}
|