diff options
Diffstat (limited to 'internal/imgw/imgw_test.go')
| -rw-r--r-- | internal/imgw/imgw_test.go | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/internal/imgw/imgw_test.go b/internal/imgw/imgw_test.go new file mode 100644 index 0000000..ffc678e --- /dev/null +++ b/internal/imgw/imgw_test.go @@ -0,0 +1,190 @@ +package imgw + +import ( + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "strings" + "testing" + "time" +) + +func serve(t *testing.T, fixture string, gotQuery *string) *httptest.Server { + t.Helper() + body, err := os.ReadFile(filepath.Join("testdata", fixture)) + if err != nil { + t.Fatal(err) + } + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if gotQuery != nil { + *gotQuery = r.URL.RawQuery + } + w.Write(body) + })) + t.Cleanup(srv.Close) + return srv +} + +func serveText(t *testing.T, body string) *httptest.Server { + t.Helper() + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(body)) + })) + t.Cleanup(srv.Close) + return srv +} + +func pinClock(t *testing.T, at time.Time) { + t.Helper() + old := now + now = func() time.Time { return at } + t.Cleanup(func() { now = old }) +} + +// A point inside Poland resolves to the first four digits of its TERYT code. +func TestPowiatTruncatesTerytToFourDigits(t *testing.T) { + srv := serve(t, "gugik_krakow.json", nil) + gugikURL = srv.URL + code, status, err := Powiat(50.0617, 19.9373) + if err != nil { + t.Fatal(err) + } + if status != StatusOK { + t.Fatalf("status = %v, want StatusOK", status) + } + if code != "1815" { + t.Fatalf("code = %q, want 1815 (first four digits of 126101)", code) + } +} + +// GUGiK answers a foreign point with HTTP 200 and no results. That is a real +// answer -- "not in Poland" -- and must never be confused with a failure, which +// is the difference between "no warnings apply" and "I do not know". +func TestPowiatOutsidePolandIsAnAnswerNotAnError(t *testing.T) { + srv := serve(t, "gugik_abroad.json", nil) + gugikURL = srv.URL + code, status, err := Powiat(52.52, 13.40) + if err != nil { + t.Fatalf("a valid 'no results' response must not be an error: %v", err) + } + if status != StatusOutside { + t.Fatalf("status = %v, want StatusOutside", status) + } + if code != "" { + t.Fatalf("code = %q, want empty", code) + } +} + +func TestPowiatUnreachableIsStatusError(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.Error(w, "boom", http.StatusBadGateway) + })) + srv.Close() // closed: the request cannot connect at all + gugikURL = srv.URL + _, status, err := Powiat(50, 21) + if status != StatusError { + t.Fatalf("status = %v, want StatusError", status) + } + if err == nil { + t.Fatal("expected an error describing the failure") + } +} + +// The default 100 m radius finds nothing in the mountains, which is +// indistinguishable from being abroad and would suppress real warnings. +func TestPowiatAsksForAWideRadius(t *testing.T) { + var query string + srv := serve(t, "gugik_krakow.json", &query) + gugikURL = srv.URL + if _, _, err := Powiat(50, 21); err != nil { + t.Fatal(err) + } + if !strings.Contains(query, "radius=10000") { + t.Fatalf("query %q must ask for a wide radius", query) + } +} + +func TestWarningsKeepOnlyThisPowiat(t *testing.T) { + pinClock(t, time.Date(2000, 1, 1, 0, 0, 0, 0, time.Local)) // nothing expired + body := `[ + {"nazwa_zdarzenia":"Upal","stopien":"1","obowiazuje_do":"2030-01-01 00:00:00","teryt":["1815","1234"]}, + {"nazwa_zdarzenia":"Burze","stopien":"2","obowiazuje_do":"2030-01-01 00:00:00","teryt":["2207"]} + ]` + srv := serveText(t, body) + warningsURL = srv.URL + + live, err := Warnings("1815") + if err != nil { + t.Fatal(err) + } + if len(live) != 1 || live[0].Event != "Upal" { + t.Fatalf("got %+v, want only the warning covering 1815", live) + } +} + +func TestWarningsDropExpiredButKeepUnparseableDates(t *testing.T) { + pinClock(t, time.Date(2026, 8, 10, 12, 0, 0, 0, time.Local)) + body := `[ + {"nazwa_zdarzenia":"Wczorajsze","stopien":"1","obowiazuje_do":"2026-08-09 23:00:00","teryt":["1815"]}, + {"nazwa_zdarzenia":"Trwajace","stopien":"1","obowiazuje_do":"2026-08-10 20:00:00","teryt":["1815"]}, + {"nazwa_zdarzenia":"Bezdaty","stopien":"1","obowiazuje_do":"","teryt":["1815"]} + ]` + srv := serveText(t, body) + warningsURL = srv.URL + + live, err := Warnings("1815") + if err != nil { + t.Fatal(err) + } + var names []string + for _, w := range live { + names = append(names, w.Event) + } + got := strings.Join(names, ",") + // Unparseable is kept: showing a stale warning beats hiding a live one. + if got != "Trwajace,Bezdaty" { + t.Fatalf("got %q, want \"Trwajace,Bezdaty\"", got) + } +} + +// IMGW has been seen to encode TERYT codes as numbers as well as strings. +func TestWarningsMatchNumericTerytCodes(t *testing.T) { + pinClock(t, time.Date(2000, 1, 1, 0, 0, 0, 0, time.Local)) + srv := serveText(t, `[{"nazwa_zdarzenia":"X","obowiazuje_do":"2030-01-01 00:00:00","teryt":[1815]}]`) + warningsURL = srv.URL + live, err := Warnings("1815") + if err != nil { + t.Fatal(err) + } + if len(live) != 1 { + t.Fatalf("a numeric teryt entry must still match, got %d warnings", len(live)) + } +} + +func TestWarningsUnreachableIsAnError(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) + srv.Close() + warningsURL = srv.URL + if _, err := Warnings("1815"); err == nil { + t.Fatal("expected an error: the caller must be able to say 'could not check'") + } +} + +// The recorded national feed must parse, and must not match a made-up powiat. +func TestWarningsParseTheRecordedFeed(t *testing.T) { + pinClock(t, time.Date(2000, 1, 1, 0, 0, 0, 0, time.Local)) + srv := serve(t, "warnings.json", nil) + warningsURL = srv.URL + + if _, err := Warnings("1815"); err != nil { + t.Fatalf("the recorded feed must parse: %v", err) + } + none, err := Warnings("9999") + if err != nil { + t.Fatal(err) + } + if len(none) != 0 { + t.Fatalf("powiat 9999 does not exist but matched %d warnings", len(none)) + } +} |
