Files
memby/server/internal/api/arr_hooks_test.go
2026-08-18 14:59:29 +12:00

169 lines
5.0 KiB
Go

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))
}
}