From 0f7d9fd0bba52ce18ac5177c9f99b91ab74eaf19 Mon Sep 17 00:00:00 2001 From: Michail Safronov Date: Mon, 18 Sep 2023 22:18:54 +0500 Subject: [PATCH] tests (find): extend metrics find e2e tests --- cmd/mockbackend/e2etesting.go | 88 ++++++++++++----- cmd/mockbackend/find.go | 10 +- .../testcases/find_error/find_error.yaml | 94 +++++++++++++++++++ 3 files changed, 168 insertions(+), 24 deletions(-) create mode 100644 cmd/mockbackend/testcases/find_error/find_error.yaml diff --git a/cmd/mockbackend/e2etesting.go b/cmd/mockbackend/e2etesting.go index 922246364..6915a0f4f 100644 --- a/cmd/mockbackend/e2etesting.go +++ b/cmd/mockbackend/e2etesting.go @@ -12,6 +12,7 @@ import ( "net/http" "net/url" "os" + "reflect" "strconv" "strings" "sync" @@ -48,16 +49,26 @@ type ExpectedResponse struct { } type ExpectedResult struct { - SHA256 []string `yaml:"sha256"` - Metrics []CarbonAPIResponse + SHA256 []string `yaml:"sha256"` + Metrics []RenderResponse `json:"metrics" yaml:"metrics"` + MetricsFind []MetricsFindResponse `json:"metricsFind" yaml:"metricsFind"` } -type CarbonAPIResponse struct { +type RenderResponse struct { Target string `json:"target" yaml:"target"` Datapoints []Datapoint `json:"datapoints" yaml:"datapoints"` Tags map[string]string `json:"tags" yaml:"tags"` } +type MetricsFindResponse struct { + AllowChildren int `json:"allowChildren" yaml:"allowChildren"` + Expandable int `json:"expandable" yaml:"expandable"` + Leaf int `json:"leaf" yaml:"leaf"` + Id string `json:"id" yaml:"id"` + Text string `json:"text" yaml:"text"` + Context map[string]string `json:"context" yaml:"context"` +} + type Datapoint struct { Timestamp int Value float64 @@ -121,7 +132,7 @@ func (d *Datapoint) UnmarshalYAML(unmarshal func(interface{}) error) error { return nil } -func isMetricsEqual(m1, m2 CarbonAPIResponse) error { +func isRenderEqual(m1, m2 RenderResponse) error { if m1.Target != m2.Target { return fmt.Errorf("target mismatch, got '%v', expected '%v'", m1.Target, m2.Target) } @@ -249,30 +260,61 @@ func doTest(logger *zap.Logger, t *Query) []error { return failures } case "application/json": - res := make([]CarbonAPIResponse, 0, 1) - err := json.Unmarshal(b, &res) - if err != nil { - err = merry2.Prepend(err, "failed to parse response") - failures = append(failures, err) - return failures - } + if strings.HasPrefix(t.URL, "/metrics/find") { + // metrics/find + res := make([]MetricsFindResponse, 0, 1) + err := json.Unmarshal(b, &res) + if err != nil { + err = merry2.Prepend(err, "failed to parse response") + failures = append(failures, err) + return failures + } - if len(t.ExpectedResponse.ExpectedResults) == 0 { - return failures - } + if len(t.ExpectedResponse.ExpectedResults) == 0 { + return failures + } - if len(res) != len(t.ExpectedResponse.ExpectedResults[0].Metrics) { - failures = append(failures, merry2.Errorf("unexpected amount of results, got %v, expected %v", - len(res), - len(t.ExpectedResponse.ExpectedResults[0].Metrics))) - return failures - } + if len(res) != len(t.ExpectedResponse.ExpectedResults[0].MetricsFind) { + failures = append(failures, merry2.Errorf("unexpected amount of results, got %v, expected %v", + len(res), + len(t.ExpectedResponse.ExpectedResults[0].Metrics))) + return failures + } + + for i := range res { + if !reflect.DeepEqual(res[i], t.ExpectedResponse.ExpectedResults[0].MetricsFind[i]) { + err = fmt.Errorf("metrics[%d] are not equal, got=`%+v`, expected=`%+v`", i, res[i], t.ExpectedResponse.ExpectedResults[0].MetricsFind[i]) + failures = append(failures, err) + } + } - for i := range res { - err := isMetricsEqual(res[i], t.ExpectedResponse.ExpectedResults[0].Metrics[i]) + } else { + // render + res := make([]RenderResponse, 0, 1) + err := json.Unmarshal(b, &res) if err != nil { - err = merry2.Prependf(err, "metrics are not equal, got=`%+v`, expected=`%+v`", res[i], t.ExpectedResponse.ExpectedResults[0].Metrics[i]) + err = merry2.Prepend(err, "failed to parse response") failures = append(failures, err) + return failures + } + + if len(t.ExpectedResponse.ExpectedResults) == 0 { + return failures + } + + if len(res) != len(t.ExpectedResponse.ExpectedResults[0].Metrics) { + failures = append(failures, merry2.Errorf("unexpected amount of results, got %v, expected %v", + len(res), + len(t.ExpectedResponse.ExpectedResults[0].Metrics))) + return failures + } + + for i := range res { + err := isRenderEqual(res[i], t.ExpectedResponse.ExpectedResults[0].Metrics[i]) + if err != nil { + err = merry2.Prependf(err, "metrics[%d] are not equal, got=`%+v`, expected=`%+v`", i, res[i], t.ExpectedResponse.ExpectedResults[0].Metrics[i]) + failures = append(failures, err) + } } } diff --git a/cmd/mockbackend/find.go b/cmd/mockbackend/find.go index 2c0c14592..b4d34eaf8 100644 --- a/cmd/mockbackend/find.go +++ b/cmd/mockbackend/find.go @@ -48,6 +48,13 @@ func (cfg *listener) findHandler(wr http.ResponseWriter, req *http.Request) { query := req.Form["query"] + if len(query) == 0 { + logger.Error("Bad request (no query)") + http.Error(wr, "Bad request (no query)", + http.StatusBadRequest) + return + } + if format == protoV3Format { body, err := io.ReadAll(req.Body) if err != nil { @@ -56,6 +63,7 @@ func (cfg *listener) findHandler(wr http.ResponseWriter, req *http.Request) { ) http.Error(wr, "Bad request (unsupported format)", http.StatusBadRequest) + return } var pv3Request carbonapi_v3_pb.MultiGlobRequest @@ -73,7 +81,7 @@ func (cfg *listener) findHandler(wr http.ResponseWriter, req *http.Request) { } if query[0] != "*" { - for m := range cfg.Listener.Expressions { + for _, m := range query { globMatches := []carbonapi_v3_pb.GlobMatch{} for _, metric := range cfg.Expressions[m].Data { diff --git a/cmd/mockbackend/testcases/find_error/find_error.yaml b/cmd/mockbackend/testcases/find_error/find_error.yaml new file mode 100644 index 000000000..35cc12029 --- /dev/null +++ b/cmd/mockbackend/testcases/find_error/find_error.yaml @@ -0,0 +1,94 @@ +version: "v1" +test: + apps: + - name: "carbonapi" + binary: "./carbonapi" + args: + - "-config" + - "./cmd/mockbackend/testcases/render_error/carbonapi.yaml" + queries: + - endpoint: "http://127.0.0.1:8081" + type: "GET" + URL: "/metrics/find?query=a&format=json" + expectedResponse: + httpCode: 200 + contentType: "application/json" + expectedResults: + - metricsFind: + - allowChildren: 0 + expandable: 0 + leaf: 1 + id: "a" + text: "a" + context: {} + + # empty + - endpoint: "http://127.0.0.1:8081" + type: "GET" + URL: "/render/?target=b&format=json" + expectedResponse: + httpCode: 200 + contentType: "application/json" + + - endpoint: "http://127.0.0.1:8081" + type: "GET" + URL: "/metrics/find?query=a&query=b&format=json" + expectedResponse: + httpCode: 200 + contentType: "application/json" + expectedResults: + - metricsFind: + - allowChildren: 0 + expandable: 0 + leaf: 1 + id: "a" + text: "a" + context: {} + + # timeout + - endpoint: "http://127.0.0.1:8081" + type: "GET" + URL: "/metrics/find?query=c&format=json" + expectedResponse: + httpCode: 503 + contentType: "text/plain; charset=utf-8" + + # 503 + - endpoint: "http://127.0.0.1:8081" + type: "GET" + URL: "/metrics/find?query=d&format=json" + expectedResponse: + httpCode: 503 + contentType: "text/plain; charset=utf-8" + + # 503, partial success + - endpoint: "http://127.0.0.1:8081" + type: "GET" + URL: "/metrics/find?query=a&query=d&format=json" + expectedResponse: + httpCode: 503 + contentType: "text/plain; charset=utf-8" + +listeners: + - address: ":9070" + expressions: + "a": + pathExpression: "a" + data: + - metricName: "a" + values: [0,1,2,2,3] + + # timeout + "c": + pathExpression: "b" + emptyBody: true + httpCode: 404 + replyDelayMS: 7000 + data: + - metricName: "c" + values: [0,1,2,2,3] + + "d": + pathExpression: "d" + emptyBody: true + httpCode: 503