improve body contains check
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2024-01-03 15:42:25 +11:00
parent af7ee7f3c1
commit b2519287ef
8 changed files with 148 additions and 62 deletions

View File

@@ -11,6 +11,7 @@ steps:
commands:
- sh ./.drone.sh
# See https://plugins.drone.io/plugins/gitea-release
- name: gitea_release
image: plugins/gitea-release
when:

View File

@@ -56,11 +56,11 @@ func HeaderContains(testCase *TestCase) (bool, error) {
checkFound := false
for hKey, hVal := range testCase.ResultHeaders {
if k == hKey {
fmt.Printf("Found header key '%s', checking value '%s' matches test definition '%s'\n", hKey, hVal, v)
//fmt.Printf("Found header key '%s', checking value '%s' matches test definition '%s'\n", hKey, hVal, v)
for i := range hVal {
if strings.Contains(hVal[i], v) {
fmt.Printf("Found match '%s' = '%s'\n", hVal[i], v)
//fmt.Printf("Found match '%s' = '%s'\n", hVal[i], v)
checkFound = true
}
}
@@ -68,7 +68,7 @@ func HeaderContains(testCase *TestCase) (bool, error) {
}
if !checkFound {
fmt.Printf("Expected to find header key-value of '%s:%s' but no matching value was found\n", k, v)
fmt.Printf("Header Contains Check failed to find expected header key-value of '%s:%s'\n", k, v)
return false, nil
}
}
@@ -90,27 +90,32 @@ func HeaderEquals(testCase *TestCase) (bool, error) {
func BodyContains(testCase *TestCase) (bool, error) {
for k, v := range testCase.Expect.Body.Contains {
//fmt.Printf("Body contains check '%s'='%s'\n", k, v)
fmt.Printf("Body contains check '%s'='%s'\n", k, v)
fmt.Printf("%+v\n", testCase.ResultBodyMap)
// If the body response was json then we stored it already when running the test
if val, ok := testCase.ResultBodyMap[k]; ok {
// TODO handle values other than string using a switch
//fmt.Printf("Found key in body with response '%s'\n", val)
results := findKey(k, testCase.ResultBodyMap)
// Check that the value matched
if strings.Contains(val.(string), v) {
fmt.Printf("Key matches, success\n")
} else {
fmt.Printf("Body Contains check failed on key '%s'. Expected value '%s' but found '%s'\n", k, v, val)
if len(results) > 0 {
fmt.Printf("Found key '%s': %v\n", k, results)
checkFound := false
// search through all the results to see if the expected value is there
for i := range results {
if strings.Contains(results[i].(string), v) {
fmt.Printf("Expected value of '%s' matches, success\n", v)
checkFound = true
break
}
}
if !checkFound {
fmt.Printf("Body Contains check failed on key '%s', did not find expected value '%s'\n", k, v)
return false, nil
}
} else {
// body did not contain the key we were looking for so return false
return false, nil
fmt.Printf("Key '%s' not found\n", k)
}
// TODO
}
return true, nil
@@ -120,7 +125,7 @@ func BodyHasKeys(testCase *TestCase) (bool, error) {
for _, key := range testCase.Expect.Body.HasKeys {
// If the body response was json then we stored it already when running the test
if _, ok := testCase.ResultBodyMap[key]; ok {
fmt.Printf("Confirmed body has key '%s'\n", key)
//fmt.Printf("Confirmed body has key '%s'\n", key)
} else {
fmt.Printf("Body HasKeys test failed, expected key '%s' not found\n", key)
return false, nil
@@ -129,3 +134,23 @@ func BodyHasKeys(testCase *TestCase) (bool, error) {
return true, nil
}
// findKey recursively searches map for specified key
func findKey(key string, data interface{}) []interface{} {
var results []interface{}
switch v := data.(type) {
case map[string]interface{}:
if val, ok := v[key]; ok {
results = append(results, val)
}
for _, value := range v {
results = append(results, findKey(key, value)...)
}
case []interface{}:
for _, value := range v {
results = append(results, findKey(key, value)...)
}
}
return results
}

7
go.mod
View File

@@ -3,3 +3,10 @@ module apitester
go 1.21.0
require github.com/iancoleman/orderedmap v0.3.0
require (
github.com/fatih/color v1.16.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
golang.org/x/sys v0.14.0 // indirect
)

11
go.sum
View File

@@ -1,2 +1,13 @@
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc=
github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=

View File

@@ -56,30 +56,6 @@ func ReadInput(data []byte, testDefinitions *TestDefinitions) {
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 {
@@ -255,12 +231,8 @@ func ReadBodyTestCases(input orderedmap.OrderedMap, result *BodyTests) {
// Has Keys check
if val, ok := input.Get("hasKeys"); ok {
// This is an array not a map
hasKeysArray := val.([]interface{})
result.HasKeys = OrderedToStringSlice(hasKeysArray)
//equalsMap := val.(orderedmap.OrderedMap)
//result.HasKeys = OrderedToStringMap(equalsMap)
}
// TODO : remaining tests

17
main.go
View File

@@ -53,12 +53,14 @@ func main() {
if !success {
fmt.Printf("Test result failed\n")
os.Exit(1)
} else {
fmt.Printf("Tests successful\n")
}
}
// For debugging, just dump the output
fmt.Printf("%+v\n", testDefinitions)
//fmt.Printf("%+v\n", testDefinitions)
}
// fileExists returns true if the specified file exists and is not a directory
@@ -92,27 +94,16 @@ func OrderedToStringMap(input orderedmap.OrderedMap) map[string]string {
func OrderedToStringSlice(input []interface{}) []string {
result := []string{}
//vs := testsInterface.([]interface{})
//fmt.Printf("Listing %d test definitions\n", len(vs))
for i := range input {
//fmt.Printf("Reading '%s'\n", input[i])
switch vType := input[i].(type) {
// TODO : more types
case string:
result = append(result, input[i].(string))
default:
fmt.Printf("OrderedToStringSlice received unexpected value type, %T\n", vType)
}
/*
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])
}
*/
}
return result

View File

@@ -25,7 +25,7 @@ func RunTest(testCase *TestCase) error {
}
client := &http.Client{Transport: tr}
fmt.Printf("Running test '%s'\n", testCase.Name)
fmt.Printf("\nRunning %s : %s\n", testCase.Name, testCase.Description)
// Determine URL
if len(testDefinitions.BaseUrl) > 0 {
@@ -50,8 +50,11 @@ func RunTest(testCase *TestCase) error {
requestType = testCase.Method
}
fmt.Printf("Preparing %s for endpoint %s\n", requestType, requestUrl)
// Create request
if len(testCase.Body) > 0 {
fmt.Printf("Adding body to request : %s\n", testCase.Body)
req, err = http.NewRequest(requestType, requestUrl, bytes.NewBuffer(testCase.Body))
} else {
req, err = http.NewRequest(requestType, requestUrl, nil)
@@ -87,6 +90,7 @@ func RunTest(testCase *TestCase) error {
}
// Perform request
fmt.Printf("Sending request\n")
resp, err := client.Do(req)
if err != nil {
fmt.Println(err)
@@ -98,7 +102,7 @@ func RunTest(testCase *TestCase) error {
testCase.ResultStatusCode = resp.StatusCode
testCase.ResultHeaders = resp.Header
fmt.Printf("http_status:\n'%+v'\n", testCase.ResultStatusCode)
fmt.Printf("Status Code: %s\n", resp.Status)
fmt.Printf("Header response:\n%+v\n", testCase.ResultHeaders)
body, err := io.ReadAll(resp.Body)

View File

@@ -22,6 +22,80 @@
}
},
"test2": {
"path": "/api/admin/user/register",
"method": "POST",
"description": "register user account",
"header": {
"Authorization": "Bearer %ACCESS_TOKEN%"
},
"body": {
"username": "test",
"password": "test"
},
"expect": {
"header": {
"contains": {
"http_status": "200"
}
},
"body": {
"contains": {
"message": "registration success"
}
}
}
},
"test3": {
"path": "/api/admin/users",
"method": "GET",
"description": "get user account listing",
"expect": {
"body": {
"contains": {
"userName": "test"
}
}
}
},
"test4": {
"path": "/api/admin/user/delete",
"method": "POST",
"description": "delete user account",
"body": {
"userName": "test"
},
"expect": {
"header": {
"contains": {
"http_status": "200"
}
},
"body": {
"contains": {
"message": "user deletion success"
}
}
}
},
"test10": {
"path": "/api/secret/store",
"method": "POST",
"description": "store new secret",
"body": {
"deviceName": "adcp01.cdc.home",
"deviceCategory": "windows-server",
"userName": "dummy@cdc.home",
"secretValue": "TheAlphabet"
},
"expect": {
"header": {
"contains": {
"http_status": "200"
}
}
}
},
"test99": {
"path": "/api/secret/list",
"method": "GET",
"description": "List secrets",
@@ -47,7 +121,8 @@
"url": "https://10.63.39.130:8443",
"insecure": true,
"header": {
"Content-Type": "application/json"
"Content-Type": "application/json",
"Authorization": "Bearer %ACCESS_TOKEN%"
},
"capture": {
"test1": {