package generatedtext import ( "strings" "testing" "gitea.maximumdirect.net/eric/weatherreporter/internal/report" ) func TestCatalogCompleteForGeneratedTextTemplateReports(t *testing.T) { expected := []struct { reportID report.ID schemaID string templateID string }{ {reportID: report.Daily, schemaID: "daily", templateID: "daily"}, {reportID: report.Today, schemaID: "today", templateID: "today"}, {reportID: report.Tomorrow, schemaID: "tomorrow", templateID: "tomorrow"}, {reportID: report.Hourly, schemaID: "hourly", templateID: "hourly"}, } registry := report.DefaultRegistry() for _, expected := range expected { t.Run(string(expected.reportID), func(t *testing.T) { definition := registry.MustLookup(expected.reportID) if definition.GeneratedTextSchemaID != expected.schemaID || definition.TemplateID != expected.templateID { t.Fatalf("definition = %#v, want schema/template %q/%q", definition, expected.schemaID, expected.templateID) } handler, err := LookupDefinition(definition) if err != nil { t.Fatalf("LookupDefinition() error = %v", err) } if handler.SchemaID() != expected.schemaID { t.Fatalf("SchemaID() = %q, want %q", handler.SchemaID(), expected.schemaID) } if handler.TemplateID() != expected.templateID { t.Fatalf("TemplateID() = %q, want %q", handler.TemplateID(), expected.templateID) } if handler.validate == nil { t.Fatal("validator is nil") } if handler.renderContextBuilder == nil { t.Fatal("render-context builder is nil") } if schema, err := handler.Schema(); err != nil { t.Fatalf("Schema() error = %v", err) } else if len(schema) == 0 { t.Fatal("Schema() returned empty asset") } if template, err := handler.Template(); err != nil { t.Fatalf("Template() error = %v", err) } else if template == "" { t.Fatal("Template() returned empty asset") } }) } } func TestCatalogRejectsKnownPairForAnotherReport(t *testing.T) { pairs := []struct { reportID report.ID schemaID string templateID string }{ {reportID: report.Daily, schemaID: "daily", templateID: "daily"}, {reportID: report.Today, schemaID: "today", templateID: "today"}, {reportID: report.Tomorrow, schemaID: "tomorrow", templateID: "tomorrow"}, {reportID: report.Hourly, schemaID: "hourly", templateID: "hourly"}, } registry := report.DefaultRegistry() for _, definitionPair := range pairs { for _, mismatchedPair := range pairs { if definitionPair.reportID == mismatchedPair.reportID { continue } t.Run(string(definitionPair.reportID)+"/"+string(mismatchedPair.reportID), func(t *testing.T) { definition := registry.MustLookup(definitionPair.reportID) definition.GeneratedTextSchemaID = mismatchedPair.schemaID definition.TemplateID = mismatchedPair.templateID if _, err := LookupDefinition(definition); err == nil || !strings.Contains(err.Error(), "do not belong") { t.Fatalf("LookupDefinition(%#v) error = %v, want wrong-report error", definition, err) } }) } } } func TestCatalogLookupRejectsUnsupportedSchemaAndTemplate(t *testing.T) { tests := []struct { name string definition report.Definition want []string }{ { name: "schema", definition: func() report.Definition { definition := report.DefaultRegistry().MustLookup(report.Hourly) definition.GeneratedTextSchemaID = "missing-schema" return definition }(), want: []string{`generated text schema "missing-schema"`, `report "hourly"`}, }, { name: "template", definition: func() report.Definition { definition := report.DefaultRegistry().MustLookup(report.Hourly) definition.TemplateID = "missing-template" return definition }(), want: []string{`report template "missing-template"`, `report "hourly"`}, }, { name: "combination", definition: func() report.Definition { definition := report.DefaultRegistry().MustLookup(report.Hourly) definition.TemplateID = "tomorrow" return definition }(), want: []string{`generated text schema "hourly"`, `report template "tomorrow"`, `report "hourly"`}, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { _, err := LookupDefinition(test.definition) if err == nil { t.Fatal("LookupDefinition() error = nil, want error") } for _, want := range test.want { if !strings.Contains(err.Error(), want) { t.Fatalf("LookupDefinition() error = %v, want %q", err, want) } } }) } } func TestCatalogLookupSupportsTodayDefinition(t *testing.T) { definition := report.Definition{ ID: report.Today, GeneratedTextSchemaID: "today", TemplateID: "today", } handler, err := LookupDefinition(definition) if err != nil { t.Fatalf("LookupDefinition(today) error = %v", err) } if handler.SchemaID() != "today" || handler.TemplateID() != "today" { t.Fatalf("handler IDs = %q/%q, want today/today", handler.SchemaID(), handler.TemplateID()) } if schema, err := handler.Schema(); err != nil { t.Fatalf("Schema() error = %v", err) } else if len(schema) == 0 { t.Fatal("Schema() returned empty asset") } if template, err := handler.Template(); err != nil { t.Fatalf("Template() error = %v", err) } else if !strings.Contains(template, "# {{ .Report.Title }}") { t.Fatalf("Template() = %q, want Today template source", template) } } func TestCatalogLookupSupportsDailyDefinitionAssets(t *testing.T) { definition := report.Definition{ ID: report.Daily, GeneratedTextSchemaID: "daily", TemplateID: "daily", } handler, err := LookupDefinition(definition) if err != nil { t.Fatalf("LookupDefinition(daily) error = %v", err) } if handler.SchemaID() != "daily" || handler.TemplateID() != "daily" { t.Fatalf("handler IDs = %q/%q, want daily/daily", handler.SchemaID(), handler.TemplateID()) } if schema, err := handler.Schema(); err != nil { t.Fatalf("Schema() error = %v", err) } else if len(schema) == 0 { t.Fatal("Schema() returned empty asset") } if template, err := handler.Template(); err != nil { t.Fatalf("Template() error = %v", err) } else if !strings.Contains(template, "# {{ .Report.Title }}") { t.Fatalf("Template() = %q, want Daily template source", template) } } func TestCatalogValidationDispatchSupportsKnownSchemas(t *testing.T) { hourlyHandler, err := LookupDefinition(report.DefaultRegistry().MustLookup(report.Hourly)) if err != nil { t.Fatalf("LookupDefinition(hourly) error = %v", err) } hourly, normalized, err := hourlyHandler.Validate([]byte(`{ "summary": " Storm chances increase. ", "forecast_discussion": " A front will keep the region unsettled. ", "precipitation_timing": "" }`)) if err != nil { t.Fatalf("Validate(hourly) error = %v", err) } if _, ok := hourly.(Hourly); !ok { t.Fatalf("hourly generated text type = %T, want generatedtext.Hourly", hourly) } if !strings.Contains(string(normalized), `"summary":"Storm chances increase."`) { t.Fatalf("hourly normalized text = %s, want trimmed summary", normalized) } tomorrowHandler, err := LookupDefinition(report.DefaultRegistry().MustLookup(report.Tomorrow)) if err != nil { t.Fatalf("LookupDefinition(tomorrow) error = %v", err) } tomorrow, normalized, err := tomorrowHandler.Validate([]byte(`{ "summary": " Storms become more likely tomorrow. ", "forecast_discussion": [" A front will keep showers in the forecast. ", ""], "precipitation_timing": "" }`)) if err != nil { t.Fatalf("Validate(tomorrow) error = %v", err) } if _, ok := tomorrow.(Tomorrow); !ok { t.Fatalf("tomorrow generated text type = %T, want generatedtext.Tomorrow", tomorrow) } if !strings.Contains(string(normalized), `"forecast_discussion":["A front will keep showers in the forecast."]`) { t.Fatalf("tomorrow normalized text = %s, want trimmed discussion paragraph", normalized) } todayHandler, err := LookupDefinition(report.Definition{ ID: report.Today, GeneratedTextSchemaID: "today", TemplateID: "today", }) if err != nil { t.Fatalf("LookupDefinition(today) error = %v", err) } today, normalized, err := todayHandler.Validate([]byte(`{ "summary": " Showers are likely today. ", "forecast_discussion": [" A front will keep rain chances elevated. ", ""], "precipitation_timing": "" }`)) if err != nil { t.Fatalf("Validate(today) error = %v", err) } if _, ok := today.(Today); !ok { t.Fatalf("today generated text type = %T, want generatedtext.Today", today) } if !strings.Contains(string(normalized), `"forecast_discussion":["A front will keep rain chances elevated."]`) { t.Fatalf("today normalized text = %s, want trimmed discussion paragraph", normalized) } dailyHandler, err := LookupDefinition(report.Definition{ ID: report.Daily, GeneratedTextSchemaID: "daily", TemplateID: "daily", }) if err != nil { t.Fatalf("LookupDefinition(daily) error = %v", err) } daily, normalized, err := dailyHandler.Validate([]byte(`{ "summary": " Showers are possible during the selected day. ", "forecast_discussion": [" A front will keep rain chances in the forecast. ", ""], "precipitation_timing": "" }`)) if err != nil { t.Fatalf("Validate(daily) error = %v", err) } if _, ok := daily.(Daily); !ok { t.Fatalf("daily generated text type = %T, want generatedtext.Daily", daily) } if !strings.Contains(string(normalized), `"forecast_discussion":["A front will keep rain chances in the forecast."]`) { t.Fatalf("daily normalized text = %s, want trimmed discussion paragraph", normalized) } } func TestCatalogBuildRenderContextRejectsMismatchedGeneratedText(t *testing.T) { hourlyHandler, err := LookupDefinition(report.DefaultRegistry().MustLookup(report.Hourly)) if err != nil { t.Fatalf("LookupDefinition(hourly) error = %v", err) } _, err = hourlyHandler.BuildRenderContext(testMetadata(), testSnapshot(t), testDerived(), Tomorrow{ Summary: "Storms become more likely tomorrow.", ForecastDiscussion: []string{"A front will keep showers in the forecast."}, }) if err == nil { t.Fatal("BuildRenderContext(hourly) error = nil, want type mismatch") } if !strings.Contains(err.Error(), `requires hourly generated text`) { t.Fatalf("BuildRenderContext(hourly) error = %v, want hourly generated text requirement", err) } tomorrowHandler, err := LookupDefinition(report.DefaultRegistry().MustLookup(report.Tomorrow)) if err != nil { t.Fatalf("LookupDefinition(tomorrow) error = %v", err) } _, err = tomorrowHandler.BuildRenderContext(testTomorrowMetadata(), testTomorrowSnapshot(t), testTomorrowDerived(), Hourly{ Summary: "Storm chances increase.", ForecastDiscussion: "A front will keep showers in the forecast.", }) if err == nil { t.Fatal("BuildRenderContext(tomorrow) error = nil, want type mismatch") } if !strings.Contains(err.Error(), `requires tomorrow generated text`) { t.Fatalf("BuildRenderContext(tomorrow) error = %v, want tomorrow generated text requirement", err) } todayHandler, err := LookupDefinition(report.Definition{ ID: report.Today, GeneratedTextSchemaID: "today", TemplateID: "today", }) if err != nil { t.Fatalf("LookupDefinition(today) error = %v", err) } _, err = todayHandler.BuildRenderContext(testTodayMetadata(), testTodaySnapshot(t), testTodayDerived(), Tomorrow{ Summary: "Storms become more likely tomorrow.", ForecastDiscussion: []string{"A front will keep showers in the forecast."}, }) if err == nil { t.Fatal("BuildRenderContext(today) error = nil, want type mismatch") } if !strings.Contains(err.Error(), `requires today generated text`) { t.Fatalf("BuildRenderContext(today) error = %v, want today generated text requirement", err) } dailyHandler, err := LookupDefinition(report.Definition{ ID: report.Daily, GeneratedTextSchemaID: "daily", TemplateID: "daily", }) if err != nil { t.Fatalf("LookupDefinition(daily) error = %v", err) } _, err = dailyHandler.BuildRenderContext(testDailyMetadata(), testDailySnapshot(t), testDailyDerived(), Tomorrow{ Summary: "Storms become more likely tomorrow.", ForecastDiscussion: []string{"A front will keep showers in the forecast."}, }) if err == nil { t.Fatal("BuildRenderContext(daily) error = nil, want type mismatch") } if !strings.Contains(err.Error(), `requires daily generated text`) { t.Fatalf("BuildRenderContext(daily) error = %v, want daily generated text requirement", err) } } func TestCatalogBuildRenderContextSupportsDaily(t *testing.T) { handler, err := LookupDefinition(report.Definition{ ID: report.Daily, GeneratedTextSchemaID: "daily", TemplateID: "daily", }) if err != nil { t.Fatalf("LookupDefinition(daily) error = %v", err) } ctx, err := handler.BuildRenderContext(testDailyMetadata(), testDailySnapshot(t), testDailyDerived(), Daily{ Summary: "Showers are possible during the selected day.", ForecastDiscussion: []string{"A front will keep rain chances in the forecast."}, }) if err != nil { t.Fatalf("BuildRenderContext(daily) error = %v", err) } if _, ok := ctx.(DailyRenderContext); !ok { t.Fatalf("BuildRenderContext(daily) = %T, want DailyRenderContext", ctx) } }