Skip to content

Commit

Permalink
- new response field header_flat:
Browse files Browse the repository at this point in the history
  - map of strings with (concatenated) header values
  - contains the same values as the header, but not in arrays
  - :control can be used on different header values
- removed unused body:control and header:control from code, check was never implemented

see #73980
  • Loading branch information
Philipp Hempel committed Oct 28, 2024
1 parent af151cc commit d341bfd
Show file tree
Hide file tree
Showing 9 changed files with 135 additions and 72 deletions.
5 changes: 3 additions & 2 deletions api_testcase.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ type Case struct {
dataStore *datastore.Datastore

standardHeader map[string]*string
headerFlat map[string]*string
standardHeaderFromStore map[string]string

ServerURL string `json:"server_url"`
Expand Down Expand Up @@ -471,7 +472,7 @@ func (testCase Case) loadRequest() (api.Request, error) {
func (testCase Case) loadExpectedResponse() (res api.Response, err error) {
// unspecified response is interpreted as status_code 200
if testCase.ResponseData == nil {
return api.NewResponse(http.StatusOK, nil, nil, nil, nil, nil, res.Format)
return api.NewResponse(http.StatusOK, nil, nil, nil, nil, res.Format)
}
spec, err := testCase.loadResponseSerialization(testCase.ResponseData)
if err != nil {
Expand All @@ -489,7 +490,7 @@ func (testCase Case) responsesEqual(expected, got api.Response) (compare.Compare
if err != nil {
return compare.CompareResult{}, fmt.Errorf("error loading expected generic json: %s", err)
}
if len(expected.Body) == 0 && len(expected.BodyControl) == 0 {
if len(expected.Body) == 0 {
expected.Format.IgnoreBody = true
} else {
expected.Format.IgnoreBody = false
Expand Down
2 changes: 2 additions & 0 deletions api_testsuite.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ type Suite struct {
Store map[string]any `json:"store"`

StandardHeader map[string]*string `yaml:"header" json:"header"`
HeaderFlat map[string]*string `yaml:"header_flat" json:"header_flat"`
StandardHeaderFromStore map[string]string `yaml:"header_from_store" json:"header_from_store"`

Config TestToolConfig
Expand Down Expand Up @@ -399,6 +400,7 @@ func (ats *Suite) runLiteralTest(
test.index = index
test.dataStore = ats.datastore
test.standardHeader = ats.StandardHeader
test.headerFlat = ats.HeaderFlat
test.standardHeaderFromStore = ats.StandardHeaderFromStore
if test.LogNetwork == nil {
test.LogNetwork = &ats.Config.LogNetwork
Expand Down
2 changes: 1 addition & 1 deletion pkg/lib/api/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ func (request Request) Send() (response Response, err error) {
if err != nil {
return response, err
}
response, err = NewResponse(httpResponse.StatusCode, header, nil, httpResponse.Cookies(), httpResponse.Body, nil, ResponseFormat{})
response, err = NewResponse(httpResponse.StatusCode, header, nil, httpResponse.Cookies(), httpResponse.Body, ResponseFormat{})
if err != nil {
return response, fmt.Errorf("error constructing response from http response")
}
Expand Down
84 changes: 40 additions & 44 deletions pkg/lib/api/response.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,32 +21,28 @@ import (
)

type Response struct {
StatusCode int
Headers map[string]any
headerControl util.JsonObject
Cookies []*http.Cookie
Body []byte
BodyControl util.JsonObject
Format ResponseFormat
StatusCode int
Headers map[string]any
HeaderFlat map[string]any
Cookies []*http.Cookie
Body []byte
Format ResponseFormat

ReqDur time.Duration
BodyLoadDur time.Duration
}

func (res Response) NeedsCheck() bool {
if res.StatusCode != http.StatusOK {
return true
}
if len(res.Headers) > 0 || len(res.Cookies) > 0 || len(res.Body) > 0 || len(res.BodyControl) > 0 {
return true
}
return false
}

func (res Response) SerializeHeaders() (headers map[string]any, err error) {
func (res Response) SerializeHeaderFlat() (headers map[string]any, err error) {
headers = map[string]any{}
for k, h := range res.Headers {
headers[k] = h
switch v := h.(type) {
case string:
headers[k] = h
case nil:
headers[k] = ""
case []string:
headers[k] = strings.Join(v, "; ")
}
}
return headers, nil
}
Expand All @@ -73,13 +69,12 @@ type Cookie struct {
}

type ResponseSerialization struct {
StatusCode int `yaml:"statuscode" json:"statuscode"`
Headers map[string]any `yaml:"header" json:"header,omitempty"`
HeaderControl util.JsonObject `yaml:"header:control" json:"header:control,omitempty"`
Cookies map[string]Cookie `yaml:"cookie" json:"cookie,omitempty"`
Body any `yaml:"body" json:"body,omitempty"`
BodyControl util.JsonObject `yaml:"body:control" json:"body:control,omitempty"`
Format ResponseFormat `yaml:"format" json:"format,omitempty"`
StatusCode int `yaml:"statuscode" json:"statuscode"`
Headers map[string]any `yaml:"header" json:"header,omitempty"`
HeaderFlat map[string]any `yaml:"header_flat" json:"header_flat,omitempty"`
Cookies map[string]Cookie `yaml:"cookie" json:"cookie,omitempty"`
Body any `yaml:"body" json:"body,omitempty"`
Format ResponseFormat `yaml:"format" json:"format,omitempty"`
}

type ResponseFormat struct {
Expand All @@ -91,14 +86,19 @@ type ResponseFormat struct {
PreProcess *PreProcess `json:"pre_process,omitempty"`
}

func NewResponse(statusCode int, headers map[string]any, headerControl util.JsonObject, cookies []*http.Cookie, body io.Reader, bodyControl util.JsonObject, bodyFormat ResponseFormat) (res Response, err error) {
func NewResponse(statusCode int,
headers map[string]any,
headerFlat map[string]any,
cookies []*http.Cookie,
body io.Reader,
bodyFormat ResponseFormat,
) (res Response, err error) {
res = Response{
StatusCode: statusCode,
Headers: headers,
Cookies: cookies,
BodyControl: bodyControl,
headerControl: headerControl,
Format: bodyFormat,
StatusCode: statusCode,
Headers: headers,
HeaderFlat: headerFlat,
Cookies: cookies,
Format: bodyFormat,
}
if body != nil {
start := time.Now()
Expand Down Expand Up @@ -144,7 +144,7 @@ func NewResponseFromSpec(spec ResponseSerialization) (res Response, err error) {
}
}

return NewResponse(spec.StatusCode, spec.Headers, spec.HeaderControl, cookies, body, spec.BodyControl, spec.Format)
return NewResponse(spec.StatusCode, spec.Headers, spec.HeaderFlat, cookies, body, spec.Format)
}

// ServerResponseToGenericJSON parse response from server. convert xml, csv, binary to json if necessary
Expand Down Expand Up @@ -214,13 +214,14 @@ func (response Response) ServerResponseToGenericJSON(responseFormat ResponseForm
return res, fmt.Errorf("Invalid response format '%s'", responseFormat.Type)
}

headers, err := resp.SerializeHeaders()
headerFlat, err := resp.SerializeHeaderFlat()
if err != nil {
return res, err
}
responseJSON := ResponseSerialization{
StatusCode: resp.StatusCode,
Headers: headers,
Headers: resp.Headers,
HeaderFlat: headerFlat,
}
// Build cookies map from standard bag
if len(resp.Cookies) > 0 {
Expand Down Expand Up @@ -284,15 +285,10 @@ func (response Response) ToGenericJSON() (any, error) {
}
}

headers, err := response.SerializeHeaders()
if err != nil {
return res, err
}
responseJSON := ResponseSerialization{
StatusCode: response.StatusCode,
BodyControl: response.BodyControl,
Headers: headers,
HeaderControl: response.headerControl,
StatusCode: response.StatusCode,
Headers: response.Headers,
HeaderFlat: response.HeaderFlat,
}

// Build cookies map from standard bag
Expand Down
6 changes: 3 additions & 3 deletions pkg/lib/api/response_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func TestResponse_NewResponseFromSpec_StatusCode_not_set(t *testing.T) {
}

func TestResponse_NewResponse(t *testing.T) {
response, err := NewResponse(200, nil, nil, nil, strings.NewReader("foo"), nil, ResponseFormat{})
response, err := NewResponse(200, nil, nil, nil, strings.NewReader("foo"), ResponseFormat{})
go_test_utils.ExpectNoError(t, err, "unexpected error")
go_test_utils.AssertIntEquals(t, response.StatusCode, 200)
}
Expand All @@ -86,7 +86,7 @@ func TestResponse_String(t *testing.T) {
}
}`

response, err := NewResponse(200, nil, nil, nil, strings.NewReader(requestString), nil, ResponseFormat{})
response, err := NewResponse(200, nil, nil, nil, strings.NewReader(requestString), ResponseFormat{})
go_test_utils.ExpectNoError(t, err, "error constructing response")

assertString := "200\n\n\n" + requestString
Expand Down Expand Up @@ -119,7 +119,7 @@ func TestResponse_Cookies(t *testing.T) {
if err != nil {
t.Fatal(err)
}
response, err := NewResponse(res.StatusCode, header, nil, res.Cookies(), res.Body, nil, ResponseFormat{})
response, err := NewResponse(res.StatusCode, header, nil, res.Cookies(), res.Body, ResponseFormat{})
if err != nil {
t.Fatal(err)
}
Expand Down
3 changes: 1 addition & 2 deletions pkg/lib/template/template_loader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func TestBigIntRender(t *testing.T) {

inputNumber := "132132132182323"

resp, _ := api.NewResponse(200, nil, nil, nil, strings.NewReader(fmt.Sprintf(`{"bigINT":%s}`, inputNumber)), nil, api.ResponseFormat{})
resp, _ := api.NewResponse(200, nil, nil, nil, strings.NewReader(fmt.Sprintf(`{"bigINT":%s}`, inputNumber)), api.ResponseFormat{})

respJson, _ := resp.ServerResponseToJsonString(false)
store.SetWithQjson(respJson, map[string]string{"testINT": "body.bigINT"})
Expand Down Expand Up @@ -415,7 +415,6 @@ func Test_DataStore_QJson(t *testing.T) {
"flob"
]
}`),
nil,
api.ResponseFormat{},
)
store := datastore.NewStore(false)
Expand Down
15 changes: 0 additions & 15 deletions test/control/header/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,6 @@
}
}
},
{
"name": "check HTTP header using control, use reverse_test_result",
"request": {
"server_url": "http://localhost:9999",
"endpoint": "bounce-json",
"method": "POST"
},
"response": {
// check number of HTTP headers, should always be > 0
"header:control": {
"element_count": 0
}
},
"reverse_test_result": true
},
{
"name": "check value in HTTP header using control, use reverse_test_result",
"request": {
Expand Down
6 changes: 1 addition & 5 deletions test/datastore/check.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,7 @@
{"some": "data"},
{"some": ["more", "data"]},
{"some": "data"}
],
"body:control": {
"order_matters": true,
"no_extra": true
}
]
}
}
}
Expand Down
84 changes: 84 additions & 0 deletions test/response/header/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
{
"http_server": {
"addr": ":9999",
"dir": "../_res",
"testmode": false
},
"name": "response header: format header_flat",
"tests": [
{
"name": "test response header_flat",
"request": {
"server_url": "http://localhost:9999",
"endpoint": "bounce-json",
"method": "POST"
},
"response": {
"statuscode": 200,
"header": {
"Content-Type": [
"text/plain; charset=utf-8"
]
},
"header_flat": {
"Content-Type": "text/plain; charset=utf-8"
}
}
},
{
"name": "test response header_flat (check for a failing test with a reverse result)",
"reverse_test_result": true,
"request": {
"server_url": "http://localhost:9999",
"endpoint": "bounce-json",
"method": "POST"
},
"response": {
"statuscode": 200,
"header_flat": {
"Content-Length": "foo",
"Content-Type": "bar"
}
}
},
{
"name": "test response header_flat: control for header values",
"request": {
"server_url": "http://localhost:9999",
"endpoint": "bounce-json",
"method": "POST"
},
"response": {
"statuscode": 200,
"header_flat": {
"Content-Length:control": {
"match": "^\\d+$"
},
"Content-Type:control": {
"match": "^text/plain;.*"
}
}
}
},
{
"name": "test response header_flat: control for header values (check for a failing test with a reverse result)",
"reverse_test_result": true,
"request": {
"server_url": "http://localhost:9999",
"endpoint": "bounce-json",
"method": "POST"
},
"response": {
"statuscode": 200,
"header_flat": {
"Content-Length:control": {
"match": "foo"
},
"Content-Type:control": {
"match": "bar"
}
}
}
}
]
}

0 comments on commit d341bfd

Please sign in to comment.