package ingest import ( "encoding/json" "testing" "textmachine/platform/internal/money" ) // Money crosses the seam exactly once, here. The cases below are the ones a float64 gets wrong or // gets right only by luck. Mutation caught: binding committed_usd to a float64 and multiplying, or // rounding to nearest instead of away from zero. func TestSpendConvertsExactlyAndRoundsUp(t *testing.T) { usd := func(v money.MicroUSD) *money.MicroUSD { return &v } cases := map[string]*money.MicroUSD{ `{"committed_usd":0}`: usd(0), `{"committed_usd":1.25}`: usd(1_250_000), `{"committed_usd":0.000001}`: usd(1), `{"committed_usd":0.0000001}`: usd(1), // a tenth of a micro-dollar still costs one `{"committed_usd":0.1}`: usd(100_000), // the classic float64 case: 0.1 is not 0.1 `{"committed_usd":8.7}`: usd(8_700_000), `{"committed_usd":29.7}`: usd(29_700_000), `{"committed_usd":1e-6}`: usd(1), `{"committed_usd":"1.25"}`: usd(1_250_000), // a quoted decimal is still a decimal // JSON null never reaches UnmarshalJSON for a pointer: it IS the absence. `{"committed_usd":null}`: nil, `{}`: nil, `{"committed_usd":123456.789012}`: usd(123_456_789_012), `{"committed_usd":123456.7890121}`: usd(123_456_789_013), } for raw, want := range cases { var r StatusReport if err := json.Unmarshal([]byte(raw), &r); err != nil { t.Fatalf("%s: %v", raw, err) } switch { case r.Spend == nil && want != nil: t.Fatalf("%s: spend is absent, want %d", raw, *want) case r.Spend != nil && want == nil: t.Fatalf("%s: spend = %d, want absent", raw, *r.Spend) case r.Spend != nil && *r.Spend != *want: t.Fatalf("%s: spend = %d, want %d", raw, *r.Spend, *want) } } } func TestSpendRefusesNonsense(t *testing.T) { for _, raw := range []string{ `{"committed_usd":"free"}`, `{"committed_usd":1e30}`, // beyond int64 micro-USD `{"committed_usd":""}`, // an empty figure is not a figure // PD-79. The STRING "null" — a sender that formatted its own absence. It used to be read as a // real zero in a non-nil pointer, and on the settlement path a zero means "the attempt cost // nothing": the hold is released and nothing is charged. Only the JSON literal above is // absence, and the literal never reaches this code. `{"committed_usd":"null"}`, `{"committed_usd":"NULL"}`, } { var r StatusReport if err := json.Unmarshal([]byte(raw), &r); err == nil { t.Fatalf("%s: accepted", raw) } } }