package generatedtext import ( "fmt" "strings" "testing" ) func TestValidateDayStyleGeneratedTextSharedBehavior(t *testing.T) { reports := []struct { name string validate func([]byte) (any, []byte, error) }{ { name: "daily", validate: func(data []byte) (any, []byte, error) { value, normalized, err := ValidateDaily(data) return value, normalized, err }, }, { name: "today", validate: func(data []byte) (any, []byte, error) { value, normalized, err := ValidateToday(data) return value, normalized, err }, }, { name: "tomorrow", validate: func(data []byte) (any, []byte, error) { value, normalized, err := ValidateTomorrow(data) return value, normalized, err }, }, } for _, report := range reports { t.Run(report.name, func(t *testing.T) { t.Run("requires summary", func(t *testing.T) { _, _, err := report.validate([]byte(`{"forecast_discussion":["First paragraph."]}`)) want := fmt.Sprintf("%s generated text summary is required", report.name) if err == nil || err.Error() != want { t.Fatalf("validate() error = %v, want %q", err, want) } }) t.Run("requires non-empty forecast discussion", func(t *testing.T) { _, _, err := report.validate([]byte(`{"summary":"Shared summary.","forecast_discussion":[" ",""]}`)) want := fmt.Sprintf("%s generated text forecast discussion is required", report.name) if err == nil || err.Error() != want { t.Fatalf("validate() error = %v, want %q", err, want) } }) t.Run("trims and normalizes", func(t *testing.T) { value, normalized, err := report.validate([]byte(`{ "summary": " Shared summary. ", "forecast_discussion": [ " First paragraph. ", "", " Second paragraph. " ], "precipitation_timing": " Afternoon. ", "confidence": " Medium " }`)) if err != nil { t.Fatalf("validate() error = %v", err) } fields := dayStyleFieldsForTest(t, value) if fields.Summary != "Shared summary." { t.Fatalf("Summary = %q, want trimmed summary", fields.Summary) } if strings.Join(fields.ForecastDiscussion, "|") != "First paragraph.|Second paragraph." { t.Fatalf("ForecastDiscussion = %#v, want trimmed non-empty paragraphs", fields.ForecastDiscussion) } if fields.PrecipitationTiming != "Afternoon." { t.Fatalf("PrecipitationTiming = %q, want trimmed precipitation timing", fields.PrecipitationTiming) } if fields.Confidence != "Medium" { t.Fatalf("Confidence = %q, want trimmed confidence", fields.Confidence) } want := `{"summary":"Shared summary.","forecast_discussion":["First paragraph.","Second paragraph."],"precipitation_timing":"Afternoon.","confidence":"Medium"}` if string(normalized) != want { t.Fatalf("normalized = %s, want %s", normalized, want) } }) t.Run("omits empty optional fields", func(t *testing.T) { _, normalized, err := report.validate([]byte(`{ "summary": "Shared summary.", "forecast_discussion": ["First paragraph."], "precipitation_timing": " ", "confidence": " " }`)) if err != nil { t.Fatalf("validate() error = %v", err) } want := `{"summary":"Shared summary.","forecast_discussion":["First paragraph."]}` if string(normalized) != want { t.Fatalf("normalized = %s, want %s", normalized, want) } }) t.Run("rejects unknown fields", func(t *testing.T) { _, _, err := report.validate([]byte(`{"summary":"Shared summary.","forecast_discussion":["First paragraph."],"extra":"value"}`)) if err == nil { t.Fatal("validate() error = nil, want error") } if !strings.Contains(err.Error(), `unknown field "extra"`) { t.Fatalf("validate() error = %v, want unknown field error", err) } }) }) } } func dayStyleFieldsForTest(t *testing.T, value any) dayStyleFields { t.Helper() switch value := value.(type) { case Daily: return (&value).dayStyleFields() case Today: return (&value).dayStyleFields() case Tomorrow: return (&value).dayStyleFields() default: t.Fatalf("value type = %T, want day-style generated text", value) return dayStyleFields{} } }