mirror of
https://github.com/IBM/fp-go.git
synced 2025-11-23 22:14:53 +02:00
770 lines
19 KiB
Go
770 lines
19 KiB
Go
// Copyright (c) 2023 - 2025 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 readerioresult
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
"testing"
|
|
|
|
E "github.com/IBM/fp-go/v2/either"
|
|
F "github.com/IBM/fp-go/v2/function"
|
|
"github.com/IBM/fp-go/v2/ioresult"
|
|
O "github.com/IBM/fp-go/v2/option"
|
|
R "github.com/IBM/fp-go/v2/reader"
|
|
RE "github.com/IBM/fp-go/v2/readereither"
|
|
RIO "github.com/IBM/fp-go/v2/readerio"
|
|
"github.com/IBM/fp-go/v2/result"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
type testContext struct {
|
|
value int
|
|
}
|
|
|
|
func TestMonadMap(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
res := MonadMap(Of[testContext](5), func(x int) int { return x * 2 })
|
|
assert.Equal(t, result.Of(10), res(ctx)())
|
|
}
|
|
|
|
func TestMonadMapTo(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
res := MonadMapTo(Of[testContext](5), 42)
|
|
assert.Equal(t, result.Of(42), res(ctx)())
|
|
}
|
|
|
|
func TestMapTo(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
res := F.Pipe1(Of[testContext](5), MapTo[testContext, int](42))
|
|
assert.Equal(t, result.Of(42), res(ctx)())
|
|
}
|
|
|
|
func TestMonadChainFirst(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
res := MonadChainFirst(
|
|
Of[testContext](5),
|
|
func(x int) ReaderIOResult[testContext, string] {
|
|
return Of[testContext](fmt.Sprintf("%d", x))
|
|
},
|
|
)
|
|
assert.Equal(t, result.Of(5), res(ctx)())
|
|
}
|
|
|
|
func TestChainFirst(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
res := F.Pipe1(
|
|
Of[testContext](5),
|
|
ChainFirst(func(x int) ReaderIOResult[testContext, string] {
|
|
return Of[testContext](fmt.Sprintf("%d", x))
|
|
}),
|
|
)
|
|
assert.Equal(t, result.Of(5), res(ctx)())
|
|
}
|
|
|
|
func TestMonadChainEitherK(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
res := MonadChainEitherK(
|
|
Of[testContext](5),
|
|
func(x int) E.Either[error, int] {
|
|
return result.Of(x * 2)
|
|
},
|
|
)
|
|
assert.Equal(t, result.Of(10), res(ctx)())
|
|
}
|
|
|
|
func TestMonadChainFirstEitherK(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
res := MonadChainFirstEitherK(
|
|
Of[testContext](5),
|
|
func(x int) E.Either[error, string] {
|
|
return result.Of(fmt.Sprintf("%d", x))
|
|
},
|
|
)
|
|
assert.Equal(t, result.Of(5), res(ctx)())
|
|
}
|
|
|
|
func TestChainFirstEitherK(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
res := F.Pipe1(
|
|
Of[testContext](5),
|
|
ChainFirstEitherK[testContext](func(x int) E.Either[error, string] {
|
|
return result.Of(fmt.Sprintf("%d", x))
|
|
}),
|
|
)
|
|
assert.Equal(t, result.Of(5), res(ctx)())
|
|
}
|
|
|
|
func TestMonadChainReaderK(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
res := MonadChainReaderK(
|
|
Of[testContext](5),
|
|
func(x int) R.Reader[testContext, int] {
|
|
return func(c testContext) int { return x + c.value }
|
|
},
|
|
)
|
|
assert.Equal(t, result.Of(15), res(ctx)())
|
|
}
|
|
|
|
func TestMonadChainIOEitherK(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
res := MonadChainIOEitherK(
|
|
Of[testContext](5),
|
|
func(x int) IOResult[int] {
|
|
return ioresult.Of(x * 2)
|
|
},
|
|
)
|
|
assert.Equal(t, result.Of(10), res(ctx)())
|
|
}
|
|
|
|
func TestChainIOEitherK(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
res := F.Pipe1(
|
|
Of[testContext](5),
|
|
ChainIOEitherK[testContext](func(x int) IOResult[int] {
|
|
return ioresult.Of(x * 2)
|
|
}),
|
|
)
|
|
assert.Equal(t, result.Of(10), res(ctx)())
|
|
}
|
|
|
|
func TestMonadChainIOK(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
res := MonadChainIOK(
|
|
Of[testContext](5),
|
|
func(x int) IO[int] {
|
|
return func() int { return x * 2 }
|
|
},
|
|
)
|
|
assert.Equal(t, result.Of(10), res(ctx)())
|
|
}
|
|
|
|
func TestChainIOK(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
res := F.Pipe1(
|
|
Of[testContext](5),
|
|
ChainIOK[testContext](func(x int) IO[int] {
|
|
return func() int { return x * 2 }
|
|
}),
|
|
)
|
|
assert.Equal(t, result.Of(10), res(ctx)())
|
|
}
|
|
|
|
func TestMonadChainFirstIOK(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
res := MonadChainFirstIOK(
|
|
Of[testContext](5),
|
|
func(x int) IO[string] {
|
|
return func() string { return fmt.Sprintf("%d", x) }
|
|
},
|
|
)
|
|
assert.Equal(t, result.Of(5), res(ctx)())
|
|
}
|
|
|
|
func TestChainFirstIOK(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
res := F.Pipe1(
|
|
Of[testContext](5),
|
|
ChainFirstIOK[testContext](func(x int) IO[string] {
|
|
return func() string { return fmt.Sprintf("%d", x) }
|
|
}),
|
|
)
|
|
assert.Equal(t, result.Of(5), res(ctx)())
|
|
}
|
|
|
|
func TestChainOptionK(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
|
|
// Test with Some
|
|
resultSome := F.Pipe1(
|
|
Of[testContext](5),
|
|
ChainOptionK[testContext, int, int](func() error {
|
|
return errors.New("none")
|
|
})(func(x int) Option[int] {
|
|
return O.Some(x * 2)
|
|
}),
|
|
)
|
|
assert.Equal(t, result.Of(10), resultSome(ctx)())
|
|
|
|
// Test with None
|
|
resultNone := F.Pipe1(
|
|
Of[testContext](5),
|
|
ChainOptionK[testContext, int, int](func() error {
|
|
return errors.New("none")
|
|
})(func(x int) Option[int] {
|
|
return O.None[int]()
|
|
}),
|
|
)
|
|
assert.True(t, E.IsLeft(resultNone(ctx)()))
|
|
}
|
|
|
|
func TestMonadApSeq(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
fab := Of[testContext](func(x int) int { return x * 2 })
|
|
fa := Of[testContext](5)
|
|
res := MonadApSeq(fab, fa)
|
|
assert.Equal(t, result.Of(10), res(ctx)())
|
|
}
|
|
|
|
func TestMonadApPar(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
fab := Of[testContext](func(x int) int { return x * 2 })
|
|
fa := Of[testContext](5)
|
|
res := MonadApPar(fab, fa)
|
|
assert.Equal(t, result.Of(10), res(ctx)())
|
|
}
|
|
|
|
func TestChain(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
res := F.Pipe1(
|
|
Of[testContext](5),
|
|
Chain(func(x int) ReaderIOResult[testContext, int] {
|
|
return Of[testContext](x * 2)
|
|
}),
|
|
)
|
|
assert.Equal(t, result.Of(10), res(ctx)())
|
|
}
|
|
|
|
func TestThrowError(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
result := ThrowError[testContext, int](errors.New("test error"))
|
|
assert.True(t, E.IsLeft(result(ctx)()))
|
|
}
|
|
|
|
func TestFlatten(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
nested := Of[testContext](Of[testContext](5))
|
|
res := Flatten(nested)
|
|
assert.Equal(t, result.Of(5), res(ctx)())
|
|
}
|
|
|
|
func TestFromEither(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
res := FromEither[testContext](result.Of(5))
|
|
assert.Equal(t, result.Of(5), res(ctx)())
|
|
}
|
|
|
|
func TestRightReader(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
rdr := func(c testContext) int { return c.value }
|
|
res := RightReader(rdr)
|
|
assert.Equal(t, result.Of(10), res(ctx)())
|
|
}
|
|
|
|
func TestLeftReader(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
reader := func(c testContext) error { return errors.New("test") }
|
|
res := LeftReader[int](reader)
|
|
assert.True(t, E.IsLeft(res(ctx)()))
|
|
}
|
|
|
|
func TestRightIO(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
ioVal := func() int { return 42 }
|
|
res := RightIO[testContext](ioVal)
|
|
assert.Equal(t, result.Of(42), res(ctx)())
|
|
}
|
|
|
|
func TestLeftIO(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
ioVal := func() error { return errors.New("test") }
|
|
res := LeftIO[testContext, int](ioVal)
|
|
assert.True(t, E.IsLeft(res(ctx)()))
|
|
}
|
|
|
|
func TestFromIO(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
ioVal := func() int { return 42 }
|
|
res := FromIO[testContext](ioVal)
|
|
assert.Equal(t, result.Of(42), res(ctx)())
|
|
}
|
|
|
|
func TestFromIOEither(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
ioe := ioresult.Of(42)
|
|
res := FromIOEither[testContext](ioe)
|
|
assert.Equal(t, result.Of(42), res(ctx)())
|
|
}
|
|
|
|
func TestFromReaderEither(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
re := RE.Of[testContext, error](42)
|
|
res := FromReaderEither(re)
|
|
assert.Equal(t, result.Of(42), res(ctx)())
|
|
}
|
|
|
|
func TestAsk(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
res := Ask[testContext]()
|
|
assert.Equal(t, result.Of(ctx), res(ctx)())
|
|
}
|
|
|
|
func TestAsks(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
res := Asks(func(c testContext) int { return c.value })
|
|
assert.Equal(t, result.Of(10), res(ctx)())
|
|
}
|
|
|
|
func TestFromOption(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
|
|
// Test with Some
|
|
resultSome := FromOption[testContext, int](func() error {
|
|
return errors.New("none")
|
|
})(O.Some(42))
|
|
assert.Equal(t, result.Of(42), resultSome(ctx)())
|
|
|
|
// Test with None
|
|
resultNone := FromOption[testContext, int](func() error {
|
|
return errors.New("none")
|
|
})(O.None[int]())
|
|
assert.True(t, E.IsLeft(resultNone(ctx)()))
|
|
}
|
|
|
|
func TestFromPredicate(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
|
|
// Test predicate true
|
|
resultTrue := FromPredicate[testContext](
|
|
func(x int) bool { return x > 0 },
|
|
func(x int) error { return errors.New("negative") },
|
|
)(5)
|
|
assert.Equal(t, result.Of(5), resultTrue(ctx)())
|
|
|
|
// Test predicate false
|
|
resultFalse := FromPredicate[testContext](
|
|
func(x int) bool { return x > 0 },
|
|
func(x int) error { return errors.New("negative") },
|
|
)(-5)
|
|
assert.True(t, E.IsLeft(resultFalse(ctx)()))
|
|
}
|
|
|
|
func TestFold(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
|
|
// Test Right case
|
|
resultRight := Fold(
|
|
func(e error) RIO.ReaderIO[testContext, string] {
|
|
return RIO.Of[testContext]("error: " + e.Error())
|
|
},
|
|
func(x int) RIO.ReaderIO[testContext, string] {
|
|
return RIO.Of[testContext](fmt.Sprintf("value: %d", x))
|
|
},
|
|
)(Of[testContext](42))
|
|
assert.Equal(t, "value: 42", resultRight(ctx)())
|
|
|
|
// Test Left case
|
|
resultLeft := Fold(
|
|
func(e error) RIO.ReaderIO[testContext, string] {
|
|
return RIO.Of[testContext]("error: " + e.Error())
|
|
},
|
|
func(x int) RIO.ReaderIO[testContext, string] {
|
|
return RIO.Of[testContext](fmt.Sprintf("value: %d", x))
|
|
},
|
|
)(Left[testContext, int](errors.New("test")))
|
|
assert.Equal(t, "error: test", resultLeft(ctx)())
|
|
}
|
|
|
|
func TestGetOrElse(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
|
|
// Test Right case
|
|
resultRight := GetOrElse(func(e error) RIO.ReaderIO[testContext, int] {
|
|
return RIO.Of[testContext](0)
|
|
})(Of[testContext](42))
|
|
assert.Equal(t, 42, resultRight(ctx)())
|
|
|
|
// Test Left case
|
|
resultLeft := GetOrElse(func(e error) RIO.ReaderIO[testContext, int] {
|
|
return RIO.Of[testContext](0)
|
|
})(Left[testContext, int](errors.New("test")))
|
|
assert.Equal(t, 0, resultLeft(ctx)())
|
|
}
|
|
|
|
func TestMonadBiMap(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
|
|
// Test Right case
|
|
resultRight := MonadBiMap(
|
|
Of[testContext](5),
|
|
error.Error,
|
|
strconv.Itoa,
|
|
)
|
|
assert.Equal(t, E.Of[string]("5"), resultRight(ctx)())
|
|
|
|
// Test Left case
|
|
resultLeft := MonadBiMap(
|
|
Left[testContext, int](errors.New("test")),
|
|
error.Error,
|
|
strconv.Itoa,
|
|
)
|
|
assert.Equal(t, E.Left[string]("test"), resultLeft(ctx)())
|
|
}
|
|
|
|
func TestBiMap(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
res := F.Pipe1(
|
|
Of[testContext](5),
|
|
BiMap[testContext](
|
|
error.Error,
|
|
strconv.Itoa,
|
|
),
|
|
)
|
|
assert.Equal(t, E.Of[string]("5"), res(ctx)())
|
|
}
|
|
|
|
func TestSwap(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
|
|
// Test Right becomes Left
|
|
resultRight := Swap(Of[testContext](5))
|
|
res := resultRight(ctx)()
|
|
assert.True(t, E.IsLeft(res))
|
|
|
|
// Test Left becomes Right
|
|
resultLeft := Swap(Left[testContext, int](errors.New("test")))
|
|
assert.True(t, E.IsRight(resultLeft(ctx)()))
|
|
}
|
|
|
|
func TestDefer(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
callCount := 0
|
|
res := Defer(func() ReaderIOResult[testContext, int] {
|
|
callCount++
|
|
return Of[testContext](42)
|
|
})
|
|
|
|
// First call
|
|
assert.Equal(t, result.Of(42), res(ctx)())
|
|
assert.Equal(t, 1, callCount)
|
|
|
|
// Second call
|
|
assert.Equal(t, result.Of(42), res(ctx)())
|
|
assert.Equal(t, 2, callCount)
|
|
}
|
|
|
|
func TestMonadAlt(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
|
|
// Test first succeeds
|
|
resultFirst := MonadAlt(
|
|
Of[testContext](42),
|
|
func() ReaderIOResult[testContext, int] {
|
|
return Of[testContext](99)
|
|
},
|
|
)
|
|
assert.Equal(t, result.Of(42), resultFirst(ctx)())
|
|
|
|
// Test first fails, second succeeds
|
|
resultSecond := MonadAlt(
|
|
Left[testContext, int](errors.New("first")),
|
|
func() ReaderIOResult[testContext, int] {
|
|
return Of[testContext](99)
|
|
},
|
|
)
|
|
assert.Equal(t, result.Of(99), resultSecond(ctx)())
|
|
}
|
|
|
|
func TestAlt(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
res := F.Pipe1(
|
|
Left[testContext, int](errors.New("first")),
|
|
Alt(func() ReaderIOResult[testContext, int] {
|
|
return Of[testContext](99)
|
|
}),
|
|
)
|
|
assert.Equal(t, result.Of(99), res(ctx)())
|
|
}
|
|
|
|
func TestMemoize(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
callCount := 0
|
|
res := Memoize(func(c testContext) IOResult[int] {
|
|
return func() E.Either[error, int] {
|
|
callCount++
|
|
return result.Of(c.value * 2)
|
|
}
|
|
})
|
|
|
|
// First call
|
|
assert.Equal(t, result.Of(20), res(ctx)())
|
|
assert.Equal(t, 1, callCount)
|
|
|
|
// Second call should use memoized value
|
|
assert.Equal(t, result.Of(20), res(ctx)())
|
|
assert.Equal(t, 1, callCount)
|
|
}
|
|
|
|
func TestMonadFlap(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
fab := Of[testContext](func(x int) int { return x * 2 })
|
|
res := MonadFlap(fab, 5)
|
|
assert.Equal(t, result.Of(10), res(ctx)())
|
|
}
|
|
|
|
func TestFlap(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
res := F.Pipe1(
|
|
Of[testContext](func(x int) int { return x * 2 }),
|
|
Flap[testContext, int](5),
|
|
)
|
|
assert.Equal(t, result.Of(10), res(ctx)())
|
|
}
|
|
|
|
func TestMonadMapLeft(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
result := MonadMapLeft(
|
|
Left[testContext, int](errors.New("test")),
|
|
func(e error) string { return e.Error() + "!" },
|
|
)
|
|
res := result(ctx)()
|
|
assert.True(t, E.IsLeft(res))
|
|
// Verify the error was transformed
|
|
E.Fold(
|
|
func(s string) int {
|
|
assert.Equal(t, "test!", s)
|
|
return 0
|
|
},
|
|
func(i int) int { return i },
|
|
)(res)
|
|
}
|
|
|
|
func TestMapLeft(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
result := F.Pipe1(
|
|
Left[testContext, int](errors.New("test")),
|
|
MapLeft[testContext, int](func(e error) string { return e.Error() + "!" }),
|
|
)
|
|
res := result(ctx)()
|
|
assert.True(t, E.IsLeft(res))
|
|
// Verify the error was transformed
|
|
E.Fold(
|
|
func(s string) int {
|
|
assert.Equal(t, "test!", s)
|
|
return 0
|
|
},
|
|
func(i int) int { return i },
|
|
)(res)
|
|
}
|
|
|
|
func TestLocal(t *testing.T) {
|
|
ctx2 := testContext{value: 20}
|
|
|
|
rdr := Asks(func(c testContext) int { return c.value })
|
|
res := Local[int](func(c testContext) testContext {
|
|
return testContext{value: c.value * 2}
|
|
})(rdr)
|
|
|
|
assert.Equal(t, result.Of(40), res(ctx2)())
|
|
}
|
|
|
|
func TestRightReaderIO(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
rio := func(c testContext) IO[int] {
|
|
return func() int { return c.value * 2 }
|
|
}
|
|
res := RightReaderIO(rio)
|
|
assert.Equal(t, result.Of(20), res(ctx)())
|
|
}
|
|
|
|
func TestLeftReaderIO(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
rio := func(c testContext) IO[error] {
|
|
return func() error { return errors.New("test") }
|
|
}
|
|
res := LeftReaderIO[int](rio)
|
|
assert.True(t, E.IsLeft(res(ctx)()))
|
|
}
|
|
|
|
func TestLet(t *testing.T) {
|
|
type State struct {
|
|
a int
|
|
b string
|
|
}
|
|
|
|
ctx := context.Background()
|
|
res := F.Pipe2(
|
|
Do[context.Context](State{}),
|
|
Let[context.Context](func(b string) func(State) State {
|
|
return func(s State) State { return State{a: s.a, b: b} }
|
|
}, func(s State) string { return "test" }),
|
|
Map[context.Context](func(s State) string { return s.b }),
|
|
)
|
|
|
|
assert.Equal(t, result.Of("test"), res(ctx)())
|
|
}
|
|
|
|
func TestLetTo(t *testing.T) {
|
|
type State struct {
|
|
a int
|
|
b string
|
|
}
|
|
|
|
ctx := context.Background()
|
|
res := F.Pipe2(
|
|
Do[context.Context](State{}),
|
|
LetTo[context.Context](func(b string) func(State) State {
|
|
return func(s State) State { return State{a: s.a, b: b} }
|
|
}, "constant"),
|
|
Map[context.Context](func(s State) string { return s.b }),
|
|
)
|
|
|
|
assert.Equal(t, result.Of("constant"), res(ctx)())
|
|
}
|
|
|
|
func TestBindTo(t *testing.T) {
|
|
type State struct {
|
|
value int
|
|
}
|
|
|
|
ctx := context.Background()
|
|
res := F.Pipe2(
|
|
Of[context.Context](42),
|
|
BindTo[context.Context](func(v int) State { return State{value: v} }),
|
|
Map[context.Context](func(s State) int { return s.value }),
|
|
)
|
|
|
|
assert.Equal(t, result.Of(42), res(ctx)())
|
|
}
|
|
|
|
func TestBracket(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
released := false
|
|
|
|
res := Bracket(
|
|
Of[testContext](42),
|
|
func(x int) ReaderIOResult[testContext, string] {
|
|
return Of[testContext](fmt.Sprintf("%d", x))
|
|
},
|
|
func(x int, result E.Either[error, string]) ReaderIOResult[testContext, int] {
|
|
released = true
|
|
return Of[testContext](0)
|
|
},
|
|
)
|
|
|
|
assert.Equal(t, result.Of("42"), res(ctx)())
|
|
assert.True(t, released)
|
|
}
|
|
|
|
func TestWithResource(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
released := false
|
|
|
|
res := WithResource[string](
|
|
Of[testContext](42),
|
|
func(x int) ReaderIOResult[testContext, int] {
|
|
released = true
|
|
return Of[testContext](0)
|
|
},
|
|
)(func(x int) ReaderIOResult[testContext, string] {
|
|
return Of[testContext](fmt.Sprintf("%d", x))
|
|
})
|
|
|
|
assert.Equal(t, result.Of("42"), res(ctx)())
|
|
assert.True(t, released)
|
|
}
|
|
|
|
func TestMonad(t *testing.T) {
|
|
m := Monad[testContext, int, string]()
|
|
assert.NotNil(t, m)
|
|
}
|
|
|
|
func TestTraverseArrayWithIndex(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
res := TraverseArrayWithIndex(func(i int, x int) ReaderIOResult[testContext, int] {
|
|
return Of[testContext](x + i)
|
|
})([]int{1, 2, 3})
|
|
|
|
assert.Equal(t, result.Of([]int{1, 3, 5}), res(ctx)())
|
|
}
|
|
|
|
func TestTraverseRecord(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
res := TraverseRecord[string](func(x int) ReaderIOResult[testContext, int] {
|
|
return Of[testContext](x * 2)
|
|
})(map[string]int{"a": 1, "b": 2})
|
|
|
|
expected := map[string]int{"a": 2, "b": 4}
|
|
assert.Equal(t, result.Of(expected), res(ctx)())
|
|
}
|
|
|
|
func TestTraverseRecordWithIndex(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
res := TraverseRecordWithIndex(func(k string, x int) ReaderIOResult[testContext, string] {
|
|
return Of[testContext](fmt.Sprintf("%s:%d", k, x))
|
|
})(map[string]int{"a": 1, "b": 2})
|
|
|
|
assert.True(t, E.IsRight(res(ctx)()))
|
|
}
|
|
|
|
func TestSequenceRecord(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
res := SequenceRecord(map[string]ReaderIOResult[testContext, int]{
|
|
"a": Of[testContext](1),
|
|
"b": Of[testContext](2),
|
|
})
|
|
|
|
expected := map[string]int{"a": 1, "b": 2}
|
|
assert.Equal(t, result.Of(expected), res(ctx)())
|
|
}
|
|
|
|
func TestSequenceT1(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
result := SequenceT1(Of[testContext](42))
|
|
res := result(ctx)()
|
|
assert.True(t, E.IsRight(res))
|
|
}
|
|
|
|
func TestSequenceT3(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
result := SequenceT3(
|
|
Of[testContext](1),
|
|
Of[testContext]("a"),
|
|
Of[testContext](true),
|
|
)
|
|
res := result(ctx)()
|
|
assert.True(t, E.IsRight(res))
|
|
}
|
|
|
|
func TestSequenceT4(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
result := SequenceT4(
|
|
Of[testContext](1),
|
|
Of[testContext]("a"),
|
|
Of[testContext](true),
|
|
Of[testContext](3.14),
|
|
)
|
|
res := result(ctx)()
|
|
assert.True(t, E.IsRight(res))
|
|
}
|
|
|
|
func TestWithLock(t *testing.T) {
|
|
ctx := testContext{value: 10}
|
|
unlocked := false
|
|
|
|
res := F.Pipe1(
|
|
Of[testContext](42),
|
|
WithLock[testContext, int](func() context.CancelFunc {
|
|
return func() { unlocked = true }
|
|
}),
|
|
)
|
|
|
|
assert.Equal(t, result.Of(42), res(ctx)())
|
|
assert.True(t, unlocked)
|
|
}
|