1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-12-21 23:47:34 +02:00
Files
fp-go/v2/iterator/iter/first_test.go
Dr. Carsten Leue 69691e9e70 fix: iterators
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-12-20 16:38:36 +01:00

347 lines
9.0 KiB
Go

// Copyright (c) 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 iter
import (
"fmt"
"testing"
F "github.com/IBM/fp-go/v2/function"
N "github.com/IBM/fp-go/v2/number"
O "github.com/IBM/fp-go/v2/option"
S "github.com/IBM/fp-go/v2/string"
"github.com/stretchr/testify/assert"
)
// TestFirst tests getting the first element from a non-empty sequence
func TestFirst(t *testing.T) {
t.Run("returns first element from integer sequence", func(t *testing.T) {
seq := From(1, 2, 3)
fst := First(seq)
assert.Equal(t, O.Of(1), fst)
})
t.Run("returns first element from string sequence", func(t *testing.T) {
seq := From("a", "b", "c")
fst := First(seq)
assert.Equal(t, O.Of("a"), fst)
})
t.Run("returns first element from single element sequence", func(t *testing.T) {
seq := From(42)
fst := First(seq)
assert.Equal(t, O.Of(42), fst)
})
t.Run("returns first element from large sequence", func(t *testing.T) {
seq := From(100, 200, 300, 400, 500)
fst := First(seq)
assert.Equal(t, O.Of(100), fst)
})
}
// TestFirstEmpty tests getting the first element from an empty sequence
func TestFirstEmpty(t *testing.T) {
t.Run("returns None for empty integer sequence", func(t *testing.T) {
seq := Empty[int]()
fst := First(seq)
assert.Equal(t, O.None[int](), fst)
})
t.Run("returns None for empty string sequence", func(t *testing.T) {
seq := Empty[string]()
fst := First(seq)
assert.Equal(t, O.None[string](), fst)
})
t.Run("returns None for empty struct sequence", func(t *testing.T) {
type TestStruct struct {
Value int
}
seq := Empty[TestStruct]()
fst := First(seq)
assert.Equal(t, O.None[TestStruct](), fst)
})
}
// TestFirstWithFiltered tests First with filtered sequences
func TestFirstWithFiltered(t *testing.T) {
t.Run("returns first element matching filter", func(t *testing.T) {
seq := From(1, 2, 3, 4, 5)
filtered := MonadFilter(seq, N.MoreThan(3))
fst := First(filtered)
assert.Equal(t, O.Of(4), fst)
})
t.Run("returns None when no elements match filter", func(t *testing.T) {
seq := From(1, 2, 3)
filtered := MonadFilter(seq, N.MoreThan(10))
fst := First(filtered)
assert.Equal(t, O.None[int](), fst)
})
t.Run("returns first even number", func(t *testing.T) {
seq := From(1, 3, 5, 6, 7, 8)
filtered := MonadFilter(seq, func(x int) bool { return x%2 == 0 })
fst := First(filtered)
assert.Equal(t, O.Of(6), fst)
})
}
// TestFirstWithMapped tests First with mapped sequences
func TestFirstWithMapped(t *testing.T) {
t.Run("returns first element after mapping", func(t *testing.T) {
seq := From(1, 2, 3)
mapped := MonadMap(seq, N.Mul(2))
fst := First(mapped)
assert.Equal(t, O.Of(2), fst)
})
t.Run("returns first string after mapping", func(t *testing.T) {
seq := From(1, 2, 3)
mapped := MonadMap(seq, S.Format[int]("num-%d"))
fst := First(mapped)
assert.Equal(t, O.Of("num-1"), fst)
})
}
// TestFirstWithComplex tests First with complex types
func TestFirstWithComplex(t *testing.T) {
type Person struct {
Name string
Age int
}
t.Run("returns first person", func(t *testing.T) {
seq := From(
Person{"Alice", 30},
Person{"Bob", 25},
Person{"Charlie", 35},
)
fst := First(seq)
expected := O.Of(Person{"Alice", 30})
assert.Equal(t, expected, fst)
})
t.Run("returns first pointer", func(t *testing.T) {
p1 := &Person{"Alice", 30}
p2 := &Person{"Bob", 25}
seq := From(p1, p2)
fst := First(seq)
assert.Equal(t, O.Of(p1), fst)
})
}
// TestFirstDoesNotConsumeEntireSequence tests that First only consumes the first element
func TestFirstDoesNotConsumeEntireSequence(t *testing.T) {
t.Run("only consumes first element", func(t *testing.T) {
callCount := 0
seq := MonadMap(From(1, 2, 3, 4, 5), func(x int) int {
callCount++
return x * 2
})
fst := First(seq)
assert.Equal(t, O.Of(2), fst)
// Should only have called the map function once for the first element
assert.Equal(t, 1, callCount)
})
}
// TestFirstWithChainedOperations tests First with multiple chained operations
func TestFirstWithChainedOperations(t *testing.T) {
t.Run("chains filter, map, and first", func(t *testing.T) {
seq := From(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
filtered := MonadFilter(seq, N.MoreThan(5))
mapped := MonadMap(filtered, N.Mul(10))
result := First(mapped)
assert.Equal(t, O.Of(60), result)
})
t.Run("chains map and filter", func(t *testing.T) {
seq := From(1, 2, 3, 4, 5)
mapped := MonadMap(seq, N.Mul(2))
filtered := MonadFilter(mapped, N.MoreThan(5))
result := First(filtered)
assert.Equal(t, O.Of(6), result)
})
}
// TestFirstWithReplicate tests First with replicated values
func TestFirstWithReplicate(t *testing.T) {
t.Run("returns first from replicated sequence", func(t *testing.T) {
seq := Replicate(5, 42)
fst := First(seq)
assert.Equal(t, O.Of(42), fst)
})
t.Run("returns None from zero replications", func(t *testing.T) {
seq := Replicate(0, 42)
fst := First(seq)
assert.Equal(t, O.None[int](), fst)
})
}
// TestFirstWithMakeBy tests First with MakeBy
func TestFirstWithMakeBy(t *testing.T) {
t.Run("returns first generated element", func(t *testing.T) {
seq := MakeBy(5, func(i int) int { return i * i })
fst := First(seq)
assert.Equal(t, O.Of(0), fst)
})
t.Run("returns None for zero elements", func(t *testing.T) {
seq := MakeBy(0, F.Identity[int])
fst := First(seq)
assert.Equal(t, O.None[int](), fst)
})
}
// TestFirstWithPrepend tests First with Prepend
func TestFirstWithPrepend(t *testing.T) {
t.Run("returns prepended element", func(t *testing.T) {
seq := From(2, 3, 4)
prepended := Prepend(1)(seq)
fst := First(prepended)
assert.Equal(t, O.Of(1), fst)
})
t.Run("returns prepended element from empty sequence", func(t *testing.T) {
seq := Empty[int]()
prepended := Prepend(42)(seq)
fst := First(prepended)
assert.Equal(t, O.Of(42), fst)
})
}
// TestFirstWithAppend tests First with Append
func TestFirstWithAppend(t *testing.T) {
t.Run("returns first element, not appended", func(t *testing.T) {
seq := From(1, 2, 3)
appended := Append(4)(seq)
fst := First(appended)
assert.Equal(t, O.Of(1), fst)
})
t.Run("returns appended element from empty sequence", func(t *testing.T) {
seq := Empty[int]()
appended := Append(42)(seq)
fst := First(appended)
assert.Equal(t, O.Of(42), fst)
})
}
// TestFirstWithChain tests First with Chain (flatMap)
func TestFirstWithChain(t *testing.T) {
t.Run("returns first from chained sequence", func(t *testing.T) {
seq := From(1, 2, 3)
chained := MonadChain(seq, func(x int) Seq[int] {
return From(x, x*10)
})
fst := First(chained)
assert.Equal(t, O.Of(1), fst)
})
t.Run("returns None when chain produces empty", func(t *testing.T) {
seq := From(1, 2, 3)
chained := MonadChain(seq, func(x int) Seq[int] {
return Empty[int]()
})
fst := First(chained)
assert.Equal(t, O.None[int](), fst)
})
}
// TestFirstWithFlatten tests First with Flatten
func TestFirstWithFlatten(t *testing.T) {
t.Run("returns first from flattened sequence", func(t *testing.T) {
nested := From(From(1, 2), From(3, 4), From(5))
flattened := Flatten(nested)
fst := First(flattened)
assert.Equal(t, O.Of(1), fst)
})
t.Run("returns None from empty nested sequence", func(t *testing.T) {
nested := Empty[Seq[int]]()
flattened := Flatten(nested)
fst := First(flattened)
assert.Equal(t, O.None[int](), fst)
})
}
// Benchmark tests
func BenchmarkFirst(b *testing.B) {
seq := From(1, 2, 3, 4, 5)
b.ResetTimer()
for b.Loop() {
First(seq)
}
}
func BenchmarkFirstLargeSequence(b *testing.B) {
data := make([]int, 1000)
for i := range data {
data[i] = i
}
seq := From(data...)
b.ResetTimer()
for b.Loop() {
First(seq)
}
}
func BenchmarkFirstFiltered(b *testing.B) {
seq := From(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
b.ResetTimer()
for b.Loop() {
filtered := MonadFilter(seq, N.MoreThan(5))
First(filtered)
}
}
// Example tests for documentation
func ExampleFirst() {
seq := From(1, 2, 3, 4, 5)
first := First(seq)
if value, ok := O.Unwrap(first); ok {
fmt.Printf("First element: %d\n", value)
}
// Output: First element: 1
}
func ExampleFirst_empty() {
seq := Empty[int]()
first := First(seq)
if _, ok := O.Unwrap(first); !ok {
fmt.Println("Sequence is empty")
}
// Output: Sequence is empty
}
func ExampleFirst_filtered() {
seq := From(1, 2, 3, 4, 5)
filtered := MonadFilter(seq, N.MoreThan(3))
first := First(filtered)
if value, ok := O.Unwrap(first); ok {
fmt.Printf("First element > 3: %d\n", value)
}
// Output: First element > 3: 4
}