predictor/internal/windviz/windviz_test.go

96 lines
2.9 KiB
Go

package windviz
import (
"testing"
"time"
"predictor-refactored/internal/weather"
)
// constWind is a WindField returning a fixed sample everywhere.
type constWind struct {
u, v float64
epoch time.Time
}
func (c constWind) Wind(_ float64, _, _, _ float64) (weather.Sample, error) {
return weather.Sample{U: c.u, V: c.v}, nil
}
func (c constWind) Epoch() time.Time { return c.epoch }
func (c constWind) Source() string { return "test" }
func TestRasterizeGlobalDropsDuplicateColumn(t *testing.T) {
f := constWind{u: 5, v: -3, epoch: time.Unix(0, 0)}
out, err := Rasterize(f, Request{MinLng: 0, MaxLng: 360, Step: 90})
if err != nil {
t.Fatalf("Rasterize: %v", err)
}
if len(out) != 2 {
t.Fatalf("expected 2 components, got %d", len(out))
}
u := out[0]
// 360/90 = 4 columns (no duplicate 360°); lat -90..90 step 90 = 3 rows.
if u.Header.Nx != 4 || u.Header.Ny != 3 {
t.Errorf("grid = %dx%d, want 4x3", u.Header.Nx, u.Header.Ny)
}
if len(u.Data) != 12 {
t.Errorf("data len = %d, want 12", len(u.Data))
}
if u.Header.La1 != 90 || u.Header.La2 != -90 {
t.Errorf("lat range = %v..%v, want 90..-90 (north first)", u.Header.La1, u.Header.La2)
}
if u.Header.Lo1 != 0 || u.Header.Lo2 != 270 {
t.Errorf("lng range = %v..%v, want 0..270", u.Header.Lo1, u.Header.Lo2)
}
for _, d := range u.Data {
if d != 5 {
t.Errorf("U data = %v, want 5", d)
break
}
}
if out[0].Header.ParameterNumber != 2 || out[1].Header.ParameterNumber != 3 {
t.Errorf("component order should be U(2) then V(3)")
}
}
func TestRasterizeSignedLongitudeConvention(t *testing.T) {
f := constWind{u: 1, v: 2, epoch: time.Unix(0, 0)}
// A [-180, 180] global request must be detected as global and tiled
// without a duplicate seam column, identical to a 0..360 request.
signed, err := Rasterize(f, Request{MinLng: -180, MaxLng: 180, Step: 90})
if err != nil {
t.Fatalf("signed-global Rasterize: %v", err)
}
if signed[0].Header.Nx != 4 {
t.Errorf("signed-global nx = %d, want 4 (no duplicate column)", signed[0].Header.Nx)
}
// A western-hemisphere box must not 400; its western edge folds into [0,360).
west, err := Rasterize(f, Request{MinLat: 10, MaxLat: 20, MinLng: -100, MaxLng: -50, Step: 10})
if err != nil {
t.Fatalf("western-box Rasterize: %v", err)
}
if west[0].Header.Lo1 != 260 {
t.Errorf("western-box lo1 = %v, want 260 (=-100 folded)", west[0].Header.Lo1)
}
}
func TestRasterizeStepClamp(t *testing.T) {
f := constWind{epoch: time.Unix(0, 0)}
// step below min gets clamped, not rejected.
if _, err := Rasterize(f, Request{MinLat: -1, MaxLat: 1, MinLng: 0, MaxLng: 2, Step: 0.01}); err != nil {
t.Fatalf("Rasterize with tiny step: %v", err)
}
}
func TestCacheRoundTrip(t *testing.T) {
c := NewCache(2, time.Minute)
if _, ok := c.Get("a"); ok {
t.Errorf("empty cache should miss")
}
c.Put("a", Field{})
if _, ok := c.Get("a"); !ok {
t.Errorf("cache should hit after put")
}
}