Skip to content

Commit

Permalink
testutil: Add ScrapeAndCompare
Browse files Browse the repository at this point in the history
Signed-off-by: sazary <soroosh@azary.ir>
  • Loading branch information
sazary committed May 3, 2022
1 parent 24605c5 commit d13b534
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 13 deletions.
66 changes: 55 additions & 11 deletions prometheus/testutil/testutil.go
Expand Up @@ -40,13 +40,12 @@ package testutil
import (
"bytes"
"fmt"
"io"
"reflect"

"github.com/davecgh/go-spew/spew"
"github.com/prometheus/common/expfmt"

dto "github.com/prometheus/client_model/go"
"github.com/prometheus/common/expfmt"
"io"
"net/http"
"reflect"

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/internal"
Expand Down Expand Up @@ -153,6 +152,33 @@ func GatherAndCount(g prometheus.Gatherer, metricNames ...string) (int, error) {
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: %s", err)
}

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
// pedantic Registry. It then calls GatherAndCompare with that Registry and with
// the provided metricNames.
Expand Down Expand Up @@ -182,17 +208,35 @@ func TransactionalGatherAndCompare(g prometheus.TransactionalGatherer, expected
if err != nil {
return fmt.Errorf("gathering metrics failed: %s", err)
}
if metricNames != nil {
got = filterMetrics(got, metricNames)

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
wantRaw, err := tp.TextToMetricFamilies(expected)
notNormalized, err := tp.TextToMetricFamilies(reader)
if err != nil {
return fmt.Errorf("parsing expected metrics failed: %s", err)
return nil, fmt.Errorf("converting reader to metric families failed: %s", 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 {
got = filterMetrics(got, metricNames)
}
want := internal.NormalizeMetricFamilies(wantRaw)

return compare(got, want)
return compare(got, expected)
}

// compare encodes both provided slices of metric families into the text format,
Expand Down
61 changes: 59 additions & 2 deletions prometheus/testutil/testutil_test.go
Expand Up @@ -14,10 +14,12 @@
package testutil

import (
"fmt"
"github.com/prometheus/client_golang/prometheus"
"net/http"
"net/http/httptest"
"strings"
"testing"

"github.com/prometheus/client_golang/prometheus"
)

type untypedCollector struct{}
Expand Down Expand Up @@ -308,6 +310,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(), "scraping endpoint returned a status other than 200") {
t.Errorf("unexpected error happened: %s", err)
}
}

func TestCollectAndCount(t *testing.T) {
c := prometheus.NewCounterVec(
prometheus.CounterOpts{
Expand Down

0 comments on commit d13b534

Please sign in to comment.