diff --git a/pkg/httpassert/request.go b/pkg/httpassert/request.go index 93e946f..89e17fb 100644 --- a/pkg/httpassert/request.go +++ b/pkg/httpassert/request.go @@ -11,7 +11,6 @@ import ( "net/http" "net/http/httptest" "os" - "strings" "testing" "time" @@ -244,10 +243,7 @@ func (m *request) JsonContentTemplate(body string, values map[string]any) Reques jsonBody := body // apply provided values into the template for k, v := range values { - // normalize JSONPath-like keys (convert $.a[0].b to a.0.b) - key := strings.TrimPrefix(k, "$.") - key = strings.ReplaceAll(key, "[", ".") - key = strings.ReplaceAll(key, "]", "") + key := normalizeJSONPath(k) if !gjson.Get(jsonBody, key).Exists() { assert.Fail(m.t, "Json key does not exist in template: "+k) diff --git a/pkg/httpassert/request_test.go b/pkg/httpassert/request_test.go index 5836b52..ed126ae 100644 --- a/pkg/httpassert/request_test.go +++ b/pkg/httpassert/request_test.go @@ -157,7 +157,7 @@ func TestRequest(t *testing.T) { StatusCode(http.StatusOK) }) - t.Run("JSON content template", func(t *testing.T) { + t.Run("JSON content template with object", func(t *testing.T) { var content []byte router := http.NewServeMux() router.HandleFunc("/json", func(w http.ResponseWriter, r *http.Request) { @@ -182,6 +182,32 @@ func TestRequest(t *testing.T) { AssertJSONCanonicalEq(t, `{"n": {"foo": "bar","asd":"123"}}`, string(content)) }) + + t.Run("JSON content template with array", func(t *testing.T) { + var content []byte + router := http.NewServeMux() + router.HandleFunc("/json", func(w http.ResponseWriter, r *http.Request) { + bodyBytes, err := io.ReadAll(r.Body) + require.NoError(t, err) + content = bodyBytes + w.WriteHeader(http.StatusOK) + }) + + New(t, router). + Post("/json"). + JsonContentTemplate(`[{ + "n": { + "foo": "bar", + "asd": "" + } + }]`, map[string]any{ + "$[0].n.asd": "123", + }). + Expect(). + StatusCode(http.StatusOK) + + AssertJSONCanonicalEq(t, `[{"n": {"foo": "bar","asd":"123"}}]`, string(content)) + }) } func TestExpectEventually(t *testing.T) { diff --git a/pkg/httpassert/response.go b/pkg/httpassert/response.go index 29d1489..6770271 100644 --- a/pkg/httpassert/response.go +++ b/pkg/httpassert/response.go @@ -140,10 +140,7 @@ func (r *responseImpl) JsonTemplate(expectedJsonTemplate string, values map[stri // apply provided values into the template for k, v := range values { - // normalize JSONPath-like keys (convert $.a[0].b to a.0.b) - key := strings.TrimPrefix(k, "$.") - key = strings.ReplaceAll(key, "[", ".") - key = strings.ReplaceAll(key, "]", "") + key := normalizeJSONPath(k) if !gjson.Get(expectedJson, key).Exists() { assert.Fail(r.t, "Json key does not exist in template: "+k) @@ -164,9 +161,7 @@ func (r *responseImpl) JsonTemplate(expectedJsonTemplate string, values map[stri continue } - key := strings.TrimPrefix(k, "$.") - key = strings.ReplaceAll(key, "[", ".") - key = strings.ReplaceAll(key, "]", "") + key := normalizeJSONPath(k) if !gjson.Get(actual, key).Exists() { assert.Fail(r.t, "Json key does not exist in template: "+k) @@ -235,3 +230,14 @@ func (r *responseImpl) Log() Response { r.response.Body) return r } + +// normalizeJSONPath converts jsonpath syntax to gjson/sjson syntax. +// E.g. convert $.a[0].b to a.0.b and $[0].a to 0.a +// This allows a unified path syntax to the caller, regardless which library is used internally. +func normalizeJSONPath(path string) string { + path = strings.ReplaceAll(path, "[", ".") + path = strings.ReplaceAll(path, "]", "") + path = strings.TrimPrefix(path, "$.") + + return path +} diff --git a/pkg/httpassert/response_test.go b/pkg/httpassert/response_test.go index e67b249..8e6c6e4 100644 --- a/pkg/httpassert/response_test.go +++ b/pkg/httpassert/response_test.go @@ -24,7 +24,7 @@ func TestResponse(t *testing.T) { JsonFile(path) }) - t.Run("JsonTemplate value replacement and ignore handling", func(t *testing.T) { + t.Run("JsonTemplate value replacement and ignore handling with object", func(t *testing.T) { router := http.NewServeMux() router.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -77,6 +77,26 @@ func TestResponse(t *testing.T) { }) }) + t.Run("JsonTemplate value replacement with array", func(t *testing.T) { + router := http.NewServeMux() + router.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, err := fmt.Fprint(w, `[{"id":42,"name":"Greenbone","version":"1.0.0","tags":["alpha","beta"]}]`) + require.NoError(t, err) + }) + + New(t, router).Get("/api"). + Expect(). + JsonTemplate(`[{"id":0,"name":"","version":"","tags":["x","y"]}]`, map[string]any{ + "$[0].id": 42, + "$[0].name": "Greenbone", + "$[0].tags[0]": "alpha", + "$[0].tags[1]": "beta", + "$[0].version": "1.0.0", + }) + }) + t.Run("GetBody returns non-empty", func(t *testing.T) { body := request.Post("/json"). Expect().