// 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 readeroption import ( "fmt" "testing" F "github.com/IBM/fp-go/v2/function" "github.com/IBM/fp-go/v2/internal/utils" O "github.com/IBM/fp-go/v2/option" "github.com/IBM/fp-go/v2/reader" "github.com/stretchr/testify/assert" ) type MyContext string const defaultContext MyContext = "default" func TestMap(t *testing.T) { g := F.Pipe1( Of[MyContext](1), Map[MyContext](utils.Double), ) assert.Equal(t, O.Of(2), g(defaultContext)) } func TestAp(t *testing.T) { g := F.Pipe1( Of[MyContext](utils.Double), Ap[int](Of[MyContext](1)), ) assert.Equal(t, O.Of(2), g(defaultContext)) } func TestFlatten(t *testing.T) { g := F.Pipe1( Of[MyContext](Of[MyContext]("a")), Flatten[MyContext, string], ) assert.Equal(t, O.Of("a"), g(defaultContext)) } func TestFromOption(t *testing.T) { // Test with Some opt1 := O.Of(42) ro1 := FromOption[MyContext](opt1) assert.Equal(t, O.Of(42), ro1(defaultContext)) // Test with None opt2 := O.None[int]() ro2 := FromOption[MyContext](opt2) assert.Equal(t, O.None[int](), ro2(defaultContext)) } func TestSome(t *testing.T) { ro := Some[MyContext](42) assert.Equal(t, O.Of(42), ro(defaultContext)) } func TestFromReader(t *testing.T) { reader := func(ctx MyContext) int { return 42 } ro := FromReader(reader) assert.Equal(t, O.Of(42), ro(defaultContext)) } func TestOf(t *testing.T) { ro := Of[MyContext](42) assert.Equal(t, O.Of(42), ro(defaultContext)) } func TestNone(t *testing.T) { ro := None[MyContext, int]() assert.Equal(t, O.None[int](), ro(defaultContext)) } func TestChain(t *testing.T) { double := func(x int) ReaderOption[MyContext, int] { return Of[MyContext](x * 2) } g := F.Pipe1( Of[MyContext](21), Chain(double), ) assert.Equal(t, O.Of(42), g(defaultContext)) // Test with None g2 := F.Pipe1( None[MyContext, int](), Chain(double), ) assert.Equal(t, O.None[int](), g2(defaultContext)) } func TestFromPredicate(t *testing.T) { isPositive := FromPredicate[MyContext](func(x int) bool { return x > 0 }) // Test with positive number g1 := F.Pipe1( Of[MyContext](42), Chain(isPositive), ) assert.Equal(t, O.Of(42), g1(defaultContext)) // Test with negative number g2 := F.Pipe1( Of[MyContext](-5), Chain(isPositive), ) assert.Equal(t, O.None[int](), g2(defaultContext)) } func TestFold(t *testing.T) { onNone := reader.Of[MyContext]("none") onSome := func(x int) Reader[MyContext, string] { return reader.Of[MyContext](fmt.Sprintf("%d", x)) } // Test with Some g1 := Fold(onNone, onSome)(Of[MyContext](42)) assert.Equal(t, "42", g1(defaultContext)) // Test with None g2 := Fold(onNone, onSome)(None[MyContext, int]()) assert.Equal(t, "none", g2(defaultContext)) } func TestGetOrElse(t *testing.T) { defaultValue := reader.Of[MyContext](0) // Test with Some g1 := GetOrElse(defaultValue)(Of[MyContext](42)) assert.Equal(t, 42, g1(defaultContext)) // Test with None g2 := GetOrElse(defaultValue)(None[MyContext, int]()) assert.Equal(t, 0, g2(defaultContext)) } func TestAsk(t *testing.T) { ro := Ask[MyContext, any]() result := ro(defaultContext) assert.Equal(t, O.Of(defaultContext), result) } func TestAsks(t *testing.T) { reader := func(ctx MyContext) string { return string(ctx) } ro := Asks(reader) result := ro(defaultContext) assert.Equal(t, O.Of("default"), result) } func TestChainOptionK(t *testing.T) { parsePositive := func(x int) O.Option[int] { if x > 0 { return O.Of(x) } return O.None[int]() } // Test with positive number g1 := F.Pipe1( Of[MyContext](42), ChainOptionK[MyContext](parsePositive), ) assert.Equal(t, O.Of(42), g1(defaultContext)) // Test with negative number g2 := F.Pipe1( Of[MyContext](-5), ChainOptionK[MyContext](parsePositive), ) assert.Equal(t, O.None[int](), g2(defaultContext)) } func TestLocal(t *testing.T) { type GlobalContext struct { Value string } // A computation that needs a string context ro := Asks(func(s string) string { return "Hello, " + s }) // Transform GlobalContext to string transformed := Local[string](func(g GlobalContext) string { return g.Value })(ro) result := transformed(GlobalContext{Value: "World"}) assert.Equal(t, O.Of("Hello, World"), result) } func TestRead(t *testing.T) { ro := Of[MyContext](42) result := Read[int](defaultContext)(ro) assert.Equal(t, O.Of(42), result) } func TestFlap(t *testing.T) { addFunc := func(x int) int { return x + 10 } g := F.Pipe1( Of[MyContext](addFunc), Flap[MyContext, int](32), ) assert.Equal(t, O.Of(42), g(defaultContext)) }