mirror of
https://github.com/onedr0p/exportarr.git
synced 2026-02-06 10:57:32 +00:00
* fix: Fix #230, Add tests for shared collectors Signed-off-by: Russell Troxel <russell@troxel.io> * Fix lint: common TestServer Signed-off-by: Russell Troxel <russell@troxel.io> --------- Signed-off-by: Russell Troxel <russell@troxel.io>
This commit is contained in:
parent
3155232c1f
commit
8c51f278ff
4
go.sum
4
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=
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
107
internal/arr/collector/health_test.go
Normal file
107
internal/arr/collector/health_test.go
Normal file
@ -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")
|
||||
}
|
||||
107
internal/arr/collector/history_test.go
Normal file
107
internal/arr/collector/history_test.go
Normal file
@ -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")
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
@ -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))
|
||||
|
||||
|
||||
@ -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" {
|
||||
|
||||
107
internal/arr/collector/queue_test.go
Normal file
107
internal/arr/collector/queue_test.go
Normal file
@ -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")
|
||||
}
|
||||
107
internal/arr/collector/rootfolder_test.go
Normal file
107
internal/arr/collector/rootfolder_test.go
Normal file
@ -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")
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
107
internal/arr/collector/status_test.go
Normal file
107
internal/arr/collector/status_test.go
Normal file
@ -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")
|
||||
}
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -0,0 +1,3 @@
|
||||
# HELP APP_system_status System Status
|
||||
# TYPE APP_system_status gauge
|
||||
APP_system_status{url="SOMEURL"} 1
|
||||
8
internal/arr/test_fixtures/common/v1_health.json
Normal file
8
internal/arr/test_fixtures/common/v1_health.json
Normal file
@ -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"
|
||||
}
|
||||
]
|
||||
8
internal/arr/test_fixtures/common/v1_history.json
Normal file
8
internal/arr/test_fixtures/common/v1_history.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"page": 1,
|
||||
"pageSize": 10,
|
||||
"sortKey": "date",
|
||||
"sortDirection": "descending",
|
||||
"totalRecords": 1368,
|
||||
"records": []
|
||||
}
|
||||
69
internal/arr/test_fixtures/common/v1_queue.json
Normal file
69
internal/arr/test_fixtures/common/v1_queue.json
Normal file
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
6
internal/arr/test_fixtures/common/v1_rootfolder.json
Normal file
6
internal/arr/test_fixtures/common/v1_rootfolder.json
Normal file
@ -0,0 +1,6 @@
|
||||
[
|
||||
{
|
||||
"path": "/media/books/",
|
||||
"freeSpace": 32147635175424
|
||||
}
|
||||
]
|
||||
32
internal/arr/test_fixtures/common/v1_system_status.json
Normal file
32
internal/arr/test_fixtures/common/v1_system_status.json
Normal file
@ -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"
|
||||
}
|
||||
8
internal/arr/test_fixtures/common/v3_health.json
Normal file
8
internal/arr/test_fixtures/common/v3_health.json
Normal file
@ -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"
|
||||
}
|
||||
]
|
||||
8
internal/arr/test_fixtures/common/v3_history.json
Normal file
8
internal/arr/test_fixtures/common/v3_history.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"page": 1,
|
||||
"pageSize": 10,
|
||||
"sortKey": "date",
|
||||
"sortDirection": "descending",
|
||||
"totalRecords": 1368,
|
||||
"records": []
|
||||
}
|
||||
69
internal/arr/test_fixtures/common/v3_queue.json
Normal file
69
internal/arr/test_fixtures/common/v3_queue.json
Normal file
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
6
internal/arr/test_fixtures/common/v3_rootfolder.json
Normal file
6
internal/arr/test_fixtures/common/v3_rootfolder.json
Normal file
@ -0,0 +1,6 @@
|
||||
[
|
||||
{
|
||||
"path": "/media/books/",
|
||||
"freeSpace": 32147635175424
|
||||
}
|
||||
]
|
||||
32
internal/arr/test_fixtures/common/v3_system_status.json
Normal file
32
internal/arr/test_fixtures/common/v3_system_status.json
Normal file
@ -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"
|
||||
}
|
||||
5
internal/test_util/const.go
Normal file
5
internal/test_util/const.go
Normal file
@ -0,0 +1,5 @@
|
||||
package test_util
|
||||
|
||||
const API_KEY = "abcdef0123456789abcdef0123456789"
|
||||
const FIXTURES_DIR = "../test_fixtures/"
|
||||
const COMMON_FIXTURES_PATH = FIXTURES_DIR + "common/"
|
||||
32
internal/test_util/test_util.go
Normal file
32
internal/test_util/test_util.go
Normal file
@ -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)
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user