Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

fp_test.gno

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