156 lines
6.0 KiB
Go
156 lines
6.0 KiB
Go
package briefing
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/module"
|
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/weatherdata"
|
|
)
|
|
|
|
func TestSPCConvectiveDiscussionModuleOmitsBelowThresholdButOutlookRemains(t *testing.T) {
|
|
registry := MustDefaultModuleRegistry()
|
|
ctx := spcConvectiveDiscussionContext(2, []weatherdata.ConvectiveOutlookDiscussion{
|
|
spcDiscussion(1, "Lower risk", "General thunderstorms.", "No organized severe weather is expected.", "2026-05-29T08:30:00-05:00"),
|
|
})
|
|
|
|
output, err := registry.BuildModule(ctx, module.ConfigItem{ID: module.SPCConvectiveDiscussion})
|
|
if err != nil {
|
|
t.Fatalf("BuildModule(discussion) error = %v", err)
|
|
}
|
|
if output != nil {
|
|
t.Fatalf("discussion output = %#v, want omitted below threshold", output)
|
|
}
|
|
|
|
outlookOutput, err := registry.BuildModule(ctx, module.ConfigItem{ID: module.SPCConvectiveOutlooks})
|
|
if err != nil {
|
|
t.Fatalf("BuildModule(outlooks) error = %v", err)
|
|
}
|
|
outlookValue := moduleValue[SPCConvectiveOutlooksModule](t, outlookOutput)
|
|
if !outlookValue.Checked || outlookValue.OutlookCount != 1 {
|
|
t.Fatalf("outlook value = %#v, want lower-risk outlook still emitted", outlookValue)
|
|
}
|
|
}
|
|
|
|
func TestSPCConvectiveDiscussionModuleIncludesEqualThresholdDiscussion(t *testing.T) {
|
|
registry := MustDefaultModuleRegistry()
|
|
ctx := spcConvectiveDiscussionContext(3, []weatherdata.ConvectiveOutlookDiscussion{
|
|
spcDiscussion(1, "Severe storms possible", "Scattered severe storms are possible.", "A few storms may become severe during the afternoon.", "2026-05-29T08:30:00-05:00"),
|
|
})
|
|
|
|
output, err := registry.BuildModule(ctx, module.ConfigItem{ID: module.SPCConvectiveDiscussion})
|
|
if err != nil {
|
|
t.Fatalf("BuildModule() error = %v", err)
|
|
}
|
|
if output == nil || output.ID != module.SPCConvectiveDiscussion || output.StanzaName != "spc_convective_discussion" {
|
|
t.Fatalf("output = %#v, want spc convective discussion stanza", output)
|
|
}
|
|
value := moduleValue[SPCConvectiveDiscussionModule](t, output)
|
|
if value.IncludedBecause != "severity_rank >= 3" || len(value.Discussions) != 1 {
|
|
t.Fatalf("value = %#v, want threshold reason and one discussion", value)
|
|
}
|
|
discussion := value.Discussions[0]
|
|
if discussion.Day != 1 || discussion.Headline != "Severe storms possible" || discussion.Summary == "" || discussion.Discussion == "" {
|
|
t.Fatalf("discussion = %#v, want prompt-facing discussion fields", discussion)
|
|
}
|
|
if discussion.UpdatedAt != "2026-05-29 at 8:30 AM" {
|
|
t.Fatalf("UpdatedAt = %q, want friendly local time", discussion.UpdatedAt)
|
|
}
|
|
data, err := json.Marshal(output.Value)
|
|
if err != nil {
|
|
t.Fatalf("Marshal() error = %v", err)
|
|
}
|
|
text := string(data)
|
|
for _, field := range []string{"included_because", "discussions", "headline", "summary", "discussion", "updated_at"} {
|
|
if !strings.Contains(text, field) {
|
|
t.Fatalf("json = %s, want field %s", text, field)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSPCConvectiveDiscussionModuleIncludesAboveThresholdDiscussion(t *testing.T) {
|
|
registry := MustDefaultModuleRegistry()
|
|
ctx := spcConvectiveDiscussionContext(4, []weatherdata.ConvectiveOutlookDiscussion{
|
|
spcDiscussion(1, "Enhanced severe risk", "Numerous severe storms are possible.", "Severe storms may produce damaging winds.", "2026-05-29T09:15:00-05:00"),
|
|
})
|
|
|
|
output, err := registry.BuildModule(ctx, module.ConfigItem{ID: module.SPCConvectiveDiscussion})
|
|
if err != nil {
|
|
t.Fatalf("BuildModule() error = %v", err)
|
|
}
|
|
value := moduleValue[SPCConvectiveDiscussionModule](t, output)
|
|
if len(value.Discussions) != 1 || value.Discussions[0].Headline != "Enhanced severe risk" {
|
|
t.Fatalf("value = %#v, want above-threshold discussion", value)
|
|
}
|
|
}
|
|
|
|
func TestSPCConvectiveDiscussionModuleOmitsMissingDiscussionText(t *testing.T) {
|
|
registry := MustDefaultModuleRegistry()
|
|
ctx := spcConvectiveDiscussionContext(3, []weatherdata.ConvectiveOutlookDiscussion{
|
|
{Day: 1, Headline: "Severe storms possible", Summary: "Scattered severe storms are possible.", UpdatedAt: ptrModuleTime("2026-05-29T08:30:00-05:00")},
|
|
})
|
|
|
|
output, err := registry.BuildModule(ctx, module.ConfigItem{ID: module.SPCConvectiveDiscussion})
|
|
if err != nil {
|
|
t.Fatalf("BuildModule() error = %v", err)
|
|
}
|
|
if output != nil {
|
|
t.Fatalf("output = %#v, want omitted when discussion text is missing", output)
|
|
}
|
|
}
|
|
|
|
func TestSPCConvectiveDiscussionModuleOmitsMissingSource(t *testing.T) {
|
|
registry := MustDefaultModuleRegistry()
|
|
ctx := testModuleContext()
|
|
ctx.Collected.SPCConvectiveOutlooks = nil
|
|
ctx.Derived.SPCConvectiveOutlooks = nil
|
|
ctx.Derived.SPCConvectiveDiscussions = nil
|
|
|
|
output, err := registry.BuildModule(ctx, module.ConfigItem{ID: module.SPCConvectiveDiscussion})
|
|
if err != nil {
|
|
t.Fatalf("BuildModule() error = %v", err)
|
|
}
|
|
if output != nil {
|
|
t.Fatalf("output = %#v, want omitted for missing optional source", output)
|
|
}
|
|
}
|
|
|
|
func spcConvectiveDiscussionContext(rank int, discussions []weatherdata.ConvectiveOutlookDiscussion) ModuleContext {
|
|
ctx := testModuleContext()
|
|
outlook := weatherdata.ConvectiveOutlook{
|
|
ID: "day1-categorical",
|
|
Day: 1,
|
|
OutlookType: "categorical",
|
|
Label: "SLGT",
|
|
LabelText: "Slight Risk",
|
|
SeverityRank: &rank,
|
|
ValidFrom: mustParseModuleTime("2026-05-29T11:00:00-05:00"),
|
|
ValidTo: mustParseModuleTime("2026-05-30T07:00:00-05:00"),
|
|
}
|
|
ctx.Collected.SPCConvectiveOutlooks = &weatherdata.ConvectiveOutlookRun{
|
|
AsOf: ptrModuleTime("2026-05-29T08:00:00-05:00"),
|
|
IssuedAt: ptrModuleTime("2026-05-29T07:45:00-05:00"),
|
|
Outlooks: []weatherdata.ConvectiveOutlook{outlook},
|
|
}
|
|
ctx.Derived.SPCConvectiveOutlooks = []weatherdata.ConvectiveOutlook{outlook}
|
|
ctx.Derived.SPCConvectiveDiscussions = discussions
|
|
return ctx
|
|
}
|
|
|
|
func spcDiscussion(day int, headline string, summary string, discussion string, updatedAt string) weatherdata.ConvectiveOutlookDiscussion {
|
|
return weatherdata.ConvectiveOutlookDiscussion{
|
|
Day: day,
|
|
Headline: headline,
|
|
Summary: summary,
|
|
Discussion: discussion,
|
|
UpdatedAt: ptrModuleTime(updatedAt),
|
|
}
|
|
}
|
|
|
|
func ptrModuleTime(value string) *time.Time {
|
|
parsed := mustParseModuleTime(value)
|
|
return &parsed
|
|
}
|