1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-11-23 22:14:53 +02:00
Files
fp-go/v2/pair/pair_test.go
Dr. Carsten Leue ed108812d6 fix: modernize codebase
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-11-15 17:00:22 +01:00

552 lines
13 KiB
Go

// Copyright (c) 2024 - 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 pair
import (
"fmt"
"strconv"
"testing"
EQ "github.com/IBM/fp-go/v2/eq"
N "github.com/IBM/fp-go/v2/number"
S "github.com/IBM/fp-go/v2/string"
"github.com/IBM/fp-go/v2/tuple"
"github.com/stretchr/testify/assert"
)
func TestOf(t *testing.T) {
p := Of(42)
assert.Equal(t, 42, Head(p))
assert.Equal(t, 42, Tail(p))
}
func TestMakePair(t *testing.T) {
p := MakePair("hello", 42)
assert.Equal(t, "hello", Head(p))
assert.Equal(t, 42, Tail(p))
}
func TestFromTuple(t *testing.T) {
tup := tuple.MakeTuple2("world", 100)
p := FromTuple(tup)
assert.Equal(t, "world", Head(p))
assert.Equal(t, 100, Tail(p))
}
func TestToTuple(t *testing.T) {
p := MakePair("hello", 42)
tup := ToTuple(p)
assert.Equal(t, "hello", tup.F1)
assert.Equal(t, 42, tup.F2)
}
func TestHeadAndTail(t *testing.T) {
p := MakePair("test", 123)
assert.Equal(t, "test", Head(p))
assert.Equal(t, 123, Tail(p))
}
func TestFirstAndSecond(t *testing.T) {
p := MakePair("first", "second")
assert.Equal(t, "first", First(p))
assert.Equal(t, "second", Second(p))
}
func TestMonadMapHead(t *testing.T) {
p := MakePair(5, "hello")
p2 := MonadMapHead(p, strconv.Itoa)
assert.Equal(t, "5", Head(p2))
assert.Equal(t, "hello", Tail(p2))
}
func TestMonadMapTail(t *testing.T) {
p := MakePair(5, "hello")
p2 := MonadMapTail(p, func(s string) int {
return len(s)
})
assert.Equal(t, 5, Head(p2))
assert.Equal(t, 5, Tail(p2))
}
func TestMonadMap(t *testing.T) {
p := MakePair(10, "test")
p2 := MonadMap(p, S.Format[int]("value: %d"))
assert.Equal(t, "value: 10", Head(p2))
assert.Equal(t, "test", Tail(p2))
}
func TestMonadBiMap(t *testing.T) {
p := MakePair(5, "hello")
p2 := MonadBiMap(p,
strconv.Itoa,
S.Size,
)
assert.Equal(t, "5", Head(p2))
assert.Equal(t, 5, Tail(p2))
}
func TestMapHead(t *testing.T) {
mapper := MapHead[string](strconv.Itoa)
p := MakePair(42, "world")
p2 := mapper(p)
assert.Equal(t, "42", Head(p2))
assert.Equal(t, "world", Tail(p2))
}
func TestMapTail(t *testing.T) {
mapper := MapTail[int](func(s string) int {
return len(s)
})
p := MakePair(10, "hello")
p2 := mapper(p)
assert.Equal(t, 10, Head(p2))
assert.Equal(t, 5, Tail(p2))
}
func TestMap(t *testing.T) {
mapper := Map[int](func(s string) int {
return len(s)
})
p := MakePair(10, "test")
p2 := mapper(p)
assert.Equal(t, 10, Head(p2))
assert.Equal(t, 4, Tail(p2))
}
func TestBiMap(t *testing.T) {
mapper := BiMap(
S.Format[int]("n=%d"),
S.Size,
)
p := MakePair(7, "hello")
p2 := mapper(p)
assert.Equal(t, "n=7", Head(p2))
assert.Equal(t, 5, Tail(p2))
}
func TestSwap(t *testing.T) {
p := MakePair("hello", 42)
swapped := Swap(p)
assert.Equal(t, 42, Head(swapped))
assert.Equal(t, "hello", Tail(swapped))
}
func TestMonadChainHead(t *testing.T) {
strConcat := S.Semigroup
p := MakePair(5, "hello")
p2 := MonadChainHead(strConcat, p, func(n int) Pair[string, string] {
return MakePair(fmt.Sprintf("%d", n), "!")
})
assert.Equal(t, "5", Head(p2))
assert.Equal(t, "hello!", Tail(p2))
}
func TestMonadChainTail(t *testing.T) {
intSum := N.SemigroupSum[int]()
p := MakePair(5, "hello")
p2 := MonadChainTail(intSum, p, func(s string) Pair[int, int] {
return MakePair(len(s), len(s)*2)
})
assert.Equal(t, 10, Head(p2)) // 5 + 5
assert.Equal(t, 10, Tail(p2))
}
func TestMonadChain(t *testing.T) {
intSum := N.SemigroupSum[int]()
p := MakePair(3, "test")
p2 := MonadChain(intSum, p, func(s string) Pair[int, int] {
return MakePair(len(s), len(s)*3)
})
assert.Equal(t, 7, Head(p2)) // 3 + 4
assert.Equal(t, 12, Tail(p2))
}
func TestChainHead(t *testing.T) {
strConcat := S.Semigroup
chain := ChainHead(strConcat, func(n int) Pair[string, string] {
return MakePair(fmt.Sprintf("%d", n), "!")
})
p := MakePair(42, "hello")
p2 := chain(p)
assert.Equal(t, "42", Head(p2))
assert.Equal(t, "hello!", Tail(p2))
}
func TestChainTail(t *testing.T) {
intSum := N.SemigroupSum[int]()
chain := ChainTail(intSum, func(s string) Pair[int, int] {
return MakePair(len(s), len(s)*2)
})
p := MakePair(10, "world")
p2 := chain(p)
assert.Equal(t, 15, Head(p2)) // 10 + 5
assert.Equal(t, 10, Tail(p2))
}
func TestChain(t *testing.T) {
intSum := N.SemigroupSum[int]()
chain := Chain(intSum, func(s string) Pair[int, int] {
return MakePair(len(s), len(s)*2)
})
p := MakePair(5, "hi")
p2 := chain(p)
assert.Equal(t, 7, Head(p2)) // 5 + 2
assert.Equal(t, 4, Tail(p2))
}
func TestMonadApHead(t *testing.T) {
strConcat := S.Semigroup
pf := MakePair(strconv.Itoa, "!")
pv := MakePair(42, "hello")
result := MonadApHead(strConcat, pf, pv)
assert.Equal(t, "42", Head(result))
assert.Equal(t, "hello!", Tail(result))
}
func TestMonadApTail(t *testing.T) {
intSum := N.SemigroupSum[int]()
pf := MakePair(10, S.Size)
pv := MakePair(5, "hello")
result := MonadApTail(intSum, pf, pv)
assert.Equal(t, 15, Head(result)) // 5 + 10
assert.Equal(t, 5, Tail(result))
}
func TestMonadAp(t *testing.T) {
intSum := N.SemigroupSum[int]()
pf := MakePair(7, func(s string) int { return len(s) * 2 })
pv := MakePair(3, "test")
result := MonadAp(intSum, pf, pv)
assert.Equal(t, 10, Head(result)) // 3 + 7
assert.Equal(t, 8, Tail(result)) // len("test") * 2
}
func TestApHead(t *testing.T) {
strConcat := S.Semigroup
pv := MakePair(100, "world")
ap := ApHead[string, int, string](strConcat, pv)
pf := MakePair(func(n int) string { return fmt.Sprintf("num=%d", n) }, "!")
result := ap(pf)
assert.Equal(t, "num=100", Head(result))
assert.Equal(t, "world!", Tail(result))
}
func TestApTail(t *testing.T) {
intSum := N.SemigroupSum[int]()
pv := MakePair(20, "hello")
ap := ApTail[int, string, int](intSum, pv)
pf := MakePair(5, S.Size)
result := ap(pf)
assert.Equal(t, 25, Head(result)) // 20 + 5
assert.Equal(t, 5, Tail(result))
}
func TestAp(t *testing.T) {
intSum := N.SemigroupSum[int]()
pv := MakePair(15, "test")
ap := Ap[int, string, int](intSum, pv)
pf := MakePair(10, func(s string) int { return len(s) * 3 })
result := ap(pf)
assert.Equal(t, 25, Head(result)) // 15 + 10
assert.Equal(t, 12, Tail(result)) // len("test") * 3
}
func TestPaired(t *testing.T) {
add := func(a, b int) int { return a + b }
pairedAdd := Paired(add)
result := pairedAdd(MakePair(3, 4))
assert.Equal(t, 7, result)
}
func TestUnpaired(t *testing.T) {
pairedAdd := func(p Pair[int, int]) int {
return Head(p) + Tail(p)
}
add := Unpaired(pairedAdd)
result := add(5, 7)
assert.Equal(t, 12, result)
}
func TestMerge(t *testing.T) {
add := N.Add[int]
merge := Merge(add)
result := merge(MakePair(3, 4))
assert.Equal(t, 7, result)
}
func TestEq(t *testing.T) {
pairEq := Eq(
EQ.FromStrictEquals[string](),
EQ.FromStrictEquals[int](),
)
p1 := MakePair("hello", 42)
p2 := MakePair("hello", 42)
p3 := MakePair("world", 42)
p4 := MakePair("hello", 100)
assert.True(t, pairEq.Equals(p1, p2))
assert.False(t, pairEq.Equals(p1, p3))
assert.False(t, pairEq.Equals(p1, p4))
}
func TestFromStrictEquals(t *testing.T) {
pairEq := FromStrictEquals[string, int]()
p1 := MakePair("test", 123)
p2 := MakePair("test", 123)
p3 := MakePair("test", 456)
assert.True(t, pairEq.Equals(p1, p2))
assert.False(t, pairEq.Equals(p1, p3))
}
func TestString(t *testing.T) {
p := MakePair("hello", 42)
str := p.String()
assert.Contains(t, str, "Pair")
assert.Contains(t, str, "hello")
assert.Contains(t, str, "42")
}
func TestFormat(t *testing.T) {
p := MakePair("test", 100)
str := fmt.Sprintf("%s", p)
assert.Contains(t, str, "Pair")
assert.Contains(t, str, "test")
assert.Contains(t, str, "100")
}
func TestMonadHead(t *testing.T) {
stringMonoid := S.Monoid
monad := MonadHead[int, string, string](stringMonoid)
// Test Of
p := monad.Of(42)
assert.Equal(t, 42, Head(p))
assert.Equal(t, "", Tail(p))
// Test Map
mapper := monad.Map(strconv.Itoa)
p2 := mapper(MakePair(100, "!"))
assert.Equal(t, "100", Head(p2))
assert.Equal(t, "!", Tail(p2))
// Test Chain
chain := monad.Chain(func(n int) Pair[string, string] {
return MakePair(fmt.Sprintf("n=%d", n), "!")
})
p3 := chain(MakePair(7, "hello"))
assert.Equal(t, "n=7", Head(p3))
assert.Equal(t, "hello!", Tail(p3))
// Test Ap
pv := MakePair(5, "world")
ap := monad.Ap(pv)
pf := MakePair(func(n int) string { return fmt.Sprintf("%d", n*2) }, "!")
p4 := ap(pf)
assert.Equal(t, "10", Head(p4))
assert.Equal(t, "world!", Tail(p4))
}
func TestPointedHead(t *testing.T) {
stringMonoid := S.Monoid
pointed := PointedHead[int](stringMonoid)
p := pointed.Of(42)
assert.Equal(t, 42, Head(p))
assert.Equal(t, "", Tail(p))
}
func TestFunctorHead(t *testing.T) {
functor := FunctorHead[int, string, string]()
mapper := functor.Map(func(n int) string { return fmt.Sprintf("value=%d", n) })
p := MakePair(42, "test")
p2 := mapper(p)
assert.Equal(t, "value=42", Head(p2))
assert.Equal(t, "test", Tail(p2))
}
func TestApplicativeHead(t *testing.T) {
stringMonoid := S.Monoid
applicative := ApplicativeHead[int, string, string](stringMonoid)
// Test Of
p := applicative.Of(100)
assert.Equal(t, 100, Head(p))
assert.Equal(t, "", Tail(p))
// Test Map
mapper := applicative.Map(strconv.Itoa)
p2 := mapper(MakePair(42, "!"))
assert.Equal(t, "42", Head(p2))
assert.Equal(t, "!", Tail(p2))
// Test Ap
pv := MakePair(7, "hello")
ap := applicative.Ap(pv)
pf := MakePair(func(n int) string { return fmt.Sprintf("n=%d", n) }, "!")
p3 := ap(pf)
assert.Equal(t, "n=7", Head(p3))
assert.Equal(t, "hello!", Tail(p3))
}
func TestMonadTail(t *testing.T) {
intSum := N.MonoidSum[int]()
monad := MonadTail[string, int, int](intSum)
// Test Of
p := monad.Of("hello")
assert.Equal(t, 0, Head(p))
assert.Equal(t, "hello", Tail(p))
// Test Map
mapper := monad.Map(S.Size)
p2 := mapper(MakePair(5, "world"))
assert.Equal(t, 5, Head(p2))
assert.Equal(t, 5, Tail(p2))
// Test Chain
chain := monad.Chain(func(s string) Pair[int, int] {
return MakePair(len(s), len(s)*2)
})
p3 := chain(MakePair(10, "test"))
assert.Equal(t, 14, Head(p3)) // 10 + 4
assert.Equal(t, 8, Tail(p3))
// Test Ap
pv := MakePair(5, "hello")
ap := monad.Ap(pv)
pf := MakePair(10, S.Size)
p4 := ap(pf)
assert.Equal(t, 15, Head(p4)) // 5 + 10
assert.Equal(t, 5, Tail(p4))
}
func TestPointedTail(t *testing.T) {
intSum := N.MonoidSum[int]()
pointed := PointedTail[string](intSum)
p := pointed.Of("test")
assert.Equal(t, 0, Head(p))
assert.Equal(t, "test", Tail(p))
}
func TestFunctorTail(t *testing.T) {
functor := FunctorTail[string, int, int]()
mapper := functor.Map(func(s string) int { return len(s) * 2 })
p := MakePair(10, "hello")
p2 := mapper(p)
assert.Equal(t, 10, Head(p2))
assert.Equal(t, 10, Tail(p2))
}
func TestApplicativeTail(t *testing.T) {
intSum := N.MonoidSum[int]()
applicative := ApplicativeTail[string, int, int](intSum)
// Test Of
p := applicative.Of("world")
assert.Equal(t, 0, Head(p))
assert.Equal(t, "world", Tail(p))
// Test Map
mapper := applicative.Map(S.Size)
p2 := mapper(MakePair(5, "test"))
assert.Equal(t, 5, Head(p2))
assert.Equal(t, 4, Tail(p2))
// Test Ap
pv := MakePair(10, "hello")
ap := applicative.Ap(pv)
pf := MakePair(5, func(s string) int { return len(s) * 2 })
p3 := ap(pf)
assert.Equal(t, 15, Head(p3)) // 10 + 5
assert.Equal(t, 10, Tail(p3))
}
func TestMonad(t *testing.T) {
intSum := N.MonoidSum[int]()
monad := Monad[string, int, int](intSum)
p := monad.Of("test")
assert.Equal(t, 0, Head(p))
assert.Equal(t, "test", Tail(p))
}
func TestPointed(t *testing.T) {
intSum := N.MonoidSum[int]()
pointed := Pointed[string](intSum)
p := pointed.Of("hello")
assert.Equal(t, 0, Head(p))
assert.Equal(t, "hello", Tail(p))
}
func TestFunctor(t *testing.T) {
functor := Functor[string, int, int]()
mapper := functor.Map(S.Size)
p := MakePair(7, "world")
p2 := mapper(p)
assert.Equal(t, 7, Head(p2))
assert.Equal(t, 5, Tail(p2))
}
func TestApplicative(t *testing.T) {
intSum := N.MonoidSum[int]()
applicative := Applicative[string, int, int](intSum)
p := applicative.Of("test")
assert.Equal(t, 0, Head(p))
assert.Equal(t, "test", Tail(p))
}
// Test edge cases and complex scenarios
func TestComplexChaining(t *testing.T) {
intSum := N.SemigroupSum[int]()
// Chain multiple operations
p := MakePair(1, "a")
p2 := MonadChainTail(intSum, p, func(s string) Pair[int, string] {
return MakePair(len(s), s+"b")
})
p3 := MonadChainTail(intSum, p2, func(s string) Pair[int, string] {
return MakePair(len(s), s+"c")
})
assert.Equal(t, 4, Head(p3)) // 1 + 1 + 2
assert.Equal(t, "abc", Tail(p3))
}
func TestBiMapWithDifferentTypes(t *testing.T) {
p := MakePair(3.14, true)
p2 := MonadBiMap(p,
func(f float64) int { return int(f * 10) },
func(b bool) string {
if b {
return "yes"
}
return "no"
},
)
assert.Equal(t, 31, Head(p2))
assert.Equal(t, "yes", Tail(p2))
}
func TestSwapTwice(t *testing.T) {
p := MakePair("original", 999)
swapped := Swap(p)
swappedBack := Swap(swapped)
assert.Equal(t, "original", Head(swappedBack))
assert.Equal(t, 999, Tail(swappedBack))
}