package reporttemplate import ( "encoding/json" "strings" "testing" ) func TestTemplateLookup(t *testing.T) { source, err := Template("hourly") if err != nil { t.Fatalf("Template() error = %v", err) } for _, want := range []string{"# {{ .ReportTitle }}", "## Summary", "## Hourly Forecast", "## Weather Story"} { if !strings.Contains(source, want) { t.Fatalf("template missing %q:\n%s", want, source) } } } func TestSchemaLookup(t *testing.T) { data, err := Schema("hourly") if err != nil { t.Fatalf("Schema() error = %v", err) } var schema struct { Type string `json:"type"` AdditionalProperties bool `json:"additionalProperties"` Required []string `json:"required"` Properties map[string]any `json:"properties"` } if err := json.Unmarshal(data, &schema); err != nil { t.Fatalf("schema is invalid JSON: %v", err) } if schema.Type != "object" { t.Fatalf("schema type = %q, want object", schema.Type) } if schema.AdditionalProperties { t.Fatal("additionalProperties = true, want false") } if strings.Join(schema.Required, ",") != "summary,timing,impacts" { t.Fatalf("required = %#v, want summary/timing/impacts", schema.Required) } for _, field := range []string{"summary", "timing", "impacts", "confidence"} { property, ok := schema.Properties[field].(map[string]any) if !ok { t.Fatalf("schema property %q missing or invalid", field) } if property["type"] != "string" { t.Fatalf("schema property %q type = %v, want string", field, property["type"]) } } } func TestRenderHourly(t *testing.T) { rendered, err := Render("hourly", testRenderContext{ ReportTitle: "Hourly Report", LocationName: "Brentwood", ValidPeriod: "May 29, 8:30 AM to 2:30 PM", GeneratedAt: "May 29, 8:30 AM", CurrentConditions: "74 F, light south wind.", PrecipitationTiming: "Showers are most likely late morning.", WeatherStory: "Morning storms remain the main story.", GeneratedText: testGeneratedText{ Summary: "Storm chances increase through late morning.", Timing: "The main window is 10 AM to noon.", Impacts: "Brief downpours may slow travel.", Confidence: "Medium confidence in timing.", }, HourlyForecast: []testHourlyRow{ {Time: "9 AM", Summary: "Cloudy", Temperature: "74 F", Precipitation: "30% showers", Wind: "S 8 mph"}, {Time: "10 AM", Summary: "Showers", Temperature: "75 F", Precipitation: "70% showers", Wind: "S 10 mph"}, }, Alerts: []string{"Flood Watch until 2:30 PM"}, SPCOutlooks: []string{"Slight Risk through afternoon"}, SPCDiscussions: []string{"Strong storms may develop late morning."}, ForecastDiscussion: testForecastDiscussion{ KeyMessages: []string{"Storms are most likely late morning."}, ShortTerm: "Short-term discussion favors increasing rain coverage.", }, }) if err != nil { t.Fatalf("Render() error = %v", err) } text := string(rendered) for _, want := range []string{ "# Hourly Report", "Valid: May 29, 8:30 AM to 2:30 PM", "Storm chances increase through late morning.", "## Confidence", "- 10 AM: Showers; 75 F; 70% showers; S 10 mph", "- Flood Watch until 2:30 PM", "Short-term discussion favors increasing rain coverage.", } { if !strings.Contains(text, want) { t.Fatalf("rendered template missing %q:\n%s", want, text) } } } func TestUnknownAssetsReturnActionableErrors(t *testing.T) { if _, err := Template("daily"); err == nil || !strings.Contains(err.Error(), `unknown report template "daily"`) { t.Fatalf("Template() error = %v, want unknown template", err) } if _, err := Schema("daily"); err == nil || !strings.Contains(err.Error(), `unknown generated text schema "daily"`) { t.Fatalf("Schema() error = %v, want unknown schema", err) } if _, err := Render("daily", testRenderContext{}); err == nil || !strings.Contains(err.Error(), `unknown report template "daily"`) { t.Fatalf("Render() error = %v, want unknown template", err) } } func TestRenderFailsForMissingContextFields(t *testing.T) { _, err := Render("hourly", map[string]any{"ReportTitle": "Hourly Report"}) if err == nil { t.Fatal("Render() error = nil, want missing field error") } if !strings.Contains(err.Error(), `render report template "hourly"`) { t.Fatalf("Render() error = %v, want render context", err) } } type testRenderContext struct { ReportTitle string LocationName string ValidPeriod string GeneratedAt string CurrentConditions string PrecipitationTiming string WeatherStory string GeneratedText testGeneratedText HourlyForecast []testHourlyRow Alerts []string SPCOutlooks []string SPCDiscussions []string ForecastDiscussion testForecastDiscussion } type testGeneratedText struct { Summary string Timing string Impacts string Confidence string } type testHourlyRow struct { Time string Summary string Temperature string Precipitation string Wind string } type testForecastDiscussion struct { KeyMessages []string ShortTerm string }