improve body contains check
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
@@ -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:
|
||||
|
@@ -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
7
go.mod
@@ -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
11
go.sum
@@ -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=
|
||||
|
@@ -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
17
main.go
@@ -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
|
||||
|
@@ -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)
|
||||
|
77
tests.json
77
tests.json
@@ -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": {
|
||||
|
Reference in New Issue
Block a user