All checks were successful
ci/woodpecker/push/build-image Pipeline was successful
139 lines
4.0 KiB
Go
139 lines
4.0 KiB
Go
package nws
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"gitea.maximumdirect.net/ejr/feedkit/config"
|
|
"gitea.maximumdirect.net/ejr/feedkit/event"
|
|
"gitea.maximumdirect.net/ejr/weatherfeeder/standards"
|
|
)
|
|
|
|
func TestForecastDiscussionSourcePollEmitsExpectedEvent(t *testing.T) {
|
|
rawHTML := loadForecastDiscussionSampleHTML(t)
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
|
_, _ = w.Write([]byte(rawHTML))
|
|
}))
|
|
defer srv.Close()
|
|
|
|
src, err := NewForecastDiscussionSource(forecastDiscussionSourceConfig(srv.URL))
|
|
if err != nil {
|
|
t.Fatalf("NewForecastDiscussionSource() error = %v", err)
|
|
}
|
|
if got := src.Kinds(); len(got) != 1 || got[0] != event.Kind("forecast_discussion") {
|
|
t.Fatalf("Kinds() = %#v, want [forecast_discussion]", got)
|
|
}
|
|
|
|
events, err := src.Poll(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("Poll() error = %v", err)
|
|
}
|
|
if len(events) != 1 {
|
|
t.Fatalf("Poll() len = %d, want 1", len(events))
|
|
}
|
|
|
|
got := events[0]
|
|
if got.Kind != event.Kind("forecast_discussion") {
|
|
t.Fatalf("Kind = %q, want forecast_discussion", got.Kind)
|
|
}
|
|
if got.Schema != standards.SchemaRawNWSForecastDiscussionV1 {
|
|
t.Fatalf("Schema = %q, want %q", got.Schema, standards.SchemaRawNWSForecastDiscussionV1)
|
|
}
|
|
wantEffectiveAt := time.Date(2026, 3, 28, 19, 24, 0, 0, time.UTC)
|
|
if got.EffectiveAt == nil || !got.EffectiveAt.Equal(wantEffectiveAt) {
|
|
t.Fatalf("EffectiveAt = %v, want %s", got.EffectiveAt, wantEffectiveAt.Format(time.RFC3339))
|
|
}
|
|
payload, ok := got.Payload.(string)
|
|
if !ok {
|
|
t.Fatalf("Payload type = %T, want string", got.Payload)
|
|
}
|
|
if payload != rawHTML {
|
|
t.Fatalf("Payload did not preserve exact HTML")
|
|
}
|
|
}
|
|
|
|
func TestForecastDiscussionSourcePollReturnsNoEventsWhenUnchanged(t *testing.T) {
|
|
rawHTML := loadForecastDiscussionSampleHTML(t)
|
|
const etag = `"discussion-v1"`
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Header.Get("If-None-Match") == etag {
|
|
w.WriteHeader(http.StatusNotModified)
|
|
return
|
|
}
|
|
w.Header().Set("ETag", etag)
|
|
_, _ = w.Write([]byte(rawHTML))
|
|
}))
|
|
defer srv.Close()
|
|
|
|
src, err := NewForecastDiscussionSource(forecastDiscussionSourceConfig(srv.URL))
|
|
if err != nil {
|
|
t.Fatalf("NewForecastDiscussionSource() error = %v", err)
|
|
}
|
|
|
|
first, err := src.Poll(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("first Poll() error = %v", err)
|
|
}
|
|
if len(first) != 1 {
|
|
t.Fatalf("first Poll() len = %d, want 1", len(first))
|
|
}
|
|
|
|
second, err := src.Poll(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("second Poll() error = %v", err)
|
|
}
|
|
if len(second) != 0 {
|
|
t.Fatalf("second Poll() len = %d, want 0", len(second))
|
|
}
|
|
}
|
|
|
|
func TestForecastDiscussionSourcePollRejectsInvalidHTML(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
_, _ = w.Write([]byte("<html><body><div>missing discussion block</div></body></html>"))
|
|
}))
|
|
defer srv.Close()
|
|
|
|
src, err := NewForecastDiscussionSource(forecastDiscussionSourceConfig(srv.URL))
|
|
if err != nil {
|
|
t.Fatalf("NewForecastDiscussionSource() error = %v", err)
|
|
}
|
|
|
|
_, err = src.Poll(context.Background())
|
|
if err == nil {
|
|
t.Fatalf("Poll() error = nil, want error")
|
|
}
|
|
if !strings.Contains(err.Error(), "glossaryProduct") {
|
|
t.Fatalf("error = %q, want glossaryProduct context", err)
|
|
}
|
|
}
|
|
|
|
func forecastDiscussionSourceConfig(url string) config.SourceConfig {
|
|
return config.SourceConfig{
|
|
Name: "test-forecast-discussion-source",
|
|
Driver: "nws_forecast_discussion",
|
|
Mode: config.SourceModePoll,
|
|
Params: map[string]any{
|
|
"url": url,
|
|
"user_agent": "test-agent",
|
|
},
|
|
}
|
|
}
|
|
|
|
func loadForecastDiscussionSampleHTML(t *testing.T) string {
|
|
t.Helper()
|
|
|
|
path := filepath.Join("..", "..", "providers", "nws", "testdata", "forecast_discussion_sample.html")
|
|
b, err := os.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatalf("os.ReadFile(%q) error = %v", path, err)
|
|
}
|
|
return string(b)
|
|
}
|