1 package prometheus
2
3 import (
4 "io/ioutil"
5 "math"
6 "math/rand"
7 "net/http"
8 "net/http/httptest"
9 "reflect"
10 "regexp"
11 "strconv"
12 "strings"
13 "testing"
14
15 "github.com/go-kit/kit/metrics/teststat"
16 stdprometheus "github.com/prometheus/client_golang/prometheus"
17 "github.com/prometheus/client_golang/prometheus/promhttp"
18 )
19
20 func TestCounter(t *testing.T) {
21 s := httptest.NewServer(promhttp.HandlerFor(stdprometheus.DefaultGatherer, promhttp.HandlerOpts{}))
22 defer s.Close()
23
24 scrape := func() string {
25 resp, _ := http.Get(s.URL)
26 buf, _ := ioutil.ReadAll(resp.Body)
27 return string(buf)
28 }
29
30 namespace, subsystem, name := "ns", "ss", "foo"
31 re := regexp.MustCompile(namespace + `_` + subsystem + `_` + name + `{alpha="alpha-value",beta="beta-value"} ([0-9\.]+)`)
32
33 counter := NewCounterFrom(stdprometheus.CounterOpts{
34 Namespace: namespace,
35 Subsystem: subsystem,
36 Name: name,
37 Help: "This is the help string.",
38 }, []string{"alpha", "beta"}).With("beta", "beta-value", "alpha", "alpha-value")
39
40 value := func() float64 {
41 matches := re.FindStringSubmatch(scrape())
42 f, _ := strconv.ParseFloat(matches[1], 64)
43 return f
44 }
45
46 if err := teststat.TestCounter(counter, value); err != nil {
47 t.Fatal(err)
48 }
49 }
50
51 func TestGauge(t *testing.T) {
52 s := httptest.NewServer(promhttp.HandlerFor(stdprometheus.DefaultGatherer, promhttp.HandlerOpts{}))
53 defer s.Close()
54
55 scrape := func() string {
56 resp, _ := http.Get(s.URL)
57 buf, _ := ioutil.ReadAll(resp.Body)
58 return string(buf)
59 }
60
61 namespace, subsystem, name := "aaa", "bbb", "ccc"
62 re := regexp.MustCompile(namespace + `_` + subsystem + `_` + name + `{foo="bar"} ([0-9\.]+)`)
63
64 gauge := NewGaugeFrom(stdprometheus.GaugeOpts{
65 Namespace: namespace,
66 Subsystem: subsystem,
67 Name: name,
68 Help: "This is a different help string.",
69 }, []string{"foo"}).With("foo", "bar")
70
71 value := func() []float64 {
72 matches := re.FindStringSubmatch(scrape())
73 f, _ := strconv.ParseFloat(matches[1], 64)
74 return []float64{f}
75 }
76
77 if err := teststat.TestGauge(gauge, value); err != nil {
78 t.Fatal(err)
79 }
80 }
81
82 func TestSummary(t *testing.T) {
83 s := httptest.NewServer(promhttp.HandlerFor(stdprometheus.DefaultGatherer, promhttp.HandlerOpts{}))
84 defer s.Close()
85
86 scrape := func() string {
87 resp, _ := http.Get(s.URL)
88 buf, _ := ioutil.ReadAll(resp.Body)
89 return string(buf)
90 }
91
92 namespace, subsystem, name := "test", "prometheus", "summary"
93 re50 := regexp.MustCompile(namespace + `_` + subsystem + `_` + name + `{a="a",b="b",quantile="0.5"} ([0-9\.]+)`)
94 re90 := regexp.MustCompile(namespace + `_` + subsystem + `_` + name + `{a="a",b="b",quantile="0.9"} ([0-9\.]+)`)
95 re99 := regexp.MustCompile(namespace + `_` + subsystem + `_` + name + `{a="a",b="b",quantile="0.99"} ([0-9\.]+)`)
96
97 summary := NewSummaryFrom(stdprometheus.SummaryOpts{
98 Namespace: namespace,
99 Subsystem: subsystem,
100 Name: name,
101 Help: "This is the help string for the summary.",
102 Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
103 }, []string{"a", "b"}).With("b", "b").With("a", "a")
104
105 quantiles := func() (float64, float64, float64, float64) {
106 buf := scrape()
107 match50 := re50.FindStringSubmatch(buf)
108 p50, _ := strconv.ParseFloat(match50[1], 64)
109 match90 := re90.FindStringSubmatch(buf)
110 p90, _ := strconv.ParseFloat(match90[1], 64)
111 match99 := re99.FindStringSubmatch(buf)
112 p99, _ := strconv.ParseFloat(match99[1], 64)
113 p95 := p90 + ((p99 - p90) / 2)
114 return p50, p90, p95, p99
115 }
116
117 if err := teststat.TestHistogram(summary, quantiles, 0.01); err != nil {
118 t.Fatal(err)
119 }
120 }
121
122 func TestHistogram(t *testing.T) {
123
124
125
126
127
128 s := httptest.NewServer(promhttp.HandlerFor(stdprometheus.DefaultGatherer, promhttp.HandlerOpts{}))
129 defer s.Close()
130
131 scrape := func() string {
132 resp, _ := http.Get(s.URL)
133 buf, _ := ioutil.ReadAll(resp.Body)
134 return string(buf)
135 }
136
137 namespace, subsystem, name := "test", "prometheus", "histogram"
138 re := regexp.MustCompile(namespace + `_` + subsystem + `_` + name + `_bucket{x="1",le="([0-9]+|\+Inf)"} ([0-9\.]+)`)
139
140 numStdev := 3
141 bucketMin := (teststat.Mean - (numStdev * teststat.Stdev))
142 bucketMax := (teststat.Mean + (numStdev * teststat.Stdev))
143 if bucketMin < 0 {
144 bucketMin = 0
145 }
146 bucketCount := 10
147 bucketDelta := (bucketMax - bucketMin) / bucketCount
148 buckets := []float64{}
149 for i := bucketMin; i <= bucketMax; i += bucketDelta {
150 buckets = append(buckets, float64(i))
151 }
152
153 histogram := NewHistogramFrom(stdprometheus.HistogramOpts{
154 Namespace: namespace,
155 Subsystem: subsystem,
156 Name: name,
157 Help: "This is the help string for the histogram.",
158 Buckets: buckets,
159 }, []string{"x"}).With("x", "1")
160
161
162
163
164 teststat.PopulateNormalHistogram(histogram, rand.Int())
165
166
167 for _, line := range strings.Split(scrape(), "\n") {
168 match := re.FindStringSubmatch(line)
169 if match == nil {
170 continue
171 }
172
173 bucket, _ := strconv.ParseInt(match[1], 10, 64)
174 have, _ := strconv.ParseFloat(match[2], 64)
175
176 want := teststat.ExpectedObservationsLessThan(bucket)
177 if match[1] == "+Inf" {
178 want = int64(teststat.Count)
179 }
180
181
182
183
184
185 tolerance := 0.25
186 if delta := math.Abs(float64(want) - float64(have)); (delta / float64(want)) > tolerance {
187 t.Errorf("Bucket %d: want %d, have %d (%.1f%%)", bucket, want, int(have), (100.0 * delta / float64(want)))
188 }
189 }
190 }
191
192 func TestInconsistentLabelCardinality(t *testing.T) {
193 defer func() {
194 x := recover()
195 if x == nil {
196 t.Fatal("expected panic, got none")
197 }
198 err, ok := x.(error)
199 if !ok {
200 t.Fatalf("expected error, got %s", reflect.TypeOf(x))
201 }
202 if want, have := "inconsistent label cardinality", err.Error(); !strings.HasPrefix(have, want) {
203 t.Fatalf("want %q, have %q", want, have)
204 }
205 }()
206
207 NewCounterFrom(stdprometheus.CounterOpts{
208 Namespace: "test",
209 Subsystem: "inconsistent_label_cardinality",
210 Name: "foobar",
211 Help: "This is the help string for the metric.",
212 }, []string{"a", "b"}).With(
213 "a", "1", "b", "2", "c", "KABOOM!",
214 ).Add(123)
215 }
216
View as plain text