From 756b5db1dff37d1bca58e56dbc2a1a37504f50de Mon Sep 17 00:00:00 2001 From: Fuochi Date: Fri, 23 Sep 2022 11:33:07 +0400 Subject: [PATCH] feat(sonarr): download client (#63) * feat(sonarr): add downloadclientconfig * feat(sonarr): add downloadclient * feat(sonarr): add remotepathmapping * fix: align comments fix typos and path joining * style: forgot a path join * fix: int64 type and few more typo * fix: wrong assertion in test --- lidarr/tag_test.go | 12 +- prowlarr/tag_test.go | 12 +- radarr/tag_test.go | 12 +- readarr/tag_test.go | 12 +- sonarr/delayprofile.go | 8 +- sonarr/downloadclient.go | 143 ++++++++ sonarr/downloadclient_test.go | 506 ++++++++++++++++++++++++++++ sonarr/downloadclientconfig.go | 63 ++++ sonarr/downloadclientconfig_test.go | 119 +++++++ sonarr/indexer.go | 8 +- sonarr/indexer_test.go | 18 +- sonarr/indexerconfig_test.go | 8 +- sonarr/languageprofile.go | 10 +- sonarr/mediamanagement_test.go | 8 +- sonarr/naming_test.go | 8 +- sonarr/remotepathmapping.go | 119 +++++++ sonarr/remotepathmapping_test.go | 253 ++++++++++++++ sonarr/rootfolder.go | 8 +- sonarr/rootfolder_test.go | 20 +- sonarr/series_test.go | 4 +- sonarr/tag_test.go | 12 +- 21 files changed, 1283 insertions(+), 80 deletions(-) create mode 100644 sonarr/downloadclient.go create mode 100644 sonarr/downloadclient_test.go create mode 100644 sonarr/downloadclientconfig.go create mode 100644 sonarr/downloadclientconfig_test.go create mode 100644 sonarr/remotepathmapping.go create mode 100644 sonarr/remotepathmapping_test.go diff --git a/lidarr/tag_test.go b/lidarr/tag_test.go index 4a1639c..9069e88 100644 --- a/lidarr/tag_test.go +++ b/lidarr/tag_test.go @@ -61,7 +61,7 @@ func TestGetTag(t *testing.T) { tests := []*starr.TestMockData{ { Name: "200", - ExpectedPath: path.Join("/", starr.API, lidarr.APIver, "tag/1"), + ExpectedPath: path.Join("/", starr.API, lidarr.APIver, "tag", "1"), ExpectedMethod: "GET", ResponseStatus: 200, WithRequest: 1, @@ -74,7 +74,7 @@ func TestGetTag(t *testing.T) { }, { Name: "404", - ExpectedPath: path.Join("/", starr.API, lidarr.APIver, "tag/1"), + ExpectedPath: path.Join("/", starr.API, lidarr.APIver, "tag", "1"), ExpectedMethod: "GET", ResponseStatus: 404, WithRequest: 1, @@ -151,7 +151,7 @@ func TestUpdateTag(t *testing.T) { tests := []*starr.TestMockData{ { Name: "200", - ExpectedPath: path.Join("/", starr.API, lidarr.APIver, "tag/1"), + ExpectedPath: path.Join("/", starr.API, lidarr.APIver, "tag", "1"), ExpectedMethod: "PUT", ResponseStatus: 200, WithRequest: &starr.Tag{ @@ -168,7 +168,7 @@ func TestUpdateTag(t *testing.T) { }, { Name: "404", - ExpectedPath: path.Join("/", starr.API, lidarr.APIver, "tag/1"), + ExpectedPath: path.Join("/", starr.API, lidarr.APIver, "tag", "1"), ExpectedMethod: "PUT", ResponseStatus: 404, WithRequest: &starr.Tag{ @@ -201,7 +201,7 @@ func TestDeleteTag(t *testing.T) { tests := []*starr.TestMockData{ { Name: "200", - ExpectedPath: path.Join("/", starr.API, lidarr.APIver, "tag/1"), + ExpectedPath: path.Join("/", starr.API, lidarr.APIver, "tag", "1"), ExpectedMethod: "DELETE", WithRequest: 1, ResponseStatus: 200, @@ -210,7 +210,7 @@ func TestDeleteTag(t *testing.T) { }, { Name: "404", - ExpectedPath: path.Join("/", starr.API, lidarr.APIver, "tag/1"), + ExpectedPath: path.Join("/", starr.API, lidarr.APIver, "tag", "1"), ExpectedMethod: "DELETE", WithRequest: 1, ResponseStatus: 404, diff --git a/prowlarr/tag_test.go b/prowlarr/tag_test.go index d229aa2..3c02900 100644 --- a/prowlarr/tag_test.go +++ b/prowlarr/tag_test.go @@ -61,7 +61,7 @@ func TestGetTag(t *testing.T) { tests := []*starr.TestMockData{ { Name: "200", - ExpectedPath: path.Join("/", starr.API, prowlarr.APIver, "tag/1"), + ExpectedPath: path.Join("/", starr.API, prowlarr.APIver, "tag", "1"), ExpectedMethod: "GET", ResponseStatus: 200, WithRequest: 1, @@ -74,7 +74,7 @@ func TestGetTag(t *testing.T) { }, { Name: "404", - ExpectedPath: path.Join("/", starr.API, prowlarr.APIver, "tag/1"), + ExpectedPath: path.Join("/", starr.API, prowlarr.APIver, "tag", "1"), ExpectedMethod: "GET", ResponseStatus: 404, WithRequest: 1, @@ -151,7 +151,7 @@ func TestUpdateTag(t *testing.T) { tests := []*starr.TestMockData{ { Name: "200", - ExpectedPath: path.Join("/", starr.API, prowlarr.APIver, "tag/1"), + ExpectedPath: path.Join("/", starr.API, prowlarr.APIver, "tag", "1"), ExpectedMethod: "PUT", ResponseStatus: 200, WithRequest: &starr.Tag{ @@ -168,7 +168,7 @@ func TestUpdateTag(t *testing.T) { }, { Name: "404", - ExpectedPath: path.Join("/", starr.API, prowlarr.APIver, "tag/1"), + ExpectedPath: path.Join("/", starr.API, prowlarr.APIver, "tag", "1"), ExpectedMethod: "PUT", ResponseStatus: 404, WithRequest: &starr.Tag{ @@ -201,7 +201,7 @@ func TestDeleteTag(t *testing.T) { tests := []*starr.TestMockData{ { Name: "200", - ExpectedPath: path.Join("/", starr.API, prowlarr.APIver, "tag/1"), + ExpectedPath: path.Join("/", starr.API, prowlarr.APIver, "tag", "1"), ExpectedMethod: "DELETE", WithRequest: 1, ResponseStatus: 200, @@ -210,7 +210,7 @@ func TestDeleteTag(t *testing.T) { }, { Name: "404", - ExpectedPath: path.Join("/", starr.API, prowlarr.APIver, "tag/1"), + ExpectedPath: path.Join("/", starr.API, prowlarr.APIver, "tag", "1"), ExpectedMethod: "DELETE", WithRequest: 1, ResponseStatus: 404, diff --git a/radarr/tag_test.go b/radarr/tag_test.go index 430a3fb..59519d0 100644 --- a/radarr/tag_test.go +++ b/radarr/tag_test.go @@ -61,7 +61,7 @@ func TestGetTag(t *testing.T) { tests := []*starr.TestMockData{ { Name: "200", - ExpectedPath: path.Join("/", starr.API, radarr.APIver, "tag/1"), + ExpectedPath: path.Join("/", starr.API, radarr.APIver, "tag", "1"), ExpectedMethod: "GET", ResponseStatus: 200, WithRequest: 1, @@ -74,7 +74,7 @@ func TestGetTag(t *testing.T) { }, { Name: "404", - ExpectedPath: path.Join("/", starr.API, radarr.APIver, "tag/1"), + ExpectedPath: path.Join("/", starr.API, radarr.APIver, "tag", "1"), ExpectedMethod: "GET", ResponseStatus: 404, WithRequest: 1, @@ -151,7 +151,7 @@ func TestUpdateTag(t *testing.T) { tests := []*starr.TestMockData{ { Name: "200", - ExpectedPath: path.Join("/", starr.API, radarr.APIver, "tag/1"), + ExpectedPath: path.Join("/", starr.API, radarr.APIver, "tag", "1"), ExpectedMethod: "PUT", ResponseStatus: 200, WithRequest: &starr.Tag{ @@ -168,7 +168,7 @@ func TestUpdateTag(t *testing.T) { }, { Name: "404", - ExpectedPath: path.Join("/", starr.API, radarr.APIver, "tag/1"), + ExpectedPath: path.Join("/", starr.API, radarr.APIver, "tag", "1"), ExpectedMethod: "PUT", ResponseStatus: 404, WithRequest: &starr.Tag{ @@ -201,7 +201,7 @@ func TestDeleteTag(t *testing.T) { tests := []*starr.TestMockData{ { Name: "200", - ExpectedPath: path.Join("/", starr.API, radarr.APIver, "tag/1"), + ExpectedPath: path.Join("/", starr.API, radarr.APIver, "tag", "1"), ExpectedMethod: "DELETE", WithRequest: 1, ResponseStatus: 200, @@ -210,7 +210,7 @@ func TestDeleteTag(t *testing.T) { }, { Name: "404", - ExpectedPath: path.Join("/", starr.API, radarr.APIver, "tag/1"), + ExpectedPath: path.Join("/", starr.API, radarr.APIver, "tag", "1"), ExpectedMethod: "DELETE", WithRequest: 1, ResponseStatus: 404, diff --git a/readarr/tag_test.go b/readarr/tag_test.go index 8042604..0160c6d 100644 --- a/readarr/tag_test.go +++ b/readarr/tag_test.go @@ -61,7 +61,7 @@ func TestGetTag(t *testing.T) { tests := []*starr.TestMockData{ { Name: "200", - ExpectedPath: path.Join("/", starr.API, readarr.APIver, "tag/1"), + ExpectedPath: path.Join("/", starr.API, readarr.APIver, "tag", "1"), ExpectedMethod: "GET", ResponseStatus: 200, WithRequest: 1, @@ -74,7 +74,7 @@ func TestGetTag(t *testing.T) { }, { Name: "404", - ExpectedPath: path.Join("/", starr.API, readarr.APIver, "tag/1"), + ExpectedPath: path.Join("/", starr.API, readarr.APIver, "tag", "1"), ExpectedMethod: "GET", ResponseStatus: 404, WithRequest: 1, @@ -151,7 +151,7 @@ func TestUpdateTag(t *testing.T) { tests := []*starr.TestMockData{ { Name: "200", - ExpectedPath: path.Join("/", starr.API, readarr.APIver, "tag/1"), + ExpectedPath: path.Join("/", starr.API, readarr.APIver, "tag", "1"), ExpectedMethod: "PUT", ResponseStatus: 200, WithRequest: &starr.Tag{ @@ -168,7 +168,7 @@ func TestUpdateTag(t *testing.T) { }, { Name: "404", - ExpectedPath: path.Join("/", starr.API, readarr.APIver, "tag/1"), + ExpectedPath: path.Join("/", starr.API, readarr.APIver, "tag", "1"), ExpectedMethod: "PUT", ResponseStatus: 404, WithRequest: &starr.Tag{ @@ -201,7 +201,7 @@ func TestDeleteTag(t *testing.T) { tests := []*starr.TestMockData{ { Name: "200", - ExpectedPath: path.Join("/", starr.API, readarr.APIver, "tag/1"), + ExpectedPath: path.Join("/", starr.API, readarr.APIver, "tag", "1"), ExpectedMethod: "DELETE", WithRequest: 1, ResponseStatus: 200, @@ -210,7 +210,7 @@ func TestDeleteTag(t *testing.T) { }, { Name: "404", - ExpectedPath: path.Join("/", starr.API, readarr.APIver, "tag/1"), + ExpectedPath: path.Join("/", starr.API, readarr.APIver, "tag", "1"), ExpectedMethod: "DELETE", WithRequest: 1, ResponseStatus: 404, diff --git a/sonarr/delayprofile.go b/sonarr/delayprofile.go index 552f003..bf66128 100644 --- a/sonarr/delayprofile.go +++ b/sonarr/delayprofile.go @@ -44,12 +44,12 @@ func (s *Sonarr) GetDelayProfilesContext(ctx context.Context) ([]*DelayProfile, } // GetDelayProfile returns a single delay profile. -func (s *Sonarr) GetDelayProfile(profileID int) (*DelayProfile, error) { +func (s *Sonarr) GetDelayProfile(profileID int64) (*DelayProfile, error) { return s.GetDelayProfileContext(context.Background(), profileID) } // GetDelayProfileContext returns a single delay profile. -func (s *Sonarr) GetDelayProfileContext(ctx context.Context, profileID int) (*DelayProfile, error) { +func (s *Sonarr) GetDelayProfileContext(ctx context.Context, profileID int64) (*DelayProfile, error) { var output DelayProfile req := starr.Request{URI: path.Join(bpDelayProfile, fmt.Sprint(profileID))} @@ -105,12 +105,12 @@ func (s *Sonarr) UpdateDelayProfileContext(ctx context.Context, profile *DelayPr } // DeleteDelayProfile removes a single delay profile. -func (s *Sonarr) DeleteDelayProfile(profileID int) error { +func (s *Sonarr) DeleteDelayProfile(profileID int64) error { return s.DeleteDelayProfileContext(context.Background(), profileID) } // DeleteDelayProfileContext removes a single delay profile. -func (s *Sonarr) DeleteDelayProfileContext(ctx context.Context, profileID int) error { +func (s *Sonarr) DeleteDelayProfileContext(ctx context.Context, profileID int64) error { req := starr.Request{URI: path.Join(bpDelayProfile, fmt.Sprint(profileID))} if err := s.DeleteAny(ctx, req); err != nil { return fmt.Errorf("api.Delete(%s): %w", &req, err) diff --git a/sonarr/downloadclient.go b/sonarr/downloadclient.go new file mode 100644 index 0000000..391c0a9 --- /dev/null +++ b/sonarr/downloadclient.go @@ -0,0 +1,143 @@ +package sonarr + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "path" + + "golift.io/starr" +) + +// Define Base Path for download client calls. +const bpDownloadClient = APIver + "/downloadClient" + +// DownloadClientInput is the input for a new or updated download client. +type DownloadClientInput struct { + Enable bool `json:"enable"` + RemoveCompletedDownloads bool `json:"removeCompletedDownloads"` + RemoveFailedDownloads bool `json:"removeFailedDownloads"` + Priority int `json:"priority"` + ID int64 `json:"id,omitempty"` + ConfigContract string `json:"configContract"` + Implementation string `json:"implementation"` + Name string `json:"name"` + Protocol string `json:"protocol"` + Tags []int `json:"tags"` + Fields []*starr.FieldInput `json:"fields"` +} + +// DownloadClientOutput is the output from the download client methods. +type DownloadClientOutput struct { + Enable bool `json:"enable"` + RemoveCompletedDownloads bool `json:"removeCompletedDownloads"` + RemoveFailedDownloads bool `json:"removeFailedDownloads"` + Priority int `json:"priority"` + ID int64 `json:"id,omitempty"` + ConfigContract string `json:"configContract"` + Implementation string `json:"implementation"` + ImplementationName string `json:"implementationName"` + InfoLink string `json:"infoLink"` + Name string `json:"name"` + Protocol string `json:"protocol"` + Tags []int `json:"tags"` + Fields []*starr.FieldOutput `json:"fields"` +} + +// GetDownloadClients returns all configured download clients. +func (s *Sonarr) GetDownloadClients() ([]*DownloadClientOutput, error) { + return s.GetDownloadClientsContext(context.Background()) +} + +// GetDownloadClientsContext returns all configured download clients. +func (s *Sonarr) GetDownloadClientsContext(ctx context.Context) ([]*DownloadClientOutput, error) { + var output []*DownloadClientOutput + + req := starr.Request{URI: bpDownloadClient} + if err := s.GetInto(ctx, req, &output); err != nil { + return nil, fmt.Errorf("api.Get(%s): %w", &req, err) + } + + return output, nil +} + +// GetDownloadClient returns a single download client. +func (s *Sonarr) GetDownloadClient(downloadclientID int64) (*DownloadClientOutput, error) { + return s.GetDownloadClientContext(context.Background(), downloadclientID) +} + +// GetDownloadClientContext returns a single download client. +func (s *Sonarr) GetDownloadClientContext(ctx context.Context, downloadclientID int64) (*DownloadClientOutput, error) { + var output DownloadClientOutput + + req := starr.Request{URI: path.Join(bpDownloadClient, fmt.Sprint(downloadclientID))} + if err := s.GetInto(ctx, req, &output); err != nil { + return nil, fmt.Errorf("api.Get(%s): %w", &req, err) + } + + return &output, nil +} + +// AddDownloadClient creates a download client. +func (s *Sonarr) AddDownloadClient(downloadclient *DownloadClientInput) (*DownloadClientOutput, error) { + return s.AddDownloadClientContext(context.Background(), downloadclient) +} + +// AddDownloadClientContext creates a download client. +func (s *Sonarr) AddDownloadClientContext(ctx context.Context, + client *DownloadClientInput, +) (*DownloadClientOutput, error) { + var output DownloadClientOutput + + var body bytes.Buffer + if err := json.NewEncoder(&body).Encode(client); err != nil { + return nil, fmt.Errorf("json.Marshal(%s): %w", bpDownloadClient, err) + } + + req := starr.Request{URI: bpDownloadClient, Body: &body} + if err := s.PostInto(ctx, req, &output); err != nil { + return nil, fmt.Errorf("api.Post(%s): %w", &req, err) + } + + return &output, nil +} + +// UpdateDownloadClient updates the download client. +func (s *Sonarr) UpdateDownloadClient(downloadclient *DownloadClientInput) (*DownloadClientOutput, error) { + return s.UpdateDownloadClientContext(context.Background(), downloadclient) +} + +// UpdateDownloadClientContext updates the download client. +func (s *Sonarr) UpdateDownloadClientContext(ctx context.Context, + client *DownloadClientInput, +) (*DownloadClientOutput, error) { + var output DownloadClientOutput + + var body bytes.Buffer + if err := json.NewEncoder(&body).Encode(client); err != nil { + return nil, fmt.Errorf("json.Marshal(%s): %w", bpDownloadClient, err) + } + + req := starr.Request{URI: path.Join(bpDownloadClient, fmt.Sprint(client.ID)), Body: &body} + if err := s.PutInto(ctx, req, &output); err != nil { + return nil, fmt.Errorf("api.Put(%s): %w", &req, err) + } + + return &output, nil +} + +// DeleteDownloadClient removes a single download client. +func (s *Sonarr) DeleteDownloadClient(downloadclientID int64) error { + return s.DeleteDownloadClientContext(context.Background(), downloadclientID) +} + +// DeleteDownloadClientContext removes a single download client. +func (s *Sonarr) DeleteDownloadClientContext(ctx context.Context, downloadclientID int64) error { + req := starr.Request{URI: path.Join(bpDownloadClient, fmt.Sprint(downloadclientID))} + if err := s.DeleteAny(ctx, req); err != nil { + return fmt.Errorf("api.Delete(%s): %w", &req, err) + } + + return nil +} diff --git a/sonarr/downloadclient_test.go b/sonarr/downloadclient_test.go new file mode 100644 index 0000000..8397024 --- /dev/null +++ b/sonarr/downloadclient_test.go @@ -0,0 +1,506 @@ +package sonarr_test + +import ( + "path" + "testing" + + "github.com/stretchr/testify/assert" + "golift.io/starr" + "golift.io/starr/sonarr" +) + +const downloadClientResponseBody = `{ + "enable": true, + "protocol": "torrent", + "priority": 1, + "removeCompletedDownloads": false, + "removeFailedDownloads": false, + "name": "Transmission", + "fields": [ + { + "order": 0, + "name": "host", + "label": "Host", + "value": "transmission", + "type": "textbox", + "advanced": false + }, + { + "order": 1, + "name": "port", + "label": "Port", + "value": 9091, + "type": "textbox", + "advanced": false + }, + { + "order": 2, + "name": "useSsl", + "label": "Use SSL", + "helpText": "Use secure connection when connecting to Transmission", + "value": false, + "type": "checkbox", + "advanced": false + } + ], + "implementationName": "Transmission", + "implementation": "Transmission", + "configContract": "TransmissionSettings", + "infoLink": "https://wiki.servarr.com/sonarr/supported#transmission", + "tags": [], + "id": 3 +}` + +const addDownloadClient = `{"enable":true,"removeCompletedDownloads":false,"removeFailedDownloads":false,` + + `"priority":1,"configContract":"TransmissionSettings","implementation":"Transmission","name":"Transmission",` + + `"protocol":"torrent","tags":null,"fields":[{"name":"host","value":"transmission"},` + + `{"name":"port","value":9091},{"name":"useSSL","value":false}]}` + +const updateDownloadClient = `{"enable":true,"removeCompletedDownloads":false,"removeFailedDownloads":false,` + + `"priority":1,"id":3,"configContract":"TransmissionSettings","implementation":"Transmission","name":"Transmission",` + + `"protocol":"torrent","tags":null,"fields":[{"name":"host","value":"transmission"},` + + `{"name":"port","value":9091},{"name":"useSSL","value":false}]}` + +func TestGetDownloadClients(t *testing.T) { + t.Parallel() + + tests := []*starr.TestMockData{ + { + Name: "200", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "downloadClient"), + ExpectedRequest: "", + ExpectedMethod: "GET", + ResponseStatus: 200, + ResponseBody: "[" + downloadClientResponseBody + "]", + WithRequest: nil, + WithResponse: []*sonarr.DownloadClientOutput{ + { + Enable: true, + Priority: 1, + ID: 3, + ConfigContract: "TransmissionSettings", + Implementation: "Transmission", + ImplementationName: "Transmission", + InfoLink: "https://wiki.servarr.com/sonarr/supported#transmission", + Name: "Transmission", + Protocol: "torrent", + Fields: []*starr.FieldOutput{ + { + Order: 0, + Name: "host", + Label: "Host", + Value: "transmission", + Type: "textbox", + Advanced: false, + }, + { + Order: 1, + Name: "port", + Label: "Port", + Value: float64(9091), + Type: "textbox", + Advanced: false, + }, + { + Order: 2, + Name: "useSsl", + Label: "Use SSL", + HelpText: "Use secure connection when connecting to Transmission", + Value: false, + Type: "checkbox", + Advanced: false, + }, + }, + Tags: []int{}, + }, + }, + WithError: nil, + }, + { + Name: "404", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "downloadClient"), + ExpectedMethod: "GET", + ResponseStatus: 404, + ResponseBody: `{"message": "NotFound"}`, + WithError: starr.ErrInvalidStatusCode, + WithResponse: ([]*sonarr.DownloadClientOutput)(nil), + }, + } + + for _, test := range tests { + test := test + t.Run(test.Name, func(t *testing.T) { + t.Parallel() + mockServer := test.GetMockServer(t) + client := sonarr.New(starr.New("mockAPIkey", mockServer.URL, 0)) + output, err := client.GetDownloadClients() + assert.ErrorIs(t, err, test.WithError, "error is not the same as expected") + assert.EqualValues(t, test.WithResponse, output, "response is not the same as expected") + }) + } +} + +func TestGetDownloadClient(t *testing.T) { + t.Parallel() + + tests := []*starr.TestMockData{ + { + Name: "200", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "downloadClient", "1"), + ExpectedRequest: "", + ExpectedMethod: "GET", + ResponseStatus: 200, + ResponseBody: downloadClientResponseBody, + WithRequest: nil, + WithResponse: &sonarr.DownloadClientOutput{ + Enable: true, + Priority: 1, + ID: 3, + ConfigContract: "TransmissionSettings", + Implementation: "Transmission", + ImplementationName: "Transmission", + InfoLink: "https://wiki.servarr.com/sonarr/supported#transmission", + Name: "Transmission", + Protocol: "torrent", + Fields: []*starr.FieldOutput{ + { + Order: 0, + Name: "host", + Label: "Host", + Value: "transmission", + Type: "textbox", + Advanced: false, + }, + { + Order: 1, + Name: "port", + Label: "Port", + Value: float64(9091), + Type: "textbox", + Advanced: false, + }, + { + Order: 2, + Name: "useSsl", + Label: "Use SSL", + HelpText: "Use secure connection when connecting to Transmission", + Value: false, + Type: "checkbox", + Advanced: false, + }, + }, + Tags: []int{}, + }, + WithError: nil, + }, + { + Name: "404", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "downloadClient", "1"), + ExpectedMethod: "GET", + ResponseStatus: 404, + ResponseBody: `{"message": "NotFound"}`, + WithError: starr.ErrInvalidStatusCode, + WithResponse: (*sonarr.DownloadClientOutput)(nil), + }, + } + + for _, test := range tests { + test := test + t.Run(test.Name, func(t *testing.T) { + t.Parallel() + mockServer := test.GetMockServer(t) + client := sonarr.New(starr.New("mockAPIkey", mockServer.URL, 0)) + output, err := client.GetDownloadClient(1) + assert.ErrorIs(t, err, test.WithError, "error is not the same as expected") + assert.EqualValues(t, test.WithResponse, output, "response is not the same as expected") + }) + } +} + +func TestAddDownloadClient(t *testing.T) { + t.Parallel() + + tests := []*starr.TestMockData{ + { + Name: "200", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "downloadClient"), + ExpectedMethod: "POST", + ResponseStatus: 200, + WithRequest: &sonarr.DownloadClientInput{ + Enable: true, + RemoveCompletedDownloads: false, + RemoveFailedDownloads: false, + Priority: 1, + ConfigContract: "TransmissionSettings", + Implementation: "Transmission", + Name: "Transmission", + Protocol: "torrent", + Fields: []*starr.FieldInput{ + { + Name: "host", + Value: "transmission", + }, + { + Name: "port", + Value: 9091, + }, + { + Name: "useSSL", + Value: false, + }, + }, + }, + ExpectedRequest: addDownloadClient + "\n", + ResponseBody: downloadClientResponseBody, + WithResponse: &sonarr.DownloadClientOutput{ + Enable: true, + Priority: 1, + ID: 3, + ConfigContract: "TransmissionSettings", + Implementation: "Transmission", + ImplementationName: "Transmission", + InfoLink: "https://wiki.servarr.com/sonarr/supported#transmission", + Name: "Transmission", + Protocol: "torrent", + Fields: []*starr.FieldOutput{ + { + Order: 0, + Name: "host", + Label: "Host", + Value: "transmission", + Type: "textbox", + Advanced: false, + }, + { + Order: 1, + Name: "port", + Label: "Port", + Value: float64(9091), + Type: "textbox", + Advanced: false, + }, + { + Order: 2, + Name: "useSsl", + Label: "Use SSL", + HelpText: "Use secure connection when connecting to Transmission", + Value: false, + Type: "checkbox", + Advanced: false, + }, + }, + Tags: []int{}, + }, + WithError: nil, + }, + { + Name: "200", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "downloadClient"), + ExpectedMethod: "POST", + ResponseStatus: 404, + WithRequest: &sonarr.DownloadClientInput{ + Enable: true, + RemoveCompletedDownloads: false, + RemoveFailedDownloads: false, + Priority: 1, + ConfigContract: "TransmissionSettings", + Implementation: "Transmission", + Name: "Transmission", + Protocol: "torrent", + Fields: []*starr.FieldInput{ + { + Name: "host", + Value: "transmission", + }, + { + Name: "port", + Value: 9091, + }, + { + Name: "useSSL", + Value: false, + }, + }, + }, + ExpectedRequest: addDownloadClient + "\n", + ResponseBody: `{"message": "NotFound"}`, + WithError: starr.ErrInvalidStatusCode, + WithResponse: (*sonarr.DownloadClientOutput)(nil), + }, + } + + for _, test := range tests { + test := test + t.Run(test.Name, func(t *testing.T) { + t.Parallel() + mockServer := test.GetMockServer(t) + client := sonarr.New(starr.New("mockAPIkey", mockServer.URL, 0)) + output, err := client.AddDownloadClient(test.WithRequest.(*sonarr.DownloadClientInput)) + assert.ErrorIs(t, err, test.WithError, "error is not the same as expected") + assert.EqualValues(t, test.WithResponse, output, "response is not the same as expected") + }) + } +} + +func TestUpdateDownloadClient(t *testing.T) { + t.Parallel() + + tests := []*starr.TestMockData{ + { + Name: "200", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "downloadClient", "3"), + ExpectedMethod: "PUT", + ResponseStatus: 200, + WithRequest: &sonarr.DownloadClientInput{ + Enable: true, + RemoveCompletedDownloads: false, + RemoveFailedDownloads: false, + Priority: 1, + ConfigContract: "TransmissionSettings", + Implementation: "Transmission", + Name: "Transmission", + Protocol: "torrent", + Fields: []*starr.FieldInput{ + { + Name: "host", + Value: "transmission", + }, + { + Name: "port", + Value: 9091, + }, + { + Name: "useSSL", + Value: false, + }, + }, + ID: 3, + }, + ExpectedRequest: updateDownloadClient + "\n", + ResponseBody: downloadClientResponseBody, + WithResponse: &sonarr.DownloadClientOutput{ + Enable: true, + Priority: 1, + ID: 3, + ConfigContract: "TransmissionSettings", + Implementation: "Transmission", + ImplementationName: "Transmission", + InfoLink: "https://wiki.servarr.com/sonarr/supported#transmission", + Name: "Transmission", + Protocol: "torrent", + Fields: []*starr.FieldOutput{ + { + Order: 0, + Name: "host", + Label: "Host", + Value: "transmission", + Type: "textbox", + Advanced: false, + }, + { + Order: 1, + Name: "port", + Label: "Port", + Value: float64(9091), + Type: "textbox", + Advanced: false, + }, + { + Order: 2, + Name: "useSsl", + Label: "Use SSL", + HelpText: "Use secure connection when connecting to Transmission", + Value: false, + Type: "checkbox", + Advanced: false, + }, + }, + Tags: []int{}, + }, + WithError: nil, + }, + { + Name: "200", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "downloadClient", "3"), + ExpectedMethod: "PUT", + ResponseStatus: 404, + WithRequest: &sonarr.DownloadClientInput{ + Enable: true, + RemoveCompletedDownloads: false, + RemoveFailedDownloads: false, + Priority: 1, + ConfigContract: "TransmissionSettings", + Implementation: "Transmission", + Name: "Transmission", + Protocol: "torrent", + Fields: []*starr.FieldInput{ + { + Name: "host", + Value: "transmission", + }, + { + Name: "port", + Value: 9091, + }, + { + Name: "useSSL", + Value: false, + }, + }, + ID: 3, + }, + ExpectedRequest: updateDownloadClient + "\n", + ResponseBody: `{"message": "NotFound"}`, + WithError: starr.ErrInvalidStatusCode, + WithResponse: (*sonarr.DownloadClientOutput)(nil), + }, + } + + for _, test := range tests { + test := test + t.Run(test.Name, func(t *testing.T) { + t.Parallel() + mockServer := test.GetMockServer(t) + client := sonarr.New(starr.New("mockAPIkey", mockServer.URL, 0)) + output, err := client.UpdateDownloadClient(test.WithRequest.(*sonarr.DownloadClientInput)) + assert.ErrorIs(t, err, test.WithError, "error is not the same as expected") + assert.EqualValues(t, test.WithResponse, output, "response is not the same as expected") + }) + } +} + +func TestDeleteDownloadClient(t *testing.T) { + t.Parallel() + + tests := []*starr.TestMockData{ + { + Name: "200", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "downloadClient", "2"), + ExpectedMethod: "DELETE", + WithRequest: int64(2), + ResponseStatus: 200, + ResponseBody: "{}", + WithError: nil, + }, + { + Name: "404", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "downloadClient", "2"), + ExpectedMethod: "DELETE", + WithRequest: int64(2), + ResponseStatus: 404, + ResponseBody: `{"message": "NotFound"}`, + WithError: starr.ErrInvalidStatusCode, + }, + } + + for _, test := range tests { + test := test + t.Run(test.Name, func(t *testing.T) { + t.Parallel() + mockServer := test.GetMockServer(t) + client := sonarr.New(starr.New("mockAPIkey", mockServer.URL, 0)) + err := client.DeleteDownloadClient(test.WithRequest.(int64)) + assert.ErrorIs(t, err, test.WithError, "error is not the same as expected") + }) + } +} diff --git a/sonarr/downloadclientconfig.go b/sonarr/downloadclientconfig.go new file mode 100644 index 0000000..2ac08f5 --- /dev/null +++ b/sonarr/downloadclientconfig.go @@ -0,0 +1,63 @@ +package sonarr + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "path" + + "golift.io/starr" +) + +// Define Base Path for download client config calls. +const bpDownloadClientConfig = APIver + "/config/downloadClient" + +// DownloadClientConfig is the /api/v3/config/downloadClientConfig endpoint. +type DownloadClientConfig struct { + EnableCompletedDownloadHandling bool `json:"enableCompletedDownloadHandling"` + AutoRedownloadFailed bool `json:"autoRedownloadFailed"` + ID int64 `json:"id"` + DownloadClientWorkingFolders string `json:"downloadClientWorkingFolders"` +} + +// GetDownloadClientConfig returns the download client config. +func (s *Sonarr) GetDownloadClientConfig() (*DownloadClientConfig, error) { + return s.GetDownloadClientConfigContext(context.Background()) +} + +// GetDownloadClientConfig returns the download client config. +func (s *Sonarr) GetDownloadClientConfigContext(ctx context.Context) (*DownloadClientConfig, error) { + var output DownloadClientConfig + + req := starr.Request{URI: bpDownloadClientConfig} + if err := s.GetInto(ctx, req, &output); err != nil { + return nil, fmt.Errorf("api.Get(%s): %w", &req, err) + } + + return &output, nil +} + +// UpdateDownloadClientConfig update the single download client config. +func (s *Sonarr) UpdateDownloadClientConfig(downloadClientConfig *DownloadClientConfig) (*DownloadClientConfig, error) { + return s.UpdateDownloadClientConfigContext(context.Background(), downloadClientConfig) +} + +// UpdateDownloadClientConfig update the single download client config. +func (s *Sonarr) UpdateDownloadClientConfigContext(ctx context.Context, + config *DownloadClientConfig, +) (*DownloadClientConfig, error) { + var output DownloadClientConfig + + var body bytes.Buffer + if err := json.NewEncoder(&body).Encode(config); err != nil { + return nil, fmt.Errorf("json.Marshal(%s): %w", bpDownloadClientConfig, err) + } + + req := starr.Request{URI: path.Join(bpDownloadClientConfig, fmt.Sprint(config.ID)), Body: &body} + if err := s.PutInto(ctx, req, &output); err != nil { + return nil, fmt.Errorf("api.Put(%s): %w", &req, err) + } + + return &output, nil +} diff --git a/sonarr/downloadclientconfig_test.go b/sonarr/downloadclientconfig_test.go new file mode 100644 index 0000000..ae6c0aa --- /dev/null +++ b/sonarr/downloadclientconfig_test.go @@ -0,0 +1,119 @@ +package sonarr_test + +import ( + "path" + "testing" + + "github.com/stretchr/testify/assert" + "golift.io/starr" + "golift.io/starr/sonarr" +) + +const downloadClientConfigBody = `{ + "downloadClientWorkingFolders": "_UNPACK_|_FAILED_", + "enableCompletedDownloadHandling": true, + "autoRedownloadFailed": false, + "id": 1 +}` + +func TestGetDownloadClientConfig(t *testing.T) { + t.Parallel() + + tests := []*starr.TestMockData{ + { + Name: "200", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config", "downloadClient"), + ExpectedRequest: "", + ExpectedMethod: "GET", + ResponseStatus: 200, + ResponseBody: downloadClientConfigBody, + WithRequest: nil, + WithResponse: &sonarr.DownloadClientConfig{ + EnableCompletedDownloadHandling: true, + AutoRedownloadFailed: false, + ID: 1, + DownloadClientWorkingFolders: "_UNPACK_|_FAILED_", + }, + WithError: nil, + }, + { + Name: "404", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config", "downloadClient"), + ExpectedMethod: "GET", + ResponseStatus: 404, + ResponseBody: `{"message": "NotFound"}`, + WithError: starr.ErrInvalidStatusCode, + WithResponse: (*sonarr.DownloadClientConfig)(nil), + }, + } + + for _, test := range tests { + test := test + t.Run(test.Name, func(t *testing.T) { + t.Parallel() + mockServer := test.GetMockServer(t) + client := sonarr.New(starr.New("mockAPIkey", mockServer.URL, 0)) + output, err := client.GetDownloadClientConfig() + assert.ErrorIs(t, err, test.WithError, "error is not the same as expected") + assert.EqualValues(t, test.WithResponse, output, "response is not the same as expected") + }) + } +} + +func TestUpdateDownloadClientConfig(t *testing.T) { + t.Parallel() + + tests := []*starr.TestMockData{ + { + Name: "202", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config", "downloadClient", "1"), + ExpectedMethod: "PUT", + ResponseStatus: 202, + WithRequest: &sonarr.DownloadClientConfig{ + EnableCompletedDownloadHandling: true, + AutoRedownloadFailed: false, + ID: 1, + DownloadClientWorkingFolders: "_UNPACK_|_FAILED_", + }, + ExpectedRequest: `{"enableCompletedDownloadHandling":true,"autoRedownloadFailed":false,` + + `"id":1,"downloadClientWorkingFolders":"_UNPACK_|_FAILED_"}` + "\n", + ResponseBody: downloadClientConfigBody, + WithResponse: &sonarr.DownloadClientConfig{ + EnableCompletedDownloadHandling: true, + AutoRedownloadFailed: false, + ID: 1, + DownloadClientWorkingFolders: "_UNPACK_|_FAILED_", + }, + WithError: nil, + }, + { + Name: "404", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config", "downloadClient", "1"), + ExpectedMethod: "PUT", + WithRequest: &sonarr.DownloadClientConfig{ + EnableCompletedDownloadHandling: true, + AutoRedownloadFailed: false, + ID: 1, + DownloadClientWorkingFolders: "_UNPACK_|_FAILED_", + }, + ExpectedRequest: `{"enableCompletedDownloadHandling":true,"autoRedownloadFailed":false,` + + `"id":1,"downloadClientWorkingFolders":"_UNPACK_|_FAILED_"}` + "\n", + ResponseStatus: 404, + ResponseBody: `{"message": "NotFound"}`, + WithError: starr.ErrInvalidStatusCode, + WithResponse: (*sonarr.DownloadClientConfig)(nil), + }, + } + + for _, test := range tests { + test := test + t.Run(test.Name, func(t *testing.T) { + t.Parallel() + mockServer := test.GetMockServer(t) + client := sonarr.New(starr.New("mockAPIkey", mockServer.URL, 0)) + output, err := client.UpdateDownloadClientConfig(test.WithRequest.(*sonarr.DownloadClientConfig)) + assert.ErrorIs(t, err, test.WithError, "error is not the same as expected") + assert.EqualValues(t, output, test.WithResponse, "response is not the same as expected") + }) + } +} diff --git a/sonarr/indexer.go b/sonarr/indexer.go index a33d917..9355ffd 100644 --- a/sonarr/indexer.go +++ b/sonarr/indexer.go @@ -66,12 +66,12 @@ func (s *Sonarr) GetIndexersContext(ctx context.Context) ([]*IndexerOutput, erro } // GetIndexer returns a single indexer. -func (s *Sonarr) GetIndexer(indexerID int) (*IndexerOutput, error) { +func (s *Sonarr) GetIndexer(indexerID int64) (*IndexerOutput, error) { return s.GetIndexerContext(context.Background(), indexerID) } // GetIndGetIndexerContextexer returns a single indexer. -func (s *Sonarr) GetIndexerContext(ctx context.Context, indexerID int) (*IndexerOutput, error) { +func (s *Sonarr) GetIndexerContext(ctx context.Context, indexerID int64) (*IndexerOutput, error) { var output IndexerOutput req := starr.Request{URI: path.Join(bpIndexer, fmt.Sprint(indexerID))} @@ -127,12 +127,12 @@ func (s *Sonarr) UpdateIndexerContext(ctx context.Context, indexer *IndexerInput } // DeleteIndexer removes a single indexer. -func (s *Sonarr) DeleteIndexer(indexerID int) error { +func (s *Sonarr) DeleteIndexer(indexerID int64) error { return s.DeleteIndexerContext(context.Background(), indexerID) } // DeleteIndexerContext removes a single indexer. -func (s *Sonarr) DeleteIndexerContext(ctx context.Context, indexerID int) error { +func (s *Sonarr) DeleteIndexerContext(ctx context.Context, indexerID int64) error { req := starr.Request{URI: path.Join(bpIndexer, fmt.Sprint(indexerID))} if err := s.DeleteAny(ctx, req); err != nil { return fmt.Errorf("api.Delete(%s): %w", &req, err) diff --git a/sonarr/indexer_test.go b/sonarr/indexer_test.go index 8f201ca..68b878b 100644 --- a/sonarr/indexer_test.go +++ b/sonarr/indexer_test.go @@ -137,7 +137,7 @@ func TestGetIndexer(t *testing.T) { tests := []*starr.TestMockData{ { Name: "200", - ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "indexer/1"), + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "indexer", "1"), ExpectedRequest: "", ExpectedMethod: "GET", ResponseStatus: 200, @@ -182,7 +182,7 @@ func TestGetIndexer(t *testing.T) { }, { Name: "404", - ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "indexer/1"), + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "indexer", "1"), ExpectedMethod: "GET", ResponseStatus: 404, ResponseBody: `{"message": "NotFound"}`, @@ -327,7 +327,7 @@ func TestUpdateIndexer(t *testing.T) { tests := []*starr.TestMockData{ { Name: "200", - ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "indexer/1"), + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "indexer", "1"), ExpectedMethod: "PUT", ResponseStatus: 200, WithRequest: &sonarr.IndexerInput{ @@ -394,7 +394,7 @@ func TestUpdateIndexer(t *testing.T) { }, { Name: "200", - ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "indexer/1"), + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "indexer", "1"), ExpectedMethod: "PUT", ResponseStatus: 404, WithRequest: &sonarr.IndexerInput{ @@ -446,18 +446,18 @@ func TestDeleteIndexer(t *testing.T) { tests := []*starr.TestMockData{ { Name: "200", - ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "indexer/2"), + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "indexer", "2"), ExpectedMethod: "DELETE", - WithRequest: 2, + WithRequest: int64(2), ResponseStatus: 200, ResponseBody: "{}", WithError: nil, }, { Name: "404", - ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "indexer/2"), + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "indexer", "2"), ExpectedMethod: "DELETE", - WithRequest: 2, + WithRequest: int64(2), ResponseStatus: 404, ResponseBody: `{"message": "NotFound"}`, WithError: starr.ErrInvalidStatusCode, @@ -470,7 +470,7 @@ func TestDeleteIndexer(t *testing.T) { t.Parallel() mockServer := test.GetMockServer(t) client := sonarr.New(starr.New("mockAPIkey", mockServer.URL, 0)) - err := client.DeleteIndexer(test.WithRequest.(int)) + err := client.DeleteIndexer(test.WithRequest.(int64)) assert.ErrorIs(t, err, test.WithError, "error is not the same as expected") }) } diff --git a/sonarr/indexerconfig_test.go b/sonarr/indexerconfig_test.go index 56c6301..d74bab3 100644 --- a/sonarr/indexerconfig_test.go +++ b/sonarr/indexerconfig_test.go @@ -23,7 +23,7 @@ func TestGetIndexerConfig(t *testing.T) { tests := []*starr.TestMockData{ { Name: "200", - ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config/indexer"), + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config", "indexer"), ExpectedMethod: "GET", ResponseStatus: 200, ResponseBody: indexerConfigBody, @@ -38,7 +38,7 @@ func TestGetIndexerConfig(t *testing.T) { }, { Name: "404", - ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config/indexer"), + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config", "indexer"), ExpectedMethod: "GET", ResponseStatus: 404, ResponseBody: `{"message": "NotFound"}`, @@ -66,7 +66,7 @@ func TestUpdateIndexerConfig(t *testing.T) { tests := []*starr.TestMockData{ { Name: "202", - ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config/indexer/1"), + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config", "indexer", "1"), ExpectedMethod: "PUT", ResponseStatus: 202, WithRequest: &sonarr.IndexerConfig{ @@ -89,7 +89,7 @@ func TestUpdateIndexerConfig(t *testing.T) { }, { Name: "404", - ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config/indexer/1"), + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config", "indexer", "1"), ExpectedMethod: "PUT", WithRequest: &sonarr.IndexerConfig{ ID: 1, diff --git a/sonarr/languageprofile.go b/sonarr/languageprofile.go index 45e2e85..1d8b20b 100644 --- a/sonarr/languageprofile.go +++ b/sonarr/languageprofile.go @@ -46,12 +46,12 @@ func (s *Sonarr) GetLanguageProfilesContext(ctx context.Context) ([]*LanguagePro } // GetLanguageProfile returns a single language profile. -func (s *Sonarr) GetLanguageProfile(profileID int) (*LanguageProfile, error) { +func (s *Sonarr) GetLanguageProfile(profileID int64) (*LanguageProfile, error) { return s.GetLanguageProfileContext(context.Background(), profileID) } // GetLanguageProfileContext returns a single language profile. -func (s *Sonarr) GetLanguageProfileContext(ctx context.Context, profileID int) (*LanguageProfile, error) { +func (s *Sonarr) GetLanguageProfileContext(ctx context.Context, profileID int64) (*LanguageProfile, error) { var output LanguageProfile req := starr.Request{URI: path.Join(bpLanguageProfile, fmt.Sprint(profileID))} @@ -98,7 +98,7 @@ func (s *Sonarr) UpdateLanguageProfileContext(ctx context.Context, profile *Lang return nil, fmt.Errorf("json.Marshal(%s): %w", bpLanguageProfile, err) } - req := starr.Request{URI: path.Join(bpLanguageProfile, fmt.Sprint(int(profile.ID))), Body: &body} + req := starr.Request{URI: path.Join(bpLanguageProfile, fmt.Sprint(profile.ID)), Body: &body} if err := s.PutInto(ctx, req, &output); err != nil { return nil, fmt.Errorf("api.Put(%s): %w", &req, err) } @@ -107,12 +107,12 @@ func (s *Sonarr) UpdateLanguageProfileContext(ctx context.Context, profile *Lang } // DeleteLanguageProfile removes a single language profile. -func (s *Sonarr) DeleteLanguageProfile(profileID int) error { +func (s *Sonarr) DeleteLanguageProfile(profileID int64) error { return s.DeleteLanguageProfileContext(context.Background(), profileID) } // DeleteLanguageProfileContext removes a single language profile. -func (s *Sonarr) DeleteLanguageProfileContext(ctx context.Context, profileID int) error { +func (s *Sonarr) DeleteLanguageProfileContext(ctx context.Context, profileID int64) error { req := starr.Request{URI: path.Join(bpLanguageProfile, fmt.Sprint(profileID))} if err := s.DeleteAny(ctx, req); err != nil { return fmt.Errorf("api.Delete(%s): %w", &req, err) diff --git a/sonarr/mediamanagement_test.go b/sonarr/mediamanagement_test.go index 10dfb05..7406105 100644 --- a/sonarr/mediamanagement_test.go +++ b/sonarr/mediamanagement_test.go @@ -37,7 +37,7 @@ func TestGetMediaManagement(t *testing.T) { tests := []*starr.TestMockData{ { Name: "200", - ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config/mediaManagement"), + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config", "mediaManagement"), ExpectedMethod: "GET", ResponseStatus: 200, ResponseBody: mediaManagementBody, @@ -66,7 +66,7 @@ func TestGetMediaManagement(t *testing.T) { }, { Name: "404", - ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config/mediaManagement"), + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config", "mediaManagement"), ExpectedMethod: "GET", ResponseStatus: 404, ResponseBody: `{"message": "NotFound"}`, @@ -94,7 +94,7 @@ func TestUpdateMediaManagement(t *testing.T) { tests := []*starr.TestMockData{ { Name: "202", - ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config/mediaManagement"), + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config", "mediaManagement"), ExpectedMethod: "PUT", ResponseStatus: 202, WithRequest: &sonarr.MediaManagement{ @@ -127,7 +127,7 @@ func TestUpdateMediaManagement(t *testing.T) { }, { Name: "404", - ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config/mediaManagement"), + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config", "mediaManagement"), ExpectedMethod: "PUT", WithRequest: &sonarr.MediaManagement{ EnableMediaInfo: true, diff --git a/sonarr/naming_test.go b/sonarr/naming_test.go index 3a21e63..988db69 100644 --- a/sonarr/naming_test.go +++ b/sonarr/naming_test.go @@ -34,7 +34,7 @@ func TestGetNaming(t *testing.T) { tests := []*starr.TestMockData{ { Name: "200", - ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config/naming"), + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config", "naming"), ExpectedMethod: "GET", ResponseStatus: 200, ResponseBody: namingBody, @@ -60,7 +60,7 @@ func TestGetNaming(t *testing.T) { }, { Name: "404", - ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config/naming"), + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config", "naming"), ExpectedMethod: "GET", ResponseStatus: 404, ResponseBody: `{"message": "NotFound"}`, @@ -88,7 +88,7 @@ func TestUpdateNaming(t *testing.T) { tests := []*starr.TestMockData{ { Name: "202", - ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config/naming"), + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config", "naming"), ExpectedMethod: "PUT", ResponseStatus: 202, WithRequest: &sonarr.Naming{ @@ -118,7 +118,7 @@ func TestUpdateNaming(t *testing.T) { }, { Name: "404", - ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config/naming"), + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config", "naming"), ExpectedMethod: "PUT", WithRequest: &sonarr.Naming{ ReplaceIllegalCharacters: true, diff --git a/sonarr/remotepathmapping.go b/sonarr/remotepathmapping.go new file mode 100644 index 0000000..b761b525 --- /dev/null +++ b/sonarr/remotepathmapping.go @@ -0,0 +1,119 @@ +package sonarr + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "path" + + "golift.io/starr" +) + +// Define Base Path for remote path mapping calls. +const bpRemotePathMapping = APIver + "/remotePathMapping" + +// RemotePathMapping is the /api/v3/remotePathMapping endpoint. +type RemotePathMapping struct { + ID int64 `json:"id,omitempty"` + Host string `json:"host"` + RemotePath string `json:"remotePath"` + LocalPath string `json:"localPath"` +} + +// GetRemotePathMappings returns all configured remote path mappings. +func (s *Sonarr) GetRemotePathMappings() ([]*RemotePathMapping, error) { + return s.GetRemotePathMappingsContext(context.Background()) +} + +// GetRemotePathMappingsContext returns all configured remote path mappings. +func (s *Sonarr) GetRemotePathMappingsContext(ctx context.Context) ([]*RemotePathMapping, error) { + var output []*RemotePathMapping + + req := starr.Request{URI: bpRemotePathMapping} + if err := s.GetInto(ctx, req, &output); err != nil { + return nil, fmt.Errorf("api.Get(%s): %w", &req, err) + } + + return output, nil +} + +// GetRemotePathMapping returns a single remote path mapping. +func (s *Sonarr) GetRemotePathMapping(mappingID int64) (*RemotePathMapping, error) { + return s.GetRemotePathMappingContext(context.Background(), mappingID) +} + +// GetRemotePathMappingContext returns a single remote path mapping. +func (s *Sonarr) GetRemotePathMappingContext(ctx context.Context, mappingID int64) (*RemotePathMapping, error) { + var output RemotePathMapping + + req := starr.Request{URI: path.Join(bpRemotePathMapping, fmt.Sprint(mappingID))} + if err := s.GetInto(ctx, req, &output); err != nil { + return nil, fmt.Errorf("api.Get(%s): %w", &req, err) + } + + return &output, nil +} + +// AddRemotePathMapping creates a remote path mapping. +func (s *Sonarr) AddRemotePathMapping(mapping *RemotePathMapping) (*RemotePathMapping, error) { + return s.AddRemotePathMappingContext(context.Background(), mapping) +} + +// AddRemotePathMappingContext creates a remote path mapping. +func (s *Sonarr) AddRemotePathMappingContext(ctx context.Context, + mapping *RemotePathMapping, +) (*RemotePathMapping, error) { + var output RemotePathMapping + + var body bytes.Buffer + if err := json.NewEncoder(&body).Encode(mapping); err != nil { + return nil, fmt.Errorf("json.Marshal(%s): %w", bpRemotePathMapping, err) + } + + req := starr.Request{URI: bpRemotePathMapping, Body: &body} + if err := s.PostInto(ctx, req, &output); err != nil { + return nil, fmt.Errorf("api.Post(%s): %w", &req, err) + } + + return &output, nil +} + +// UpdateRemotePathMapping updates the remote path mapping. +func (s *Sonarr) UpdateRemotePathMapping(mapping *RemotePathMapping) (*RemotePathMapping, error) { + return s.UpdateRemotePathMappingContext(context.Background(), mapping) +} + +// UpdateRemotePathMappingContext updates the remote path mapping. +func (s *Sonarr) UpdateRemotePathMappingContext(ctx context.Context, + mapping *RemotePathMapping, +) (*RemotePathMapping, error) { + var output RemotePathMapping + + var body bytes.Buffer + if err := json.NewEncoder(&body).Encode(mapping); err != nil { + return nil, fmt.Errorf("json.Marshal(%s): %w", bpRemotePathMapping, err) + } + + req := starr.Request{URI: path.Join(bpRemotePathMapping, fmt.Sprint(mapping.ID)), Body: &body} + if err := s.PutInto(ctx, req, &output); err != nil { + return nil, fmt.Errorf("api.Put(%s): %w", &req, err) + } + + return &output, nil +} + +// DeleteRemotePathMapping removes a single remote path mapping. +func (s *Sonarr) DeleteRemotePathMapping(mappingID int64) error { + return s.DeleteRemotePathMappingContext(context.Background(), mappingID) +} + +// DeleteRemotePathMappingContext removes a single remote path mapping. +func (s *Sonarr) DeleteRemotePathMappingContext(ctx context.Context, mappingID int64) error { + req := starr.Request{URI: path.Join(bpRemotePathMapping, fmt.Sprint(mappingID))} + if err := s.DeleteAny(ctx, req); err != nil { + return fmt.Errorf("api.Delete(%s): %w", &req, err) + } + + return nil +} diff --git a/sonarr/remotepathmapping_test.go b/sonarr/remotepathmapping_test.go new file mode 100644 index 0000000..7d3a575 --- /dev/null +++ b/sonarr/remotepathmapping_test.go @@ -0,0 +1,253 @@ +package sonarr_test + +import ( + "path" + "testing" + + "github.com/stretchr/testify/assert" + "golift.io/starr" + "golift.io/starr/sonarr" +) + +const ( + remotePathMapping = `{ + "host": "transmission", + "remotePath": "/remote/", + "localPath": "/local/", + "id": 2 + }` +) + +func TestGetRemotePathMappings(t *testing.T) { + t.Parallel() + + tests := []*starr.TestMockData{ + { + Name: "200", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "remotePathMapping"), + ExpectedMethod: "GET", + ResponseStatus: 200, + ResponseBody: `[` + remotePathMapping + `]`, + WithResponse: []*sonarr.RemotePathMapping{ + { + Host: "transmission", + RemotePath: "/remote/", + LocalPath: "/local/", + ID: 2, + }, + }, + WithError: nil, + }, + { + Name: "404", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "remotePathMapping"), + ExpectedMethod: "GET", + ResponseStatus: 404, + ResponseBody: `{"message": "NotFound"}`, + WithError: starr.ErrInvalidStatusCode, + WithResponse: []*sonarr.RemotePathMapping(nil), + }, + } + + for _, test := range tests { + test := test + t.Run(test.Name, func(t *testing.T) { + t.Parallel() + mockServer := test.GetMockServer(t) + client := sonarr.New(starr.New("mockAPIkey", mockServer.URL, 0)) + output, err := client.GetRemotePathMappings() + assert.ErrorIs(t, err, test.WithError, "error is not the same as expected") + assert.EqualValues(t, test.WithResponse, output, "response is not the same as expected") + }) + } +} + +func TestGetRemotePathMapping(t *testing.T) { + t.Parallel() + + tests := []*starr.TestMockData{ + { + Name: "200", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "remotePathMapping", "1"), + ExpectedMethod: "GET", + ResponseStatus: 200, + WithRequest: int64(1), + ResponseBody: remotePathMapping, + WithResponse: &sonarr.RemotePathMapping{ + Host: "transmission", + RemotePath: "/remote/", + LocalPath: "/local/", + ID: 2, + }, + WithError: nil, + }, + { + Name: "404", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "remotePathMapping", "1"), + ExpectedMethod: "GET", + ResponseStatus: 404, + WithRequest: int64(1), + ResponseBody: `{"message": "NotFound"}`, + WithResponse: (*sonarr.RemotePathMapping)(nil), + WithError: starr.ErrInvalidStatusCode, + }, + } + + for _, test := range tests { + test := test + t.Run(test.Name, func(t *testing.T) { + t.Parallel() + mockServer := test.GetMockServer(t) + client := sonarr.New(starr.New("mockAPIkey", mockServer.URL, 0)) + output, err := client.GetRemotePathMapping(test.WithRequest.(int64)) + assert.ErrorIs(t, err, test.WithError, "error is not the same as expected") + assert.EqualValues(t, test.WithResponse, output, "response is not the same as expected") + }) + } +} + +func TestAddRemotePathMapping(t *testing.T) { + t.Parallel() + + tests := []*starr.TestMockData{ + { + Name: "201", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "remotePathMapping"), + ExpectedMethod: "POST", + ResponseStatus: 201, + WithRequest: &sonarr.RemotePathMapping{ + Host: "transmission", + RemotePath: "/remote/", + LocalPath: "/local/", + }, + ExpectedRequest: `{"host":"transmission","remotePath":"/remote/","localPath":"/local/"}` + "\n", + ResponseBody: remotePathMapping, + WithResponse: &sonarr.RemotePathMapping{ + Host: "transmission", + RemotePath: "/remote/", + LocalPath: "/local/", + ID: 2, + }, + WithError: nil, + }, + { + Name: "404", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "remotePathMapping"), + ExpectedMethod: "POST", + ResponseStatus: 404, + WithRequest: &sonarr.RemotePathMapping{ + Host: "transmission", + RemotePath: "/remote/", + LocalPath: "/local/", + }, + ExpectedRequest: `{"host":"transmission","remotePath":"/remote/","localPath":"/local/"}` + "\n", + ResponseBody: `{"message": "NotFound"}`, + WithError: starr.ErrInvalidStatusCode, + WithResponse: (*sonarr.RemotePathMapping)(nil), + }, + } + + for _, test := range tests { + test := test + t.Run(test.Name, func(t *testing.T) { + t.Parallel() + mockServer := test.GetMockServer(t) + client := sonarr.New(starr.New("mockAPIkey", mockServer.URL, 0)) + output, err := client.AddRemotePathMapping(test.WithRequest.(*sonarr.RemotePathMapping)) + assert.ErrorIs(t, err, test.WithError, "error is not the same as expected") + assert.EqualValues(t, test.WithResponse, output, "response is not the same as expected") + }) + } +} + +func TestUpdateRemotePathMapping(t *testing.T) { + t.Parallel() + + tests := []*starr.TestMockData{ + { + Name: "201", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "remotePathMapping", "2"), + ExpectedMethod: "PUT", + ResponseStatus: 201, + WithRequest: &sonarr.RemotePathMapping{ + Host: "transmission", + RemotePath: "/remote/", + LocalPath: "/local/", + ID: 2, + }, + ExpectedRequest: `{"id":2,"host":"transmission","remotePath":"/remote/","localPath":"/local/"}` + "\n", + ResponseBody: remotePathMapping, + WithResponse: &sonarr.RemotePathMapping{ + Host: "transmission", + RemotePath: "/remote/", + LocalPath: "/local/", + ID: 2, + }, + WithError: nil, + }, + { + Name: "404", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "remotePathMapping", "2"), + ExpectedMethod: "PUT", + ResponseStatus: 404, + WithRequest: &sonarr.RemotePathMapping{ + Host: "transmission", + RemotePath: "/remote/", + LocalPath: "/local/", + ID: 2, + }, + ExpectedRequest: `{"id":2,"host":"transmission","remotePath":"/remote/","localPath":"/local/"}` + "\n", + ResponseBody: `{"message": "NotFound"}`, + WithError: starr.ErrInvalidStatusCode, + WithResponse: (*sonarr.RemotePathMapping)(nil), + }, + } + + for _, test := range tests { + test := test + t.Run(test.Name, func(t *testing.T) { + t.Parallel() + mockServer := test.GetMockServer(t) + client := sonarr.New(starr.New("mockAPIkey", mockServer.URL, 0)) + output, err := client.UpdateRemotePathMapping(test.WithRequest.(*sonarr.RemotePathMapping)) + assert.ErrorIs(t, err, test.WithError, "error is not the same as expected") + assert.EqualValues(t, test.WithResponse, output, "response is not the same as expected") + }) + } +} + +func TestDeleteRemotePathMapping(t *testing.T) { + t.Parallel() + + tests := []*starr.TestMockData{ + { + Name: "200", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "remotePathMapping", "2"), + ExpectedMethod: "DELETE", + WithRequest: int64(2), + ResponseStatus: 200, + ResponseBody: "{}", + WithError: nil, + }, + { + Name: "404", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "remotePathMapping", "2"), + ExpectedMethod: "DELETE", + WithRequest: int64(2), + ResponseStatus: 404, + ResponseBody: `{"message": "NotFound"}`, + WithError: starr.ErrInvalidStatusCode, + }, + } + + for _, test := range tests { + test := test + t.Run(test.Name, func(t *testing.T) { + t.Parallel() + mockServer := test.GetMockServer(t) + client := sonarr.New(starr.New("mockAPIkey", mockServer.URL, 0)) + err := client.DeleteRemotePathMapping(test.WithRequest.(int64)) + assert.ErrorIs(t, err, test.WithError, "error is not the same as expected") + }) + } +} diff --git a/sonarr/rootfolder.go b/sonarr/rootfolder.go index 1216437..06c2615 100644 --- a/sonarr/rootfolder.go +++ b/sonarr/rootfolder.go @@ -40,12 +40,12 @@ func (s *Sonarr) GetRootFoldersContext(ctx context.Context) ([]*RootFolder, erro } // GetRootFolder returns a single root folder. -func (s *Sonarr) GetRootFolder(folderID int) (*RootFolder, error) { +func (s *Sonarr) GetRootFolder(folderID int64) (*RootFolder, error) { return s.GetRootFolderContext(context.Background(), folderID) } // GetRootFolderContext returns a single root folder. -func (s *Sonarr) GetRootFolderContext(ctx context.Context, folderID int) (*RootFolder, error) { +func (s *Sonarr) GetRootFolderContext(ctx context.Context, folderID int64) (*RootFolder, error) { var output RootFolder req := starr.Request{URI: path.Join(bpRootFolder, fmt.Sprint(folderID))} @@ -79,12 +79,12 @@ func (s *Sonarr) AddRootFolderContext(ctx context.Context, folder *RootFolder) ( } // DeleteRootFolder removes a single root folder. -func (s *Sonarr) DeleteRootFolder(folderID int) error { +func (s *Sonarr) DeleteRootFolder(folderID int64) error { return s.DeleteRootFolderContext(context.Background(), folderID) } // DeleteRootFolderContext removes a single root folder. -func (s *Sonarr) DeleteRootFolderContext(ctx context.Context, folderID int) error { +func (s *Sonarr) DeleteRootFolderContext(ctx context.Context, folderID int64) error { req := starr.Request{URI: path.Join(bpRootFolder, fmt.Sprint(folderID))} if err := s.DeleteAny(ctx, req); err != nil { return fmt.Errorf("api.Delete(%s): %w", &req, err) diff --git a/sonarr/rootfolder_test.go b/sonarr/rootfolder_test.go index 00304b9..2808bf2 100644 --- a/sonarr/rootfolder_test.go +++ b/sonarr/rootfolder_test.go @@ -94,10 +94,10 @@ func TestGetRootFolder(t *testing.T) { tests := []*starr.TestMockData{ { Name: "200", - ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "rootFolder/1"), + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "rootFolder", "1"), ExpectedMethod: "GET", ResponseStatus: 200, - WithRequest: 1, + WithRequest: int64(1), ResponseBody: firstRootFolder, WithResponse: &sonarr.RootFolder{ Path: "/series", @@ -110,10 +110,10 @@ func TestGetRootFolder(t *testing.T) { }, { Name: "404", - ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "rootFolder/1"), + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "rootFolder", "1"), ExpectedMethod: "GET", ResponseStatus: 404, - WithRequest: 1, + WithRequest: int64(1), ResponseBody: `{"message": "NotFound"}`, WithResponse: (*sonarr.RootFolder)(nil), WithError: starr.ErrInvalidStatusCode, @@ -126,7 +126,7 @@ func TestGetRootFolder(t *testing.T) { t.Parallel() mockServer := test.GetMockServer(t) client := sonarr.New(starr.New("mockAPIkey", mockServer.URL, 0)) - output, err := client.GetRootFolder(test.WithRequest.(int)) + output, err := client.GetRootFolder(test.WithRequest.(int64)) assert.ErrorIs(t, err, test.WithError, "error is not the same as expected") assert.EqualValues(t, test.WithResponse, output, "response is not the same as expected") }) @@ -195,18 +195,18 @@ func TestDeleteRootFolder(t *testing.T) { tests := []*starr.TestMockData{ { Name: "200", - ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "rootFolder/2"), + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "rootFolder", "2"), ExpectedMethod: "DELETE", - WithRequest: 2, + WithRequest: int64(2), ResponseStatus: 200, ResponseBody: "{}", WithError: nil, }, { Name: "404", - ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "rootFolder/2"), + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "rootFolder", "2"), ExpectedMethod: "DELETE", - WithRequest: 2, + WithRequest: int64(2), ResponseStatus: 404, ResponseBody: `{"message": "NotFound"}`, WithError: starr.ErrInvalidStatusCode, @@ -219,7 +219,7 @@ func TestDeleteRootFolder(t *testing.T) { t.Parallel() mockServer := test.GetMockServer(t) client := sonarr.New(starr.New("mockAPIkey", mockServer.URL, 0)) - err := client.DeleteRootFolder(test.WithRequest.(int)) + err := client.DeleteRootFolder(test.WithRequest.(int64)) assert.ErrorIs(t, err, test.WithError, "error is not the same as expected") }) } diff --git a/sonarr/series_test.go b/sonarr/series_test.go index 8b8c807..669f348 100644 --- a/sonarr/series_test.go +++ b/sonarr/series_test.go @@ -686,7 +686,7 @@ func TestGetSeriesByID(t *testing.T) { tests := []*starr.TestMockData{ { Name: "200", - ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "series/2"), + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "series", "2"), ExpectedMethod: "GET", ResponseStatus: 200, ResponseBody: secondSeries, @@ -789,7 +789,7 @@ func TestGetSeriesByID(t *testing.T) { }, { Name: "404", - ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "series/2"), + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "series", "2"), ExpectedMethod: "GET", ResponseStatus: 404, ResponseBody: starr.BodyNotFound, diff --git a/sonarr/tag_test.go b/sonarr/tag_test.go index 4ac8396..367dc01 100644 --- a/sonarr/tag_test.go +++ b/sonarr/tag_test.go @@ -61,7 +61,7 @@ func TestGetTag(t *testing.T) { tests := []*starr.TestMockData{ { Name: "200", - ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "tag/1"), + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "tag", "1"), ExpectedMethod: "GET", ResponseStatus: 200, WithRequest: 1, @@ -74,7 +74,7 @@ func TestGetTag(t *testing.T) { }, { Name: "404", - ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "tag/1"), + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "tag", "1"), ExpectedMethod: "GET", ResponseStatus: 404, WithRequest: 1, @@ -151,7 +151,7 @@ func TestUpdateTag(t *testing.T) { tests := []*starr.TestMockData{ { Name: "200", - ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "tag/1"), + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "tag", "1"), ExpectedMethod: "PUT", ResponseStatus: 200, WithRequest: &starr.Tag{ @@ -168,7 +168,7 @@ func TestUpdateTag(t *testing.T) { }, { Name: "404", - ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "tag/1"), + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "tag", "1"), ExpectedMethod: "PUT", ResponseStatus: 404, WithRequest: &starr.Tag{ @@ -201,7 +201,7 @@ func TestDeleteTag(t *testing.T) { tests := []*starr.TestMockData{ { Name: "200", - ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "tag/1"), + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "tag", "1"), ExpectedMethod: "DELETE", WithRequest: 1, ResponseStatus: 200, @@ -210,7 +210,7 @@ func TestDeleteTag(t *testing.T) { }, { Name: "404", - ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "tag/1"), + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "tag", "1"), ExpectedMethod: "DELETE", WithRequest: 1, ResponseStatus: 404,