Пример #1
0
// New makes a new Runner with the given testing T target and the
// root URL.
func New(t T, URL string) *Runner {
	r := &Runner{
		t:         t,
		rootURL:   URL,
		vars:      make(map[string]*parse.Value),
		DoRequest: http.DefaultTransport.RoundTrip,
		Log: func(s string) {
			fmt.Println(s)
		},
		Verbose: func(args ...interface{}) {
			if !testing.Verbose() {
				return
			}
			fmt.Println(args...)
		},
		ParseBody:  ParseJSONBody,
		NewRequest: http.NewRequest,
	}
	// capture environment variables by default
	for _, e := range os.Environ() {
		pair := strings.Split(e, "=")
		r.vars[pair[0]] = parse.ParseValue([]byte(pair[1]))
	}
	return r
}
Пример #2
0
func (r *Runner) assertDetail(key string, actual interface{}, expected *parse.Value) bool {
	if actual != expected.Data {
		actualVal := parse.ParseValue([]byte(fmt.Sprintf("%v", actual)))
		r.log(key, fmt.Sprintf("expected %s: %s  actual %T: %s", expected.Type(), expected, actual, actualVal))
		return false
	}
	return true
}
Пример #3
0
func (r *Runner) assertDetail(line *parse.Line, key string, actual interface{}, expected *parse.Value) bool {
	if !expected.Equal(actual) {
		actualVal := parse.ParseValue([]byte(fmt.Sprintf("%v", actual)))
		actualString := actualVal.String()
		if v, ok := actual.(string); ok {
			actualString = fmt.Sprintf(`"%s"`, v)
		}

		if expected.Type() == actualVal.Type() {
			r.log(key, fmt.Sprintf("expected: %s  actual: %s", expected, actualString))
		} else {
			r.log(key, fmt.Sprintf("expected %s: %s  actual %T: %s", expected.Type(), expected, actual, actualString))
		}

		return false
	}
	// capture any vars (// e.g. {placeholder})
	if capture := line.Capture(); len(capture) > 0 {
		r.capture(capture, actual)
	}
	return true
}
Пример #4
0
func (r *Runner) assertData(line *parse.Line, data interface{}, errData error, key string, expected *parse.Value) bool {
	if errData != nil {
		r.log(key, fmt.Sprintf("expected %s: %s  actual: failed to parse body: %s", expected.Type(), expected, errData))
		return false
	}
	if data == nil {
		r.log(key, fmt.Sprintf("expected %s: %s  actual: no data", expected.Type(), expected))
		return false
	}
	actual, ok := m.GetOK(map[string]interface{}{"Data": data}, key)
	if !ok && expected.Data != nil {
		r.log(key, fmt.Sprintf("expected %s: %s  actual: (missing)", expected.Type(), expected))
		return false
	}
	// capture any vars (// e.g. {placeholder})
	if capture := line.Capture(); len(capture) > 0 {
		r.capture(capture, actual)
	}
	if !ok && expected.Data == nil {
		return true
	}
	if !expected.Equal(actual) {
		actualVal := parse.ParseValue([]byte(fmt.Sprintf("%v", actual)))
		actualString := actualVal.String()
		if v, ok := actual.(string); ok {
			actualString = fmt.Sprintf(`"%s"`, v)
		}
		if expected.Type() == actualVal.Type() {
			r.log(key, fmt.Sprintf("expected: %s  actual: %s", expected, actualString))
		} else {
			r.log(key, fmt.Sprintf("expected %s: %s  actual %T: %s", expected.Type(), expected, actual, actualString))
		}
		return false
	}
	return true
}
Пример #5
0
func (r *Runner) assertData(data interface{}, errData error, key string, expected *parse.Value) bool {
	if errData != nil {
		r.log(key, fmt.Sprintf("expected %s: %s  actual: failed to parse body: %s", expected.Type(), expected, errData))
		return false
	}
	if data == nil {
		r.log(key, fmt.Sprintf("expected %s: %s  actual: no data", expected.Type(), expected))
		return false
	}
	actual, ok := m.GetOK(map[string]interface{}{"Data": data}, key)
	if !ok && expected.Data != nil {
		r.log(key, fmt.Sprintf("expected %s: %s  actual: (missing)", expected.Type(), expected))
		return false
	}
	if !ok && expected.Data == nil {
		return true
	}
	if !expected.Equal(actual) {
		actualVal := parse.ParseValue([]byte(fmt.Sprintf("%v", actual)))
		r.log(key, fmt.Sprintf("expected %s: %s  actual %T: %s", expected.Type(), expected, actual, actualVal))
		return false
	}
	return true
}
Пример #6
0
func (r *Runner) runRequest(group *parse.Group, req *parse.Request) {
	m := string(req.Method)
	p := string(req.Path)
	absPath := r.resolveVars(r.rootURL + p)
	m = r.resolveVars(m)
	r.Verbose(string(req.Method), absPath)
	var body io.Reader
	var bodyStr string
	if len(req.Body) > 0 {
		bodyStr = r.resolveVars(req.Body.String())
		body = strings.NewReader(bodyStr)
	}
	// make request
	httpReq, err := r.NewRequest(m, absPath, body)
	if err != nil {
		r.log("invalid request: ", err)
		r.t.FailNow()
		return
	}
	// set body
	bodyLen := len(bodyStr)
	if bodyLen > 0 {
		httpReq.ContentLength = int64(bodyLen)
	}
	// set request headers
	for _, line := range req.Details {
		detail := line.Detail()
		val := fmt.Sprintf("%v", detail.Value.Data)
		val = r.resolveVars(val)
		detail.Value = parse.ParseValue([]byte(val))
		r.Verbose(indent, detail.String())
		httpReq.Header.Add(detail.Key, val)
	}
	// set parameters
	q := httpReq.URL.Query()
	for _, line := range req.Params {
		detail := line.Detail()
		val := fmt.Sprintf("%v", detail.Value.Data)
		val = r.resolveVars(val)
		detail.Value = parse.ParseValue([]byte(val))
		r.Verbose(indent, detail.String())
		q.Add(detail.Key, val)
	}
	httpReq.URL.RawQuery = q.Encode()

	// print request body
	if bodyLen > 0 {
		r.Verbose("```")
		r.Verbose(bodyStr)
		r.Verbose("```")
	}
	// perform request
	httpRes, err := r.DoRequest(httpReq)
	if err != nil {
		r.log(err)
		r.t.FailNow()
		return
	}

	// collect response details
	responseDetails := make(map[string]interface{})
	for k, vs := range httpRes.Header {
		for _, v := range vs {
			responseDetails[k] = v
		}
	}
	// add cookies to repsonse details
	var cookieStrs []string
	for _, cookie := range httpRes.Cookies() {
		cookieStrs = append(cookieStrs, cookie.String())
	}
	responseDetails["Set-Cookie"] = strings.Join(cookieStrs, "|")

	// set other details
	responseDetails["Status"] = float64(httpRes.StatusCode)

	actualBody, err := ioutil.ReadAll(httpRes.Body)
	if err != nil {
		r.log("failed to read body: ", err)
		r.t.FailNow()
		return
	}
	if len(actualBody) > 0 {
		r.Verbose("```")
		r.Verbose(string(actualBody))
		r.Verbose("```")
	}

	// set the body as a field (see issue #15)
	responseDetails["Body"] = string(actualBody)

	/*
		Assertions
		---------------------------------------------------------
	*/

	// assert the body
	if len(req.ExpectedBody) > 0 {
		// check body against expected body
		exp := r.resolveVars(req.ExpectedBody.String())

		// depending on the expectedBodyType:
		// json*: check if expectedBody as JSON is a subset of the actualBody as json
		// json(exact): check JSON for deep equality (avoids checking diffs in white space and order)
		// *: check string for verbatim equality

		expectedTypeIsJSON := strings.HasPrefix(req.ExpectedBodyType, "json")
		if expectedTypeIsJSON {
			// decode json from string
			var expectedJSON interface{}
			var actualJSON interface{}
			json.Unmarshal([]byte(exp), &expectedJSON)
			json.Unmarshal(actualBody, &actualJSON)

			if !strings.Contains(req.ExpectedBodyType, "exact") {
				eq, err := r.assertJSONIsEqualOrSubset(expectedJSON, actualJSON)
				if !eq {
					r.fail(group, req, req.ExpectedBody.Number(), "- body doesn't match", err)
					return
				}
			} else if !reflect.DeepEqual(actualJSON, expectedJSON) {
				r.fail(group, req, req.ExpectedBody.Number(), "- body doesn't match")
				return
			}
		} else if !r.assertBody(actualBody, []byte(exp)) {
			r.fail(group, req, req.ExpectedBody.Number(), "- body doesn't match")
			return
		}
	}

	// assert the details
	var parseDataOnce sync.Once
	var data interface{}
	var errData error
	if len(req.ExpectedDetails) > 0 {
		for _, line := range req.ExpectedDetails {
			detail := line.Detail()
			// resolve any variables mentioned in this detail value
			if detail.Value.Type() == "string" {
				detail.Value.Data = r.resolveVars(detail.Value.Data.(string))
			}
			if strings.HasPrefix(detail.Key, "Data") {
				parseDataOnce.Do(func() {
					data, errData = r.ParseBody(bytes.NewReader(actualBody))
				})
				if !r.assertData(line, data, errData, detail.Key, detail.Value) {
					r.fail(group, req, line.Number, "- "+detail.Key+" doesn't match")
					return
				}
				continue
			}
			var actual interface{}
			var present bool
			if actual, present = responseDetails[detail.Key]; !present {
				r.log(detail.Key, fmt.Sprintf("expected %s: %s  actual %T: %s", detail.Value.Type(), detail.Value, actual, "(missing)"))
				r.fail(group, req, line.Number, "- "+detail.Key+" doesn't match")
				return
			}
			if !r.assertDetail(line, detail.Key, actual, detail.Value) {
				r.fail(group, req, line.Number, "- "+detail.Key+" doesn't match")
				return
			}
		}
	}

}