master
go 206 lines 5.23 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package prometheus
4
5 import (
6 "bytes"
7 "compress/gzip"
8 "context"
9 "errors"
10 "net/http"
11 "net/http/httptest"
12 "os"
13 "strings"
14 "testing"
15
16 "github.com/stretchr/testify/assert"
17 "github.com/stretchr/testify/require"
18
19 "github.com/netdata/netdata/go/plugins/pkg/prometheus/selector"
20 "github.com/netdata/netdata/go/plugins/pkg/web"
21 )
22
23 var (
24 testData, _ = os.ReadFile("testdata/testdata.txt")
25 testDataNoMeta, _ = os.ReadFile("testdata/testdata.nometa.txt")
26 )
27
28 func Test_testClientDataIsValid(t *testing.T) {
29 for name, data := range map[string][]byte{
30 "testData": testData,
31 } {
32 require.NotNilf(t, data, name)
33 }
34 }
35
36 func TestPrometheus404(t *testing.T) {
37 tsMux := http.NewServeMux()
38 tsMux.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) {
39 w.WriteHeader(404)
40 })
41 ts := httptest.NewServer(tsMux)
42 defer ts.Close()
43
44 req := web.RequestConfig{URL: ts.URL + "/metrics"}
45 prom := New(http.DefaultClient, req)
46 res, err := prom.ScrapeSeries()
47
48 assert.Error(t, err)
49 assert.Nil(t, res)
50 }
51
52 func TestPrometheusPlain(t *testing.T) {
53 tsMux := http.NewServeMux()
54 tsMux.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) {
55 _, _ = w.Write(testData)
56 })
57 ts := httptest.NewServer(tsMux)
58 defer ts.Close()
59
60 req := web.RequestConfig{URL: ts.URL + "/metrics"}
61 prom := New(http.DefaultClient, req)
62 res, err := prom.ScrapeSeries()
63
64 assert.NoError(t, err)
65 verifyTestData(t, res)
66 }
67
68 func TestPrometheusPlainWithSelector(t *testing.T) {
69 tsMux := http.NewServeMux()
70 tsMux.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) {
71 _, _ = w.Write(testData)
72 })
73 ts := httptest.NewServer(tsMux)
74 defer ts.Close()
75
76 req := web.RequestConfig{URL: ts.URL + "/metrics"}
77 sr, err := selector.Parse("go_gc*")
78 require.NoError(t, err)
79 prom := NewWithSelector(http.DefaultClient, req, sr)
80
81 res, err := prom.ScrapeSeries()
82 require.NoError(t, err)
83
84 for _, v := range res {
85 assert.Truef(t, strings.HasPrefix(v.Name(), "go_gc"), v.Name())
86 }
87 }
88
89 func TestPrometheusGzip(t *testing.T) {
90 counter := 0
91 rawTestData := [][]byte{testData, testDataNoMeta}
92 tsMux := http.NewServeMux()
93 tsMux.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) {
94 w.Header().Set("Content-Encoding", "gzip")
95 w.WriteHeader(200)
96 gz := new(bytes.Buffer)
97 ww := gzip.NewWriter(gz)
98 _, _ = ww.Write(rawTestData[counter])
99 _ = ww.Close()
100 _, _ = gz.WriteTo(w)
101 counter++
102 })
103 ts := httptest.NewServer(tsMux)
104 defer ts.Close()
105
106 req := web.RequestConfig{URL: ts.URL + "/metrics"}
107 prom := New(http.DefaultClient, req)
108
109 for range 2 {
110 res, err := prom.ScrapeSeries()
111 assert.NoError(t, err)
112 verifyTestData(t, res)
113 }
114 }
115
116 func TestPrometheusReadFromFile(t *testing.T) {
117 req := web.RequestConfig{URL: "file://testdata/testdata.txt"}
118
119 prom := NewWithSelector(http.DefaultClient, req, nil)
120
121 for range 2 {
122 res, err := prom.ScrapeSeries()
123 assert.NoError(t, err)
124 verifyTestData(t, res)
125 }
126
127 prom = New(http.DefaultClient, req)
128
129 for range 2 {
130 res, err := prom.ScrapeSeries()
131 assert.NoError(t, err)
132 verifyTestData(t, res)
133 }
134 }
135
136 func TestPrometheusScrapeWithTransform(t *testing.T) {
137 errBoom := errors.New("boom")
138
139 tests := map[string]struct {
140 transform func(seen *int) SampleTransform
141 wantErr error
142 check func(t *testing.T, mfs MetricFamilies, seen int)
143 }{
144 "nil transform assembles like Scrape": {
145 transform: func(*int) SampleTransform { return nil },
146 check: func(t *testing.T, mfs MetricFamilies, seen int) {
147 assert.Positive(t, mfs.Len())
148 assert.Zero(t, seen, "nil transform must not be invoked")
149 },
150 },
151 "transform sees every sample and keeps them": {
152 transform: func(seen *int) SampleTransform {
153 return func(s Sample) (Sample, bool, error) { *seen++; return s, true, nil }
154 },
155 check: func(t *testing.T, mfs MetricFamilies, seen int) {
156 assert.Positive(t, seen)
157 assert.Positive(t, mfs.Len())
158 },
159 },
160 "dropping every sample yields no families": {
161 transform: func(seen *int) SampleTransform {
162 return func(s Sample) (Sample, bool, error) { *seen++; return s, false, nil }
163 },
164 check: func(t *testing.T, mfs MetricFamilies, seen int) {
165 assert.Positive(t, seen)
166 assert.Zero(t, mfs.Len())
167 },
168 },
169 "transform error aborts the scrape": {
170 transform: func(*int) SampleTransform {
171 return func(s Sample) (Sample, bool, error) { return s, false, errBoom }
172 },
173 wantErr: errBoom,
174 },
175 }
176
177 for name, tc := range tests {
178 t.Run(name, func(t *testing.T) {
179 prom := New(http.DefaultClient, web.RequestConfig{URL: "file://testdata/testdata.txt"})
180
181 var seen int
182 mfs, err := prom.ScrapeWithTransform(context.Background(), tc.transform(&seen))
183
184 if tc.wantErr != nil {
185 assert.ErrorIs(t, err, tc.wantErr)
186 return
187 }
188 require.NoError(t, err)
189 tc.check(t, mfs, seen)
190 })
191 }
192 }
193
194 func verifyTestData(t *testing.T, ms Series) {
195 assert.Equal(t, 410, len(ms))
196 assert.Equal(t, "go_gc_duration_seconds", ms[0].Labels.Get("__name__"))
197 assert.Equal(t, "0.25", ms[0].Labels.Get("quantile"))
198 assert.InDelta(t, 4.9351e-05, ms[0].Value, 0.0001)
199
200 notExistYet := ms.FindByName("not_exist_yet")
201 assert.NotNil(t, notExistYet)
202 assert.Len(t, notExistYet, 0)
203
204 targetInterval := ms.FindByName("prometheus_target_interval_length_seconds")
205 assert.Len(t, targetInterval, 5)
206 }