Files
weatherreporter/internal/promptinput/package_test.go

205 lines
6.5 KiB
Go

package promptinput
import (
"strings"
"testing"
"time"
"gitea.maximumdirect.net/eric/weatherreporter/internal/module"
"gitea.maximumdirect.net/eric/weatherreporter/internal/report"
"gitea.maximumdirect.net/eric/weatherreporter/internal/timeutil"
)
func TestBuildDailyDataPackage(t *testing.T) {
pkg, err := Build(validBuildRequest(t))
if err != nil {
t.Fatalf("Build() error = %v", err)
}
if pkg.SchemaVersion != SchemaVersion {
t.Fatalf("SchemaVersion = %q, want %q", pkg.SchemaVersion, SchemaVersion)
}
if pkg.RunID != "20260529T100000Z_daily_today" {
t.Fatalf("RunID = %q, want metadata run id", pkg.RunID)
}
if pkg.Report.PromptID != "weather.daily_report" {
t.Fatalf("PromptID = %q, want weather.daily_report", pkg.Report.PromptID)
}
if pkg.Report.CurrentLocalDate != "2026-05-29" {
t.Fatalf("CurrentLocalDate = %q, want 2026-05-29", pkg.Report.CurrentLocalDate)
}
if pkg.Briefing.Order[0] != "metadata" || pkg.Briefing.Order[1] != "current_conditions" || pkg.Briefing.Order[2] != "derived_daily_summary" {
t.Fatalf("Briefing.Order = %#v, want snapshot stanza order", pkg.Briefing.Order)
}
if got := pkg.Briefing.Values["current_conditions"].(map[string]string)["condition_text"]; got != "Partly cloudy" {
t.Fatalf("current_conditions.condition_text = %q, want Partly cloudy", got)
}
if pkg.RecentChanges.Items == nil || len(pkg.RecentChanges.Items) != 0 {
t.Fatalf("RecentChanges.Items = %#v, want empty slice", pkg.RecentChanges.Items)
}
}
func TestBuildCurrentLocalDateUsesReportTimezone(t *testing.T) {
req := validBuildRequest(t)
req.Metadata.GeneratedAt = time.Date(2026, 5, 30, 2, 30, 0, 0, time.UTC)
req.Metadata.Timezone = "America/Chicago"
pkg, err := Build(req)
if err != nil {
t.Fatalf("Build() error = %v", err)
}
if pkg.Report.CurrentLocalDate != "2026-05-29" {
t.Fatalf("CurrentLocalDate = %q, want local Chicago date 2026-05-29", pkg.Report.CurrentLocalDate)
}
}
func TestBuildRejectsInvalidReportTimezone(t *testing.T) {
req := validBuildRequest(t)
req.Metadata.Timezone = "Not/AZone"
_, err := Build(req)
if err == nil {
t.Fatal("Build() error = nil, want invalid timezone error")
}
if !strings.Contains(err.Error(), "report timezone") {
t.Fatalf("error = %q, want report timezone context", err.Error())
}
}
func TestValidateRequiresFields(t *testing.T) {
pkg, err := Build(validBuildRequest(t))
if err != nil {
t.Fatalf("Build() error = %v", err)
}
pkg.RunID = ""
err = Validate(pkg)
if err == nil {
t.Fatal("Validate() error = nil, want required field error")
}
if !strings.Contains(err.Error(), "runId") {
t.Fatalf("error = %q, want runId context", err.Error())
}
}
func TestValidateRequiresCurrentLocalDate(t *testing.T) {
pkg, err := Build(validBuildRequest(t))
if err != nil {
t.Fatalf("Build() error = %v", err)
}
pkg.Report.CurrentLocalDate = ""
err = Validate(pkg)
if err == nil {
t.Fatal("Validate() error = nil, want required field error")
}
if !strings.Contains(err.Error(), "currentLocalDate") {
t.Fatalf("error = %q, want currentLocalDate context", err.Error())
}
}
func TestBuildUsesNamedSnapshotStanzas(t *testing.T) {
req := validBuildRequest(t)
req.Metadata.RunID = "20260529T100000Z_three_day"
req.Metadata.ReportID = report.ThreeDay
req.Metadata.PromptID = "weather.three_day_outlook"
req.Modules = snapshotWithOutputs(t,
module.Output{ID: module.Metadata, StanzaName: "metadata", Value: map[string]string{"run_id": req.Metadata.RunID}},
module.Output{ID: module.ForecastDelta, StanzaName: "three_day", Value: map[string]any{"days": []string{"2026-05-29"}}},
)
pkg, err := Build(req)
if err != nil {
t.Fatalf("Build() error = %v", err)
}
if pkg.Report.ID != report.ThreeDay {
t.Fatalf("Report.ID = %q, want three_day", pkg.Report.ID)
}
if _, ok := pkg.Briefing.Values["three_day"]; !ok {
t.Fatal("Briefing.Values[three_day] missing")
}
}
func TestMarshalYAMLIsDeterministicAndUsesNamedStanzas(t *testing.T) {
pkg, err := Build(validBuildRequest(t))
if err != nil {
t.Fatalf("Build() error = %v", err)
}
first, err := MarshalYAML(pkg)
if err != nil {
t.Fatalf("first marshal: %v", err)
}
second, err := MarshalYAML(pkg)
if err != nil {
t.Fatalf("second marshal: %v", err)
}
if string(first) != string(second) {
t.Fatalf("YAML output changed between marshals:\n%s\n---\n%s", string(first), string(second))
}
if !strings.Contains(string(first), "schema_version: weatherreporter.data_package.v2") ||
!strings.Contains(string(first), "briefing:\n") ||
!strings.Contains(string(first), " current_conditions:\n") ||
!strings.Contains(string(first), " condition_text: Partly cloudy") {
t.Fatalf("YAML output missing expected named stanzas:\n%s", string(first))
}
}
func TestLoadYAMLRoundTrip(t *testing.T) {
pkg, err := Build(validBuildRequest(t))
if err != nil {
t.Fatalf("Build() error = %v", err)
}
data, err := MarshalYAML(pkg)
if err != nil {
t.Fatalf("MarshalYAML() error = %v", err)
}
loaded, err := LoadYAML(data)
if err != nil {
t.Fatalf("LoadYAML() error = %v", err)
}
if loaded.SchemaVersion != SchemaVersion || loaded.RunID != pkg.RunID {
t.Fatalf("loaded package = %#v, want schema and run id", loaded)
}
if loaded.Briefing.Order[1] != "current_conditions" {
t.Fatalf("loaded package order = %#v, want current_conditions second", loaded.Briefing.Order)
}
}
func validBuildRequest(t *testing.T) BuildRequest {
t.Helper()
generatedAt := time.Date(2026, 5, 29, 10, 0, 0, 0, time.UTC)
return BuildRequest{
Metadata: Metadata{
RunID: "20260529T100000Z_daily_today",
ReportID: report.DailyToday,
Variant: "today",
PromptID: "weather.daily_report",
GeneratedAt: generatedAt,
Timezone: "America/Chicago",
ValidPeriod: timeutil.Period{
Start: time.Date(2026, 5, 29, 5, 0, 0, 0, time.UTC),
End: time.Date(2026, 5, 30, 5, 0, 0, 0, time.UTC),
},
},
Modules: snapshotWithOutputs(t,
module.Output{ID: module.Metadata, StanzaName: "metadata", Value: map[string]string{"run_id": "20260529T100000Z_daily_today"}},
module.Output{ID: module.CurrentConditions, StanzaName: "current_conditions", Value: map[string]string{"condition_text": "Partly cloudy"}},
module.Output{ID: module.DerivedDailySummary, StanzaName: "derived_daily_summary", Value: map[string]string{"date": "2026-05-29"}},
),
}
}
func snapshotWithOutputs(t *testing.T, outputs ...module.Output) module.Snapshot {
t.Helper()
snapshot, err := module.NewSnapshot(outputs)
if err != nil {
t.Fatalf("NewSnapshot() error = %v", err)
}
return snapshot
}