niko_trust/pkg/tce/number_test.go
Niko Marmeladkov 3bf13fa488 Public SDK packages, proxy-aware rate limits, service login recipe
- internal/{address,identity,protocol,tce,transport,verify} -> pkg/ so
  external Go projects can import the verified core; invariant tests
  updated for the new paths
- Config.TrustProxy: key rate limiting by X-Forwarded-For when the relay
  sits behind a reverse proxy (off by default, header never trusted
  otherwise)
- examples/service + examples/approve: complete passwordless login round
  trip (mint request -> wallet approves -> local verify), run live in CI
- docs/SERVICE-GUIDE.md: the integration recipe
2026-08-26 12:49:54 +03:00

120 lines
3.1 KiB
Go

package tce_test
import (
"testing"
"git.n1ko.dev/Niko/niko_trust/pkg/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)
}
}
}