package briefing import ( "strings" "testing" "gitea.maximumdirect.net/eric/weatherreporter/internal/module" "gitea.maximumdirect.net/eric/weatherreporter/internal/report" ) func TestDefaultModuleRegistryValidatesReportDefaults(t *testing.T) { registry := MustDefaultModuleRegistry() for _, definition := range report.DefaultRegistry().All() { if err := registry.ValidateComposition(definition.ID, definition.Modules); err != nil { t.Fatalf("ValidateComposition(%s) error = %v", definition.ID, err) } for _, item := range definition.Modules { moduleDefinition, err := registry.Lookup(item.ID) if err != nil { t.Fatalf("Lookup(%s) error = %v", item.ID, err) } if moduleDefinition.Builder == nil { t.Fatalf("report %s module %s has no builder", definition.ID, item.ID) } } } } func TestDefaultReportModulesBuildSnapshots(t *testing.T) { registry := MustDefaultModuleRegistry() for _, definition := range report.DefaultRegistry().All() { t.Run(string(definition.ID), func(t *testing.T) { ctx := derivedModuleContext(definition.ID) var outputs []module.Output for _, item := range definition.Modules { output, err := registry.BuildModule(ctx, item) if err != nil { t.Fatalf("BuildModule(%s) error = %v", item.ID, err) } if output != nil { outputs = append(outputs, *output) } } snapshot, err := module.NewSnapshot(outputs) if err != nil { t.Fatalf("NewSnapshot() error = %v", err) } if len(snapshot.Outputs) == 0 { t.Fatal("snapshot outputs = 0, want default report modules") } }) } } func TestModuleRegistryRejectsUnknownModule(t *testing.T) { registry := MustDefaultModuleRegistry() err := registry.ValidateComposition(report.DailyToday, []module.ConfigItem{{ID: module.ID("unknown")}}) if err == nil || !strings.Contains(err.Error(), `unknown module "unknown"`) { t.Fatalf("error = %v, want unknown module", err) } } func TestModuleRegistryRejectsDuplicateModuleIDs(t *testing.T) { registry := MustDefaultModuleRegistry() err := registry.ValidateComposition(report.DailyToday, []module.ConfigItem{ {ID: module.Metadata}, {ID: module.Metadata}, }) if err == nil || !strings.Contains(err.Error(), `duplicate module "metadata"`) { t.Fatalf("error = %v, want duplicate module", err) } } func TestModuleRegistryRejectsDuplicateStanzaNames(t *testing.T) { _, err := NewModuleRegistry([]ModuleDefinition{ {ID: module.Metadata, StanzaName: "metadata", DefaultOptions: module.MetadataOptions{}, Builder: noopModuleBuilder}, {ID: module.CurrentConditions, StanzaName: "metadata", DefaultOptions: module.CurrentConditionsOptions{}, Builder: noopModuleBuilder}, }) if err == nil || !strings.Contains(err.Error(), `duplicate stanza name "metadata"`) { t.Fatalf("error = %v, want duplicate stanza name", err) } } func TestModuleRegistryRejectsIncompatibleReports(t *testing.T) { registry := MustDefaultModuleRegistry() err := registry.ValidateComposition(report.DailyToday, []module.ConfigItem{{ID: module.TomorrowPlanning}}) if err == nil || !strings.Contains(err.Error(), `module "tomorrow_planning" is not compatible with report "daily_today"`) { t.Fatalf("error = %v, want incompatible report", err) } } func TestModuleRegistryRejectsDefinitionsWithoutBuilders(t *testing.T) { _, err := NewModuleRegistry([]ModuleDefinition{ {ID: module.Metadata, StanzaName: "metadata", DefaultOptions: module.MetadataOptions{}}, }) if err == nil || !strings.Contains(err.Error(), `module "metadata" has no builder`) { t.Fatalf("error = %v, want missing builder", err) } } func TestModuleRegistryRejectsUnsupportedMissingDataWarn(t *testing.T) { _, err := NewModuleRegistry([]ModuleDefinition{ {ID: module.Metadata, StanzaName: "metadata", DefaultOptions: module.MetadataOptions{}, MissingData: module.MissingDataWarn, Builder: noopModuleBuilder}, }) if err == nil || !strings.Contains(err.Error(), `unsupported missing data behavior`) { t.Fatalf("error = %v, want unsupported missing-data behavior", err) } } func TestModuleRegistryRejectsInvalidOptionShapes(t *testing.T) { registry := MustDefaultModuleRegistry() err := registry.ValidateComposition(report.DailyToday, []module.ConfigItem{ {ID: module.Metadata, Options: module.CurrentConditionsOptions{}}, }) if err == nil || !strings.Contains(err.Error(), `module "metadata" options have type module.CurrentConditionsOptions, want module.MetadataOptions`) { t.Fatalf("error = %v, want invalid option shape", err) } } func TestModuleRegistryAcceptsTypedOptions(t *testing.T) { registry := MustDefaultModuleRegistry() err := registry.ValidateComposition(report.DailyToday, []module.ConfigItem{ {ID: module.Metadata, Options: module.MetadataOptions{}}, {ID: module.CurrentConditions, Options: &module.CurrentConditionsOptions{}}, }) if err != nil { t.Fatalf("ValidateComposition() error = %v", err) } } func noopModuleBuilder(ModuleContext, any) (*module.Output, error) { return &module.Output{ID: module.Metadata, StanzaName: "metadata", Value: struct{}{}}, nil }