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.

luhn_test.gno

2.39 Kb · 95 lines
 1package luhn
 2
 3import (
 4	"strings"
 5	"testing"
 6
 7	"gno.land/p/nt/uassert/v0"
 8)
 9
10func TestValid(t *testing.T) {
11	cases := []struct {
12		name string
13		in   string
14		want bool
15	}{
16		{"known-good test card", "4539148803436467", true},
17		{"spaces ignored", "4539 1488 0343 6467", true},
18		{"hyphens ignored", "4539-1488-0343-6467", true},
19		{"single digit flipped", "4539148803436468", false},
20		{"adjacent transposition", "4539148803436647", false},
21		{"classic 79927398713", "79927398713", true},
22		{"classic off by one", "79927398710", false},
23		{"all zeros is valid", "00", true},
24		{"letters rejected", "4539abc8803436467", false},
25		{"empty rejected", "", false},
26		{"one digit rejected", "7", false},
27		{"separators only rejected", " - ", false},
28		{"over MaxLen rejected", strings.Repeat("0", MaxLen+1), false},
29	}
30	for _, c := range cases {
31		t.Run(c.name, func(t *testing.T) {
32			uassert.Equal(t, c.want, Valid(c.in))
33		})
34	}
35}
36
37func TestCheckDigit(t *testing.T) {
38	cases := []struct {
39		payload string
40		want    int
41	}{
42		{"7992739871", 3},
43		{"453914880343646", 7},
44		{"0", 0},
45	}
46	for _, c := range cases {
47		got, ok := CheckDigit(c.payload)
48		uassert.True(t, ok)
49		uassert.Equal(t, c.want, got)
50	}
51}
52
53func TestCheckDigitRejectsBadInput(t *testing.T) {
54	for _, in := range []string{"", "abc", " - "} {
55		_, ok := CheckDigit(in)
56		uassert.False(t, ok)
57	}
58}
59
60// Append must produce something Valid accepts — the round-trip property.
61func TestAppendRoundTrip(t *testing.T) {
62	payloads := []string{"7992739871", "453914880343646", "1", "123456789012345"}
63	for _, p := range payloads {
64		got, ok := Append(p)
65		uassert.True(t, ok)
66		uassert.True(t, Valid(got))
67		uassert.Equal(t, len(p)+1, len(got))
68	}
69}
70
71// Append strips separators, so a spaced payload yields the same digits.
72func TestAppendStripsSeparators(t *testing.T) {
73	a, ok := Append("4539 1488 0343 646")
74	uassert.True(t, ok)
75	b, ok2 := Append("453914880343646")
76	uassert.True(t, ok2)
77	uassert.Equal(t, b, a)
78	uassert.Equal(t, "4539148803436467", a)
79}
80
81// Every single-digit change to a valid number must be caught: that is the
82// guarantee Luhn actually makes.
83func TestDetectsEverySingleDigitError(t *testing.T) {
84	const base = "4539148803436467"
85	uassert.True(t, Valid(base))
86	for i := 0; i < len(base); i++ {
87		for d := byte('0'); d <= '9'; d++ {
88			if d == base[i] {
89				continue
90			}
91			mutated := base[:i] + string(d) + base[i+1:]
92			uassert.False(t, Valid(mutated))
93		}
94	}
95}