Correct daypart identity and display handling
This commit is contained in:
@@ -355,6 +355,52 @@ func TestDerivedDaypartSummariesExposeConfiguredKeysAndHazards(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDerivedDaypartSummariesRejectCanonicalKeyCollisions(t *testing.T) {
|
||||
registry := MustDefaultModuleRegistry()
|
||||
ctx := derivedModuleContext(report.Daily)
|
||||
first := ctx.Derived.DaypartSummaries[0]
|
||||
first.Name = "Morning"
|
||||
second := first
|
||||
second.Name = "morning!"
|
||||
ctx.Derived.DaypartSummaries = []forecast.DaypartSummary{first, second}
|
||||
ctx.Derived.DailySummaries = []forecast.DailySummary{{Date: first.Period.Start.Format(timeutil.DateLayout)}}
|
||||
|
||||
_, err := registry.BuildModule(ctx, module.ConfigItem{ID: module.DerivedDaypartSummaries})
|
||||
if err == nil || !strings.Contains(err.Error(), "collides") {
|
||||
t.Fatalf("BuildModule() error = %v, want canonical daypart-key collision", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDerivedDaypartSummaryDisplayCapitalizesUnicodeNames(t *testing.T) {
|
||||
value := derivedDaypartSummaryValue(forecast.DaypartSummary{
|
||||
Name: "mañana",
|
||||
DominantCondition: "llovizna",
|
||||
}, "UTC")
|
||||
if value.DisplayName != "Mañana" || value.DominantConditionDisplay != "Llovizna" {
|
||||
t.Fatalf("daypart display = %#v, want rune-safe capitalization", value)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDerivedDaypartSummariesKeepDistinctUnicodeKeys(t *testing.T) {
|
||||
registry := MustDefaultModuleRegistry()
|
||||
ctx := derivedModuleContext(report.Daily)
|
||||
first := ctx.Derived.DaypartSummaries[0]
|
||||
first.Name = "mañana"
|
||||
second := first
|
||||
second.Name = "manana"
|
||||
ctx.Derived.DaypartSummaries = []forecast.DaypartSummary{first, second}
|
||||
ctx.Derived.DailySummaries = []forecast.DailySummary{{Date: first.Period.Start.Format(timeutil.DateLayout)}}
|
||||
|
||||
output, err := registry.BuildModule(ctx, module.ConfigItem{ID: module.DerivedDaypartSummaries})
|
||||
if err != nil {
|
||||
t.Fatalf("BuildModule() error = %v", err)
|
||||
}
|
||||
value := moduleValue[map[string]DerivedDaypartSummaryModule](t, output)
|
||||
if len(value) != 2 || value["mañana"].DisplayName != "Mañana" || value["manana"].DisplayName != "Manana" {
|
||||
t.Fatalf("daypart summaries = %#v, want distinct Unicode canonical keys", value)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDerivedDaypartSummariesPromptExportOmitsTemplateHelpers(t *testing.T) {
|
||||
registry := MustDefaultModuleRegistry()
|
||||
ctx := derivedModuleContext(report.Daily)
|
||||
@@ -670,6 +716,65 @@ func TestOutdoorWindowsScoreSnowIceAndFog(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlanningUsesCanonicalDaypartIdentities(t *testing.T) {
|
||||
timedValue := func(value float64) *forecast.TimedValue {
|
||||
return &forecast.TimedValue{Value: value}
|
||||
}
|
||||
containsText := func(values []string, text string) bool {
|
||||
return strings.Contains(strings.Join(values, "\n"), text)
|
||||
}
|
||||
summary := &forecast.DailySummary{Dayparts: []forecast.DaypartSummary{
|
||||
{Name: "Overnight!", MaxPrecipitationProbability: timedValue(60)},
|
||||
{Name: "MORNING", MaxPrecipitationProbability: timedValue(60)},
|
||||
{Name: "Afternoon!!!", MaxPrecipitationProbability: timedValue(60)},
|
||||
{Name: "EVENING!", MaxPrecipitationProbability: timedValue(60)},
|
||||
}}
|
||||
|
||||
today := buildTodayPlanning(summary)
|
||||
if !containsText(today.MorningReadiness, "Morning precipitation chance peaks near 60%.") {
|
||||
t.Fatalf("today morning readiness = %#v, want canonical morning window", today.MorningReadiness)
|
||||
}
|
||||
if !containsText(today.CommuteSchoolWorkdayConcerns, "Afternoon!!! precipitation chance reaches 60%.") ||
|
||||
containsText(today.CommuteSchoolWorkdayConcerns, "Overnight!") ||
|
||||
containsText(today.CommuteSchoolWorkdayConcerns, "EVENING!") {
|
||||
t.Fatalf("today workday concerns = %#v, want only canonical workday windows", today.CommuteSchoolWorkdayConcerns)
|
||||
}
|
||||
if !containsText(today.LateDayChangeWatch, "Afternoon!!! precipitation timing may shift") ||
|
||||
!containsText(today.LateDayChangeWatch, "EVENING! precipitation timing may shift") {
|
||||
t.Fatalf("today late-day watch = %#v, want canonical afternoon and evening windows", today.LateDayChangeWatch)
|
||||
}
|
||||
|
||||
base := buildMorningCommuteOvernightPlanning(summary)
|
||||
if !containsText(base.MorningReadiness, "Morning precipitation chance peaks near 60%.") ||
|
||||
!containsText(base.OvernightChangeWatch, "Overnight precipitation timing may shift") ||
|
||||
containsText(base.CommuteSchoolWorkdayConcerns, "Overnight!") ||
|
||||
containsText(base.CommuteSchoolWorkdayConcerns, "EVENING!") {
|
||||
t.Fatalf("daily/tomorrow planning = %#v, want canonical daypart treatment", base)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCapitalizeFirst(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want string
|
||||
}{
|
||||
{name: "empty", input: "", want: ""},
|
||||
{name: "ASCII", input: "morning", want: "Morning"},
|
||||
{name: "multibyte", input: "mañana", want: "Mañana"},
|
||||
{name: "already uppercase", input: "Morning", want: "Morning"},
|
||||
{name: "leading space", input: " morning", want: " morning"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := capitalizeFirst(tt.input); got != tt.want {
|
||||
t.Fatalf("capitalizeFirst(%q) = %q, want %q", tt.input, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestOutdoorWindowsAndTomorrowPlanningModulesPreserveDailyContent(t *testing.T) {
|
||||
registry := MustDefaultModuleRegistry()
|
||||
ctx := derivedModuleContext(report.Tomorrow)
|
||||
|
||||
Reference in New Issue
Block a user