mirror of
https://github.com/IBM/fp-go.git
synced 2025-12-17 23:37:41 +02:00
503 lines
10 KiB
Go
503 lines
10 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 readerio
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
F "github.com/IBM/fp-go/v2/function"
|
|
"github.com/IBM/fp-go/v2/internal/utils"
|
|
G "github.com/IBM/fp-go/v2/io"
|
|
N "github.com/IBM/fp-go/v2/number"
|
|
"github.com/IBM/fp-go/v2/reader"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestMonadMap(t *testing.T) {
|
|
rio := Of(5)
|
|
doubled := MonadMap(rio, N.Mul(2))
|
|
|
|
result := doubled(context.Background())()
|
|
assert.Equal(t, 10, result)
|
|
}
|
|
|
|
func TestMap(t *testing.T) {
|
|
g := F.Pipe1(
|
|
Of(1),
|
|
Map(utils.Double),
|
|
)
|
|
|
|
assert.Equal(t, 2, g(context.Background())())
|
|
}
|
|
|
|
func TestMonadMapTo(t *testing.T) {
|
|
rio := Of(42)
|
|
replaced := MonadMapTo(rio, "constant")
|
|
|
|
result := replaced(context.Background())()
|
|
assert.Equal(t, "constant", result)
|
|
}
|
|
|
|
func TestMapTo(t *testing.T) {
|
|
result := F.Pipe1(
|
|
Of(42),
|
|
MapTo[int]("constant"),
|
|
)
|
|
|
|
assert.Equal(t, "constant", result(context.Background())())
|
|
}
|
|
|
|
func TestMonadChain(t *testing.T) {
|
|
rio1 := Of(5)
|
|
result := MonadChain(rio1, func(n int) ReaderIO[int] {
|
|
return Of(n * 3)
|
|
})
|
|
|
|
assert.Equal(t, 15, result(context.Background())())
|
|
}
|
|
|
|
func TestChain(t *testing.T) {
|
|
result := F.Pipe1(
|
|
Of(5),
|
|
Chain(func(n int) ReaderIO[int] {
|
|
return Of(n * 3)
|
|
}),
|
|
)
|
|
|
|
assert.Equal(t, 15, result(context.Background())())
|
|
}
|
|
|
|
func TestMonadChainFirst(t *testing.T) {
|
|
sideEffect := 0
|
|
rio := Of(42)
|
|
result := MonadChainFirst(rio, func(n int) ReaderIO[string] {
|
|
sideEffect = n
|
|
return Of("side effect")
|
|
})
|
|
|
|
value := result(context.Background())()
|
|
assert.Equal(t, 42, value)
|
|
assert.Equal(t, 42, sideEffect)
|
|
}
|
|
|
|
func TestChainFirst(t *testing.T) {
|
|
sideEffect := 0
|
|
result := F.Pipe1(
|
|
Of(42),
|
|
ChainFirst(func(n int) ReaderIO[string] {
|
|
sideEffect = n
|
|
return Of("side effect")
|
|
}),
|
|
)
|
|
|
|
value := result(context.Background())()
|
|
assert.Equal(t, 42, value)
|
|
assert.Equal(t, 42, sideEffect)
|
|
}
|
|
|
|
func TestMonadTap(t *testing.T) {
|
|
sideEffect := 0
|
|
rio := Of(42)
|
|
result := MonadTap(rio, func(n int) ReaderIO[func()] {
|
|
sideEffect = n
|
|
return Of(func() {})
|
|
})
|
|
|
|
value := result(context.Background())()
|
|
assert.Equal(t, 42, value)
|
|
assert.Equal(t, 42, sideEffect)
|
|
}
|
|
|
|
func TestTap(t *testing.T) {
|
|
sideEffect := 0
|
|
result := F.Pipe1(
|
|
Of(42),
|
|
Tap(func(n int) ReaderIO[func()] {
|
|
sideEffect = n
|
|
return Of(func() {})
|
|
}),
|
|
)
|
|
|
|
value := result(context.Background())()
|
|
assert.Equal(t, 42, value)
|
|
assert.Equal(t, 42, sideEffect)
|
|
}
|
|
|
|
func TestOf(t *testing.T) {
|
|
rio := Of(100)
|
|
result := rio(context.Background())()
|
|
|
|
assert.Equal(t, 100, result)
|
|
}
|
|
|
|
func TestMonadAp(t *testing.T) {
|
|
fabIO := Of(N.Mul(2))
|
|
faIO := Of(5)
|
|
result := MonadAp(fabIO, faIO)
|
|
|
|
assert.Equal(t, 10, result(context.Background())())
|
|
}
|
|
|
|
func TestAp(t *testing.T) {
|
|
g := F.Pipe1(
|
|
Of(utils.Double),
|
|
Ap[int](Of(1)),
|
|
)
|
|
|
|
assert.Equal(t, 2, g(context.Background())())
|
|
}
|
|
|
|
func TestMonadApSeq(t *testing.T) {
|
|
fabIO := Of(N.Add(10))
|
|
faIO := Of(5)
|
|
result := MonadApSeq(fabIO, faIO)
|
|
|
|
assert.Equal(t, 15, result(context.Background())())
|
|
}
|
|
|
|
func TestApSeq(t *testing.T) {
|
|
g := F.Pipe1(
|
|
Of(N.Add(10)),
|
|
ApSeq[int](Of(5)),
|
|
)
|
|
|
|
assert.Equal(t, 15, g(context.Background())())
|
|
}
|
|
|
|
func TestMonadApPar(t *testing.T) {
|
|
fabIO := Of(N.Add(10))
|
|
faIO := Of(5)
|
|
result := MonadApPar(fabIO, faIO)
|
|
|
|
assert.Equal(t, 15, result(context.Background())())
|
|
}
|
|
|
|
func TestApPar(t *testing.T) {
|
|
g := F.Pipe1(
|
|
Of(N.Add(10)),
|
|
ApPar[int](Of(5)),
|
|
)
|
|
|
|
assert.Equal(t, 15, g(context.Background())())
|
|
}
|
|
|
|
func TestAsk(t *testing.T) {
|
|
rio := Ask()
|
|
ctx := context.WithValue(context.Background(), "key", "value")
|
|
result := rio(ctx)()
|
|
|
|
assert.Equal(t, ctx, result)
|
|
}
|
|
|
|
func TestFromIO(t *testing.T) {
|
|
ioAction := G.Of(42)
|
|
rio := FromIO(ioAction)
|
|
|
|
result := rio(context.Background())()
|
|
assert.Equal(t, 42, result)
|
|
}
|
|
|
|
func TestFromReader(t *testing.T) {
|
|
rdr := func(ctx context.Context) int {
|
|
return 42
|
|
}
|
|
|
|
rio := FromReader(rdr)
|
|
result := rio(context.Background())()
|
|
|
|
assert.Equal(t, 42, result)
|
|
}
|
|
|
|
func TestFromLazy(t *testing.T) {
|
|
lazy := func() int { return 42 }
|
|
rio := FromLazy(lazy)
|
|
|
|
result := rio(context.Background())()
|
|
assert.Equal(t, 42, result)
|
|
}
|
|
|
|
func TestMonadChainIOK(t *testing.T) {
|
|
rio := Of(5)
|
|
result := MonadChainIOK(rio, func(n int) G.IO[int] {
|
|
return G.Of(n * 4)
|
|
})
|
|
|
|
assert.Equal(t, 20, result(context.Background())())
|
|
}
|
|
|
|
func TestChainIOK(t *testing.T) {
|
|
result := F.Pipe1(
|
|
Of(5),
|
|
ChainIOK(func(n int) G.IO[int] {
|
|
return G.Of(n * 4)
|
|
}),
|
|
)
|
|
|
|
assert.Equal(t, 20, result(context.Background())())
|
|
}
|
|
|
|
func TestMonadChainFirstIOK(t *testing.T) {
|
|
sideEffect := 0
|
|
rio := Of(42)
|
|
result := MonadChainFirstIOK(rio, func(n int) G.IO[string] {
|
|
sideEffect = n
|
|
return G.Of("side effect")
|
|
})
|
|
|
|
value := result(context.Background())()
|
|
assert.Equal(t, 42, value)
|
|
assert.Equal(t, 42, sideEffect)
|
|
}
|
|
|
|
func TestChainFirstIOK(t *testing.T) {
|
|
sideEffect := 0
|
|
result := F.Pipe1(
|
|
Of(42),
|
|
ChainFirstIOK(func(n int) G.IO[string] {
|
|
sideEffect = n
|
|
return G.Of("side effect")
|
|
}),
|
|
)
|
|
|
|
value := result(context.Background())()
|
|
assert.Equal(t, 42, value)
|
|
assert.Equal(t, 42, sideEffect)
|
|
}
|
|
|
|
func TestMonadTapIOK(t *testing.T) {
|
|
sideEffect := 0
|
|
rio := Of(42)
|
|
result := MonadTapIOK(rio, func(n int) G.IO[func()] {
|
|
sideEffect = n
|
|
return G.Of(func() {})
|
|
})
|
|
|
|
value := result(context.Background())()
|
|
assert.Equal(t, 42, value)
|
|
assert.Equal(t, 42, sideEffect)
|
|
}
|
|
|
|
func TestTapIOK(t *testing.T) {
|
|
sideEffect := 0
|
|
result := F.Pipe1(
|
|
Of(42),
|
|
TapIOK(func(n int) G.IO[func()] {
|
|
sideEffect = n
|
|
return G.Of(func() {})
|
|
}),
|
|
)
|
|
|
|
value := result(context.Background())()
|
|
assert.Equal(t, 42, value)
|
|
assert.Equal(t, 42, sideEffect)
|
|
}
|
|
|
|
func TestDefer(t *testing.T) {
|
|
counter := 0
|
|
rio := Defer(func() ReaderIO[int] {
|
|
counter++
|
|
return Of(counter)
|
|
})
|
|
|
|
result1 := rio(context.Background())()
|
|
result2 := rio(context.Background())()
|
|
|
|
assert.Equal(t, 1, result1)
|
|
assert.Equal(t, 2, result2)
|
|
}
|
|
|
|
func TestMemoize(t *testing.T) {
|
|
counter := 0
|
|
rio := Of(0)
|
|
memoized := Memoize(MonadMap(rio, func(int) int {
|
|
counter++
|
|
return counter
|
|
}))
|
|
|
|
result1 := memoized(context.Background())()
|
|
result2 := memoized(context.Background())()
|
|
|
|
assert.Equal(t, 1, result1)
|
|
assert.Equal(t, 1, result2) // Same value, memoized
|
|
}
|
|
|
|
func TestFlatten(t *testing.T) {
|
|
nested := Of(Of(42))
|
|
flattened := Flatten(nested)
|
|
|
|
result := flattened(context.Background())()
|
|
assert.Equal(t, 42, result)
|
|
}
|
|
|
|
func TestMonadFlap(t *testing.T) {
|
|
fabIO := Of(N.Mul(3))
|
|
result := MonadFlap(fabIO, 7)
|
|
|
|
assert.Equal(t, 21, result(context.Background())())
|
|
}
|
|
|
|
func TestFlap(t *testing.T) {
|
|
result := F.Pipe1(
|
|
Of(N.Mul(3)),
|
|
Flap[int](7),
|
|
)
|
|
|
|
assert.Equal(t, 21, result(context.Background())())
|
|
}
|
|
|
|
func TestMonadChainReaderK(t *testing.T) {
|
|
rio := Of(5)
|
|
result := MonadChainReaderK(rio, func(n int) reader.Reader[context.Context, int] {
|
|
return func(ctx context.Context) int { return n * 2 }
|
|
})
|
|
|
|
assert.Equal(t, 10, result(context.Background())())
|
|
}
|
|
|
|
func TestChainReaderK(t *testing.T) {
|
|
result := F.Pipe1(
|
|
Of(5),
|
|
ChainReaderK(func(n int) reader.Reader[context.Context, int] {
|
|
return func(ctx context.Context) int { return n * 2 }
|
|
}),
|
|
)
|
|
|
|
assert.Equal(t, 10, result(context.Background())())
|
|
}
|
|
|
|
func TestMonadChainFirstReaderK(t *testing.T) {
|
|
sideEffect := 0
|
|
rio := Of(42)
|
|
result := MonadChainFirstReaderK(rio, func(n int) reader.Reader[context.Context, string] {
|
|
return func(ctx context.Context) string {
|
|
sideEffect = n
|
|
return "side effect"
|
|
}
|
|
})
|
|
|
|
value := result(context.Background())()
|
|
assert.Equal(t, 42, value)
|
|
assert.Equal(t, 42, sideEffect)
|
|
}
|
|
|
|
func TestChainFirstReaderK(t *testing.T) {
|
|
sideEffect := 0
|
|
result := F.Pipe1(
|
|
Of(42),
|
|
ChainFirstReaderK(func(n int) reader.Reader[context.Context, string] {
|
|
return func(ctx context.Context) string {
|
|
sideEffect = n
|
|
return "side effect"
|
|
}
|
|
}),
|
|
)
|
|
|
|
value := result(context.Background())()
|
|
assert.Equal(t, 42, value)
|
|
assert.Equal(t, 42, sideEffect)
|
|
}
|
|
|
|
func TestMonadTapReaderK(t *testing.T) {
|
|
sideEffect := 0
|
|
rio := Of(42)
|
|
result := MonadTapReaderK(rio, func(n int) reader.Reader[context.Context, func()] {
|
|
return func(ctx context.Context) func() {
|
|
sideEffect = n
|
|
return func() {}
|
|
}
|
|
})
|
|
|
|
value := result(context.Background())()
|
|
assert.Equal(t, 42, value)
|
|
assert.Equal(t, 42, sideEffect)
|
|
}
|
|
|
|
func TestTapReaderK(t *testing.T) {
|
|
sideEffect := 0
|
|
result := F.Pipe1(
|
|
Of(42),
|
|
TapReaderK(func(n int) reader.Reader[context.Context, func()] {
|
|
return func(ctx context.Context) func() {
|
|
sideEffect = n
|
|
return func() {}
|
|
}
|
|
}),
|
|
)
|
|
|
|
value := result(context.Background())()
|
|
assert.Equal(t, 42, value)
|
|
assert.Equal(t, 42, sideEffect)
|
|
}
|
|
|
|
func TestRead(t *testing.T) {
|
|
rio := Of(42)
|
|
ctx := context.Background()
|
|
ioAction := Read[int](ctx)(rio)
|
|
result := ioAction()
|
|
|
|
assert.Equal(t, 42, result)
|
|
}
|
|
|
|
func TestComplexPipeline(t *testing.T) {
|
|
// Test a complex pipeline combining multiple operations
|
|
result := F.Pipe3(
|
|
Ask(),
|
|
Map(func(ctx context.Context) int { return 5 }),
|
|
Chain(func(n int) ReaderIO[int] {
|
|
return Of(n * 2)
|
|
}),
|
|
Map(N.Add(10)),
|
|
)
|
|
|
|
assert.Equal(t, 20, result(context.Background())()) // (5 * 2) + 10 = 20
|
|
}
|
|
|
|
func TestFromIOWithChain(t *testing.T) {
|
|
ioAction := G.Of(10)
|
|
|
|
result := F.Pipe1(
|
|
FromIO(ioAction),
|
|
Chain(func(n int) ReaderIO[int] {
|
|
return Of(n + 5)
|
|
}),
|
|
)
|
|
|
|
assert.Equal(t, 15, result(context.Background())())
|
|
}
|
|
|
|
func TestTapWithLogging(t *testing.T) {
|
|
// Simulate logging scenario
|
|
logged := []int{}
|
|
|
|
result := F.Pipe3(
|
|
Of(42),
|
|
Tap(func(n int) ReaderIO[func()] {
|
|
logged = append(logged, n)
|
|
return Of(func() {})
|
|
}),
|
|
Map(N.Mul(2)),
|
|
Tap(func(n int) ReaderIO[func()] {
|
|
logged = append(logged, n)
|
|
return Of(func() {})
|
|
}),
|
|
)
|
|
|
|
value := result(context.Background())()
|
|
assert.Equal(t, 84, value)
|
|
assert.Equal(t, []int{42, 84}, logged)
|
|
}
|