testutil: Add ScrapeAndCompare (#1043)
* testutil: Add ScrapeAndCompare Signed-off-by: sazary <soroosh@azary.ir> * testutil: Use %w verb wherever we're using an error in fmt.Errorf Signed-off-by: sazary <soroosh@azary.ir> * Format Signed-off-by: Kemal Akkoyun <kakkoyun@gmail.com> Co-authored-by: Kemal Akkoyun <kakkoyun@users.noreply.github.com> Co-authored-by: Kemal Akkoyun <kakkoyun@gmail.com>
This commit is contained in:
parent
c576b951ad
commit
1638da9ae4
|
@ -41,12 +41,12 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/prometheus/common/expfmt"
|
|
||||||
|
|
||||||
dto "github.com/prometheus/client_model/go"
|
dto "github.com/prometheus/client_model/go"
|
||||||
|
"github.com/prometheus/common/expfmt"
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/internal"
|
"github.com/prometheus/client_golang/prometheus/internal"
|
||||||
|
@ -155,6 +155,34 @@ func GatherAndCount(g prometheus.Gatherer, metricNames ...string) (int, error) {
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ScrapeAndCompare calls a remote exporter's endpoint which is expected to return some metrics in
|
||||||
|
// plain text format. Then it compares it with the results that the `expected` would return.
|
||||||
|
// If the `metricNames` is not empty it would filter the comparison only to the given metric names.
|
||||||
|
func ScrapeAndCompare(url string, expected io.Reader, metricNames ...string) error {
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("scraping metrics failed: %w", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return fmt.Errorf("the scraping target returned a status code other than 200: %d",
|
||||||
|
resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
scraped, err := convertReaderToMetricFamily(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
wanted, err := convertReaderToMetricFamily(expected)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return compareMetricFamilies(scraped, wanted, metricNames...)
|
||||||
|
}
|
||||||
|
|
||||||
// CollectAndCompare registers the provided Collector with a newly created
|
// CollectAndCompare registers the provided Collector with a newly created
|
||||||
// pedantic Registry. It then calls GatherAndCompare with that Registry and with
|
// pedantic Registry. It then calls GatherAndCompare with that Registry and with
|
||||||
// the provided metricNames.
|
// the provided metricNames.
|
||||||
|
@ -184,17 +212,35 @@ func TransactionalGatherAndCompare(g prometheus.TransactionalGatherer, expected
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("gathering metrics failed: %w", err)
|
return fmt.Errorf("gathering metrics failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wanted, err := convertReaderToMetricFamily(expected)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return compareMetricFamilies(got, wanted, metricNames...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertReaderToMetricFamily would read from a io.Reader object and convert it to a slice of
|
||||||
|
// dto.MetricFamily.
|
||||||
|
func convertReaderToMetricFamily(reader io.Reader) ([]*dto.MetricFamily, error) {
|
||||||
|
var tp expfmt.TextParser
|
||||||
|
notNormalized, err := tp.TextToMetricFamilies(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("converting reader to metric families failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return internal.NormalizeMetricFamilies(notNormalized), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// compareMetricFamilies would compare 2 slices of metric families, and optionally filters both of
|
||||||
|
// them to the `metricNames` provided.
|
||||||
|
func compareMetricFamilies(got, expected []*dto.MetricFamily, metricNames ...string) error {
|
||||||
if metricNames != nil {
|
if metricNames != nil {
|
||||||
got = filterMetrics(got, metricNames)
|
got = filterMetrics(got, metricNames)
|
||||||
}
|
}
|
||||||
var tp expfmt.TextParser
|
|
||||||
wantRaw, err := tp.TextToMetricFamilies(expected)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("parsing expected metrics failed: %w", err)
|
|
||||||
}
|
|
||||||
want := internal.NormalizeMetricFamilies(wantRaw)
|
|
||||||
|
|
||||||
return compare(got, want)
|
return compare(got, expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
// compare encodes both provided slices of metric families into the text format,
|
// compare encodes both provided slices of metric families into the text format,
|
||||||
|
|
|
@ -14,6 +14,9 @@
|
||||||
package testutil
|
package testutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -308,6 +311,61 @@ Diff:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestScrapeAndCompare(t *testing.T) {
|
||||||
|
const expected = `
|
||||||
|
# HELP some_total A value that represents a counter.
|
||||||
|
# TYPE some_total counter
|
||||||
|
|
||||||
|
some_total{ label1 = "value1" } 1
|
||||||
|
`
|
||||||
|
|
||||||
|
expectedReader := strings.NewReader(expected)
|
||||||
|
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Fprintln(w, expected)
|
||||||
|
}))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
if err := ScrapeAndCompare(ts.URL, expectedReader, "some_total"); err != nil {
|
||||||
|
t.Errorf("unexpected scraping result:\n%s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScrapeAndCompareFetchingFail(t *testing.T) {
|
||||||
|
err := ScrapeAndCompare("some_url", strings.NewReader("some expectation"), "some_total")
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected an error but got nil")
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(err.Error(), "scraping metrics failed") {
|
||||||
|
t.Errorf("unexpected error happened: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScrapeAndCompareBadStatusCode(t *testing.T) {
|
||||||
|
const expected = `
|
||||||
|
# HELP some_total A value that represents a counter.
|
||||||
|
# TYPE some_total counter
|
||||||
|
|
||||||
|
some_total{ label1 = "value1" } 1
|
||||||
|
`
|
||||||
|
|
||||||
|
expectedReader := strings.NewReader(expected)
|
||||||
|
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusBadGateway)
|
||||||
|
fmt.Fprintln(w, expected)
|
||||||
|
}))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
err := ScrapeAndCompare(ts.URL, expectedReader, "some_total")
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected an error but got nil")
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(err.Error(), "the scraping target returned a status code other than 200") {
|
||||||
|
t.Errorf("unexpected error happened: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCollectAndCount(t *testing.T) {
|
func TestCollectAndCount(t *testing.T) {
|
||||||
c := prometheus.NewCounterVec(
|
c := prometheus.NewCounterVec(
|
||||||
prometheus.CounterOpts{
|
prometheus.CounterOpts{
|
||||||
|
|
Loading…
Reference in New Issue