Validate hourly forecast time bounds
This commit is contained in:
@@ -224,6 +224,11 @@ func (b *bundleBuilder) fetchHourly(ctx context.Context) error {
|
||||
if len(hourly.Periods) == 0 {
|
||||
return fmt.Errorf("hourly forecast from %s contains no periods", source.Endpoint)
|
||||
}
|
||||
for i, period := range hourly.Periods {
|
||||
if !period.HasUsableTimeBounds() {
|
||||
return fmt.Errorf("hourly forecast from %s has unusable time bounds for period %d", source.Endpoint, i+1)
|
||||
}
|
||||
}
|
||||
source.IssuedAt = &hourly.IssuedAt
|
||||
source.UpdatedAt = hourly.UpdatedAt
|
||||
b.bundle.Hourly = &hourly
|
||||
|
||||
@@ -501,6 +501,66 @@ func TestRequiredHourlyForecast(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequiredHourlyForecastValidatesPeriodBounds(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
body string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid period",
|
||||
body: `{"data":{"periods":[{"startTime":"2026-05-29T13:00:00Z","endTime":"2026-05-29T14:00:00Z"}]}}`,
|
||||
},
|
||||
{
|
||||
name: "missing start",
|
||||
body: `{"data":{"periods":[{"endTime":"2026-05-29T14:00:00Z"}]}}`,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "missing end",
|
||||
body: `{"data":{"periods":[{"startTime":"2026-05-29T13:00:00Z"}]}}`,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "empty range",
|
||||
body: `{"data":{"periods":[{"startTime":"2026-05-29T13:00:00Z","endTime":"2026-05-29T13:00:00Z"}]}}`,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "reversed range",
|
||||
body: `{"data":{"periods":[{"startTime":"2026-05-29T14:00:00Z","endTime":"2026-05-29T13:00:00Z"}]}}`,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var requested []string
|
||||
server := fixtureServer(t, map[string]handlerOverride{
|
||||
"/forecast/hourly": {status: http.StatusOK, body: tt.body},
|
||||
}, &requested)
|
||||
client := newTestClient(t, server.URL+"/", nil)
|
||||
|
||||
bundle, err := client.FetchBundle(context.Background())
|
||||
if tt.wantErr {
|
||||
if err == nil || !strings.Contains(err.Error(), "hourly forecast") || !strings.Contains(err.Error(), "time bounds") {
|
||||
t.Fatalf("FetchBundle() error = %v, want hourly time-bounds failure", err)
|
||||
}
|
||||
if got := countPath(requested, "/forecast/hourly"); got != 1 {
|
||||
t.Fatalf("hourly requests = %d, want no retry; all requests = %v", got, requested)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("FetchBundle() error = %v", err)
|
||||
}
|
||||
if bundle.Hourly == nil || len(bundle.Hourly.Periods) != 1 {
|
||||
t.Fatalf("Hourly = %#v, want accepted hourly period", bundle.Hourly)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNullAlertsMeansNoActiveAlerts(t *testing.T) {
|
||||
server := fixtureServer(t, map[string]handlerOverride{
|
||||
"/alerts/active": {status: http.StatusOK, body: `{"data": null}`},
|
||||
|
||||
Reference in New Issue
Block a user