split code into multiple files
This commit is contained in:
6
check_results.go
Normal file
6
check_results.go
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
func CheckResults(testCase *TestCase) (bool, error) {
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
256
load_input.go
Normal file
256
load_input.go
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/iancoleman/orderedmap"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ReadInput(data []byte, testDefinitions *TestDefinitions) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Unmarshal the json into an orderedmap to preserve the ordering of json structure
|
||||||
|
o := orderedmap.New()
|
||||||
|
err = json.Unmarshal([]byte(data), &o)
|
||||||
|
if err != nil {
|
||||||
|
error := fmt.Sprintf("JSON Unmarshal error %s\n", err)
|
||||||
|
panic(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
topLevel := o.Keys()
|
||||||
|
//fmt.Printf("Found %d top-level keys in json data\n", len(topLevel))
|
||||||
|
|
||||||
|
// TODO : Check required top level keys are present
|
||||||
|
if len(topLevel) <= 1 {
|
||||||
|
error := "Missing required keys in input json"
|
||||||
|
panic(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the name of the group of tests
|
||||||
|
if data, ok := o.Get("name"); ok {
|
||||||
|
testDefinitions.Name = data.(string)
|
||||||
|
//fmt.Printf("test name: '%s'\n", testDefinitions.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the base url if defined
|
||||||
|
if data, ok := o.Get("url"); ok {
|
||||||
|
testDefinitions.BaseUrl = data.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for any headers to include with all requests
|
||||||
|
if data, ok := o.Get("header"); ok {
|
||||||
|
headerMap := data.(orderedmap.OrderedMap)
|
||||||
|
testDefinitions.Headers = OrderedToStringMap(headerMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we should ignore certificate errors
|
||||||
|
if data, ok := o.Get("insecure"); ok {
|
||||||
|
testDefinitions.Insecure = data.(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a reference to the node containing each of the test cases
|
||||||
|
testsInterface, ok := o.Get("testCases")
|
||||||
|
if !ok {
|
||||||
|
panic("No key defining test cases found\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Test Cases is an array, need to go one level down before we start looking at key/values
|
||||||
|
vs := testsInterface.([]interface{})
|
||||||
|
fmt.Printf("Listing %d test definitions\n", len(vs))
|
||||||
|
for i, vInterface := range vs {
|
||||||
|
v := vInterface.(orderedmap.OrderedMap)
|
||||||
|
keys := v.Keys()
|
||||||
|
fmt.Printf("Test %d\n", i)
|
||||||
|
for j := range keys {
|
||||||
|
fmt.Printf("[%d] : %s\n", j, keys[j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
vslice := testsInterface.([]interface{})
|
||||||
|
vmap := vslice[2].(orderedmap.OrderedMap)
|
||||||
|
k := vmap.Keys()
|
||||||
|
for i := range k {
|
||||||
|
fmt.Printf("[%d] : %s\n", i, k[i])
|
||||||
|
}
|
||||||
|
return
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Process the "capture" settings that allow us to use values from one test case in subsequent test cases
|
||||||
|
// each capture node has a name that links it to the test case that captures that data
|
||||||
|
if capture, ok := o.Get("capture"); ok {
|
||||||
|
captureMap := capture.(orderedmap.OrderedMap)
|
||||||
|
captureKeys := captureMap.Keys()
|
||||||
|
for _, outerKey := range captureKeys {
|
||||||
|
// Define a new capture case object to record these details
|
||||||
|
thisCaptureCase := new(CaptureCase)
|
||||||
|
|
||||||
|
// Make sure we can access the capture defintiion
|
||||||
|
if captureCase, ok := captureMap.Get(outerKey); ok {
|
||||||
|
// Store the test case name that this capture will come from
|
||||||
|
thisCaptureCase.TestCaseName = outerKey
|
||||||
|
//fmt.Printf("[%d] : Capture from %s\n", i, outerKey)
|
||||||
|
|
||||||
|
// Get capture data from body
|
||||||
|
thisCaptureCaseMap := captureCase.(orderedmap.OrderedMap)
|
||||||
|
if body, ok := thisCaptureCaseMap.Get("body"); ok {
|
||||||
|
//fmt.Printf("This capture case has body defined\n")
|
||||||
|
|
||||||
|
bodyMap := body.(orderedmap.OrderedMap)
|
||||||
|
bodyMapKeys := bodyMap.Keys()
|
||||||
|
|
||||||
|
bodyData := make(map[string]string)
|
||||||
|
|
||||||
|
for _, bodyKey := range bodyMapKeys {
|
||||||
|
if bodyVal, ok := bodyMap.Get(bodyKey); ok {
|
||||||
|
switch vType := bodyVal.(type) {
|
||||||
|
case string:
|
||||||
|
//fmt.Printf("Capturing '%s' from test result and using '%s' for replacement token\n", bodyKey, bodyVal.(string))
|
||||||
|
|
||||||
|
// Store capture info
|
||||||
|
bodyData[bodyKey] = bodyVal.(string)
|
||||||
|
|
||||||
|
default:
|
||||||
|
fmt.Printf("received unexpected value type, %T\n", vType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
captureBody := new(CaptureBody)
|
||||||
|
captureBody.Data = bodyData
|
||||||
|
thisCaptureCase.CaptureData.Body = *captureBody
|
||||||
|
|
||||||
|
}
|
||||||
|
// TODO : header
|
||||||
|
}
|
||||||
|
|
||||||
|
testDefinitions.CaptureCases = append(testDefinitions.CaptureCases, *thisCaptureCase)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the keys for the first test so we know what config options have been specified
|
||||||
|
testCasesMap := testsInterface.(orderedmap.OrderedMap)
|
||||||
|
testCasesKeys := testCasesMap.Keys()
|
||||||
|
|
||||||
|
// Parse json into our testCaseDefinition
|
||||||
|
|
||||||
|
// Parse each key into our config struct
|
||||||
|
fmt.Printf("Loading %d test cases\n", len(testCasesKeys))
|
||||||
|
for _, outerKey := range testCasesKeys {
|
||||||
|
|
||||||
|
//fmt.Printf("Test %d : %s\n", i, outerKey)
|
||||||
|
|
||||||
|
// Get the name of the test case
|
||||||
|
thisTestCase := new(TestCase)
|
||||||
|
thisTestCase.Name = outerKey
|
||||||
|
|
||||||
|
if testCase, ok := testCasesMap.Get(outerKey); ok {
|
||||||
|
// Get the details of the test case
|
||||||
|
thisTestCaseMap := testCase.(orderedmap.OrderedMap)
|
||||||
|
|
||||||
|
// Path
|
||||||
|
if val, ok := thisTestCaseMap.Get("path"); ok {
|
||||||
|
thisTestCase.Path = val.(string)
|
||||||
|
}
|
||||||
|
// Method
|
||||||
|
if val, ok := thisTestCaseMap.Get("method"); ok {
|
||||||
|
thisTestCase.Method = val.(string)
|
||||||
|
}
|
||||||
|
// Description
|
||||||
|
if val, ok := thisTestCaseMap.Get("description"); ok {
|
||||||
|
thisTestCase.Description = val.(string)
|
||||||
|
}
|
||||||
|
// Body
|
||||||
|
if val, ok := thisTestCaseMap.Get("body"); ok {
|
||||||
|
// Create a normal string map for all the body key-value pairs
|
||||||
|
|
||||||
|
// TODO : Support nested json instead of single level flat request
|
||||||
|
|
||||||
|
//thisTestCase.Body = OrderedToStringMap(val.(orderedmap.OrderedMap))
|
||||||
|
|
||||||
|
bytes, err := json.Marshal(val)
|
||||||
|
if err != nil {
|
||||||
|
error := fmt.Sprintf("Error processing request body for test case : '%s'\n", err)
|
||||||
|
panic(error)
|
||||||
|
} else {
|
||||||
|
thisTestCase.Body = bytes
|
||||||
|
//fmt.Printf("Body marshalled:\n%v\n", string(bytes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Header
|
||||||
|
if val, ok := thisTestCaseMap.Get("header"); ok {
|
||||||
|
// Create a normal string map for all the body key-value pairs
|
||||||
|
thisTestCase.Header = OrderedToStringMap(val.(orderedmap.OrderedMap))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expect - this is more tricky since it is yet another json fragment
|
||||||
|
if val, ok := thisTestCaseMap.Get("expect"); ok {
|
||||||
|
expectOptions := new(ExpectOptions)
|
||||||
|
|
||||||
|
// expect can have header and body definitions
|
||||||
|
expectMap := val.(orderedmap.OrderedMap)
|
||||||
|
|
||||||
|
// Handle the possible checks for a header
|
||||||
|
if expectVal, ok2 := expectMap.Get("header"); ok2 {
|
||||||
|
ReadHeaderTestCases(expectVal.(orderedmap.OrderedMap), &expectOptions.Header)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the possible checks for a body
|
||||||
|
if expectVal, ok2 := expectMap.Get("body"); ok2 {
|
||||||
|
ReadBodyTestCases(expectVal.(orderedmap.OrderedMap), &expectOptions.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
thisTestCase.Expect = *expectOptions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
testDefinitions.TestCases = append(testDefinitions.TestCases, *thisTestCase)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadHeaderTestCases(input orderedmap.OrderedMap, result *HeaderTests) {
|
||||||
|
result.Contains = make(map[string]string)
|
||||||
|
result.Equals = make(map[string]string)
|
||||||
|
|
||||||
|
// Contains check
|
||||||
|
if val, ok := input.Get("contains"); ok {
|
||||||
|
containsMap := val.(orderedmap.OrderedMap)
|
||||||
|
result.Contains = OrderedToStringMap(containsMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals check
|
||||||
|
if val, ok := input.Get("equals"); ok {
|
||||||
|
equalsMap := val.(orderedmap.OrderedMap)
|
||||||
|
result.Equals = OrderedToStringMap(equalsMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadBodyTestCases(input orderedmap.OrderedMap, result *BodyTests) {
|
||||||
|
result.Contains = make(map[string]string)
|
||||||
|
result.Equals = make(map[string]string)
|
||||||
|
result.HasKeys = make(map[string]string)
|
||||||
|
|
||||||
|
// TODO : Use tags in struct rather than hard coding all the different check types
|
||||||
|
// using https://stackoverflow.com/a/23840419 as an idea
|
||||||
|
|
||||||
|
// Contains check
|
||||||
|
if val, ok := input.Get("contains"); ok {
|
||||||
|
containsMap := val.(orderedmap.OrderedMap)
|
||||||
|
result.Contains = OrderedToStringMap(containsMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals check
|
||||||
|
if val, ok := input.Get("equals"); ok {
|
||||||
|
equalsMap := val.(orderedmap.OrderedMap)
|
||||||
|
result.Equals = OrderedToStringMap(equalsMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Has Keys check
|
||||||
|
if val, ok := input.Get("haskeys"); ok {
|
||||||
|
equalsMap := val.(orderedmap.OrderedMap)
|
||||||
|
result.HasKeys = OrderedToStringMap(equalsMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO : remaining tests
|
||||||
|
}
|
539
main.go
539
main.go
@@ -1,287 +1,17 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"crypto/tls"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/iancoleman/orderedmap"
|
"github.com/iancoleman/orderedmap"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TestDefinitions struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
TestCases []TestCase `json:"testCases"`
|
|
||||||
BaseUrl string `json:"url"`
|
|
||||||
CaptureCases []CaptureCase `json:"capture"`
|
|
||||||
Headers map[string]string
|
|
||||||
Insecure bool `json:"insecure"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type TestCase struct {
|
|
||||||
Name string
|
|
||||||
Path string `json:"path"`
|
|
||||||
Method string `json:"method"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Expect ExpectOptions `json:"expect"`
|
|
||||||
|
|
||||||
Header map[string]string
|
|
||||||
//Body map[string]string
|
|
||||||
Body []byte
|
|
||||||
|
|
||||||
// Something to store results in
|
|
||||||
ResultStatusCode int
|
|
||||||
ResultHeaders map[string]string
|
|
||||||
ResultBody string
|
|
||||||
}
|
|
||||||
|
|
||||||
type CaptureCase struct {
|
|
||||||
TestCaseName string
|
|
||||||
CaptureData CaptureCaseData
|
|
||||||
}
|
|
||||||
|
|
||||||
type CaptureCaseData struct {
|
|
||||||
Header CaptureHeader `json:"header"`
|
|
||||||
Body CaptureBody `json:"body"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CaptureHeader struct {
|
|
||||||
Data map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
type CaptureBody struct {
|
|
||||||
Data map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
type CaptureValues struct {
|
|
||||||
Data map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
type ExpectOptions struct {
|
|
||||||
Header HeaderTests `json:"header"`
|
|
||||||
Body BodyTests `json:"body"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test types
|
|
||||||
type HeaderTests struct {
|
|
||||||
Contains map[string]string
|
|
||||||
Equals map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
type BodyTests struct {
|
|
||||||
Contains map[string]string
|
|
||||||
Equals map[string]string
|
|
||||||
HasKeys map[string]string
|
|
||||||
PathEquals string
|
|
||||||
PathContains map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
var testDefinitions *TestDefinitions
|
var testDefinitions *TestDefinitions
|
||||||
var captureValues *CaptureValues
|
var captureValues *CaptureValues
|
||||||
|
|
||||||
// fileExists returns true if the specified file exists and is not a directory
|
|
||||||
func fileExists(filename string) bool {
|
|
||||||
info, err := os.Stat(filename)
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return !info.IsDir()
|
|
||||||
}
|
|
||||||
|
|
||||||
func PerformGet() {
|
|
||||||
// Sample code from https://golangnote.com/request/sending-post-request-in-golang-with-header
|
|
||||||
url := "https://www.example.com/api/v1/create"
|
|
||||||
contentType := "application/json"
|
|
||||||
data := []byte(`{"name": "Test User", "email": "test@example.com"}`)
|
|
||||||
|
|
||||||
client := &http.Client{}
|
|
||||||
req, err := http.NewRequest("GET", url, bytes.NewBuffer(data))
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
req.Header.Add("Content-Type", contentType)
|
|
||||||
req.Header.Add("Authorization", "Bearer YOUR_ACCESS_TOKEN")
|
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
fmt.Printf("%+v\n", resp.Header)
|
|
||||||
fmt.Printf("%+v\n", resp.StatusCode)
|
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(string(body))
|
|
||||||
}
|
|
||||||
|
|
||||||
func PerformPost() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOutput() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunTest Executes each individual test case
|
|
||||||
func RunTest(testCase *TestCase) error {
|
|
||||||
var err error
|
|
||||||
var requestUrl string
|
|
||||||
var requestType string
|
|
||||||
var req *http.Request
|
|
||||||
var bodyMap map[string]interface{}
|
|
||||||
|
|
||||||
tr := &http.Transport{
|
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: testDefinitions.Insecure},
|
|
||||||
}
|
|
||||||
client := &http.Client{Transport: tr}
|
|
||||||
|
|
||||||
fmt.Printf("Running test '%s'\n", testCase.Name)
|
|
||||||
|
|
||||||
// Determine URL
|
|
||||||
if len(testDefinitions.BaseUrl) > 0 {
|
|
||||||
requestUrl, err = url.JoinPath(testDefinitions.BaseUrl, testCase.Path)
|
|
||||||
if err != nil {
|
|
||||||
errMessage := fmt.Sprintf("error combining request URL : '%s'\n", err)
|
|
||||||
return errors.New(errMessage)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
requestUrl = testCase.Path
|
|
||||||
}
|
|
||||||
|
|
||||||
// Populate request body
|
|
||||||
|
|
||||||
// Get request type, default to GET
|
|
||||||
if len(testCase.Method) == 0 {
|
|
||||||
//return errors.New("no test method specifed, must be either GET or POST")
|
|
||||||
|
|
||||||
// Assume GET if no method specified
|
|
||||||
requestType = "GET"
|
|
||||||
} else {
|
|
||||||
requestType = testCase.Method
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create request
|
|
||||||
if len(testCase.Body) > 0 {
|
|
||||||
req, err = http.NewRequest(requestType, requestUrl, bytes.NewBuffer(testCase.Body))
|
|
||||||
} else {
|
|
||||||
req, err = http.NewRequest(requestType, requestUrl, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
errMessage := fmt.Sprintf("error submitting request : '%s'\n", err)
|
|
||||||
return errors.New(errMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assemble headers
|
|
||||||
// Global headers first
|
|
||||||
if len(testDefinitions.Headers) > 0 {
|
|
||||||
fmt.Printf("Adding global headers to request\n")
|
|
||||||
for k, v := range testDefinitions.Headers {
|
|
||||||
// Check for capture tokens that need to be replaced before adding the value
|
|
||||||
key := HeaderReplaceCaptures(k)
|
|
||||||
val := HeaderReplaceCaptures(v)
|
|
||||||
|
|
||||||
fmt.Printf("Add global header %s = %s\n", key, val)
|
|
||||||
req.Header.Add(key, val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then any test case specific headers
|
|
||||||
for k, v := range testCase.Header {
|
|
||||||
// Check for capture tokens that need to be replaced before adding the value
|
|
||||||
key := HeaderReplaceCaptures(k)
|
|
||||||
val := HeaderReplaceCaptures(v)
|
|
||||||
|
|
||||||
fmt.Printf("Add header %s = %s\n", key, val)
|
|
||||||
req.Header.Add(key, val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform request
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
// Store response
|
|
||||||
fmt.Printf("Header response:\n%+v\n", resp.Header)
|
|
||||||
fmt.Printf("http_status:\n'%+v'\n", resp.StatusCode)
|
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("Body response:\n%s\n", string(body))
|
|
||||||
|
|
||||||
// We have no idea what the response will look like so use an interface and unmarshal manually as needed
|
|
||||||
if len(body) > 0 {
|
|
||||||
var b interface{}
|
|
||||||
json.Unmarshal(body, &b)
|
|
||||||
if b != nil {
|
|
||||||
bodyMap = b.(map[string]interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate through testDefinitions.CaptureCases and see if there are any that match this test
|
|
||||||
// If we found one, add the appropriate replacement to captureValues
|
|
||||||
for i := range testDefinitions.CaptureCases {
|
|
||||||
if testDefinitions.CaptureCases[i].TestCaseName == testCase.Name {
|
|
||||||
fmt.Printf("Need to capture value from this test case\n")
|
|
||||||
|
|
||||||
captureData := testDefinitions.CaptureCases[i].CaptureData
|
|
||||||
|
|
||||||
for k, v := range captureData.Body.Data {
|
|
||||||
fmt.Printf("Body capture %s = %s\n", k, v)
|
|
||||||
|
|
||||||
if val, ok := bodyMap[k]; ok {
|
|
||||||
// TODO handle values other than string using a switch
|
|
||||||
fmt.Printf("Found matching capture in body with value : '%v', storing replacement with key '%s'\n", val, v)
|
|
||||||
captureValues.Data[v] = val.(string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Capture anything needed
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// No errors if we reached this point
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// HeaderReplaceCaptures iterates through captureValues to apply any text replacements necessary for input string
|
|
||||||
func HeaderReplaceCaptures(input string) string {
|
|
||||||
|
|
||||||
for k, v := range captureValues.Data {
|
|
||||||
if strings.Contains(input, k) {
|
|
||||||
fmt.Printf("Found key '%s' in input string '%s', replacing with '%s'\n", k, input, v)
|
|
||||||
input = strings.Replace(input, k, v, -1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return input
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var s []byte
|
var s []byte
|
||||||
var err error
|
var err error
|
||||||
@@ -312,222 +42,27 @@ func main() {
|
|||||||
// Process the input json
|
// Process the input json
|
||||||
ReadInput(s, testDefinitions)
|
ReadInput(s, testDefinitions)
|
||||||
|
|
||||||
// For debugging, just dump the output
|
|
||||||
fmt.Printf("%+v\n", testDefinitions)
|
|
||||||
|
|
||||||
// Perform specified test cases
|
// Perform specified test cases
|
||||||
for i := range testDefinitions.TestCases {
|
for i := range testDefinitions.TestCases {
|
||||||
RunTest(&testDefinitions.TestCases[i])
|
RunTest(&testDefinitions.TestCases[i])
|
||||||
|
success, _ := CheckResults(&testDefinitions.TestCases[i])
|
||||||
|
if !success {
|
||||||
|
fmt.Printf("Test result failed\n")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For debugging, just dump the output
|
||||||
|
fmt.Printf("%+v\n", testDefinitions)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadInput(data []byte, testDefinitions *TestDefinitions) {
|
// fileExists returns true if the specified file exists and is not a directory
|
||||||
var err error
|
func fileExists(filename string) bool {
|
||||||
|
info, err := os.Stat(filename)
|
||||||
// Unmarshal the json into an orderedmap to preserve the ordering of json structure
|
if os.IsNotExist(err) {
|
||||||
o := orderedmap.New()
|
return false
|
||||||
err = json.Unmarshal([]byte(data), &o)
|
|
||||||
if err != nil {
|
|
||||||
error := fmt.Sprintf("JSON Unmarshal error %s\n", err)
|
|
||||||
panic(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
topLevel := o.Keys()
|
|
||||||
fmt.Printf("Found %d top-level keys in json data\n", len(topLevel))
|
|
||||||
|
|
||||||
// TODO : Check required top level keys are present
|
|
||||||
if len(topLevel) <= 1 {
|
|
||||||
error := "Missing required keys in input json"
|
|
||||||
panic(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the name of the group of tests
|
|
||||||
if data, ok := o.Get("name"); ok {
|
|
||||||
testDefinitions.Name = data.(string)
|
|
||||||
fmt.Printf("test name: '%s'\n", testDefinitions.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the base url if defined
|
|
||||||
if data, ok := o.Get("url"); ok {
|
|
||||||
testDefinitions.BaseUrl = data.(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for any headers to include with all requests
|
|
||||||
if data, ok := o.Get("header"); ok {
|
|
||||||
headerMap := data.(orderedmap.OrderedMap)
|
|
||||||
testDefinitions.Headers = OrderedToStringMap(headerMap)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we should ignore certificate errors
|
|
||||||
if data, ok := o.Get("insecure"); ok {
|
|
||||||
testDefinitions.Insecure = data.(bool)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a reference to the node containing each of the test cases
|
|
||||||
testsInterface, ok := o.Get("testCases")
|
|
||||||
if !ok {
|
|
||||||
fmt.Printf("No key defining test cases found\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
// Test Cases is an array, need to go one level down before we start looking at key/values
|
|
||||||
vs := testsInterface.([]interface{})
|
|
||||||
fmt.Printf("Listing %d test definitions\n", len(vs))
|
|
||||||
for i, vInterface := range vs {
|
|
||||||
v := vInterface.(orderedmap.OrderedMap)
|
|
||||||
keys := v.Keys()
|
|
||||||
fmt.Printf("Test %d\n", i)
|
|
||||||
for j := range keys {
|
|
||||||
fmt.Printf("[%d] : %s\n", j, keys[j])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
vslice := testsInterface.([]interface{})
|
|
||||||
vmap := vslice[2].(orderedmap.OrderedMap)
|
|
||||||
k := vmap.Keys()
|
|
||||||
for i := range k {
|
|
||||||
fmt.Printf("[%d] : %s\n", i, k[i])
|
|
||||||
}
|
|
||||||
return
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Process the "capture" settings that allow us to use values from one test case in subsequent test cases
|
|
||||||
// each capture node has a name that links it to the test case that captures that data
|
|
||||||
if capture, ok := o.Get("capture"); ok {
|
|
||||||
captureMap := capture.(orderedmap.OrderedMap)
|
|
||||||
captureKeys := captureMap.Keys()
|
|
||||||
for i, outerKey := range captureKeys {
|
|
||||||
// Define a new capture case object to record these details
|
|
||||||
thisCaptureCase := new(CaptureCase)
|
|
||||||
|
|
||||||
// Make sure we can access the capture defintiion
|
|
||||||
if captureCase, ok := captureMap.Get(outerKey); ok {
|
|
||||||
// Store the test case name that this capture will come from
|
|
||||||
thisCaptureCase.TestCaseName = outerKey
|
|
||||||
fmt.Printf("[%d] : Capture from %s\n", i, outerKey)
|
|
||||||
|
|
||||||
// Get capture data from body
|
|
||||||
thisCaptureCaseMap := captureCase.(orderedmap.OrderedMap)
|
|
||||||
if body, ok := thisCaptureCaseMap.Get("body"); ok {
|
|
||||||
//fmt.Printf("This capture case has body defined\n")
|
|
||||||
|
|
||||||
bodyMap := body.(orderedmap.OrderedMap)
|
|
||||||
bodyMapKeys := bodyMap.Keys()
|
|
||||||
|
|
||||||
bodyData := make(map[string]string)
|
|
||||||
|
|
||||||
for _, bodyKey := range bodyMapKeys {
|
|
||||||
if bodyVal, ok := bodyMap.Get(bodyKey); ok {
|
|
||||||
switch vType := bodyVal.(type) {
|
|
||||||
case string:
|
|
||||||
fmt.Printf("Capturing '%s' from test result and using '%s' for replacement token\n", bodyKey, bodyVal.(string))
|
|
||||||
|
|
||||||
// Store capture info
|
|
||||||
bodyData[bodyKey] = bodyVal.(string)
|
|
||||||
|
|
||||||
default:
|
|
||||||
fmt.Printf("received unexpected value type, %T\n", vType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
captureBody := new(CaptureBody)
|
|
||||||
captureBody.Data = bodyData
|
|
||||||
thisCaptureCase.CaptureData.Body = *captureBody
|
|
||||||
|
|
||||||
}
|
|
||||||
// TODO : header
|
|
||||||
}
|
|
||||||
|
|
||||||
testDefinitions.CaptureCases = append(testDefinitions.CaptureCases, *thisCaptureCase)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the keys for the first test so we know what config options have been specified
|
|
||||||
testCasesMap := testsInterface.(orderedmap.OrderedMap)
|
|
||||||
testCasesKeys := testCasesMap.Keys()
|
|
||||||
|
|
||||||
// Parse json into our testCaseDefinition
|
|
||||||
|
|
||||||
// Parse each key into our config struct
|
|
||||||
fmt.Printf("Listing %d test cases\n", len(testCasesKeys))
|
|
||||||
for i, outerKey := range testCasesKeys {
|
|
||||||
|
|
||||||
fmt.Printf("Test %d : %s\n", i, outerKey)
|
|
||||||
|
|
||||||
// Get the name of the test case
|
|
||||||
thisTestCase := new(TestCase)
|
|
||||||
thisTestCase.Name = outerKey
|
|
||||||
|
|
||||||
if testCase, ok := testCasesMap.Get(outerKey); ok {
|
|
||||||
// Get the details of the test case
|
|
||||||
thisTestCaseMap := testCase.(orderedmap.OrderedMap)
|
|
||||||
|
|
||||||
// Path
|
|
||||||
if val, ok := thisTestCaseMap.Get("path"); ok {
|
|
||||||
thisTestCase.Path = val.(string)
|
|
||||||
}
|
|
||||||
// Method
|
|
||||||
if val, ok := thisTestCaseMap.Get("method"); ok {
|
|
||||||
thisTestCase.Method = val.(string)
|
|
||||||
}
|
|
||||||
// Description
|
|
||||||
if val, ok := thisTestCaseMap.Get("description"); ok {
|
|
||||||
thisTestCase.Description = val.(string)
|
|
||||||
}
|
|
||||||
// Body
|
|
||||||
if val, ok := thisTestCaseMap.Get("body"); ok {
|
|
||||||
// Create a normal string map for all the body key-value pairs
|
|
||||||
|
|
||||||
// TODO : Support nested json instead of single level flat request
|
|
||||||
|
|
||||||
//thisTestCase.Body = OrderedToStringMap(val.(orderedmap.OrderedMap))
|
|
||||||
|
|
||||||
bytes, err := json.Marshal(val)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Error processing request body for test case : '%s'\n", err)
|
|
||||||
} else {
|
|
||||||
thisTestCase.Body = bytes
|
|
||||||
//fmt.Printf("Body marshalled:\n%v\n", string(bytes))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Header
|
|
||||||
if val, ok := thisTestCaseMap.Get("header"); ok {
|
|
||||||
// Create a normal string map for all the body key-value pairs
|
|
||||||
thisTestCase.Header = OrderedToStringMap(val.(orderedmap.OrderedMap))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expect - this is more tricky since it is yet another json fragment
|
|
||||||
if val, ok := thisTestCaseMap.Get("expect"); ok {
|
|
||||||
expectOptions := new(ExpectOptions)
|
|
||||||
|
|
||||||
// expect can have header and body definitions
|
|
||||||
expectMap := val.(orderedmap.OrderedMap)
|
|
||||||
|
|
||||||
// Handle the possible checks for a header
|
|
||||||
if expectVal, ok2 := expectMap.Get("header"); ok2 {
|
|
||||||
ReadHeaderTestCases(expectVal.(orderedmap.OrderedMap), &expectOptions.Header)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle the possible checks for a body
|
|
||||||
if expectVal, ok2 := expectMap.Get("body"); ok2 {
|
|
||||||
ReadBodyTestCases(expectVal.(orderedmap.OrderedMap), &expectOptions.Body)
|
|
||||||
}
|
|
||||||
|
|
||||||
thisTestCase.Expect = *expectOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
for j, key := range thisTestCaseMap.Keys() {
|
|
||||||
val, _ := thisTestCaseMap.Get(key)
|
|
||||||
|
|
||||||
fmt.Printf("[%d] %s : %s\n", j, key, val)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
testDefinitions.TestCases = append(testDefinitions.TestCases, *thisTestCase)
|
|
||||||
}
|
}
|
||||||
|
return !info.IsDir()
|
||||||
}
|
}
|
||||||
|
|
||||||
func OrderedToStringMap(input orderedmap.OrderedMap) map[string]string {
|
func OrderedToStringMap(input orderedmap.OrderedMap) map[string]string {
|
||||||
@@ -549,49 +84,3 @@ func OrderedToStringMap(input orderedmap.OrderedMap) map[string]string {
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadHeaderTestCases(input orderedmap.OrderedMap, result *HeaderTests) {
|
|
||||||
result.Contains = make(map[string]string)
|
|
||||||
result.Equals = make(map[string]string)
|
|
||||||
|
|
||||||
// Contains check
|
|
||||||
if val, ok := input.Get("contains"); ok {
|
|
||||||
containsMap := val.(orderedmap.OrderedMap)
|
|
||||||
result.Contains = OrderedToStringMap(containsMap)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equals check
|
|
||||||
if val, ok := input.Get("equals"); ok {
|
|
||||||
equalsMap := val.(orderedmap.OrderedMap)
|
|
||||||
result.Equals = OrderedToStringMap(equalsMap)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReadBodyTestCases(input orderedmap.OrderedMap, result *BodyTests) {
|
|
||||||
result.Contains = make(map[string]string)
|
|
||||||
result.Equals = make(map[string]string)
|
|
||||||
result.HasKeys = make(map[string]string)
|
|
||||||
|
|
||||||
// TODO : Use tags in struct rather than hard coding all the different check types
|
|
||||||
// using https://stackoverflow.com/a/23840419 as an idea
|
|
||||||
|
|
||||||
// Contains check
|
|
||||||
if val, ok := input.Get("contains"); ok {
|
|
||||||
containsMap := val.(orderedmap.OrderedMap)
|
|
||||||
result.Contains = OrderedToStringMap(containsMap)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equals check
|
|
||||||
if val, ok := input.Get("equals"); ok {
|
|
||||||
equalsMap := val.(orderedmap.OrderedMap)
|
|
||||||
result.Equals = OrderedToStringMap(equalsMap)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Has Keys check
|
|
||||||
if val, ok := input.Get("haskeys"); ok {
|
|
||||||
equalsMap := val.(orderedmap.OrderedMap)
|
|
||||||
result.HasKeys = OrderedToStringMap(equalsMap)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO : remaining tests
|
|
||||||
}
|
|
||||||
|
169
run_tests.go
Normal file
169
run_tests.go
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RunTest Executes each individual test case
|
||||||
|
func RunTest(testCase *TestCase) error {
|
||||||
|
var err error
|
||||||
|
var requestUrl string
|
||||||
|
var requestType string
|
||||||
|
var req *http.Request
|
||||||
|
var bodyMap map[string]interface{}
|
||||||
|
|
||||||
|
tr := &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: testDefinitions.Insecure},
|
||||||
|
}
|
||||||
|
client := &http.Client{Transport: tr}
|
||||||
|
|
||||||
|
fmt.Printf("Running test '%s'\n", testCase.Name)
|
||||||
|
|
||||||
|
// Determine URL
|
||||||
|
if len(testDefinitions.BaseUrl) > 0 {
|
||||||
|
requestUrl, err = url.JoinPath(testDefinitions.BaseUrl, testCase.Path)
|
||||||
|
if err != nil {
|
||||||
|
errMessage := fmt.Sprintf("error combining request URL : '%s'\n", err)
|
||||||
|
return errors.New(errMessage)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
requestUrl = testCase.Path
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate request body
|
||||||
|
|
||||||
|
// Get request type, default to GET
|
||||||
|
if len(testCase.Method) == 0 {
|
||||||
|
//return errors.New("no test method specifed, must be either GET or POST")
|
||||||
|
|
||||||
|
// Assume GET if no method specified
|
||||||
|
requestType = "GET"
|
||||||
|
} else {
|
||||||
|
requestType = testCase.Method
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create request
|
||||||
|
if len(testCase.Body) > 0 {
|
||||||
|
req, err = http.NewRequest(requestType, requestUrl, bytes.NewBuffer(testCase.Body))
|
||||||
|
} else {
|
||||||
|
req, err = http.NewRequest(requestType, requestUrl, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
errMessage := fmt.Sprintf("error submitting request : '%s'\n", err)
|
||||||
|
return errors.New(errMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assemble headers
|
||||||
|
// Global headers first
|
||||||
|
if len(testDefinitions.Headers) > 0 {
|
||||||
|
//fmt.Printf("Adding global headers to request\n")
|
||||||
|
for k, v := range testDefinitions.Headers {
|
||||||
|
// Check for capture tokens that need to be replaced before adding the value
|
||||||
|
key := HeaderReplaceCaptures(k)
|
||||||
|
val := HeaderReplaceCaptures(v)
|
||||||
|
|
||||||
|
//fmt.Printf("Add global header %s = %s\n", key, val)
|
||||||
|
req.Header.Add(key, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then any test case specific headers
|
||||||
|
for k, v := range testCase.Header {
|
||||||
|
// Check for capture tokens that need to be replaced before adding the value
|
||||||
|
key := HeaderReplaceCaptures(k)
|
||||||
|
val := HeaderReplaceCaptures(v)
|
||||||
|
|
||||||
|
//fmt.Printf("Add header %s = %s\n", key, val)
|
||||||
|
req.Header.Add(key, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform request
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// Store response
|
||||||
|
testCase.ResultStatusCode = resp.StatusCode
|
||||||
|
testCase.ResultHeaders = resp.Header
|
||||||
|
|
||||||
|
fmt.Printf("http_status:\n'%+v'\n", testCase.ResultStatusCode)
|
||||||
|
fmt.Printf("Header response:\n%+v\n", testCase.ResultHeaders)
|
||||||
|
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
testCase.ResultBody = string(body)
|
||||||
|
fmt.Printf("Body response:\n%s\n", testCase.ResultBody)
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Something to store results in
|
||||||
|
ResultStatusCode int
|
||||||
|
ResultHeaders map[string]string
|
||||||
|
ResultBody string
|
||||||
|
*/
|
||||||
|
|
||||||
|
// We have no idea what the response will look like so use an interface and unmarshal manually as needed
|
||||||
|
if len(body) > 0 {
|
||||||
|
var b interface{}
|
||||||
|
json.Unmarshal(body, &b)
|
||||||
|
if b != nil {
|
||||||
|
bodyMap = b.(map[string]interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate through testDefinitions.CaptureCases and see if there are any that match this test
|
||||||
|
// If we found one, add the appropriate replacement to captureValues
|
||||||
|
for i := range testDefinitions.CaptureCases {
|
||||||
|
if testDefinitions.CaptureCases[i].TestCaseName == testCase.Name {
|
||||||
|
captureData := testDefinitions.CaptureCases[i].CaptureData
|
||||||
|
|
||||||
|
for k, v := range captureData.Body.Data {
|
||||||
|
//fmt.Printf("Body capture %s = %s\n", k, v)
|
||||||
|
|
||||||
|
if val, ok := bodyMap[k]; ok {
|
||||||
|
// TODO handle values other than string using a switch
|
||||||
|
//fmt.Printf("Found matching capture in body with value : '%v', storing replacement with key '%s'\n", val, v)
|
||||||
|
captureValues.Data[v] = val.(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capture anything needed
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No errors if we reached this point
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HeaderReplaceCaptures iterates through captureValues to apply any text replacements necessary for input string
|
||||||
|
func HeaderReplaceCaptures(input string) string {
|
||||||
|
|
||||||
|
for k, v := range captureValues.Data {
|
||||||
|
input = strings.Replace(input, k, v, -1)
|
||||||
|
/*
|
||||||
|
if strings.Contains(input, k) {
|
||||||
|
//fmt.Printf("Found key '%s' in input string '%s', replacing with '%s'\n", k, input, v)
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
return input
|
||||||
|
}
|
68
structs.go
Normal file
68
structs.go
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type TestDefinitions struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
TestCases []TestCase `json:"testCases"`
|
||||||
|
BaseUrl string `json:"url"`
|
||||||
|
CaptureCases []CaptureCase `json:"capture"`
|
||||||
|
Headers map[string]string
|
||||||
|
Insecure bool `json:"insecure"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestCase struct {
|
||||||
|
Name string
|
||||||
|
Path string `json:"path"`
|
||||||
|
Method string `json:"method"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Expect ExpectOptions `json:"expect"`
|
||||||
|
|
||||||
|
Header map[string]string
|
||||||
|
//Body map[string]string
|
||||||
|
Body []byte
|
||||||
|
|
||||||
|
// Something to store results in
|
||||||
|
ResultStatusCode int
|
||||||
|
ResultHeaders map[string][]string
|
||||||
|
ResultBody string
|
||||||
|
}
|
||||||
|
|
||||||
|
type CaptureCase struct {
|
||||||
|
TestCaseName string
|
||||||
|
CaptureData CaptureCaseData
|
||||||
|
}
|
||||||
|
|
||||||
|
type CaptureCaseData struct {
|
||||||
|
Header CaptureHeader `json:"header"`
|
||||||
|
Body CaptureBody `json:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CaptureHeader struct {
|
||||||
|
Data map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
type CaptureBody struct {
|
||||||
|
Data map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
type CaptureValues struct {
|
||||||
|
Data map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExpectOptions struct {
|
||||||
|
Header HeaderTests `json:"header"`
|
||||||
|
Body BodyTests `json:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test types
|
||||||
|
type HeaderTests struct {
|
||||||
|
Contains map[string]string
|
||||||
|
Equals map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
type BodyTests struct {
|
||||||
|
Contains map[string]string
|
||||||
|
Equals map[string]string
|
||||||
|
HasKeys map[string]string
|
||||||
|
PathEquals string
|
||||||
|
PathContains map[string]string
|
||||||
|
}
|
Reference in New Issue
Block a user