Add daily generated text assets
This commit is contained in:
@@ -2,6 +2,7 @@ package reporttemplate
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@@ -30,6 +31,18 @@ func TestTomorrowTemplateLookup(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDailyTemplateLookup(t *testing.T) {
|
||||
source, err := Template("daily")
|
||||
if err != nil {
|
||||
t.Fatalf("Template() error = %v", err)
|
||||
}
|
||||
for _, want := range []string{"# {{ .Report.Title }}", "**Forecast date:**", "## Daypart Forecast", "## Precipitation Timing", "## Forecast Discussion"} {
|
||||
if !strings.Contains(source, want) {
|
||||
t.Fatalf("template missing %q:\n%s", want, source)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTodayTemplateLookup(t *testing.T) {
|
||||
source, err := Template("today")
|
||||
if err != nil {
|
||||
@@ -84,6 +97,37 @@ func TestTomorrowSchemaLookup(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDailySchemaLookup(t *testing.T) {
|
||||
data, err := Schema("daily")
|
||||
if err != nil {
|
||||
t.Fatalf("Schema() error = %v", err)
|
||||
}
|
||||
schema := assertSchema(t, data, "summary,forecast_discussion")
|
||||
property, ok := schema.Properties["forecast_discussion"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatal("schema property forecast_discussion missing or invalid")
|
||||
}
|
||||
if property["type"] != "array" {
|
||||
t.Fatalf("forecast_discussion type = %v, want array", property["type"])
|
||||
}
|
||||
if property["minItems"] != float64(1) {
|
||||
t.Fatalf("forecast_discussion minItems = %v, want 1", property["minItems"])
|
||||
}
|
||||
items, ok := property["items"].(map[string]any)
|
||||
if !ok || items["type"] != "string" {
|
||||
t.Fatalf("forecast_discussion items = %#v, want string items", property["items"])
|
||||
}
|
||||
for _, field := range []string{"summary", "precipitation_timing", "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 TestTodaySchemaLookup(t *testing.T) {
|
||||
data, err := Schema("today")
|
||||
if err != nil {
|
||||
@@ -351,6 +395,85 @@ func TestRenderTomorrow(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestRenderDaily(t *testing.T) {
|
||||
rendered, err := Render("daily", testDailyRenderContext{
|
||||
Report: testDailyReportContext{
|
||||
Title: "Monday's Weather",
|
||||
ForecastDateLabel: "Monday, June 15, 2026",
|
||||
GeneratedAtLabel: "Sunday, June 14, 2026 at 9:14 AM",
|
||||
},
|
||||
GeneratedText: testDailyGeneratedText{
|
||||
Summary: "The selected day starts dry before showers return later in the day.",
|
||||
ForecastDiscussion: []string{
|
||||
"Clouds increase after sunrise.",
|
||||
"Rain chances peak during the afternoon.",
|
||||
},
|
||||
PrecipitationTiming: "A few showers may linger into early evening.",
|
||||
},
|
||||
Modules: testDailyModules{
|
||||
Dayparts: []testDailyDaypart{
|
||||
{
|
||||
Key: "morning",
|
||||
Summary: testDaypartSummary{
|
||||
DisplayName: "Morning",
|
||||
TemperatureTrend: "rising",
|
||||
TemperatureStartPhraseF: "upper 50s",
|
||||
TemperatureEndPhraseF: "upper 60s",
|
||||
DominantConditionDisplay: "Sunny",
|
||||
DominantConditionLower: "sunny",
|
||||
},
|
||||
},
|
||||
{
|
||||
Key: "afternoon",
|
||||
Summary: testDaypartSummary{
|
||||
DisplayName: "Afternoon",
|
||||
TemperatureTrend: "steady",
|
||||
TemperatureSteadyPhraseF: "upper 70s",
|
||||
DominantConditionDisplay: "Showers",
|
||||
DominantConditionLower: "showers",
|
||||
MaxPopPercent: intPtr(70),
|
||||
MaxPopTimeLabel: "3:00 PM",
|
||||
MentionPrecipitation: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
PrecipTiming: &testPrecipTiming{
|
||||
PrecipitationWindows: []testPrecipWindow{
|
||||
{PeriodBeginsHourLabel: "3:00 PM", PeriodEndsHourLabel: "6:00 PM", MaxPopPercent: intPtr(70), MaxPopHourLabel: "3:00 PM"},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Render() error = %v", err)
|
||||
}
|
||||
text := string(rendered)
|
||||
for _, want := range []string{
|
||||
"# Monday's Weather",
|
||||
"**Forecast date:** Monday, June 15, 2026",
|
||||
"**Updated:** Sunday, June 14, 2026 at 9:14 AM",
|
||||
"The selected day starts dry before showers return later in the day.",
|
||||
"- **Morning:** Sunny, with temperatures rising from the upper 50s to the upper 60s.",
|
||||
"- **Afternoon:** Showers, with temperatures in the upper 70s. Chance of precipitation is 70%.",
|
||||
"- **3:00 PM** to **6:00 PM**: Precipitation is expected during this period. The peak precipitation chance is 70% at 3:00 PM.",
|
||||
"A few showers may linger into early evening.",
|
||||
"Clouds increase after sunrise.",
|
||||
"Rain chances peak during the afternoon.",
|
||||
} {
|
||||
if !strings.Contains(text, want) {
|
||||
t.Fatalf("rendered template missing %q:\n%s", want, text)
|
||||
}
|
||||
}
|
||||
assertOrderedText(t, text, []string{
|
||||
"# Monday's Weather",
|
||||
"## Daypart Forecast",
|
||||
"- **Morning:**",
|
||||
"- **Afternoon:**",
|
||||
"## Precipitation Timing",
|
||||
"## Forecast Discussion",
|
||||
})
|
||||
}
|
||||
|
||||
func TestRenderToday(t *testing.T) {
|
||||
rendered, err := Render("today", testTodayRenderContext{
|
||||
Report: testTodayReportContext{
|
||||
@@ -532,17 +655,29 @@ func TestRenderHourlyOmitsConditionalSectionsForClearWeather(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUnknownAssetsReturnActionableErrors(t *testing.T) {
|
||||
if _, err := Template("daily"); err == nil || !strings.Contains(err.Error(), `unknown report template "daily"`) {
|
||||
if _, err := Template("missing"); err == nil || !strings.Contains(err.Error(), `unknown report template "missing"`) {
|
||||
t.Fatalf("Template() error = %v, want unknown template", err)
|
||||
}
|
||||
if _, err := Schema("daily"); err == nil || !strings.Contains(err.Error(), `unknown generated text schema "daily"`) {
|
||||
if _, err := Schema("missing"); err == nil || !strings.Contains(err.Error(), `unknown generated text schema "missing"`) {
|
||||
t.Fatalf("Schema() error = %v, want unknown schema", err)
|
||||
}
|
||||
if _, err := Render("daily", testRenderContext{}); err == nil || !strings.Contains(err.Error(), `unknown report template "daily"`) {
|
||||
if _, err := Render("missing", testRenderContext{}); err == nil || !strings.Contains(err.Error(), `unknown report template "missing"`) {
|
||||
t.Fatalf("Render() error = %v, want unknown template", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDailyPromptAssetExists(t *testing.T) {
|
||||
data, err := os.ReadFile("prompts/daily.generated_text.md")
|
||||
if err != nil {
|
||||
t.Fatalf("read Daily prompt asset: %v", err)
|
||||
}
|
||||
for _, want := range []string{"TASK:", "`summary`", "`forecast_discussion`", "`daily_planning`"} {
|
||||
if !strings.Contains(string(data), want) {
|
||||
t.Fatalf("Daily prompt asset missing %q:\n%s", want, string(data))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenderFailsForMissingContextFields(t *testing.T) {
|
||||
_, err := Render("hourly", map[string]any{"Report": map[string]any{"Title": "Hourly Report"}})
|
||||
if err == nil {
|
||||
@@ -571,6 +706,12 @@ type testTodayRenderContext struct {
|
||||
Modules testTodayModules
|
||||
}
|
||||
|
||||
type testDailyRenderContext struct {
|
||||
Report testDailyReportContext
|
||||
GeneratedText testDailyGeneratedText
|
||||
Modules testDailyModules
|
||||
}
|
||||
|
||||
type testReportContext struct {
|
||||
Title string
|
||||
LocationName string
|
||||
@@ -597,6 +738,12 @@ type testTodayReportContext struct {
|
||||
GeneratedAtLabel string
|
||||
}
|
||||
|
||||
type testDailyReportContext struct {
|
||||
Title string
|
||||
ForecastDateLabel string
|
||||
GeneratedAtLabel string
|
||||
}
|
||||
|
||||
type testTomorrowGeneratedText struct {
|
||||
Summary string
|
||||
ForecastDiscussion []string
|
||||
@@ -604,6 +751,13 @@ type testTomorrowGeneratedText struct {
|
||||
Confidence string
|
||||
}
|
||||
|
||||
type testDailyGeneratedText struct {
|
||||
Summary string
|
||||
ForecastDiscussion []string
|
||||
PrecipitationTiming string
|
||||
Confidence string
|
||||
}
|
||||
|
||||
type testModules struct {
|
||||
CurrentConditions *testCurrentConditions
|
||||
HourlyForecast *testHourlyForecast
|
||||
@@ -627,6 +781,11 @@ type testTodayModules struct {
|
||||
TodayPlanning *testTodayPlanning
|
||||
}
|
||||
|
||||
type testDailyModules struct {
|
||||
Dayparts []testDailyDaypart
|
||||
PrecipTiming *testPrecipTiming
|
||||
}
|
||||
|
||||
type testTodayPlanning struct {
|
||||
MorningReadiness []string
|
||||
CommuteSchoolWorkdayConcerns []string
|
||||
@@ -639,6 +798,11 @@ type testTomorrowDaypart struct {
|
||||
Summary testDaypartSummary
|
||||
}
|
||||
|
||||
type testDailyDaypart struct {
|
||||
Key string
|
||||
Summary testDaypartSummary
|
||||
}
|
||||
|
||||
type testDaypartSummary struct {
|
||||
DisplayName string
|
||||
TempRangeF string
|
||||
|
||||
Reference in New Issue
Block a user