Add module contracts and registry validation

This commit is contained in:
2026-06-09 20:30:49 +00:00
parent e9508089ab
commit 24dba3bd60
11 changed files with 802 additions and 3 deletions

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"time"
"gitea.maximumdirect.net/eric/weatherreporter/internal/module"
"gitea.maximumdirect.net/eric/weatherreporter/internal/timeutil"
)
@@ -42,6 +43,7 @@ type Definition struct {
BatchOutputName string
Generated bool
CompatiblePriorIDs []ID
Modules []module.ConfigItem
Morning bool
Evening bool
resolve func(ResolveRequest) (timeutil.Period, error)
@@ -63,6 +65,14 @@ func (d Definition) CompatibleWithPrior(id ID) bool {
return false
}
func (d Definition) ModuleIDs() []module.ID {
ids := make([]module.ID, 0, len(d.Modules))
for _, item := range d.Modules {
ids = append(ids, item.ID)
}
return ids
}
type ResolveRequest struct {
Now time.Time
Location *time.Location

View File

@@ -6,6 +6,7 @@ import (
"testing"
"time"
"gitea.maximumdirect.net/eric/weatherreporter/internal/module"
"gitea.maximumdirect.net/eric/weatherreporter/internal/timeutil"
)
@@ -251,6 +252,99 @@ func TestRegistryDefinitionsDeclarePathAndCompatibilityPolicy(t *testing.T) {
}
}
func TestRegistryDefinitionsDeclareDefaultModules(t *testing.T) {
tests := []struct {
id ID
want []module.ID
}{
{
id: DailyToday,
want: []module.ID{
module.Metadata,
module.CurrentConditions,
module.DerivedDailySummary,
module.DerivedDaypartSummaries,
module.PrecipTiming,
module.AlertDigest,
module.ForecastDelta,
module.AreaForecastDiscussion,
module.WeatherStory,
module.OutdoorWindows,
},
},
{
id: DailyTomorrow,
want: []module.ID{
module.Metadata,
module.CurrentConditions,
module.DerivedDailySummary,
module.DerivedDaypartSummaries,
module.PrecipTiming,
module.AlertDigest,
module.ForecastDelta,
module.AreaForecastDiscussion,
module.WeatherStory,
module.OutdoorWindows,
module.TomorrowPlanning,
},
},
{
id: ThreeDay,
want: []module.ID{
module.Metadata,
module.CurrentConditions,
module.DerivedDaypartSummaries,
module.PrecipTiming,
module.AlertDigest,
module.ForecastDelta,
module.AreaForecastDiscussion,
module.WeatherStory,
module.OutdoorWindows,
},
},
{
id: Weekend,
want: []module.ID{
module.Metadata,
module.CurrentConditions,
module.DerivedDaypartSummaries,
module.PrecipTiming,
module.AlertDigest,
module.AreaForecastDiscussion,
module.WeatherStory,
module.OutdoorWindows,
module.WeekendPlanning,
},
},
{
id: Storm,
want: []module.ID{
module.Metadata,
module.CurrentConditions,
module.HourlyTable,
module.PrecipTiming,
module.AlertDigest,
module.AreaForecastDiscussion,
module.WeatherStory,
module.StormWindowSummary,
},
},
}
registry := DefaultRegistry()
for _, tt := range tests {
t.Run(string(tt.id), func(t *testing.T) {
definition, err := registry.Lookup(tt.id)
if err != nil {
t.Fatalf("Lookup() error = %v", err)
}
if !reflect.DeepEqual(definition.ModuleIDs(), tt.want) {
t.Fatalf("ModuleIDs() = %#v, want %#v", definition.ModuleIDs(), tt.want)
}
})
}
}
func TestResolvedMetadata(t *testing.T) {
location := mustLoadLocation(t)
resolved, err := Resolve(DailyToday, ResolveRequest{Now: mustParse("2026-05-29T05:00:00-05:00"), Location: location})

View File

@@ -1,6 +1,10 @@
package report
import "fmt"
import (
"fmt"
"gitea.maximumdirect.net/eric/weatherreporter/internal/module"
)
type Registry struct {
definitions map[ID]Definition
@@ -17,6 +21,7 @@ func DefaultRegistry() Registry {
BatchOutputName: "daily.md",
Generated: true,
CompatiblePriorIDs: []ID{DailyToday, DailyTomorrow},
Modules: dailyTodayModules(),
Morning: true,
resolve: resolveDailyToday,
},
@@ -29,6 +34,7 @@ func DefaultRegistry() Registry {
BatchOutputName: "tomorrow.md",
Generated: true,
CompatiblePriorIDs: []ID{DailyToday, DailyTomorrow},
Modules: dailyTomorrowModules(),
Evening: true,
resolve: resolveDailyTomorrow,
},
@@ -41,6 +47,7 @@ func DefaultRegistry() Registry {
BatchOutputName: "three-day.md",
Generated: true,
CompatiblePriorIDs: []ID{ThreeDay},
Modules: threeDayModules(),
Morning: true,
resolve: resolveThreeDay,
},
@@ -53,6 +60,7 @@ func DefaultRegistry() Registry {
BatchOutputName: "weekend.md",
Generated: true,
CompatiblePriorIDs: []ID{Weekend},
Modules: weekendModules(),
Morning: true,
resolve: resolveWeekend,
},
@@ -65,6 +73,7 @@ func DefaultRegistry() Registry {
BatchOutputName: "storm.md",
Generated: true,
CompatiblePriorIDs: []ID{Storm},
Modules: stormModules(),
resolve: resolveStorm,
},
}
@@ -75,6 +84,76 @@ func DefaultRegistry() Registry {
return registry
}
func dailyTodayModules() []module.ConfigItem {
return moduleItems(
module.Metadata,
module.CurrentConditions,
module.DerivedDailySummary,
module.DerivedDaypartSummaries,
module.PrecipTiming,
module.AlertDigest,
module.ForecastDelta,
module.AreaForecastDiscussion,
module.WeatherStory,
module.OutdoorWindows,
)
}
func dailyTomorrowModules() []module.ConfigItem {
items := dailyTodayModules()
items = append(items, module.ConfigItem{ID: module.TomorrowPlanning})
return items
}
func threeDayModules() []module.ConfigItem {
return moduleItems(
module.Metadata,
module.CurrentConditions,
module.DerivedDaypartSummaries,
module.PrecipTiming,
module.AlertDigest,
module.ForecastDelta,
module.AreaForecastDiscussion,
module.WeatherStory,
module.OutdoorWindows,
)
}
func weekendModules() []module.ConfigItem {
return moduleItems(
module.Metadata,
module.CurrentConditions,
module.DerivedDaypartSummaries,
module.PrecipTiming,
module.AlertDigest,
module.AreaForecastDiscussion,
module.WeatherStory,
module.OutdoorWindows,
module.WeekendPlanning,
)
}
func stormModules() []module.ConfigItem {
return moduleItems(
module.Metadata,
module.CurrentConditions,
module.HourlyTable,
module.PrecipTiming,
module.AlertDigest,
module.AreaForecastDiscussion,
module.WeatherStory,
module.StormWindowSummary,
)
}
func moduleItems(ids ...module.ID) []module.ConfigItem {
items := make([]module.ConfigItem, 0, len(ids))
for _, id := range ids {
items = append(items, module.ConfigItem{ID: id})
}
return items
}
func (r Registry) Lookup(id ID) (Definition, error) {
definition, ok := r.definitions[id]
if !ok {