Add Today generated text assets
This commit is contained in:
@@ -136,6 +136,130 @@ func TestBuildRenderContextReportsModuleExtractionError(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildTodayRenderContext(t *testing.T) {
|
||||
metadata := testTodayMetadata()
|
||||
snapshot := testTodaySnapshot(t)
|
||||
generated := Today{
|
||||
Summary: "Today starts quiet, then showers become more likely later in the day.",
|
||||
ForecastDiscussion: []string{
|
||||
"Morning conditions should stay mostly dry.",
|
||||
"Rain chances increase during the afternoon as deeper moisture arrives.",
|
||||
},
|
||||
PrecipitationTiming: "The most likely rain window is from midafternoon into early evening.",
|
||||
}
|
||||
collected := testCollected()
|
||||
derived := testTodayDerived()
|
||||
|
||||
ctx, err := BuildTodayRenderContext(metadata, snapshot, generated, collected, derived)
|
||||
if err != nil {
|
||||
t.Fatalf("BuildTodayRenderContext() error = %v", err)
|
||||
}
|
||||
if ctx.Report.Title != "Today's Weather" {
|
||||
t.Fatalf("Report.Title = %q, want Today's Weather", ctx.Report.Title)
|
||||
}
|
||||
if ctx.Report.ForecastDateLabel != "Monday, June 15, 2026" || ctx.Report.ForecastDayName != "Monday" {
|
||||
t.Fatalf("forecast date labels = %q/%q, want Monday labels", ctx.Report.ForecastDateLabel, ctx.Report.ForecastDayName)
|
||||
}
|
||||
if ctx.Report.GeneratedAtLabel != "Monday, June 15, 2026 at 7:14 AM" {
|
||||
t.Fatalf("Report.GeneratedAtLabel = %q, want friendly generated-at label", ctx.Report.GeneratedAtLabel)
|
||||
}
|
||||
if ctx.Modules.Metadata == nil || ctx.Modules.Metadata.ReportID != report.Today {
|
||||
t.Fatalf("Modules.Metadata = %#v, want today metadata", ctx.Modules.Metadata)
|
||||
}
|
||||
if ctx.Modules.CurrentConditions == nil || ctx.Modules.HourlyForecast == nil {
|
||||
t.Fatalf("current/hourly modules = %#v/%#v, want available in render context", ctx.Modules.CurrentConditions, ctx.Modules.HourlyForecast)
|
||||
}
|
||||
if ctx.Modules.DerivedDailySummary == nil || ctx.Modules.DerivedDailySummary.HighTempF == nil || *ctx.Modules.DerivedDailySummary.HighTempF != 74 {
|
||||
t.Fatalf("Modules.DerivedDailySummary = %#v, want daily summary", ctx.Modules.DerivedDailySummary)
|
||||
}
|
||||
if ctx.Modules.DerivedDaypartSummaries == nil || len(*ctx.Modules.DerivedDaypartSummaries) != 2 {
|
||||
t.Fatalf("Modules.DerivedDaypartSummaries = %#v, want daypart map", ctx.Modules.DerivedDaypartSummaries)
|
||||
}
|
||||
if len(ctx.Modules.Dayparts) != 2 || ctx.Modules.Dayparts[0].Key != "morning" || ctx.Modules.Dayparts[1].Key != "afternoon" {
|
||||
t.Fatalf("Modules.Dayparts = %#v, want configured order", ctx.Modules.Dayparts)
|
||||
}
|
||||
if ctx.Modules.PrecipTiming == nil || len(ctx.Modules.PrecipTiming.PrecipitationWindows) != 1 {
|
||||
t.Fatalf("Modules.PrecipTiming = %#v, want precipitation window", ctx.Modules.PrecipTiming)
|
||||
}
|
||||
if ctx.Modules.AlertDigest == nil || ctx.Modules.SPCConvectiveOutlooks == nil || ctx.Modules.AreaForecastDiscussion == nil || ctx.Modules.SPCConvectiveDiscussion == nil || ctx.Modules.WeatherStory == nil || ctx.Modules.TodayPlanning == nil {
|
||||
t.Fatalf("optional modules missing from render context: %#v", ctx.Modules)
|
||||
}
|
||||
if ctx.Modules.TodayPlanning.MorningReadiness[0] != "Take sunglasses early." {
|
||||
t.Fatalf("Modules.TodayPlanning = %#v, want today planning facts", ctx.Modules.TodayPlanning)
|
||||
}
|
||||
if !ctx.Collected.FetchedAt.Equal(collected.FetchedAt) {
|
||||
t.Fatalf("Collected.FetchedAt = %s, want %s", ctx.Collected.FetchedAt, collected.FetchedAt)
|
||||
}
|
||||
if len(ctx.Derived.DaypartSummaries) != 2 {
|
||||
t.Fatalf("Derived.DaypartSummaries = %#v, want passthrough facts", ctx.Derived.DaypartSummaries)
|
||||
}
|
||||
|
||||
rendered, err := reporttemplate.Render("today", ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Render() error = %v", err)
|
||||
}
|
||||
text := string(rendered)
|
||||
for _, want := range []string{
|
||||
"# Today's Weather",
|
||||
"**Forecast date:** Monday, June 15, 2026",
|
||||
"Today starts quiet, then showers become more likely later in the day.",
|
||||
"Currently, it is 58°F and clear.",
|
||||
"- **Morning:** Partly cloudy, with temperatures in the low 60s.",
|
||||
"- **Afternoon:** Showers, with temperatures in the mid 70s. Chance of precipitation is 70%.",
|
||||
"## Precipitation Timing",
|
||||
"- **3:00 PM** to **6:00 PM**: Precipitation is expected during this period. The peak precipitation chance is 70% at 3:00 PM.",
|
||||
"The most likely rain window is from midafternoon into early evening.",
|
||||
"## Planning Notes",
|
||||
"- Take sunglasses early.",
|
||||
"Morning conditions should stay mostly dry.",
|
||||
"Rain chances increase during the afternoon as deeper moisture arrives.",
|
||||
} {
|
||||
if !strings.Contains(text, want) {
|
||||
t.Fatalf("rendered template missing %q:\n%s", want, text)
|
||||
}
|
||||
}
|
||||
assertOrderedText(t, text, []string{
|
||||
"# Today's Weather",
|
||||
"## Current Conditions",
|
||||
"## Daypart Forecast",
|
||||
"- **Morning:**",
|
||||
"- **Afternoon:**",
|
||||
"## Precipitation Timing",
|
||||
"## Planning Notes",
|
||||
"## Forecast Discussion",
|
||||
})
|
||||
}
|
||||
|
||||
func TestBuildTodayRenderContextAllowsOmittedOptionalModules(t *testing.T) {
|
||||
snapshot, err := module.NewSnapshot(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("NewSnapshot() error = %v", err)
|
||||
}
|
||||
ctx, err := BuildTodayRenderContext(testTodayMetadata(), snapshot, Today{
|
||||
Summary: "Dry weather is expected today.",
|
||||
ForecastDiscussion: []string{"High pressure keeps conditions quiet."},
|
||||
}, testCollected(), facts.DerivedFacts{})
|
||||
if err != nil {
|
||||
t.Fatalf("BuildTodayRenderContext() error = %v", err)
|
||||
}
|
||||
if ctx.Modules.Metadata != nil || ctx.Modules.CurrentConditions != nil || ctx.Modules.PrecipTiming != nil || len(ctx.Modules.Dayparts) != 0 || ctx.Modules.TodayPlanning != nil {
|
||||
t.Fatalf("Modules = %#v, want omitted optional modules", ctx.Modules)
|
||||
}
|
||||
rendered, err := reporttemplate.Render("today", ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Render() error = %v", err)
|
||||
}
|
||||
text := string(rendered)
|
||||
for _, unwanted := range []string{"## Current Conditions", "## Precipitation Timing", "## Planning Notes"} {
|
||||
if strings.Contains(text, unwanted) {
|
||||
t.Fatalf("rendered template included %q without module data:\n%s", unwanted, text)
|
||||
}
|
||||
}
|
||||
if !strings.Contains(text, "- No daypart forecast details are available.") {
|
||||
t.Fatalf("rendered template missing daypart fallback:\n%s", text)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildTomorrowRenderContext(t *testing.T) {
|
||||
metadata := testTomorrowMetadata()
|
||||
snapshot := testTomorrowSnapshot(t)
|
||||
@@ -290,6 +414,26 @@ func testTomorrowMetadata() briefing.Metadata {
|
||||
}
|
||||
}
|
||||
|
||||
func testTodayMetadata() briefing.Metadata {
|
||||
generatedAt := time.Date(2026, 6, 15, 12, 14, 0, 0, time.UTC)
|
||||
return briefing.Metadata{
|
||||
RunID: "run-today",
|
||||
ReportID: report.Today,
|
||||
PromptID: "weather.today_generated_text",
|
||||
GeneratedAt: generatedAt,
|
||||
Units: "imperial",
|
||||
Timezone: "America/Chicago",
|
||||
ValidPeriod: timeutil.Period{
|
||||
Start: time.Date(2026, 6, 15, 5, 0, 0, 0, time.UTC),
|
||||
End: time.Date(2026, 6, 16, 5, 0, 0, 0, time.UTC),
|
||||
},
|
||||
Location: &briefing.LocationContext{
|
||||
Name: "Brentwood",
|
||||
Region: "MO",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func testCollected() facts.CollectedFacts {
|
||||
return facts.CollectedFacts{FetchedAt: time.Date(2026, 5, 29, 13, 31, 0, 0, time.UTC)}
|
||||
}
|
||||
@@ -299,6 +443,14 @@ func testDerived() facts.DerivedFacts {
|
||||
}
|
||||
|
||||
func testTomorrowDerived() facts.DerivedFacts {
|
||||
return testCivilDayDerived()
|
||||
}
|
||||
|
||||
func testTodayDerived() facts.DerivedFacts {
|
||||
return testCivilDayDerived()
|
||||
}
|
||||
|
||||
func testCivilDayDerived() facts.DerivedFacts {
|
||||
morningStart := time.Date(2026, 6, 15, 11, 0, 0, 0, time.UTC)
|
||||
afternoonStart := time.Date(2026, 6, 15, 17, 0, 0, 0, time.UTC)
|
||||
return facts.DerivedFacts{
|
||||
@@ -321,6 +473,134 @@ func testTomorrowDerived() facts.DerivedFacts {
|
||||
}
|
||||
}
|
||||
|
||||
func testTodaySnapshot(t *testing.T) module.Snapshot {
|
||||
t.Helper()
|
||||
snapshot, err := module.NewSnapshot([]module.Output{
|
||||
{
|
||||
ID: module.Metadata,
|
||||
StanzaName: string(module.Metadata),
|
||||
Value: briefing.MetadataModule{
|
||||
RunID: "run-today",
|
||||
ReportID: report.Today,
|
||||
PromptID: "weather.today_generated_text",
|
||||
GeneratedAt: testTodayMetadata().GeneratedAt,
|
||||
Units: "imperial",
|
||||
Timezone: "America/Chicago",
|
||||
ValidPeriod: testTodayMetadata().ValidPeriod,
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: module.CurrentConditions,
|
||||
StanzaName: string(module.CurrentConditions),
|
||||
Value: briefing.CurrentConditionsModule{
|
||||
ConditionTextLower: "clear",
|
||||
TemperatureF: intPtr(58),
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: module.HourlyForecast,
|
||||
StanzaName: string(module.HourlyForecast),
|
||||
Value: briefing.HourlyForecastModule{
|
||||
Periods: []briefing.HourlyForecastPeriod{
|
||||
{HourLabel: "7:00 AM", TextDescriptionLower: "partly cloudy", TemperatureF: floatPtr(63)},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: module.DerivedDailySummary,
|
||||
StanzaName: string(module.DerivedDailySummary),
|
||||
Value: briefing.DerivedDailySummaryModule{
|
||||
Date: "Monday, June 15, 2026",
|
||||
HighTempF: intPtr(74),
|
||||
LowTempF: intPtr(61),
|
||||
DailyPrecipitationProbability: intPtr(70),
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: module.DerivedDaypartSummaries,
|
||||
StanzaName: string(module.DerivedDaypartSummaries),
|
||||
Value: map[string]briefing.DerivedDaypartSummaryModule{
|
||||
"afternoon": {
|
||||
DisplayName: "Afternoon",
|
||||
TemperatureTrend: "steady",
|
||||
TemperatureSteadyPhraseF: "mid 70s",
|
||||
DominantConditionDisplay: "Showers",
|
||||
DominantConditionLower: "showers",
|
||||
MaxPopPercent: intPtr(70),
|
||||
MaxPopTimeLabel: "3:00 PM",
|
||||
MentionPrecipitation: true,
|
||||
},
|
||||
"morning": {
|
||||
DisplayName: "Morning",
|
||||
TemperatureTrend: "steady",
|
||||
TemperatureSteadyPhraseF: "low 60s",
|
||||
DominantConditionDisplay: "Partly cloudy",
|
||||
DominantConditionLower: "partly cloudy",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: module.PrecipTiming,
|
||||
StanzaName: string(module.PrecipTiming),
|
||||
Value: briefing.PrecipTimingModule{
|
||||
MaxPopPercent: intPtr(70),
|
||||
MaxPopTime: "3 PM",
|
||||
PrecipitationWindows: []briefing.PrecipitationWindowModule{
|
||||
{PeriodBeginsHourLabel: "3:00 PM", PeriodEndsHourLabel: "6:00 PM", MaxPopPercent: intPtr(70), MaxPopHourLabel: "3:00 PM"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: module.AlertDigest,
|
||||
StanzaName: string(module.AlertDigest),
|
||||
Value: briefing.AlertDigestModule{
|
||||
Checked: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: module.SPCConvectiveOutlooks,
|
||||
StanzaName: string(module.SPCConvectiveOutlooks),
|
||||
Value: briefing.SPCConvectiveOutlooksModule{
|
||||
Checked: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: module.AreaForecastDiscussion,
|
||||
StanzaName: string(module.AreaForecastDiscussion),
|
||||
Value: briefing.AreaForecastDiscussionModule{
|
||||
KeyMessages: []string{"Rain chances increase late."},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: module.SPCConvectiveDiscussion,
|
||||
StanzaName: string(module.SPCConvectiveDiscussion),
|
||||
Value: briefing.SPCConvectiveDiscussionModule{
|
||||
IncludedBecause: "convective outlook",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: module.WeatherStory,
|
||||
StanzaName: string(module.WeatherStory),
|
||||
Value: briefing.WeatherStoryModule{
|
||||
Available: true,
|
||||
Title: "Rain returns",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: module.TodayPlanning,
|
||||
StanzaName: string(module.TodayPlanning),
|
||||
Value: briefing.TodayPlanningModule{
|
||||
MorningReadiness: []string{"Take sunglasses early."},
|
||||
OutdoorPlanning: []string{"Best outdoor window: Morning (quiet weather)."},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("NewSnapshot() error = %v", err)
|
||||
}
|
||||
return snapshot
|
||||
}
|
||||
|
||||
func testSnapshot(t *testing.T) module.Snapshot {
|
||||
t.Helper()
|
||||
snapshot, err := module.NewSnapshot([]module.Output{
|
||||
|
||||
Reference in New Issue
Block a user