package tce_test import ( "testing" "git.n1ko.dev/Niko/niko_trust/internal/tce" ) // TestCanonicalNumber exercises section 5.1: arbitrary-precision decimals must // collapse to one canonical spelling, and malformed tokens must be refused. func TestCanonicalNumber(t *testing.T) { cases := []struct { in string want string wantErr bool }{ // Integers and their redundant spellings. {"1", "1", false}, {"1.0", "1", false}, {"1e0", "1", false}, {"1E0", "1", false}, {"1.00", "1", false}, {"001", "", true}, // leading zero {"0", "0", false}, {"0.0", "0", false}, {"-0", "0", false}, {"-0.0", "0", false}, {"0e10", "0", false}, {"-0.000", "0", false}, // Fractions (no trailing zeros, no leading zero in int part). {"0.1", "0.1", false}, {"0.10", "0.1", false}, {".5", "", true}, // missing integer part {"1.", "", true}, // missing fraction digits {"1.2.3", "", true}, // Exponents move the point and strip factors of ten. {"123e2", "12300", false}, {"123E-2", "1.23", false}, {"1.5e1", "15", false}, {"150e-2", "1.5", false}, {"1e+2", "100", false}, {"1e-2", "0.01", false}, {"1e999999999", "", true}, // exponent too large {"1e", "", true}, // exponent without digits {"1e+x", "", true}, {"1e-", "", true}, // Negatives. {"-1", "-1", false}, {"-1.0", "-1", false}, {"-0.5", "-0.5", false}, {"-1e1", "-10", false}, // Grammar rejections. {"", "", true}, {"abc", "", true}, {"+1", "", true}, // sign must be minus {" 1", "", true}, // no whitespace {"1 ", "", true}, {"0x1", "", true}, {"1_000", "", true}, {" Infinity", "", true}, {"NaN", "", true}, // Range: large integers and deep fractions are bounded. {"123456789012345678901234567890", "123456789012345678901234567890", false}, {"1e40", "", true}, // far beyond max integer digits } for _, c := range cases { got, err := tce.CanonicalNumber(c.in) if c.wantErr { if err == nil { t.Errorf("CanonicalNumber(%q): expected error, got %q", c.in, got) } continue } if err != nil { t.Errorf("CanonicalNumber(%q): unexpected error %v", c.in, err) continue } if got != c.want { t.Errorf("CanonicalNumber(%q) = %q, want %q", c.in, got, c.want) } } } // TestIsCanonicalNumber checks the decoder's accept/reject predicate. func TestIsCanonicalNumber(t *testing.T) { if !tce.IsCanonicalNumber("1.5") { t.Error("1.5 is canonical") } if tce.IsCanonicalNumber("1.50") { t.Error("1.50 is not canonical") } if tce.IsCanonicalNumber("01") { t.Error("01 must be rejected") } } // TestNumberRoundTripEquality confirms numbers that mean the same value // canonicalize identically, so they encode to the same bytes. func TestNumberRoundTripEquality(t *testing.T) { forms := []string{"1", "1.0", "1e0", "1E0", "1.00", "001"} base, _ := tce.CanonicalNumber("1") for _, f := range forms { c, err := tce.CanonicalNumber(f) if err != nil { // 001 is rejected; that is expected and not an equality case. if f == "001" { continue } t.Fatalf("CanonicalNumber(%q): %v", f, err) } if c != base { t.Errorf("CanonicalNumber(%q) = %q, want %q", f, c, base) } } }