package api import ( "context" "net/http" "net/http/httptest" "strings" "testing" "github.com/ponzischeme89/memby/server/internal/config" "github.com/ponzischeme89/memby/server/internal/library" ) // recordingIngester stands in for the worker. The hook's whole job is to record, so what // it recorded is the only thing worth asserting on here. type recordingIngester struct { sources []string requests []library.IngestRequest } func (r *recordingIngester) Enqueue( _ context.Context, source string, requests []library.IngestRequest, ) (int, error) { r.sources = append(r.sources, source) r.requests = append(r.requests, requests...) return len(requests), nil } func sonarrHookRequest(body string) *http.Request { return httptest.NewRequest( http.MethodPost, "/hooks/sonarr?token=hook-secret", strings.NewReader(body)) } func TestSonarrWebhookIsHiddenUntilATokenIsConfigured(t *testing.T) { s := &Server{cfg: config.Config{}, log: discardLogger()} rec := httptest.NewRecorder() s.handleSonarrWebhook(rec, sonarrHookRequest("{}")) if rec.Code != http.StatusNotFound { t.Fatalf("got %d, want 404 for an unconfigured hook", rec.Code) } } func TestSonarrWebhookRejectsAWrongToken(t *testing.T) { ingester := &recordingIngester{} s := &Server{ cfg: config.Config{SonarrWebhookToken: "hook-secret"}, log: discardLogger(), ingester: ingester, } rec := httptest.NewRecorder() s.handleSonarrWebhook(rec, httptest.NewRequest( http.MethodPost, "/hooks/sonarr?token=guess", strings.NewReader("{}"))) if rec.Code != http.StatusUnauthorized { t.Fatalf("got %d, want 401", rec.Code) } if len(ingester.requests) != 0 { t.Fatal("an unauthorised delivery recorded work") } } func TestSonarrWebhookRecordsAnImport(t *testing.T) { ingester := &recordingIngester{} s := &Server{ cfg: config.Config{SonarrWebhookToken: "hook-secret"}, log: discardLogger(), ingester: ingester, } rec := httptest.NewRecorder() s.handleSonarrWebhook(rec, sonarrHookRequest(`{ "eventType":"Download", "series":{"id":12,"title":"Blue Bloods","year":2010}, "episodes":[{"id":551,"seasonNumber":6,"episodeNumber":7}], "episodeFile":{"id":8123} }`)) if rec.Code != http.StatusOK { t.Fatalf("got %d, want 200", rec.Code) } if len(ingester.requests) != 1 { t.Fatalf("expected one recorded request, got %d", len(ingester.requests)) } request := ingester.requests[0] if request.Kind != library.KindEpisode || request.Episode != 7 || request.Season != 6 { t.Fatalf("unexpected request: %+v", request) } if ingester.sources[0] != "sonarr" { t.Fatalf("unexpected source: %q", ingester.sources[0]) } } // The Test button must answer without recording work about a series that does not exist. func TestSonarrWebhookTestEventRecordsNothing(t *testing.T) { ingester := &recordingIngester{} s := &Server{ cfg: config.Config{SonarrWebhookToken: "hook-secret"}, log: discardLogger(), ingester: ingester, } rec := httptest.NewRecorder() s.handleSonarrWebhook(rec, sonarrHookRequest( `{"eventType":"Test","series":{"id":1,"title":"Test Title"}}`)) if rec.Code != http.StatusOK { t.Fatalf("got %d, want 200", rec.Code) } if len(ingester.requests) != 0 { t.Fatalf("the test event recorded work: %+v", ingester.requests) } } // A Radarr upgrade is silent as news and still a reason to re-read the row. The two // judgements are made in different places and this is what pins them apart. func TestRadarrUpgradeIsRecordedThoughItIsNotAnnounced(t *testing.T) { ingester := &recordingIngester{} s := &Server{ cfg: config.Config{ RadarrWebhookToken: "hook-secret", RadarrAlertWindow: 0, // no announcement is possible }, log: discardLogger(), ingester: ingester, } rec := httptest.NewRecorder() s.handleRadarrWebhook(rec, httptest.NewRequest( http.MethodPost, "/hooks/radarr?token=hook-secret", strings.NewReader(`{ "eventType":"Download","isUpgrade":true, "movie":{"id":44,"title":"Arrival","year":2016}, "movieFile":{"id":441,"quality":"Bluray-1080p"} }`))) if rec.Code != http.StatusOK { t.Fatalf("got %d, want 200", rec.Code) } if len(ingester.requests) != 1 { t.Fatalf("the upgrade was not recorded: %+v", ingester.requests) } if ingester.requests[0].Reason != library.ReasonUpgrade { t.Fatalf("unexpected reason: %q", ingester.requests[0].Reason) } if ingester.requests[0].Action != library.ActionRefresh { t.Fatalf("an upgrade must refresh, got %q", ingester.requests[0].Action) } } // A grab is a real event and not a reason to re-read anything. func TestSonarrGrabRecordsNothing(t *testing.T) { ingester := &recordingIngester{} s := &Server{ cfg: config.Config{SonarrWebhookToken: "hook-secret"}, log: discardLogger(), ingester: ingester, } rec := httptest.NewRecorder() s.handleSonarrWebhook(rec, sonarrHookRequest( `{"eventType":"Grab","series":{"id":12,"title":"Blue Bloods"}}`)) if rec.Code != http.StatusOK || len(ingester.requests) != 0 { t.Fatalf("got %d with %d requests", rec.Code, len(ingester.requests)) } }