88 lines
3.6 KiB
Go
88 lines
3.6 KiB
Go
package api
|
|
|
|
import (
|
|
"math"
|
|
"testing"
|
|
)
|
|
|
|
// Longitude handling is shared by both API versions on purpose: the two may
|
|
// differ in format, names and features, never in what the maths does. v1 used to
|
|
// apply no range check at all while v2 enforced [-180, 360), so the same launch
|
|
// could be accepted by one and refused by the other.
|
|
|
|
func TestValidateLngAcceptsBothConventionsAndOneTurnEitherWay(t *testing.T) {
|
|
// Upstream Tawhiri implements a half-open [0, 360) — measured: 0, 180, 270 and
|
|
// 308.3 accepted, everything negative and 360 refused. We accept a superset
|
|
// because the frontend holds longitudes in [-180, 180], so a western launch
|
|
// arrives negative. The bound is symmetric so there is nothing to explain
|
|
// about one end that does not hold for the other.
|
|
for _, lng := range []float64{0, 0.1, 180, 270, 308.3, 359.999, 360, -0.0001, -51.7, -180, -200, -360} {
|
|
if err := validateLng(lng); err != nil {
|
|
t.Errorf("validateLng(%g) = %v, want accepted", lng, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestValidateLngRefusesTypos(t *testing.T) {
|
|
// Arithmetically every one of these names a real meridian — 5170 is 50 E. They
|
|
// are refused because they are far likelier to be a unit slip or a swapped
|
|
// field than an intended notation, and upstream refuses them too.
|
|
for _, lng := range []float64{360.0001, -360.0001, 400, -400, 5170, math.Inf(1), math.Inf(-1)} {
|
|
if err := validateLng(lng); err == nil {
|
|
t.Errorf("validateLng(%g) was accepted, want refused", lng)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNormalizeLngFoldsAnyInputIntoTheGridDomain(t *testing.T) {
|
|
// The wind grid is indexed from LonStart = 0 over 360 degrees, so every value
|
|
// handed to it must land in [0, 360).
|
|
//
|
|
// This was a single `if lng < 0 { lng += 360 }`, correct only on [-360, 360):
|
|
// -400 became -40, reached the grid, and came back as "lng=-40 out of range" —
|
|
// a complaint about wind data for what was an input problem.
|
|
cases := map[float64]float64{
|
|
0: 0, 0.5: 0.5, 180: 180, 359.5: 359.5,
|
|
360: 0, 720: 0, -360: 0,
|
|
-0.5: 359.5, -51.7: 308.3, -180: 180, -200: 160, -400: 320, 5170: 130,
|
|
}
|
|
for in, want := range cases {
|
|
if got := normalizeLng(in); math.Abs(got-want) > 1e-9 {
|
|
t.Errorf("normalizeLng(%g) = %g, want %g", in, got, want)
|
|
}
|
|
if got := normalizeLng(in); got < 0 || got >= 360 {
|
|
t.Errorf("normalizeLng(%g) = %g, outside [0, 360)", in, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSignedLngIsTheV2Format(t *testing.T) {
|
|
// v2 publishes [-180, 180): its own convenient format. v1 publishes the
|
|
// internal [0, 360) unchanged, because that is what upstream Tawhiri publishes
|
|
// and v1 exists to be a drop-in for it.
|
|
cases := map[float64]float64{0: 0, 90: 90, 180: 180, 180.5: -179.5, 308.3: -51.7, 359.5: -0.5}
|
|
for in, want := range cases {
|
|
if got := signedLng(in); math.Abs(got-want) > 1e-9 {
|
|
t.Errorf("signedLng(%g) = %g, want %g", in, got, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestValidateLatIsSharedByBothVersions(t *testing.T) {
|
|
// Unlike longitude, latitude is a genuine geometric bound: +-90 are the poles
|
|
// and there is nothing beyond them, so no notation makes 100 meaningful.
|
|
//
|
|
// v2 checked this and v1 did not, which is a difference in behaviour rather
|
|
// than format. On v1 an out-of-range latitude reached the wind grid and came
|
|
// back as "lat=... out of range" — a complaint about data for an input problem.
|
|
for _, lat := range []float64{-90, -89.999, 0, 52.2, 89.999, 90} {
|
|
if err := validateLat(lat); err != nil {
|
|
t.Errorf("validateLat(%g) = %v, want accepted", lat, err)
|
|
}
|
|
}
|
|
for _, lat := range []float64{-90.0001, 90.0001, 100, -100, math.NaN(), math.Inf(1)} {
|
|
if err := validateLat(lat); err == nil {
|
|
t.Errorf("validateLat(%g) was accepted, want refused", lat)
|
|
}
|
|
}
|
|
}
|