num_test.gno
3.48 Kb · 130 lines
1package num
2
3import (
4 "strconv"
5 "testing"
6
7 "gno.land/p/nt/uassert/v0"
8)
9
10const minInt64 = -9223372036854775808
11
12func TestDec(t *testing.T) {
13 cases := []struct {
14 name string
15 v int64
16 decimals int
17 want string
18 }{
19 {"whole", 2000000, 6, "2"},
20 {"trimmed", 1234500, 6, "1.2345"},
21 {"full", 1234567, 6, "1.234567"},
22 {"zero", 0, 6, "0"},
23 {"sub-unit", 100, 6, "0.0001"},
24 {"smallest unit", 1, 6, "0.000001"},
25 {"no decimals", 42, 0, "42"},
26 {"negative whole", -2000000, 6, "-2"},
27 {"negative fraction", -1500000, 6, "-1.5"},
28 {"negative sub-unit", -100, 6, "-0.0001"},
29 {"negative smallest unit", -1, 6, "-0.000001"},
30 {"two decimals", 1234, 2, "12.34"},
31 {"int64 max", 9223372036854775807, 6, "9223372036854.775807"},
32 {"int64 min", minInt64, 6, "-9223372036854.775808"},
33 }
34 for _, tc := range cases {
35 t.Run(tc.name, func(t *testing.T) {
36 uassert.Equal(t, tc.want, Dec(tc.v, tc.decimals))
37 })
38 }
39}
40
41func TestDecFixedKeepsWidth(t *testing.T) {
42 cases := []struct {
43 v int64
44 want string
45 }{
46 {2000000, "2.000000"},
47 {1234500, "1.234500"},
48 {0, "0.000000"},
49 {-1, "-0.000001"},
50 }
51 for _, tc := range cases {
52 uassert.Equal(t, tc.want, DecFixed(tc.v, 6))
53 }
54}
55
56// TestEveryDecimalStringParsesBack is the invariant the hand-rolled copies
57// break: whatever comes out must be a number again. "-1." and "0.999" for a
58// negative input both fail here, which is what the two live implementations
59// return.
60func TestEveryDecimalStringParsesBack(t *testing.T) {
61 values := []int64{
62 0, 1, -1, 100, -100, 999999, -999999, 1000000, -1000000,
63 1234567, -1234567, 9223372036854775807, minInt64,
64 }
65 for _, v := range values {
66 for _, fixed := range []bool{false, true} {
67 got := dec(v, 6, fixed)
68 f, err := strconv.ParseFloat(got, 64)
69 if err != nil {
70 t.Errorf("dec(%d, 6, %t) = %q, not a number: %v", v, fixed, got, err)
71 continue
72 }
73 if (v < 0) != (f < 0) && f != 0 {
74 t.Errorf("dec(%d, 6, %t) = %q, sign flipped", v, fixed, got)
75 }
76 }
77 }
78}
79
80// TestMagnitudeSurvivesTheInt64Minimum pins the one input that makes the
81// obvious implementations fail. `-v` on the minimum is the minimum again, so a
82// `if v < 0 { v = -v }` keeps it negative and a recursive `"-" + f(-v)` never
83// terminates. A mainnet realm does the second.
84func TestMagnitudeSurvivesTheInt64Minimum(t *testing.T) {
85 neg, m := mag(minInt64)
86 uassert.True(t, neg)
87 uassert.Equal(t, "9223372036854775808", strconv.FormatUint(m, 10))
88}
89
90func TestGNOT(t *testing.T) {
91 uassert.Equal(t, "1.234567", GNOT(1234567))
92 uassert.Equal(t, "1.234567 GNOT", GNOTf(1234567))
93 uassert.Equal(t, "0 GNOT", GNOTf(0))
94 uassert.Equal(t, "-1.5 GNOT", GNOTf(-1500000))
95}
96
97func TestPct(t *testing.T) {
98 uassert.Equal(t, "12.34%", Pct(1234))
99 uassert.Equal(t, "1%", Pct(100))
100 uassert.Equal(t, "0.01%", Pct(1))
101 uassert.Equal(t, "0%", Pct(0))
102 uassert.Equal(t, "-12.34%", Pct(-1234))
103}
104
105func TestPad(t *testing.T) {
106 cases := []struct {
107 v int64
108 width int
109 want string
110 }{
111 {7, 3, "007"},
112 {123, 3, "123"},
113 {1234, 3, "1234"},
114 {0, 2, "00"},
115 {7, 0, "7"},
116 {-7, 3, "-007"},
117 {minInt64, 3, "-9223372036854775808"},
118 }
119 for _, tc := range cases {
120 uassert.Equal(t, tc.want, Pad(tc.v, tc.width))
121 }
122}
123
124func TestDecRejectsAnImpossibleScale(cur realm, t *testing.T) {
125 msg := "num: decimals out of range [0, 19]"
126 uassert.PanicsWithMessage(t, cur, msg, func() { Dec(1, -1) })
127 uassert.PanicsWithMessage(t, cur, msg, func() { Dec(1, 20) })
128 uassert.NotPanics(t, cur, func() { Dec(1, 0) })
129 uassert.NotPanics(t, cur, func() { Dec(1, 19) })
130}