diff --git a/go.sum b/go.sum index a530430..a1c53f8 100644 --- a/go.sum +++ b/go.sum @@ -68,16 +68,12 @@ go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= diff --git a/internal/arr/collector/bazarr_test.go b/internal/arr/collector/bazarr_test.go index d330797..98d4e9f 100644 --- a/internal/arr/collector/bazarr_test.go +++ b/internal/arr/collector/bazarr_test.go @@ -8,26 +8,15 @@ import ( "testing" "github.com/onedr0p/exportarr/internal/arr/config" + "github.com/onedr0p/exportarr/internal/test_util" "github.com/prometheus/client_golang/prometheus/testutil" "github.com/stretchr/testify/require" ) -const test_fixtures_path = "../test_fixtures/bazarr/" -const API_KEY = "abcdef0123456789abcdef0123456789" +const bazarr_test_fixtures_path = "../test_fixtures/bazarr/" func newTestBazarrServer(t *testing.T, fn func(http.ResponseWriter, *http.Request)) (*httptest.Server, error) { - return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fn(w, r) - require.NotEmpty(t, r.URL.Path) - // turns /api/some/path into some_path - endpoint := strings.Replace(strings.Replace(r.URL.Path, "/api/", "", -1), "/", "_", -1) - w.WriteHeader(http.StatusOK) - // NOTE: this assumes there is a file that matches the some_path - json, err := os.ReadFile(test_fixtures_path + endpoint + ".json") - require.NoError(t, err) - _, err = w.Write(json) - require.NoError(t, err) - })), nil + return test_util.NewTestServer(t, bazarr_test_fixtures_path, fn) } func TestBazarrCollect(t *testing.T) { @@ -42,12 +31,12 @@ func TestBazarrCollect(t *testing.T) { config := &config.ArrConfig{ URL: ts.URL, App: "bazarr", - ApiKey: API_KEY, + ApiKey: test_util.API_KEY, } collector := NewBazarrCollector(config) require.NoError(err) - b, err := os.ReadFile(test_fixtures_path + "expected_metrics.txt") + b, err := os.ReadFile(bazarr_test_fixtures_path + "expected_metrics.txt") require.NoError(err) expected := strings.Replace(string(b), "SOMEURL", ts.URL, -1) @@ -109,7 +98,7 @@ func TestBazarrCollect_FailureDoesntPanic(t *testing.T) { config := &config.ArrConfig{ URL: ts.URL, - ApiKey: API_KEY, + ApiKey: test_util.API_KEY, } collector := NewBazarrCollector(config) diff --git a/internal/arr/collector/health_test.go b/internal/arr/collector/health_test.go new file mode 100644 index 0000000..448251a --- /dev/null +++ b/internal/arr/collector/health_test.go @@ -0,0 +1,107 @@ +package collector + +import ( + "net/http" + "net/http/httptest" + "os" + "strings" + "testing" + + "github.com/onedr0p/exportarr/internal/arr/config" + "github.com/onedr0p/exportarr/internal/test_util" + "github.com/prometheus/client_golang/prometheus/testutil" + "github.com/stretchr/testify/require" +) + +func TestSystemHealthCollect(t *testing.T) { + var tests = []struct { + name string + config *config.ArrConfig + path string + }{ + { + name: "radarr", + config: &config.ArrConfig{ + App: "radarr", + ApiVersion: "v3", + }, + path: "/api/v3/health", + }, + { + name: "sonarr", + config: &config.ArrConfig{ + App: "sonarr", + ApiVersion: "v3", + }, + path: "/api/v3/health", + }, + { + name: "lidarr", + config: &config.ArrConfig{ + App: "lidarr", + ApiVersion: "v1", + }, + path: "/api/v1/health", + }, + { + name: "readarr", + config: &config.ArrConfig{ + App: "readarr", + ApiVersion: "v1", + }, + path: "/api/v1/health", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + ts, err := test_util.NewTestSharedServer(t, func(w http.ResponseWriter, r *http.Request) { + require.Contains(r.URL.Path, tt.path) + }) + require.NoError(err) + + defer ts.Close() + + tt.config.URL = ts.URL + tt.config.ApiKey = test_util.API_KEY + + collector := NewSystemHealthCollector(tt.config) + + b, err := os.ReadFile(test_util.COMMON_FIXTURES_PATH + "expected_health_metrics.txt") + require.NoError(err) + + expected := strings.Replace(string(b), "SOMEURL", ts.URL, -1) + expected = strings.Replace(expected, "APP", tt.config.App, -1) + + f := strings.NewReader(expected) + + require.NotPanics(func() { + err = testutil.CollectAndCompare(collector, f) + }) + require.NoError(err) + }) + } +} + +func TestSystemHealthCollect_FailureDoesntPanic(t *testing.T) { + require := require.New(t) + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusInternalServerError) + })) + defer ts.Close() + + config := &config.ArrConfig{ + URL: ts.URL, + ApiKey: test_util.API_KEY, + } + collector := NewSystemHealthCollector(config) + + f := strings.NewReader("") + + require.NotPanics(func() { + err := testutil.CollectAndCompare(collector, f) + require.Error(err) + }, "Collecting metrics should not panic on failure") +} diff --git a/internal/arr/collector/history_test.go b/internal/arr/collector/history_test.go new file mode 100644 index 0000000..7f30e56 --- /dev/null +++ b/internal/arr/collector/history_test.go @@ -0,0 +1,107 @@ +package collector + +import ( + "net/http" + "net/http/httptest" + "os" + "strings" + "testing" + + "github.com/onedr0p/exportarr/internal/arr/config" + "github.com/onedr0p/exportarr/internal/test_util" + "github.com/prometheus/client_golang/prometheus/testutil" + "github.com/stretchr/testify/require" +) + +func TestHistoryCollect(t *testing.T) { + var tests = []struct { + name string + config *config.ArrConfig + path string + }{ + { + name: "radarr", + config: &config.ArrConfig{ + App: "radarr", + ApiVersion: "v3", + }, + path: "/api/v3/history", + }, + { + name: "sonarr", + config: &config.ArrConfig{ + App: "sonarr", + ApiVersion: "v3", + }, + path: "/api/v3/history", + }, + { + name: "lidarr", + config: &config.ArrConfig{ + App: "lidarr", + ApiVersion: "v1", + }, + path: "/api/v1/history", + }, + { + name: "readarr", + config: &config.ArrConfig{ + App: "readarr", + ApiVersion: "v1", + }, + path: "/api/v1/history", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + ts, err := test_util.NewTestSharedServer(t, func(w http.ResponseWriter, r *http.Request) { + require.Contains(r.URL.Path, tt.path) + }) + require.NoError(err) + + defer ts.Close() + + tt.config.URL = ts.URL + tt.config.ApiKey = test_util.API_KEY + + collector := NewHistoryCollector(tt.config) + + b, err := os.ReadFile(test_util.COMMON_FIXTURES_PATH + "expected_history_metrics.txt") + require.NoError(err) + + expected := strings.Replace(string(b), "SOMEURL", ts.URL, -1) + expected = strings.Replace(expected, "APP", tt.config.App, -1) + + f := strings.NewReader(expected) + + require.NotPanics(func() { + err = testutil.CollectAndCompare(collector, f) + }) + require.NoError(err) + }) + } +} + +func TestHistoryCollect_FailureDoesntPanic(t *testing.T) { + require := require.New(t) + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusInternalServerError) + })) + defer ts.Close() + + config := &config.ArrConfig{ + URL: ts.URL, + ApiKey: test_util.API_KEY, + } + collector := NewHistoryCollector(config) + + f := strings.NewReader("") + + require.NotPanics(func() { + err := testutil.CollectAndCompare(collector, f) + require.Error(err) + }, "Collecting metrics should not panic on failure") +} diff --git a/internal/arr/collector/lidarr.go b/internal/arr/collector/lidarr.go index 4000b71..f54dd46 100644 --- a/internal/arr/collector/lidarr.go +++ b/internal/arr/collector/lidarr.go @@ -169,7 +169,7 @@ func (collector *lidarrCollector) Collect(ch chan<- prometheus.Metric) { if collector.config.EnableAdditionalMetrics { songFile := model.SongFile{} - var params client.QueryParams + params := client.QueryParams{} params.Add("artistid", fmt.Sprintf("%d", s.Id)) if err := c.DoRequest("trackfile", &songFile, params); err != nil { diff --git a/internal/arr/collector/prowlarr.go b/internal/arr/collector/prowlarr.go index d67aa81..b756327 100644 --- a/internal/arr/collector/prowlarr.go +++ b/internal/arr/collector/prowlarr.go @@ -318,7 +318,7 @@ func (collector *prowlarrCollector) Collect(ch chan<- prometheus.Metric) { startDate := collector.lastStatUpdate.In(time.UTC) endDate := time.Now().In(time.UTC) - var params client.QueryParams + params := client.QueryParams{} params.Add("startDate", startDate.Format(time.RFC3339)) params.Add("endDate", endDate.Format(time.RFC3339)) diff --git a/internal/arr/collector/queue.go b/internal/arr/collector/queue.go index c57f790..f6696a5 100644 --- a/internal/arr/collector/queue.go +++ b/internal/arr/collector/queue.go @@ -48,7 +48,7 @@ func (collector *queueCollector) Collect(ch chan<- prometheus.Metric) { return } - var params client.QueryParams + params := client.QueryParams{} params.Add("page", "1") if collector.config.EnableUnknownQueueItems { if collector.config.App == "sonarr" { diff --git a/internal/arr/collector/queue_test.go b/internal/arr/collector/queue_test.go new file mode 100644 index 0000000..ddea91f --- /dev/null +++ b/internal/arr/collector/queue_test.go @@ -0,0 +1,107 @@ +package collector + +import ( + "net/http" + "net/http/httptest" + "os" + "strings" + "testing" + + "github.com/onedr0p/exportarr/internal/arr/config" + "github.com/onedr0p/exportarr/internal/test_util" + "github.com/prometheus/client_golang/prometheus/testutil" + "github.com/stretchr/testify/require" +) + +func TestQueueCollect(t *testing.T) { + var tests = []struct { + name string + config *config.ArrConfig + path string + }{ + { + name: "radarr", + config: &config.ArrConfig{ + App: "radarr", + ApiVersion: "v3", + }, + path: "/api/v3/queue", + }, + { + name: "sonarr", + config: &config.ArrConfig{ + App: "sonarr", + ApiVersion: "v3", + }, + path: "/api/v3/queue", + }, + { + name: "lidarr", + config: &config.ArrConfig{ + App: "lidarr", + ApiVersion: "v1", + }, + path: "/api/v1/queue", + }, + { + name: "readarr", + config: &config.ArrConfig{ + App: "readarr", + ApiVersion: "v1", + }, + path: "/api/v1/queue", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + ts, err := test_util.NewTestSharedServer(t, func(w http.ResponseWriter, r *http.Request) { + require.Contains(r.URL.Path, tt.path) + }) + require.NoError(err) + + defer ts.Close() + + tt.config.URL = ts.URL + tt.config.ApiKey = test_util.API_KEY + + collector := NewQueueCollector(tt.config) + + b, err := os.ReadFile(test_util.COMMON_FIXTURES_PATH + "expected_queue_metrics.txt") + require.NoError(err) + + expected := strings.Replace(string(b), "SOMEURL", ts.URL, -1) + expected = strings.Replace(expected, "APP", tt.config.App, -1) + + f := strings.NewReader(expected) + + require.NotPanics(func() { + err = testutil.CollectAndCompare(collector, f) + }) + require.NoError(err) + }) + } +} + +func TestQueueCollect_FailureDoesntPanic(t *testing.T) { + require := require.New(t) + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusInternalServerError) + })) + defer ts.Close() + + config := &config.ArrConfig{ + URL: ts.URL, + ApiKey: test_util.API_KEY, + } + collector := NewQueueCollector(config) + + f := strings.NewReader("") + + require.NotPanics(func() { + err := testutil.CollectAndCompare(collector, f) + require.Error(err) + }, "Collecting metrics should not panic on failure") +} diff --git a/internal/arr/collector/rootfolder_test.go b/internal/arr/collector/rootfolder_test.go new file mode 100644 index 0000000..f203119 --- /dev/null +++ b/internal/arr/collector/rootfolder_test.go @@ -0,0 +1,107 @@ +package collector + +import ( + "net/http" + "net/http/httptest" + "os" + "strings" + "testing" + + "github.com/onedr0p/exportarr/internal/arr/config" + "github.com/onedr0p/exportarr/internal/test_util" + "github.com/prometheus/client_golang/prometheus/testutil" + "github.com/stretchr/testify/require" +) + +func TestRootFolderCollect(t *testing.T) { + var tests = []struct { + name string + config *config.ArrConfig + path string + }{ + { + name: "radarr", + config: &config.ArrConfig{ + App: "radarr", + ApiVersion: "v3", + }, + path: "/api/v3/rootfolder", + }, + { + name: "sonarr", + config: &config.ArrConfig{ + App: "sonarr", + ApiVersion: "v3", + }, + path: "/api/v3/rootfolder", + }, + { + name: "lidarr", + config: &config.ArrConfig{ + App: "lidarr", + ApiVersion: "v1", + }, + path: "/api/v1/rootfolder", + }, + { + name: "readarr", + config: &config.ArrConfig{ + App: "readarr", + ApiVersion: "v1", + }, + path: "/api/v1/rootfolder", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + ts, err := test_util.NewTestSharedServer(t, func(w http.ResponseWriter, r *http.Request) { + require.Contains(r.URL.Path, tt.path) + }) + require.NoError(err) + + defer ts.Close() + + tt.config.URL = ts.URL + tt.config.ApiKey = test_util.API_KEY + + collector := NewRootFolderCollector(tt.config) + + b, err := os.ReadFile(test_util.COMMON_FIXTURES_PATH + "expected_rootfolder_metrics.txt") + require.NoError(err) + + expected := strings.Replace(string(b), "SOMEURL", ts.URL, -1) + expected = strings.Replace(expected, "APP", tt.config.App, -1) + + f := strings.NewReader(expected) + + require.NotPanics(func() { + err = testutil.CollectAndCompare(collector, f) + }) + require.NoError(err) + }) + } +} + +func TestRootFolderCollect_FailureDoesntPanic(t *testing.T) { + require := require.New(t) + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusInternalServerError) + })) + defer ts.Close() + + config := &config.ArrConfig{ + URL: ts.URL, + ApiKey: test_util.API_KEY, + } + collector := NewRootFolderCollector(config) + + f := strings.NewReader("") + + require.NotPanics(func() { + err := testutil.CollectAndCompare(collector, f) + require.Error(err) + }, "Collecting metrics should not panic on failure") +} diff --git a/internal/arr/collector/sonarr.go b/internal/arr/collector/sonarr.go index d0751b3..69df535 100644 --- a/internal/arr/collector/sonarr.go +++ b/internal/arr/collector/sonarr.go @@ -220,7 +220,7 @@ func (collector *sonarrCollector) Collect(ch chan<- prometheus.Metric) { textra := time.Now() episodeFile := model.EpisodeFile{} - var params client.QueryParams + params := client.QueryParams{} params.Add("seriesId", fmt.Sprintf("%d", s.Id)) if err := c.DoRequest("episodefile", &episodeFile, params); err != nil { @@ -261,7 +261,7 @@ func (collector *sonarrCollector) Collect(ch chan<- prometheus.Metric) { episodesMissing := model.Missing{} - var params client.QueryParams + params := client.QueryParams{} params.Add("sortKey", "airDateUtc") if err := c.DoRequest("wanted/missing", &episodesMissing, params); err != nil { diff --git a/internal/arr/collector/status_test.go b/internal/arr/collector/status_test.go new file mode 100644 index 0000000..947f854 --- /dev/null +++ b/internal/arr/collector/status_test.go @@ -0,0 +1,107 @@ +package collector + +import ( + "net/http" + "net/http/httptest" + "os" + "strings" + "testing" + + "github.com/onedr0p/exportarr/internal/arr/config" + "github.com/onedr0p/exportarr/internal/test_util" + "github.com/prometheus/client_golang/prometheus/testutil" + "github.com/stretchr/testify/require" +) + +func TestStatusCollect(t *testing.T) { + var tests = []struct { + name string + config *config.ArrConfig + path string + }{ + { + name: "radarr", + config: &config.ArrConfig{ + App: "radarr", + ApiVersion: "v3", + }, + path: "/api/v3/system/status", + }, + { + name: "sonarr", + config: &config.ArrConfig{ + App: "sonarr", + ApiVersion: "v3", + }, + path: "/api/v3/system/status", + }, + { + name: "lidarr", + config: &config.ArrConfig{ + App: "lidarr", + ApiVersion: "v1", + }, + path: "/api/v1/system/status", + }, + { + name: "readarr", + config: &config.ArrConfig{ + App: "readarr", + ApiVersion: "v1", + }, + path: "/api/v1/system/status", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + ts, err := test_util.NewTestSharedServer(t, func(w http.ResponseWriter, r *http.Request) { + require.Contains(r.URL.Path, tt.path) + }) + require.NoError(err) + + defer ts.Close() + + tt.config.URL = ts.URL + tt.config.ApiKey = test_util.API_KEY + + collector := NewSystemStatusCollector(tt.config) + + b, err := os.ReadFile(test_util.COMMON_FIXTURES_PATH + "expected_status_metrics.txt") + require.NoError(err) + + expected := strings.Replace(string(b), "SOMEURL", ts.URL, -1) + expected = strings.Replace(expected, "APP", tt.config.App, -1) + + f := strings.NewReader(expected) + + require.NotPanics(func() { + err = testutil.CollectAndCompare(collector, f) + }) + require.NoError(err) + }) + } +} + +func TestStatusCollect_FailureDoesntPanic(t *testing.T) { + require := require.New(t) + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusInternalServerError) + })) + defer ts.Close() + + config := &config.ArrConfig{ + URL: ts.URL, + ApiKey: test_util.API_KEY, + } + collector := NewSystemStatusCollector(config) + + f := strings.NewReader("") + + require.NotPanics(func() { + err := testutil.CollectAndCompare(collector, f) + require.Error(err) + }, "Collecting metrics should not panic on failure") +} diff --git a/internal/arr/test_fixtures/common/expected_health_metrics.txt b/internal/arr/test_fixtures/common/expected_health_metrics.txt new file mode 100644 index 0000000..8832965 --- /dev/null +++ b/internal/arr/test_fixtures/common/expected_health_metrics.txt @@ -0,0 +1,3 @@ +# HELP APP_system_health_issues Total number of health issues by source, type, message and wikiurl +# TYPE APP_system_health_issues gauge +APP_system_health_issues{message="Indexers unavailable due to failures for more than 6 hours: SomeIndexer",source="IndexerLongTermStatusCheck",type="warning",url="SOMEURL",wikiurl="https://wiki.servarr.com/readarr/system#indexers-are-unavailable-due-to-failures"} 1 diff --git a/internal/arr/test_fixtures/common/expected_history_metrics.txt b/internal/arr/test_fixtures/common/expected_history_metrics.txt new file mode 100644 index 0000000..d6461b8 --- /dev/null +++ b/internal/arr/test_fixtures/common/expected_history_metrics.txt @@ -0,0 +1,3 @@ +# HELP APP_history_total Total number of item in the history +# TYPE APP_history_total gauge +APP_history_total{url="SOMEURL"} 1368 diff --git a/internal/arr/test_fixtures/common/expected_queue_metrics.txt b/internal/arr/test_fixtures/common/expected_queue_metrics.txt new file mode 100644 index 0000000..f691eb4 --- /dev/null +++ b/internal/arr/test_fixtures/common/expected_queue_metrics.txt @@ -0,0 +1,3 @@ +# HELP APP_queue_total Total number of items in the queue by status, download_status, and download_state +# TYPE APP_queue_total gauge +APP_queue_total{download_state="downloading",download_status="warning",status="completed",url="SOMEURL"} 1 diff --git a/internal/arr/test_fixtures/common/expected_rootfolder_metrics.txt b/internal/arr/test_fixtures/common/expected_rootfolder_metrics.txt new file mode 100644 index 0000000..c754e0d --- /dev/null +++ b/internal/arr/test_fixtures/common/expected_rootfolder_metrics.txt @@ -0,0 +1,3 @@ +# HELP APP_rootfolder_freespace_bytes Root folder space in bytes by path +# TYPE APP_rootfolder_freespace_bytes gauge +APP_rootfolder_freespace_bytes{path="/media/books/",url="SOMEURL"} 3.2147635175424e+13 diff --git a/internal/arr/test_fixtures/common/expected_status_metrics.txt b/internal/arr/test_fixtures/common/expected_status_metrics.txt new file mode 100644 index 0000000..605bdc3 --- /dev/null +++ b/internal/arr/test_fixtures/common/expected_status_metrics.txt @@ -0,0 +1,3 @@ +# HELP APP_system_status System Status +# TYPE APP_system_status gauge +APP_system_status{url="SOMEURL"} 1 diff --git a/internal/arr/test_fixtures/common/v1_health.json b/internal/arr/test_fixtures/common/v1_health.json new file mode 100644 index 0000000..155a404 --- /dev/null +++ b/internal/arr/test_fixtures/common/v1_health.json @@ -0,0 +1,8 @@ +[ + { + "source": "IndexerLongTermStatusCheck", + "type": "warning", + "message": "Indexers unavailable due to failures for more than 6 hours: SomeIndexer", + "wikiUrl": "https://wiki.servarr.com/readarr/system#indexers-are-unavailable-due-to-failures" + } + ] \ No newline at end of file diff --git a/internal/arr/test_fixtures/common/v1_history.json b/internal/arr/test_fixtures/common/v1_history.json new file mode 100644 index 0000000..7914f4e --- /dev/null +++ b/internal/arr/test_fixtures/common/v1_history.json @@ -0,0 +1,8 @@ +{ + "page": 1, + "pageSize": 10, + "sortKey": "date", + "sortDirection": "descending", + "totalRecords": 1368, + "records": [] + } \ No newline at end of file diff --git a/internal/arr/test_fixtures/common/v1_queue.json b/internal/arr/test_fixtures/common/v1_queue.json new file mode 100644 index 0000000..7c641e7 --- /dev/null +++ b/internal/arr/test_fixtures/common/v1_queue.json @@ -0,0 +1,69 @@ +{ + "page": 1, + "pageSize": 10, + "sortKey": "timeleft", + "sortDirection": "ascending", + "totalRecords": 1, + "records": [ + { + "movieId": 91, + "languages": [], + "quality": { + "quality": { + "id": 7, + "name": "Bluray-1080p", + "source": "bluray", + "resolution": 1080, + "modifier": "none" + }, + "revision": { + "version": 2, + "real": 0, + "isRepack": false + } + }, + "customFormats": [ + { + "id": 6, + "name": "Special Edition" + }, + { + "id": 8, + "name": "Repack/Proper" + }, + { + "id": 27, + "name": "DTS-ES" + }, + { + "id": 44, + "name": "HD Bluray Tier 02" + } + ], + "customFormatScore": 1880, + "size": 24397988960, + "title": "Some.Movie.1.Has.A.Title-1080P", + "sizeleft": 0, + "timeleft": "00:00:00", + "estimatedCompletionTime": "2023-10-17T23:23:09Z", + "status": "completed", + "trackedDownloadStatus": "warning", + "trackedDownloadState": "downloading", + "statusMessages": [ + { + "title": "Some.Movie.1.Has.A.Title-1080P", + "messages": [ + "Found matching movie via grab history, but release was matched to movie by ID. Manual Import required." + ] + } + ], + "errorMessage": "", + "downloadId": "SABnzbd_nzo_asdf1234", + "protocol": "usenet", + "downloadClient": "SabNZBd", + "indexer": "Some Indexer", + "outputPath": "/media/.downloads/complete/movies/Some.Movie.1.Has.A.Title-1080P", + "id": 8537983 + } + ] + } \ No newline at end of file diff --git a/internal/arr/test_fixtures/common/v1_rootfolder.json b/internal/arr/test_fixtures/common/v1_rootfolder.json new file mode 100644 index 0000000..27cfa47 --- /dev/null +++ b/internal/arr/test_fixtures/common/v1_rootfolder.json @@ -0,0 +1,6 @@ +[ + { + "path": "/media/books/", + "freeSpace": 32147635175424 + } + ] \ No newline at end of file diff --git a/internal/arr/test_fixtures/common/v1_system_status.json b/internal/arr/test_fixtures/common/v1_system_status.json new file mode 100644 index 0000000..9b7b054 --- /dev/null +++ b/internal/arr/test_fixtures/common/v1_system_status.json @@ -0,0 +1,32 @@ +{ + "appName": "Radarr", + "instanceName": "Radarr", + "version": "5.0.3.8127", + "buildTime": "2023-10-07T22:36:20Z", + "isDebug": false, + "isProduction": true, + "isAdmin": false, + "isUserInteractive": true, + "startupPath": "/app/bin", + "appData": "/config", + "osName": "alpine", + "osVersion": "3.18.4", + "isNetCore": true, + "isLinux": true, + "isOsx": false, + "isWindows": false, + "isDocker": false, + "mode": "console", + "branch": "develop", + "databaseType": "sqLite", + "databaseVersion": "3.41.2", + "authentication": "none", + "migrationVersion": 233, + "urlBase": "", + "runtimeVersion": "6.0.21", + "runtimeName": "netcore", + "startTime": "2023-10-13T20:45:26Z", + "packageVersion": "5.0.3.8127", + "packageAuthor": "[onedr0p](https://github.com/onedr0p)", + "packageUpdateMechanism": "docker" +} \ No newline at end of file diff --git a/internal/arr/test_fixtures/common/v3_health.json b/internal/arr/test_fixtures/common/v3_health.json new file mode 100644 index 0000000..155a404 --- /dev/null +++ b/internal/arr/test_fixtures/common/v3_health.json @@ -0,0 +1,8 @@ +[ + { + "source": "IndexerLongTermStatusCheck", + "type": "warning", + "message": "Indexers unavailable due to failures for more than 6 hours: SomeIndexer", + "wikiUrl": "https://wiki.servarr.com/readarr/system#indexers-are-unavailable-due-to-failures" + } + ] \ No newline at end of file diff --git a/internal/arr/test_fixtures/common/v3_history.json b/internal/arr/test_fixtures/common/v3_history.json new file mode 100644 index 0000000..7914f4e --- /dev/null +++ b/internal/arr/test_fixtures/common/v3_history.json @@ -0,0 +1,8 @@ +{ + "page": 1, + "pageSize": 10, + "sortKey": "date", + "sortDirection": "descending", + "totalRecords": 1368, + "records": [] + } \ No newline at end of file diff --git a/internal/arr/test_fixtures/common/v3_queue.json b/internal/arr/test_fixtures/common/v3_queue.json new file mode 100644 index 0000000..7c641e7 --- /dev/null +++ b/internal/arr/test_fixtures/common/v3_queue.json @@ -0,0 +1,69 @@ +{ + "page": 1, + "pageSize": 10, + "sortKey": "timeleft", + "sortDirection": "ascending", + "totalRecords": 1, + "records": [ + { + "movieId": 91, + "languages": [], + "quality": { + "quality": { + "id": 7, + "name": "Bluray-1080p", + "source": "bluray", + "resolution": 1080, + "modifier": "none" + }, + "revision": { + "version": 2, + "real": 0, + "isRepack": false + } + }, + "customFormats": [ + { + "id": 6, + "name": "Special Edition" + }, + { + "id": 8, + "name": "Repack/Proper" + }, + { + "id": 27, + "name": "DTS-ES" + }, + { + "id": 44, + "name": "HD Bluray Tier 02" + } + ], + "customFormatScore": 1880, + "size": 24397988960, + "title": "Some.Movie.1.Has.A.Title-1080P", + "sizeleft": 0, + "timeleft": "00:00:00", + "estimatedCompletionTime": "2023-10-17T23:23:09Z", + "status": "completed", + "trackedDownloadStatus": "warning", + "trackedDownloadState": "downloading", + "statusMessages": [ + { + "title": "Some.Movie.1.Has.A.Title-1080P", + "messages": [ + "Found matching movie via grab history, but release was matched to movie by ID. Manual Import required." + ] + } + ], + "errorMessage": "", + "downloadId": "SABnzbd_nzo_asdf1234", + "protocol": "usenet", + "downloadClient": "SabNZBd", + "indexer": "Some Indexer", + "outputPath": "/media/.downloads/complete/movies/Some.Movie.1.Has.A.Title-1080P", + "id": 8537983 + } + ] + } \ No newline at end of file diff --git a/internal/arr/test_fixtures/common/v3_rootfolder.json b/internal/arr/test_fixtures/common/v3_rootfolder.json new file mode 100644 index 0000000..27cfa47 --- /dev/null +++ b/internal/arr/test_fixtures/common/v3_rootfolder.json @@ -0,0 +1,6 @@ +[ + { + "path": "/media/books/", + "freeSpace": 32147635175424 + } + ] \ No newline at end of file diff --git a/internal/arr/test_fixtures/common/v3_system_status.json b/internal/arr/test_fixtures/common/v3_system_status.json new file mode 100644 index 0000000..9b7b054 --- /dev/null +++ b/internal/arr/test_fixtures/common/v3_system_status.json @@ -0,0 +1,32 @@ +{ + "appName": "Radarr", + "instanceName": "Radarr", + "version": "5.0.3.8127", + "buildTime": "2023-10-07T22:36:20Z", + "isDebug": false, + "isProduction": true, + "isAdmin": false, + "isUserInteractive": true, + "startupPath": "/app/bin", + "appData": "/config", + "osName": "alpine", + "osVersion": "3.18.4", + "isNetCore": true, + "isLinux": true, + "isOsx": false, + "isWindows": false, + "isDocker": false, + "mode": "console", + "branch": "develop", + "databaseType": "sqLite", + "databaseVersion": "3.41.2", + "authentication": "none", + "migrationVersion": 233, + "urlBase": "", + "runtimeVersion": "6.0.21", + "runtimeName": "netcore", + "startTime": "2023-10-13T20:45:26Z", + "packageVersion": "5.0.3.8127", + "packageAuthor": "[onedr0p](https://github.com/onedr0p)", + "packageUpdateMechanism": "docker" +} \ No newline at end of file diff --git a/internal/test_util/const.go b/internal/test_util/const.go new file mode 100644 index 0000000..b2b7d45 --- /dev/null +++ b/internal/test_util/const.go @@ -0,0 +1,5 @@ +package test_util + +const API_KEY = "abcdef0123456789abcdef0123456789" +const FIXTURES_DIR = "../test_fixtures/" +const COMMON_FIXTURES_PATH = FIXTURES_DIR + "common/" diff --git a/internal/test_util/test_util.go b/internal/test_util/test_util.go new file mode 100644 index 0000000..8246f72 --- /dev/null +++ b/internal/test_util/test_util.go @@ -0,0 +1,32 @@ +package test_util + +import ( + "os" + + "net/http" + "net/http/httptest" + "path/filepath" + "strings" + "testing" + + "github.com/stretchr/testify/require" +) + +func NewTestServer(t *testing.T, fixture_dir string, fn func(http.ResponseWriter, *http.Request)) (*httptest.Server, error) { + return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fn(w, r) + require.NotEmpty(t, r.URL.Path) + // turns /api/some/path into some_path + endpoint := strings.Replace(strings.Replace(r.URL.Path, "/api/", "", -1), "/", "_", -1) + w.WriteHeader(http.StatusOK) + // NOTE: this assumes there is a file that matches the some_path + json, err := os.ReadFile(filepath.Join(fixture_dir, endpoint+".json")) + require.NoError(t, err) + _, err = w.Write(json) + require.NoError(t, err) + })), nil +} + +func NewTestSharedServer(t *testing.T, fn func(http.ResponseWriter, *http.Request)) (*httptest.Server, error) { + return NewTestServer(t, COMMON_FIXTURES_PATH, fn) +}