Adds internal/arr with a shared HTTP layer and per-service clients that look up candidates and POST the chosen one back with monitored=true and search-on-add enabled. Lidarr requires metadata_profile_id, now part of ArrConfig and validated when lidarr is set.
97 lines
3.0 KiB
Go
97 lines
3.0 KiB
Go
package arr
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"bellhop/internal/config"
|
|
)
|
|
|
|
func TestRadarrSearch(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if got := r.Header.Get("X-Api-Key"); got != "secret" {
|
|
t.Errorf("api key header = %q, want secret", got)
|
|
}
|
|
if r.URL.Path != "/api/v3/movie/lookup" {
|
|
t.Errorf("path = %q", r.URL.Path)
|
|
}
|
|
if r.URL.Query().Get("term") != "dune" {
|
|
t.Errorf("term = %q", r.URL.Query().Get("term"))
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, _ = io.WriteString(w, `[
|
|
{"title":"Dune","year":2021,"tmdbId":438631,"titleSlug":"dune-2021"},
|
|
{"title":"Dune: Part Two","year":2024,"tmdbId":693134,"titleSlug":"dune-part-two-2024"}
|
|
]`)
|
|
}))
|
|
defer srv.Close()
|
|
|
|
r := NewRadarr(&config.ArrConfig{URL: srv.URL, APIKey: "secret", QualityProfileID: 6, RootFolder: "/movies"})
|
|
results, err := r.Search(context.Background(), "dune")
|
|
if err != nil {
|
|
t.Fatalf("search: %v", err)
|
|
}
|
|
if len(results) != 2 {
|
|
t.Fatalf("got %d results, want 2", len(results))
|
|
}
|
|
if results[0].Title != "Dune" || results[0].Year != 2021 {
|
|
t.Errorf("first result = %+v", results[0])
|
|
}
|
|
}
|
|
|
|
func TestRadarrAdd(t *testing.T) {
|
|
var got map[string]any
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost || r.URL.Path != "/api/v3/movie" {
|
|
t.Errorf("unexpected %s %s", r.Method, r.URL.Path)
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&got); err != nil {
|
|
t.Fatalf("decode body: %v", err)
|
|
}
|
|
w.WriteHeader(http.StatusCreated)
|
|
_, _ = io.WriteString(w, `{"id":42}`)
|
|
}))
|
|
defer srv.Close()
|
|
|
|
r := NewRadarr(&config.ArrConfig{URL: srv.URL, APIKey: "k", QualityProfileID: 6, RootFolder: "/movies"})
|
|
res := makeResult("Dune", 2021, `{"title":"Dune","year":2021,"tmdbId":438631,"titleSlug":"dune-2021"}`)
|
|
if err := r.Add(context.Background(), res); err != nil {
|
|
t.Fatalf("add: %v", err)
|
|
}
|
|
|
|
if got["qualityProfileId"].(float64) != 6 {
|
|
t.Errorf("qualityProfileId = %v", got["qualityProfileId"])
|
|
}
|
|
if got["rootFolderPath"] != "/movies" {
|
|
t.Errorf("rootFolderPath = %v", got["rootFolderPath"])
|
|
}
|
|
if got["monitored"] != true {
|
|
t.Errorf("monitored = %v", got["monitored"])
|
|
}
|
|
if got["tmdbId"].(float64) != 438631 {
|
|
t.Errorf("tmdbId not preserved from lookup: %v", got["tmdbId"])
|
|
}
|
|
opts := got["addOptions"].(map[string]any)
|
|
if opts["searchForMovie"] != true {
|
|
t.Errorf("addOptions.searchForMovie = %v", opts["searchForMovie"])
|
|
}
|
|
}
|
|
|
|
func TestRadarrAddError(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
_, _ = io.WriteString(w, `{"error":"already exists"}`)
|
|
}))
|
|
defer srv.Close()
|
|
|
|
r := NewRadarr(&config.ArrConfig{URL: srv.URL, APIKey: "k", QualityProfileID: 1, RootFolder: "/m"})
|
|
err := r.Add(context.Background(), makeResult("X", 0, `{"title":"X"}`))
|
|
if err == nil {
|
|
t.Fatal("expected error, got nil")
|
|
}
|
|
}
|