64 lines
2.5 KiB
Go
64 lines
2.5 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|