147 lines
4.5 KiB
Go
147 lines
4.5 KiB
Go
package nws
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"gitea.maximumdirect.net/ejr/feedkit/event"
|
|
"gitea.maximumdirect.net/ejr/weatherfeeder/model"
|
|
"gitea.maximumdirect.net/ejr/weatherfeeder/standards"
|
|
)
|
|
|
|
func TestWeatherStoriesNormalizerProducesCanonicalSchemaAndMapsSample(t *testing.T) {
|
|
out, err := (WeatherStoriesNormalizer{}).Normalize(nil, weatherStoriesRawEvent(weatherStoriesSamplePayload()))
|
|
if err != nil {
|
|
t.Fatalf("Normalize() error = %v", err)
|
|
}
|
|
if out == nil {
|
|
t.Fatalf("Normalize() returned nil output")
|
|
}
|
|
if out.Schema != standards.SchemaWeatherStoryV1 {
|
|
t.Fatalf("Schema = %q, want %q", out.Schema, standards.SchemaWeatherStoryV1)
|
|
}
|
|
if out.Kind != event.Kind(standards.KindWeatherStory) {
|
|
t.Fatalf("Kind = %q, want weather_story", out.Kind)
|
|
}
|
|
|
|
payload, ok := out.Payload.(model.WeatherStoryRun)
|
|
if !ok {
|
|
t.Fatalf("Payload type = %T, want model.WeatherStoryRun", out.Payload)
|
|
}
|
|
if payload.OfficeID != "LSX" {
|
|
t.Fatalf("OfficeID = %q, want LSX", payload.OfficeID)
|
|
}
|
|
wantAsOf := time.Date(2026, 5, 30, 9, 0, 34, 0, time.UTC)
|
|
if !payload.AsOf.Equal(wantAsOf) {
|
|
t.Fatalf("AsOf = %s, want %s", payload.AsOf, wantAsOf)
|
|
}
|
|
if out.EffectiveAt == nil || !out.EffectiveAt.Equal(wantAsOf) {
|
|
t.Fatalf("EffectiveAt = %v, want %s", out.EffectiveAt, wantAsOf)
|
|
}
|
|
if len(payload.Stories) != 1 {
|
|
t.Fatalf("Stories len = %d, want 1", len(payload.Stories))
|
|
}
|
|
story := payload.Stories[0]
|
|
if story.Title != "Several Chances for Rain Through Monday" {
|
|
t.Fatalf("Title = %q", story.Title)
|
|
}
|
|
if story.Description != "A stagnant weather pattern." {
|
|
t.Fatalf("Description = %q", story.Description)
|
|
}
|
|
if story.AltText != "This slide shows the forecast." {
|
|
t.Fatalf("AltText = %q", story.AltText)
|
|
}
|
|
if story.Priority {
|
|
t.Fatalf("Priority = true, want false")
|
|
}
|
|
if story.Order != 1 {
|
|
t.Fatalf("Order = %d, want 1", story.Order)
|
|
}
|
|
if story.DownloadURL != "https://api.weather.gov/offices/LSX/weatherstories/download/3228e499-2aae-45a8-9ff9-1c060311026f" {
|
|
t.Fatalf("DownloadURL = %q", story.DownloadURL)
|
|
}
|
|
}
|
|
|
|
func TestWeatherStoriesNormalizerEmptyStoriesUsesFallbackAsOf(t *testing.T) {
|
|
effectiveAt := time.Date(2026, 5, 30, 12, 0, 0, 0, time.UTC)
|
|
in := weatherStoriesRawEvent(`{"stories":[]}`)
|
|
in.EffectiveAt = &effectiveAt
|
|
|
|
out, err := (WeatherStoriesNormalizer{}).Normalize(nil, in)
|
|
if err != nil {
|
|
t.Fatalf("Normalize() error = %v", err)
|
|
}
|
|
payload, ok := out.Payload.(model.WeatherStoryRun)
|
|
if !ok {
|
|
t.Fatalf("Payload type = %T, want model.WeatherStoryRun", out.Payload)
|
|
}
|
|
if !payload.AsOf.Equal(effectiveAt) {
|
|
t.Fatalf("AsOf = %s, want fallback %s", payload.AsOf, effectiveAt)
|
|
}
|
|
if payload.Stories == nil {
|
|
t.Fatalf("Stories = nil, want empty slice")
|
|
}
|
|
if len(payload.Stories) != 0 {
|
|
t.Fatalf("Stories len = %d, want 0", len(payload.Stories))
|
|
}
|
|
}
|
|
|
|
func TestWeatherStoriesNormalizerRejectsInvalidRequiredStoryTime(t *testing.T) {
|
|
_, err := (WeatherStoriesNormalizer{}).Normalize(nil, weatherStoriesRawEvent(`{
|
|
"stories": [{
|
|
"officeId": "LSX",
|
|
"startTime": "bad",
|
|
"endTime": "2026-05-31T11:00:00+00:00",
|
|
"updateTime": "2026-05-30T09:00:34+00:00"
|
|
}]
|
|
}`))
|
|
if err == nil {
|
|
t.Fatalf("Normalize() error = nil, want error")
|
|
}
|
|
if !strings.Contains(err.Error(), "stories[0].startTime") {
|
|
t.Fatalf("error = %q, want field context", err)
|
|
}
|
|
}
|
|
|
|
func TestWeatherStoriesNormalizerMatch(t *testing.T) {
|
|
n := WeatherStoriesNormalizer{}
|
|
if !n.Match(event.Event{Schema: standards.SchemaRawNWSWeatherStoriesV1}) {
|
|
t.Fatalf("Match(raw weatherstories) = false, want true")
|
|
}
|
|
if n.Match(event.Event{Schema: standards.SchemaRawNWSAlertsV1}) {
|
|
t.Fatalf("Match(raw alerts) = true, want false")
|
|
}
|
|
}
|
|
|
|
func weatherStoriesRawEvent(payload string) event.Event {
|
|
return event.Event{
|
|
ID: "evt-weatherstories-1",
|
|
Kind: event.Kind(standards.KindWeatherStory),
|
|
Source: "nws-weatherstories-test",
|
|
EmittedAt: time.Date(2026, 5, 30, 9, 5, 0, 0, time.UTC),
|
|
Schema: standards.SchemaRawNWSWeatherStoriesV1,
|
|
Payload: json.RawMessage(payload),
|
|
}
|
|
}
|
|
|
|
func weatherStoriesSamplePayload() string {
|
|
return `{
|
|
"stories": [
|
|
{
|
|
"officeId": " LSX ",
|
|
"startTime": "2026-05-30T08:46:00+00:00",
|
|
"endTime": "2026-05-31T11:00:00+00:00",
|
|
"updateTime": "2026-05-30T09:00:34+00:00",
|
|
"title": " Several Chances for Rain Through Monday ",
|
|
"description": " A stagnant weather pattern. ",
|
|
"altText": " This slide shows the forecast. ",
|
|
"priority": false,
|
|
"order": 1,
|
|
"download": " https://api.weather.gov/offices/LSX/weatherstories/download/3228e499-2aae-45a8-9ff9-1c060311026f "
|
|
}
|
|
]
|
|
}`
|
|
}
|