fp_test.gno

14.18 Kb ยท 666 lines
  1package fp
  2
  3import (
  4	"fmt"
  5	"testing"
  6)
  7
  8func TestMap(t *testing.T) {
  9	tests := []struct {
 10		name     string
 11		input    []any
 12		fn       func(any) any
 13		expected []any
 14	}{
 15		{
 16			name:     "multiply numbers by 2",
 17			input:    []any{1, 2, 3},
 18			fn:       func(v any) any { return v.(int) * 2 },
 19			expected: []any{2, 4, 6},
 20		},
 21		{
 22			name:     "empty slice",
 23			input:    []any{},
 24			fn:       func(v any) any { return v.(int) * 2 },
 25			expected: []any{},
 26		},
 27		{
 28			name:     "convert numbers to strings",
 29			input:    []any{1, 2, 3},
 30			fn:       func(v any) any { return fmt.Sprintf("%d", v.(int)) },
 31			expected: []any{"1", "2", "3"},
 32		},
 33	}
 34
 35	for _, tt := range tests {
 36		t.Run(tt.name, func(t *testing.T) {
 37			result := Map(tt.input, tt.fn)
 38			if !equalSlices(result, tt.expected) {
 39				t.Errorf("Map failed, expected %v, got %v", tt.expected, result)
 40			}
 41		})
 42	}
 43}
 44
 45func TestFilter(t *testing.T) {
 46	tests := []struct {
 47		name     string
 48		input    []any
 49		fn       func(any) bool
 50		expected []any
 51	}{
 52		{
 53			name:     "filter even numbers",
 54			input:    []any{1, 2, 3, 4},
 55			fn:       func(v any) bool { return v.(int)%2 == 0 },
 56			expected: []any{2, 4},
 57		},
 58		{
 59			name:     "empty slice",
 60			input:    []any{},
 61			fn:       func(v any) bool { return v.(int)%2 == 0 },
 62			expected: []any{},
 63		},
 64		{
 65			name:     "no matches",
 66			input:    []any{1, 3, 5},
 67			fn:       func(v any) bool { return v.(int)%2 == 0 },
 68			expected: []any{},
 69		},
 70		{
 71			name:     "all matches",
 72			input:    []any{2, 4, 6},
 73			fn:       func(v any) bool { return v.(int)%2 == 0 },
 74			expected: []any{2, 4, 6},
 75		},
 76	}
 77
 78	for _, tt := range tests {
 79		t.Run(tt.name, func(t *testing.T) {
 80			result := Filter(tt.input, tt.fn)
 81			if !equalSlices(result, tt.expected) {
 82				t.Errorf("Filter failed, expected %v, got %v", tt.expected, result)
 83			}
 84		})
 85	}
 86}
 87
 88func TestReduce(t *testing.T) {
 89	tests := []struct {
 90		name     string
 91		input    []any
 92		fn       func(any, any) any
 93		initial  any
 94		expected any
 95	}{
 96		{
 97			name:     "sum numbers",
 98			input:    []any{1, 2, 3},
 99			fn:       func(a, b any) any { return a.(int) + b.(int) },
100			initial:  0,
101			expected: 6,
102		},
103		{
104			name:     "empty slice",
105			input:    []any{},
106			fn:       func(a, b any) any { return a.(int) + b.(int) },
107			initial:  0,
108			expected: 0,
109		},
110		{
111			name:     "concatenate strings",
112			input:    []any{"a", "b", "c"},
113			fn:       func(a, b any) any { return a.(string) + b.(string) },
114			initial:  "",
115			expected: "abc",
116		},
117	}
118
119	for _, tt := range tests {
120		t.Run(tt.name, func(t *testing.T) {
121			result := Reduce(tt.input, tt.fn, tt.initial)
122			if result != tt.expected {
123				t.Errorf("Reduce failed, expected %v, got %v", tt.expected, result)
124			}
125		})
126	}
127}
128
129func TestFlatMap(t *testing.T) {
130	tests := []struct {
131		name     string
132		input    []any
133		fn       func(any) any
134		expected []any
135	}{
136		{
137			name:  "split words into chars",
138			input: []any{"go", "fn"},
139			fn: func(word any) any {
140				chars := []any{}
141				for _, c := range word.(string) {
142					chars = append(chars, string(c))
143				}
144				return chars
145			},
146			expected: []any{"g", "o", "f", "n"},
147		},
148		{
149			name:  "empty string handling",
150			input: []any{"", "a", ""},
151			fn: func(word any) any {
152				chars := []any{}
153				for _, c := range word.(string) {
154					chars = append(chars, string(c))
155				}
156				return chars
157			},
158			expected: []any{"a"},
159		},
160		{
161			name:  "nil handling",
162			input: []any{nil, "a", nil},
163			fn: func(word any) any {
164				if word == nil {
165					return []any{}
166				}
167				return []any{word}
168			},
169			expected: []any{"a"},
170		},
171		{
172			name:  "empty slice result",
173			input: []any{"", "", ""},
174			fn: func(word any) any {
175				return []any{}
176			},
177			expected: []any{},
178		},
179		{
180			name:  "nested array flattening",
181			input: []any{1, 2, 3},
182			fn: func(n any) any {
183				return []any{n, n}
184			},
185			expected: []any{1, 1, 2, 2, 3, 3},
186		},
187	}
188
189	for _, tt := range tests {
190		t.Run(tt.name, func(t *testing.T) {
191			result := FlatMap(tt.input, tt.fn)
192			if !equalSlices(result, tt.expected) {
193				t.Errorf("FlatMap failed, expected %v, got %v", tt.expected, result)
194			}
195		})
196	}
197}
198
199func TestAllAnyNone(t *testing.T) {
200	tests := []struct {
201		name         string
202		input        []any
203		fn           func(any) bool
204		expectedAll  bool
205		expectedAny  bool
206		expectedNone bool
207	}{
208		{
209			name:         "all even numbers",
210			input:        []any{2, 4, 6, 8},
211			fn:           func(x any) bool { return x.(int)%2 == 0 },
212			expectedAll:  true,
213			expectedAny:  true,
214			expectedNone: false,
215		},
216		{
217			name:         "no even numbers",
218			input:        []any{1, 3, 5, 7},
219			fn:           func(x any) bool { return x.(int)%2 == 0 },
220			expectedAll:  false,
221			expectedAny:  false,
222			expectedNone: true,
223		},
224		{
225			name:         "mixed even/odd numbers",
226			input:        []any{1, 2, 3, 4},
227			fn:           func(x any) bool { return x.(int)%2 == 0 },
228			expectedAll:  false,
229			expectedAny:  true,
230			expectedNone: false,
231		},
232		{
233			name:         "empty slice",
234			input:        []any{},
235			fn:           func(x any) bool { return x.(int)%2 == 0 },
236			expectedAll:  true,  // vacuously true
237			expectedAny:  false, // vacuously false
238			expectedNone: true,  // vacuously true
239		},
240		{
241			name:         "nil predicate handling",
242			input:        []any{nil, nil, nil},
243			fn:           func(x any) bool { return x == nil },
244			expectedAll:  true,
245			expectedAny:  true,
246			expectedNone: false,
247		},
248	}
249
250	for _, tt := range tests {
251		t.Run(tt.name, func(t *testing.T) {
252			resultAll := All(tt.input, tt.fn)
253			if resultAll != tt.expectedAll {
254				t.Errorf("All failed, expected %v, got %v", tt.expectedAll, resultAll)
255			}
256
257			resultAny := Any(tt.input, tt.fn)
258			if resultAny != tt.expectedAny {
259				t.Errorf("Any failed, expected %v, got %v", tt.expectedAny, resultAny)
260			}
261
262			resultNone := None(tt.input, tt.fn)
263			if resultNone != tt.expectedNone {
264				t.Errorf("None failed, expected %v, got %v", tt.expectedNone, resultNone)
265			}
266		})
267	}
268}
269
270func TestChunk(t *testing.T) {
271	tests := []struct {
272		name     string
273		input    []any
274		size     int
275		expected [][]any
276	}{
277		{
278			name:     "normal chunks",
279			input:    []any{1, 2, 3, 4, 5},
280			size:     2,
281			expected: [][]any{{1, 2}, {3, 4}, {5}},
282		},
283		{
284			name:     "empty slice",
285			input:    []any{},
286			size:     2,
287			expected: [][]any{},
288		},
289		{
290			name:     "chunk size equals length",
291			input:    []any{1, 2, 3},
292			size:     3,
293			expected: [][]any{{1, 2, 3}},
294		},
295		{
296			name:     "chunk size larger than length",
297			input:    []any{1, 2},
298			size:     3,
299			expected: [][]any{{1, 2}},
300		},
301	}
302
303	for _, tt := range tests {
304		t.Run(tt.name, func(t *testing.T) {
305			result := Chunk(tt.input, tt.size)
306			if !equalNestedSlices(result, tt.expected) {
307				t.Errorf("Chunk failed, expected %v, got %v", tt.expected, result)
308			}
309		})
310	}
311}
312
313func TestFind(t *testing.T) {
314	tests := []struct {
315		name        string
316		input       []any
317		fn          func(any) bool
318		expected    any
319		shouldFound bool
320	}{
321		{
322			name:        "find first number greater than 2",
323			input:       []any{1, 2, 3, 4},
324			fn:          func(v any) bool { return v.(int) > 2 },
325			expected:    3,
326			shouldFound: true,
327		},
328		{
329			name:        "empty slice",
330			input:       []any{},
331			fn:          func(v any) bool { return v.(int) > 2 },
332			expected:    nil,
333			shouldFound: false,
334		},
335		{
336			name:        "no match",
337			input:       []any{1, 2},
338			fn:          func(v any) bool { return v.(int) > 10 },
339			expected:    nil,
340			shouldFound: false,
341		},
342	}
343
344	for _, tt := range tests {
345		t.Run(tt.name, func(t *testing.T) {
346			result, found := Find(tt.input, tt.fn)
347			if found != tt.shouldFound {
348				t.Errorf("Find failed, expected found=%v, got found=%v", tt.shouldFound, found)
349			}
350			if found && result != tt.expected {
351				t.Errorf("Find failed, expected %v, got %v", tt.expected, result)
352			}
353		})
354	}
355}
356
357func TestReverse(t *testing.T) {
358	tests := []struct {
359		name     string
360		input    []any
361		expected []any
362	}{
363		{
364			name:     "normal sequence",
365			input:    []any{1, 2, 3, 4},
366			expected: []any{4, 3, 2, 1},
367		},
368		{
369			name:     "empty slice",
370			input:    []any{},
371			expected: []any{},
372		},
373		{
374			name:     "single element",
375			input:    []any{1},
376			expected: []any{1},
377		},
378		{
379			name:     "mixed types",
380			input:    []any{1, "a", true, 2.5},
381			expected: []any{2.5, true, "a", 1},
382		},
383	}
384
385	for _, tt := range tests {
386		t.Run(tt.name, func(t *testing.T) {
387			result := Reverse(tt.input)
388			if !equalSlices(result, tt.expected) {
389				t.Errorf("Reverse failed, expected %v, got %v", tt.expected, result)
390			}
391		})
392	}
393}
394
395func TestZipUnzip(t *testing.T) {
396	tests := []struct {
397		name        string
398		a           []any
399		b           []any
400		expectedZip [][2]any
401		expectedA   []any
402		expectedB   []any
403	}{
404		{
405			name:        "normal case",
406			a:           []any{1, 2, 3},
407			b:           []any{"a", "b", "c"},
408			expectedZip: [][2]any{{1, "a"}, {2, "b"}, {3, "c"}},
409			expectedA:   []any{1, 2, 3},
410			expectedB:   []any{"a", "b", "c"},
411		},
412		{
413			name:        "empty slices",
414			a:           []any{},
415			b:           []any{},
416			expectedZip: [][2]any{},
417			expectedA:   []any{},
418			expectedB:   []any{},
419		},
420		{
421			name:        "different lengths - a shorter",
422			a:           []any{1, 2},
423			b:           []any{"a", "b", "c"},
424			expectedZip: [][2]any{{1, "a"}, {2, "b"}},
425			expectedA:   []any{1, 2},
426			expectedB:   []any{"a", "b"},
427		},
428		{
429			name:        "different lengths - b shorter",
430			a:           []any{1, 2, 3},
431			b:           []any{"a"},
432			expectedZip: [][2]any{{1, "a"}},
433			expectedA:   []any{1},
434			expectedB:   []any{"a"},
435		},
436		{
437			name:        "mixed types",
438			a:           []any{1, true, "x"},
439			b:           []any{2.5, false, "y"},
440			expectedZip: [][2]any{{1, 2.5}, {true, false}, {"x", "y"}},
441			expectedA:   []any{1, true, "x"},
442			expectedB:   []any{2.5, false, "y"},
443		},
444	}
445
446	for _, tt := range tests {
447		t.Run(tt.name, func(t *testing.T) {
448			zipped := Zip(tt.a, tt.b)
449			if len(zipped) != len(tt.expectedZip) {
450				t.Errorf("Zip failed, expected length %v, got %v", len(tt.expectedZip), len(zipped))
451			}
452			for i, pair := range zipped {
453				if pair[0] != tt.expectedZip[i][0] || pair[1] != tt.expectedZip[i][1] {
454					t.Errorf("Zip failed at index %d, expected %v, got %v", i, tt.expectedZip[i], pair)
455				}
456			}
457
458			unzippedA, unzippedB := Unzip(zipped)
459			if !equalSlices(unzippedA, tt.expectedA) {
460				t.Errorf("Unzip failed for slice A, expected %v, got %v", tt.expectedA, unzippedA)
461			}
462			if !equalSlices(unzippedB, tt.expectedB) {
463				t.Errorf("Unzip failed for slice B, expected %v, got %v", tt.expectedB, unzippedB)
464			}
465		})
466	}
467}
468
469func TestGroupBy(t *testing.T) {
470	tests := []struct {
471		name     string
472		input    []any
473		fn       func(any) any
474		expected map[any][]any
475	}{
476		{
477			name:  "group by even/odd",
478			input: []any{1, 2, 3, 4, 5, 6},
479			fn:    func(v any) any { return v.(int) % 2 },
480			expected: map[any][]any{
481				0: {2, 4, 6},
482				1: {1, 3, 5},
483			},
484		},
485		{
486			name:     "empty slice",
487			input:    []any{},
488			fn:       func(v any) any { return v.(int) % 2 },
489			expected: map[any][]any{},
490		},
491		{
492			name:  "single group",
493			input: []any{2, 4, 6},
494			fn:    func(v any) any { return v.(int) % 2 },
495			expected: map[any][]any{
496				0: {2, 4, 6},
497			},
498		},
499		{
500			name:  "group by type",
501			input: []any{1, "a", 2, "b", true},
502			fn: func(v any) any {
503				switch v.(type) {
504				case int:
505					return "int"
506				case string:
507					return "string"
508				default:
509					return "other"
510				}
511			},
512			expected: map[any][]any{
513				"int":    {1, 2},
514				"string": {"a", "b"},
515				"other":  {true},
516			},
517		},
518	}
519
520	for _, tt := range tests {
521		t.Run(tt.name, func(t *testing.T) {
522			result := GroupBy(tt.input, tt.fn)
523			if len(result) != len(tt.expected) {
524				t.Errorf("GroupBy failed, expected %d groups, got %d", len(tt.expected), len(result))
525			}
526			for k, v := range tt.expected {
527				if !equalSlices(result[k], v) {
528					t.Errorf("GroupBy failed for key %v, expected %v, got %v", k, v, result[k])
529				}
530			}
531		})
532	}
533}
534
535func TestFlatten(t *testing.T) {
536	tests := []struct {
537		name     string
538		input    [][]any
539		expected []any
540	}{
541		{
542			name:     "normal nested slices",
543			input:    [][]any{{1, 2}, {3, 4}, {5}},
544			expected: []any{1, 2, 3, 4, 5},
545		},
546		{
547			name:     "empty outer slice",
548			input:    [][]any{},
549			expected: []any{},
550		},
551		{
552			name:     "empty inner slices",
553			input:    [][]any{{}, {}, {}},
554			expected: []any{},
555		},
556		{
557			name:     "mixed types",
558			input:    [][]any{{1, "a"}, {true, 2.5}, {nil}},
559			expected: []any{1, "a", true, 2.5, nil},
560		},
561		{
562			name:     "single element slices",
563			input:    [][]any{{1}, {2}, {3}},
564			expected: []any{1, 2, 3},
565		},
566	}
567
568	for _, tt := range tests {
569		t.Run(tt.name, func(t *testing.T) {
570			result := Flatten(tt.input)
571			if !equalSlices(result, tt.expected) {
572				t.Errorf("Flatten failed, expected %v, got %v", tt.expected, result)
573			}
574		})
575	}
576}
577
578func TestContains(t *testing.T) {
579	tests := []struct {
580		name     string
581		slice    []any
582		item     any
583		expected bool
584	}{
585		{
586			name:     "contains integer",
587			slice:    []any{1, 2, 3},
588			item:     2,
589			expected: true,
590		},
591		{
592			name:     "does not contain integer",
593			slice:    []any{1, 2, 3},
594			item:     4,
595			expected: false,
596		},
597		{
598			name:     "contains string",
599			slice:    []any{"a", "b", "c"},
600			item:     "b",
601			expected: true,
602		},
603		{
604			name:     "empty slice",
605			slice:    []any{},
606			item:     1,
607			expected: false,
608		},
609		{
610			name:     "contains nil",
611			slice:    []any{1, nil, 3},
612			item:     nil,
613			expected: true,
614		},
615		{
616			name:     "mixed types",
617			slice:    []any{1, "a", true},
618			item:     true,
619			expected: true,
620		},
621	}
622
623	for _, tt := range tests {
624		t.Run(tt.name, func(t *testing.T) {
625			result := contains(tt.slice, tt.item)
626			if result != tt.expected {
627				t.Errorf("contains failed, expected %v, got %v", tt.expected, result)
628			}
629		})
630	}
631}
632
633// Helper function for testing
634func contains(slice []any, item any) bool {
635	for _, v := range slice {
636		if v == item {
637			return true
638		}
639	}
640	return false
641}
642
643// Helper functions for comparing slices
644func equalSlices(a, b []any) bool {
645	if len(a) != len(b) {
646		return false
647	}
648	for i := range a {
649		if a[i] != b[i] {
650			return false
651		}
652	}
653	return true
654}
655
656func equalNestedSlices(a, b [][]any) bool {
657	if len(a) != len(b) {
658		return false
659	}
660	for i := range a {
661		if !equalSlices(a[i], b[i]) {
662			return false
663		}
664	}
665	return true
666}