1package num23import (4 "strconv"5 "testing"67 "gno.land/p/nt/uassert/v0"8)910const minInt64 = -92233720368547758081112func TestDec(t *testing.T) {13 cases := []struct {14 name string15 v int6416 decimals int17 want string18 }{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}4041func TestDecFixedKeepsWidth(t *testing.T) {42 cases := []struct {43 v int6444 want string45 }{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}5556// TestEveryDecimalStringParsesBack is the invariant the hand-rolled copies57// break: whatever comes out must be a number again. "-1." and "0.999" for a58// negative input both fail here, which is what the two live implementations59// 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 continue72 }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}7980// TestMagnitudeSurvivesTheInt64Minimum pins the one input that makes the81// obvious implementations fail. `-v` on the minimum is the minimum again, so a82// `if v < 0 { v = -v }` keeps it negative and a recursive `"-" + f(-v)` never83// 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}8990func 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}9697func 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}104105func TestPad(t *testing.T) {106 cases := []struct {107 v int64108 width int109 want string110 }{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}123124func 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}131Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.