This is an automated email from the ASF dual-hosted git repository.

rawlin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git


The following commit(s) were added to refs/heads/master by this push:
     new 85dee64661 Refactor iso, loginfail, readonlycannotmodify, steering, 
servercheckextension tests (#6889)
85dee64661 is described below

commit 85dee646614f1556eacf540db65db9a154dc5a48
Author: Eric Holguin <[email protected]>
AuthorDate: Wed Jun 29 16:30:33 2022 -0600

    Refactor iso, loginfail, readonlycannotmodify, steering, 
servercheckextension tests (#6889)
    
    * Refactored steering tests
    
    * Move forbidden tests to cdn tests
    
    * Refactor loginfail tests
    
    * Refactor iso tests
    
    * Refactor server check extension tests
---
 traffic_ops/testing/api/v3/cdns_test.go            |  22 ++-
 traffic_ops/testing/api/v3/iso_test.go             |  55 ++----
 traffic_ops/testing/api/v3/loginfail_test.go       |  58 ++----
 .../testing/api/v3/readonlycannotmodify_test.go    |  69 --------
 .../testing/api/v3/servercheckextension_test.go    | 177 +++++++++----------
 traffic_ops/testing/api/v3/steering_test.go        | 101 ++++++-----
 traffic_ops/testing/api/v4/cdns_test.go            |  13 +-
 traffic_ops/testing/api/v4/iso_test.go             |  49 ++----
 traffic_ops/testing/api/v4/loginfail_test.go       |  61 +++----
 .../testing/api/v4/readonlycannotmodify_test.go    |  72 --------
 .../testing/api/v4/servercheckextension_test.go    | 195 ++++++++++-----------
 traffic_ops/testing/api/v4/steering_test.go        | 100 ++++++-----
 12 files changed, 407 insertions(+), 565 deletions(-)

diff --git a/traffic_ops/testing/api/v3/cdns_test.go 
b/traffic_ops/testing/api/v3/cdns_test.go
index c7229c951a..ee6842dd62 100644
--- a/traffic_ops/testing/api/v3/cdns_test.go
+++ b/traffic_ops/testing/api/v3/cdns_test.go
@@ -31,7 +31,9 @@ import (
 )
 
 func TestCDNs(t *testing.T) {
-       WithObjs(t, []TCObj{CDNs, Parameters}, func() {
+       WithObjs(t, []TCObj{CDNs, Parameters, Tenants, Users}, func() {
+
+               readOnlyUserSession := utils.CreateV3Session(t, 
Config.TrafficOps.URL, "readonlyuser", "pa$$word", 
Config.Default.Session.TimeoutInSecs)
 
                currentTime := time.Now().UTC().Add(-15 * time.Second)
                currentTimeRFC := currentTime.Format(time.RFC1123)
@@ -55,6 +57,17 @@ func TestCDNs(t *testing.T) {
                                                
validateCDNFields(map[string]interface{}{"Name": "cdn1"})),
                                },
                        },
+                       "POST": {
+                               "FORBIDDEN when READ ONLY USER": {
+                                       ClientSession: readOnlyUserSession,
+                                       RequestBody: map[string]interface{}{
+                                               "name":          "readOnlyTest",
+                                               "dnssecEnabled": false,
+                                               "domainName":    "test.ro",
+                                       },
+                                       Expectations: 
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusForbidden)),
+                               },
+                       },
                        "PUT": {
                                "OK when VALID request": {
                                        EndpointId:    GetCDNID(t, "cdn1"),
@@ -125,6 +138,13 @@ func TestCDNs(t *testing.T) {
                                                                }
                                                        }
                                                })
+                                       case "POST":
+                                               t.Run(name, func(t *testing.T) {
+                                                       alerts, reqInf, err := 
testCase.ClientSession.CreateCDN(cdn)
+                                                       for _, check := range 
testCase.Expectations {
+                                                               check(t, 
reqInf, nil, alerts, err)
+                                                       }
+                                               })
                                        case "PUT":
                                                t.Run(name, func(t *testing.T) {
                                                        alerts, reqInf, err := 
testCase.ClientSession.UpdateCDNByIDWithHdr(testCase.EndpointId(), cdn, 
testCase.RequestHeaders)
diff --git a/traffic_ops/testing/api/v3/iso_test.go 
b/traffic_ops/testing/api/v3/iso_test.go
index 26c0074764..13e97503e1 100644
--- a/traffic_ops/testing/api/v3/iso_test.go
+++ b/traffic_ops/testing/api/v3/iso_test.go
@@ -23,6 +23,7 @@ import (
        "testing"
 
        "github.com/apache/trafficcontrol/lib/go-tc"
+       "github.com/apache/trafficcontrol/traffic_ops/testing/api/assert"
 )
 
 func TestGetOSVersions(t *testing.T) {
@@ -36,31 +37,20 @@ func TestGetOSVersions(t *testing.T) {
        }
 
        // Ensure request with an authenticated client returns expected data.
-       t.Run("authenticated", func(t *testing.T) {
+       t.Run("OK when AUTHENTICATED", func(t *testing.T) {
                got, _, err := TOSession.GetOSVersions()
-               if err != nil {
-                       t.Fatalf("unexpected error from authenticated 
GetOSVersions(): %v", err)
-               }
-
-               t.Logf("GetOSVersions() response: %#v", got)
+               assert.NoError(t, err, "Unexpected error from authenticated 
GetOSVersions(): %v", err)
+               assert.RequireEqual(t, len(expected), len(got), "Incorrect map 
length: got %d map entries, expected %d", len(expected), len(got))
 
-               if lenGot, lenExp := len(got), len(expected); lenGot != lenExp {
-                       t.Fatalf("incorrect map length: got %d map entries, 
expected %d", lenGot, lenExp)
-               }
                for k, expectedVal := range expected {
-                       if gotVal := got[k]; gotVal != expectedVal {
-                               t.Fatalf("incorrect map entry for key %q: got 
%q, expected %q", k, gotVal, expectedVal)
-                       }
+                       assert.RequireEqual(t, expectedVal, got[k], "Incorrect 
map entry for key %q: got %q, expected %q", k, got[k], expectedVal)
                }
        })
 
        // Ensure request with an un-authenticated client returns an error.
-       t.Run("un-authenticated", func(t *testing.T) {
+       t.Run("ERROR when UNAUTHENTICATED", func(t *testing.T) {
                _, _, err := NoAuthTOSession.GetOSVersions()
-               if err == nil {
-                       t.Fatalf("expected error from unauthenticated 
GetOSVersions(), got: %v", err)
-               }
-               t.Logf("unauthenticated GetOSVersions() error (expected): %v", 
err)
+               assert.Error(t, err, "Expected error from unauthenticated 
GetOSVersions(), got: <nil>")
        })
 
        // Update database with a Parameter entry. This should cause the 
endpoint
@@ -70,34 +60,27 @@ func TestGetOSVersions(t *testing.T) {
        // NOTE: This does not assume this test and TO are using the same 
filesystem, but
        // does make the reasonable assumption that 
`/DOES/NOT/EXIST/osversions.json` will not exist
        // on the TO host.
-       t.Run("parameter-invalid", func(t *testing.T) {
+       t.Run("ERROR when INVALID PARAMETER", func(t *testing.T) {
                p := tc.Parameter{
                        ConfigFile: "mkisofs",
                        Name:       "kickstart.files.location",
                        Value:      "/DOES/NOT/EXIST",
                }
-               if _, _, err := TOSession.CreateParameter(p); err != nil {
-                       t.Fatalf("could not CREATE parameter: %v\n", err)
-               }
+               alerts, _, err := TOSession.CreateParameter(p)
+               assert.RequireNoError(t, err, "Could not create Parameter: %v - 
alerts: %+v", err, alerts.Alerts)
+
                // Cleanup DB entry
                defer func() {
-                       resp, _, err := 
TOSession.GetParameterByNameAndConfigFileAndValue(p.Name, p.ConfigFile, p.Value)
-                       if err != nil {
-                               t.Fatalf("cannot GET Parameter by name: %v - 
%v\n", p.Name, err)
-                       }
-                       if len(resp) != 1 {
-                               t.Fatalf("unexpected response length %d", 
len(resp))
-                       }
+                       resp, _, err := 
TOSession.GetParameterByNameAndConfigFileAndValueWithHdr(p.Name, p.ConfigFile, 
p.Value, nil)
+                       assert.RequireNoError(t, err, "Cannot GET Parameter by 
name '%s', configFile '%s' and value '%s': %v", p.Name, p.ConfigFile, p.Value, 
err)
+                       assert.RequireEqual(t, 1, len(resp), "Unexpected 
response length %d", len(resp))
+
+                       delResp, _, err := 
TOSession.DeleteParameterByID(resp[0].ID)
+                       assert.RequireNoError(t, err, "Cannot delete Parameter 
#%d: %v - alerts: %+v", resp[0].ID, err, delResp.Alerts)
 
-                       if delResp, _, err := 
TOSession.DeleteParameterByID(resp[0].ID); err != nil {
-                               t.Fatalf("cannot DELETE Parameter by name: %v - 
%v\n", err, delResp)
-                       }
                }()
 
-               _, _, err := TOSession.GetOSVersions()
-               if err == nil {
-                       t.Fatalf("expected error from GetOSVersions() after 
adding invalid Parameter DB entry, got: %v", err)
-               }
-               t.Logf("got expected error from GetOSVersions() after adding 
Parameter DB entry with config directory %q: %v", p.Value, err)
+               _, _, err = TOSession.GetOSVersions()
+               assert.Error(t, err, "Expected error from GetOSVersions() after 
adding invalid Parameter DB entry, got: <nil>")
        })
 }
diff --git a/traffic_ops/testing/api/v3/loginfail_test.go 
b/traffic_ops/testing/api/v3/loginfail_test.go
index d669a42bd0..fd4fedd298 100644
--- a/traffic_ops/testing/api/v3/loginfail_test.go
+++ b/traffic_ops/testing/api/v3/loginfail_test.go
@@ -22,6 +22,7 @@ import (
        "testing"
        "time"
 
+       "github.com/apache/trafficcontrol/traffic_ops/testing/api/assert"
        "golang.org/x/net/publicsuffix"
 
        toclient "github.com/apache/trafficcontrol/traffic_ops/v3-client"
@@ -42,72 +43,49 @@ func PostTestLoginFail(t *testing.T) {
 
        userAgent := "to-api-v3-client-tests-loginfailtest"
        uninitializedTOClient, err := 
getUninitializedTOClient(Config.TrafficOps.Users.Admin, 
Config.TrafficOps.UserPassword, Config.TrafficOps.URL, userAgent, 
time.Second*time.Duration(Config.Default.Session.TimeoutInSecs))
-       if err != nil {
-               t.Fatalf("getting uninitialized client: %+v", err)
-       }
+       assert.RequireNoError(t, err, "Error: Getting uninitialized client: 
%+v", err)
 
-       if len(testData.CDNs) < 1 {
-               t.Fatal("cannot test login: must have at least 1 test data cdn")
-       }
+       assert.RequireGreaterOrEqual(t, len(testData.CDNs), 1, "cannot test 
login: must have at least 1 test data cdn")
        expectedCDN := testData.CDNs[0]
-       actualCDNs, _, err := 
uninitializedTOClient.GetCDNByName(expectedCDN.Name)
-       if err != nil {
-               t.Fatalf("GetCDNByName err expected nil, actual '%+v'", err)
-       }
-       if len(actualCDNs) < 1 {
-               t.Fatal("uninitialized client should have retried login 
(possibly login failed with a 200, so it didn't try again, and the CDN request 
returned an auth failure with a 200, which the client reasonably thought was 
success, and deserialized with no matching keys, resulting in an empty object); 
len(actualCDNs) expected >1, actual 0")
-       }
+       actualCDNs, _, err := 
uninitializedTOClient.GetCDNByNameWithHdr(expectedCDN.Name, nil)
+       assert.RequireNoError(t, err, "GetCDNByName err expected nil, actual 
'%+v'", err)
+       assert.RequireGreaterOrEqual(t, len(actualCDNs), 1, "Uninitialized 
client should have retried login (possibly login failed with a 200, so it 
didn't try again, and the CDN request returned an auth failure with a 200, 
which the client reasonably thought was success, and deserialized with no 
matching keys, resulting in an empty object); len(actualCDNs) expected >1, 
actual 0")
+
        actualCDN := actualCDNs[0]
-       if expectedCDN.Name != actualCDN.Name {
-               t.Fatalf("cdn.Name expected '%+v' actual '%+v'", 
expectedCDN.Name, actualCDN.Name)
-       }
+       assert.Equal(t, expectedCDN.Name, actualCDN.Name, "cdn.Name expected 
'%+v' actual '%+v'", expectedCDN.Name, actualCDN.Name)
 }
 
 func LoginWithEmptyCredentialsTest(t *testing.T) {
        userAgent := "to-api-v3-client-tests-loginfailtest"
        _, _, err := toclient.LoginWithAgent(Config.TrafficOps.URL, 
Config.TrafficOps.Users.Admin, "", true, userAgent, false, 
time.Second*time.Duration(Config.Default.Session.TimeoutInSecs))
-       if err == nil {
-               t.Fatal("expected error when logging in with empty credentials, 
actual nil")
-       }
+       assert.Error(t, err, "Expected error when logging in with empty 
credentials, actual nil")
 }
 
 func LoginWithTokenTest(t *testing.T) {
        db, err := OpenConnection()
-       if err != nil {
-               t.Fatalf("Failed to get database connection: %v", err)
-       }
+       assert.RequireNoError(t, err, "Failed to get database connection: %v", 
err)
 
        allowedToken := "test"
        disallowedToken := "quest"
 
-       if _, err = db.Exec(`UPDATE tm_user SET token=$1 WHERE id = (SELECT id 
FROM tm_user WHERE role != (SELECT id FROM role WHERE name='disallowed') LIMIT 
1)`, allowedToken); err != nil {
-               t.Fatalf("Failed to set allowed token: %v", err)
-       }
+       _, err = db.Exec(`UPDATE tm_user SET token=$1 WHERE id = (SELECT id 
FROM tm_user WHERE role != (SELECT id FROM role WHERE name='disallowed') LIMIT 
1)`, allowedToken)
+       assert.RequireNoError(t, err, "Failed to set allowed token: %v", err)
 
-       if _, err = db.Exec(`UPDATE tm_user SET token=$1 WHERE id = (SELECT id 
FROM tm_user WHERE role = (SELECT id FROM role WHERE name='disallowed') LIMIT 
1)`, disallowedToken); err != nil {
-               t.Fatalf("Failed to set disallowed token: %v", err)
-       }
+       _, err = db.Exec(`UPDATE tm_user SET token=$1 WHERE id = (SELECT id 
FROM tm_user WHERE role = (SELECT id FROM role WHERE name='disallowed') LIMIT 
1)`, disallowedToken)
+       assert.RequireNoError(t, err, "Failed to set disallowed token: %v", err)
 
        userAgent := "to-api-v3-client-tests-loginfailtest"
        s, _, err := toclient.LoginWithToken(Config.TrafficOps.URL, 
allowedToken, true, userAgent, false, 
time.Second*time.Duration(Config.Default.Session.TimeoutInSecs))
-       if err != nil {
-               t.Errorf("unexpected error when logging in with a token: %v", 
err)
-       }
-       if s == nil {
-               t.Error("returned client was nil")
-       }
+       assert.NoError(t, err, "unexpected error when logging in with a token: 
%v", err)
+       assert.NotNil(t, s, nil, "returned client was nil")
 
        // disallowed token
        _, _, err = toclient.LoginWithToken(Config.TrafficOps.URL, 
disallowedToken, true, userAgent, false, 
time.Second*time.Duration(Config.Default.Session.TimeoutInSecs))
-       if err == nil {
-               t.Error("expected an error when logging in with a disallowed 
token, actual nil")
-       }
+       assert.Error(t, err, "expected an error when logging in with a 
disallowed token, actual nil")
 
        // nonexistent token
        _, _, err = toclient.LoginWithToken(Config.TrafficOps.URL, 
"notarealtoken", true, userAgent, false, 
time.Second*time.Duration(Config.Default.Session.TimeoutInSecs))
-       if err == nil {
-               t.Error("expected an error when logging in with a nonexistent 
token, actual nil")
-       }
+       assert.Error(t, err, "expected an error when logging in with a 
nonexistent token, actual nil")
 }
 
 func getUninitializedTOClient(user, pass, uri, agent string, reqTimeout 
time.Duration) (*toclient.Session, error) {
diff --git a/traffic_ops/testing/api/v3/readonlycannotmodify_test.go 
b/traffic_ops/testing/api/v3/readonlycannotmodify_test.go
deleted file mode 100644
index 740963a549..0000000000
--- a/traffic_ops/testing/api/v3/readonlycannotmodify_test.go
+++ /dev/null
@@ -1,69 +0,0 @@
-package v3
-
-/*
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-   http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-*/
-
-import (
-       "strings"
-       "testing"
-       "time"
-
-       "github.com/apache/trafficcontrol/lib/go-tc"
-       toclient "github.com/apache/trafficcontrol/traffic_ops/v3-client"
-)
-
-func TestReadOnlyCannotModify(t *testing.T) {
-       WithObjs(t, []TCObj{CDNs, Types, Tenants, Parameters, Profiles, 
Statuses, Divisions, Regions, PhysLocations, CacheGroups, Servers, Topologies, 
DeliveryServices, Users}, func() {
-               CreateTestCDNWithReadOnlyUser(t)
-       })
-}
-
-func CreateTestCDNWithReadOnlyUser(t *testing.T) {
-       if len(testData.CDNs) == 0 {
-               t.Fatal("Can't test readonly user creating a cdns: test data 
has no cdns")
-       }
-
-       toReqTimeout := time.Second * 
time.Duration(Config.Default.Session.TimeoutInSecs)
-       readonlyTOClient, _, err := toclient.LoginWithAgent(TOSession.URL, 
"readonlyuser", "pa$$word", true, "to-api-v3-client-tests/readonlyuser", true, 
toReqTimeout)
-       if err != nil {
-               t.Fatalf("failed to get log in with readonlyuser: " + 
err.Error())
-       }
-
-       cdn := tc.CDN{
-               Name:       "cdn-test-readonly-create-failure",
-               DomainName: "createfailure.invalid",
-       }
-
-       alerts, _, err := readonlyTOClient.CreateCDN(cdn)
-
-       if err == nil {
-               t.Error("readonlyuser creating cdn error expected: not nil, 
actual: nil error")
-       }
-
-       if !strings.Contains(strings.ToLower(err.Error()), "forbidden") {
-               t.Errorf("readonlyuser creating cdn error expected: contains 
'forbidden', actual: '" + err.Error() + "'")
-       }
-
-       for _, alert := range alerts.Alerts {
-               if alert.Level == tc.SuccessLevel.String() {
-                       t.Errorf("readonlyuser creating cdn, alerts expected: 
no success alert, actual: got success alert '" + alert.Text + "'")
-               }
-       }
-
-       cdns, _, _ := TOSession.GetCDNByName(cdn.Name)
-       if len(cdns) > 0 {
-               t.Errorf("readonlyuser getting created cdn, len(cdns) expected: 
0, actual: %+v %+v", len(cdns), cdns)
-       }
-}
diff --git a/traffic_ops/testing/api/v3/servercheckextension_test.go 
b/traffic_ops/testing/api/v3/servercheckextension_test.go
index b5c75a00c7..cb59e8e407 100644
--- a/traffic_ops/testing/api/v3/servercheckextension_test.go
+++ b/traffic_ops/testing/api/v3/servercheckextension_test.go
@@ -16,11 +16,14 @@ package v3
 */
 
 import (
+       "encoding/json"
+       "net/http"
        "testing"
        "time"
 
        "github.com/apache/trafficcontrol/lib/go-tc"
-       "github.com/apache/trafficcontrol/lib/go-util"
+       "github.com/apache/trafficcontrol/traffic_ops/testing/api/assert"
+       "github.com/apache/trafficcontrol/traffic_ops/testing/api/utils"
 )
 
 var (
@@ -29,104 +32,102 @@ var (
 
 func TestServerCheckExtensions(t *testing.T) {
        WithObjs(t, []TCObj{ServerCheckExtensions}, func() {
-               CreateTestInvalidServerCheckExtensions(t)
+
+               extensionUser := utils.CreateV3Session(t, 
Config.TrafficOps.URL, Config.TrafficOps.Users.Extension, 
Config.TrafficOps.UserPassword, Config.Default.Session.TimeoutInSecs)
+
+               methodTests := utils.V3TestCase{
+                       "POST": {
+                               "FORBIDDEN when NOT EXTENSION USER": {
+                                       ClientSession: TOSession,
+                                       RequestBody: map[string]interface{}{
+                                               "name":                   
"MEM_CHECKER",
+                                               "version":                
"3.0.3",
+                                               "info_url":               "-",
+                                               "script_file":            
"mem.py",
+                                               "isactive":               1,
+                                               "servercheck_short_name": "MC",
+                                               "type":                   
"CHECK_EXTENSION_MEM",
+                                       },
+                                       Expectations: 
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusForbidden)),
+                               },
+                               "BAD REQUEST when NO OPEN SLOTS": {
+                                       ClientSession: extensionUser,
+                                       RequestBody: map[string]interface{}{
+                                               "name":                   
"MEM_CHECKER",
+                                               "version":                
"3.0.3",
+                                               "info_url":               "-",
+                                               "script_file":            
"mem.py",
+                                               "isactive":               1,
+                                               "servercheck_short_name": "MC",
+                                               "type":                   
"CHECK_EXTENSION_NUM",
+                                       },
+                                       Expectations: 
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
+                               },
+                               "BAD REQUEST when INVALID TYPE": {
+                                       ClientSession: extensionUser,
+                                       RequestBody: map[string]interface{}{
+                                               "name":                   
"MEM_CHECKER",
+                                               "version":                
"3.0.3",
+                                               "info_url":               "-",
+                                               "script_file":            
"mem.py",
+                                               "isactive":               1,
+                                               "servercheck_short_name": "MC",
+                                               "type":                   
"INVALID_TYPE",
+                                       },
+                                       Expectations: 
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
+                               },
+                       },
+               }
+               for method, testCases := range methodTests {
+                       t.Run(method, func(t *testing.T) {
+                               for name, testCase := range testCases {
+                                       serverCheckExtension := 
tc.ServerCheckExtensionNullable{}
+
+                                       if testCase.RequestBody != nil {
+                                               dat, err := 
json.Marshal(testCase.RequestBody)
+                                               assert.NoError(t, err, "Error 
occurred when marshalling request body: %v", err)
+                                               err = json.Unmarshal(dat, 
&serverCheckExtension)
+                                               assert.NoError(t, err, "Error 
occurred when unmarshalling request body: %v", err)
+                                       }
+
+                                       switch method {
+                                       case "POST":
+                                               t.Run(name, func(t *testing.T) {
+                                                       alerts, reqInf, err := 
testCase.ClientSession.CreateServerCheckExtension(serverCheckExtension)
+                                                       for _, check := range 
testCase.Expectations {
+                                                               check(t, 
reqInf, nil, alerts, err)
+                                                       }
+                                               })
+                                       }
+                               }
+                       })
+               }
        })
 }
 
 func CreateTestServerCheckExtensions(t *testing.T) {
-       SwitchSession(toReqTimeout, Config.TrafficOps.URL, 
Config.TrafficOps.Users.Admin, Config.TrafficOps.UserPassword, 
Config.TrafficOps.Users.Extension, Config.TrafficOps.UserPassword)
-
+       extensionUser := utils.CreateV3Session(t, Config.TrafficOps.URL, 
Config.TrafficOps.Users.Extension, Config.TrafficOps.UserPassword, 
Config.Default.Session.TimeoutInSecs)
        for _, ext := range testData.ServerCheckExtensions {
-               resp, _, err := TOSession.CreateServerCheckExtension(ext)
-               t.Logf("Response: %v %v", *ext.Name, resp)
-               if err != nil {
-                       t.Errorf("could not create to_extension %v: %v", 
ext.Name, err)
-               }
-       }
-
-       SwitchSession(toReqTimeout, Config.TrafficOps.URL, 
Config.TrafficOps.Users.Extension, Config.TrafficOps.UserPassword, 
Config.TrafficOps.Users.Admin, Config.TrafficOps.UserPassword)
-}
-
-func CreateTestInvalidServerCheckExtensions(t *testing.T) {
-       // Fail Attempt to Create ServerCheckExtension as non extension user
-       _, _, err := 
TOSession.CreateServerCheckExtension(testData.ServerCheckExtensions[0])
-       if err == nil {
-               t.Error("expected to receive error with non extension user")
-       }
-
-       SwitchSession(toReqTimeout, Config.TrafficOps.URL, 
Config.TrafficOps.Users.Admin, Config.TrafficOps.UserPassword, 
Config.TrafficOps.Users.Extension, Config.TrafficOps.UserPassword)
-
-       // Attempt to create another valid ServerCheckExtension and it should 
fail as there is no open slots
-       toExt := tc.ServerCheckExtensionNullable{
-               Name:                 util.StrPtr("MEM_CHECKER"),
-               Version:              util.StrPtr("3.0.3"),
-               InfoURL:              util.StrPtr("-"),
-               ScriptFile:           util.StrPtr("mem.py"),
-               ServercheckShortName: util.StrPtr("MC"),
-               Type:                 util.StrPtr("CHECK_EXTENSION_MEM"),
+               resp, _, err := extensionUser.CreateServerCheckExtension(ext)
+               assert.NoError(t, err, "Could not create Servercheck Extension: 
%v - alerts: %+v", err, resp.Alerts)
        }
-       _, _, err = TOSession.CreateServerCheckExtension(toExt)
-       if err == nil {
-               t.Error("expected to receive error with no open slots left")
-       }
-
-       // Attempt to create a TO Extension with an invalid type
-       toExt.Type = util.StrPtr("INVALID_TYPE")
-       _, _, err = TOSession.CreateServerCheckExtension(toExt)
-       if err == nil {
-               t.Error("expected to receive error with invalid TO extension 
type")
-       }
-       SwitchSession(toReqTimeout, Config.TrafficOps.URL, 
Config.TrafficOps.Users.Extension, Config.TrafficOps.UserPassword, 
Config.TrafficOps.Users.Admin, Config.TrafficOps.UserPassword)
-
 }
 
 func DeleteTestServerCheckExtensions(t *testing.T) {
-       SwitchSession(toReqTimeout, Config.TrafficOps.URL, 
Config.TrafficOps.Users.Admin, Config.TrafficOps.UserPassword, 
Config.TrafficOps.Users.Extension, Config.TrafficOps.UserPassword)
-
+       extensionUser := utils.CreateV3Session(t, Config.TrafficOps.URL, 
Config.TrafficOps.Users.Extension, Config.TrafficOps.UserPassword, 
Config.Default.Session.TimeoutInSecs)
        extensions, _, err := TOSession.GetServerCheckExtensions()
-       if err != nil {
-               t.Fatalf("could not get to_extensions: %v", err)
-       }
-
-       ids := []int{}
-       for _, ext := range testData.ServerCheckExtensions {
-               found := false
-               for _, respTOExt := range extensions.Response {
-                       if *ext.Name == *respTOExt.Name {
-                               ids = append(ids, *respTOExt.ID)
-                               found = true
-                               continue
-                       }
-               }
-               if !found {
-                       t.Errorf("expected to find to_extension %v", *ext.Name)
-               }
-       }
-
-       for _, id := range ids {
-               resp, _, err := TOSession.DeleteServerCheckExtension(id)
-               t.Logf("Response: %v %v", id, resp)
-               if err != nil {
-                       t.Errorf("cannot delete to_extension: %v - %v", id, err)
-               }
-       }
-       extensions, _, err = TOSession.GetServerCheckExtensions()
-       if err != nil {
-               t.Fatalf("could not get to_extensions: %v", err)
-       }
-
-       for _, ext := range testData.ServerCheckExtensions {
-               found := false
-               for _, respTOExt := range extensions.Response {
-                       if *ext.Name == *respTOExt.Name {
-                               found = true
-                               continue
+       assert.RequireNoError(t, err, "Could not get Servercheck Extensions: %v 
- alerts: %+v", err, extensions.Alerts)
+
+       for _, extension := range extensions.Response {
+               alerts, _, err := 
extensionUser.DeleteServerCheckExtension(*extension.ID)
+               assert.NoError(t, err, "Unexpected error deleting Servercheck 
Extension '%s' (#%d): %v - alerts: %+v", *extension.Name, *extension.ID, err, 
alerts.Alerts)
+               // Retrieve the Server Extension to see if it got deleted
+               getExtensions, _, err := TOSession.GetServerCheckExtensions()
+               assert.NoError(t, err, "Error getting Servercheck Extensions 
after deletion: %v - alerts: %+v", err, getExtensions.Alerts)
+               for _, getExtension := range getExtensions.Response {
+                       if *getExtension.ID == *extension.ID {
+                               t.Errorf("Expected Servercheck Extension '%s' 
to be deleted, but it was found in Traffic Ops", *extension.Name)
                        }
                }
-               if found {
-                       t.Errorf("to_extension %v should have been deleted", 
*ext.Name)
-               }
        }
-
-       SwitchSession(toReqTimeout, Config.TrafficOps.URL, 
Config.TrafficOps.Users.Extension, Config.TrafficOps.UserPassword, 
Config.TrafficOps.Users.Admin, Config.TrafficOps.UserPassword)
 }
diff --git a/traffic_ops/testing/api/v3/steering_test.go 
b/traffic_ops/testing/api/v3/steering_test.go
index 2baefeeab0..8b729acac2 100644
--- a/traffic_ops/testing/api/v3/steering_test.go
+++ b/traffic_ops/testing/api/v3/steering_test.go
@@ -16,52 +16,73 @@ package v3
 */
 
 import (
+       "net/http"
        "testing"
+
+       "github.com/apache/trafficcontrol/lib/go-tc"
+       "github.com/apache/trafficcontrol/traffic_ops/testing/api/assert"
+       "github.com/apache/trafficcontrol/traffic_ops/testing/api/utils"
+       "github.com/apache/trafficcontrol/traffic_ops/toclientlib"
 )
 
 func TestSteering(t *testing.T) {
-       WithObjs(t, []TCObj{CDNs, Types, Tenants, Parameters, Profiles, 
Statuses, Divisions, Regions, PhysLocations, CacheGroups, Servers, Topologies, 
DeliveryServices, Users, SteeringTargets}, func() {
-               GetTestSteering(t)
+       WithObjs(t, []TCObj{CDNs, Types, Tenants, Parameters, Profiles, 
Statuses, Divisions, Regions, PhysLocations, CacheGroups, Servers, Topologies, 
ServiceCategories, DeliveryServices, Users, SteeringTargets}, func() {
+               methodTests := utils.V3TestCase{
+                       "GET": {
+                               "OK when VALID request": {
+                                       ClientSession: TOSession,
+                                       Expectations: 
utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK), 
utils.ResponseHasLength(1),
+                                               
validateSteeringTargetFields(map[string]interface{}{"TargetsLength": 1, 
"TargetsOrder": int32(0),
+                                                       "TargetsGeoOrderPtr": 
(*int)(nil), "TargetsLongitudePtr": (*float64)(nil), "TargetsLatitudePtr": 
(*float64)(nil), "TargetsWeight": int32(42)})),
+                               },
+                       },
+               }
+               for method, testCases := range methodTests {
+                       t.Run(method, func(t *testing.T) {
+                               for name, testCase := range testCases {
+                                       switch method {
+                                       case "GET":
+                                               t.Run(name, func(t *testing.T) {
+                                                       resp, reqInf, err := 
testCase.ClientSession.SteeringWithHdr(testCase.RequestHeaders)
+                                                       for _, check := range 
testCase.Expectations {
+                                                               check(t, 
reqInf, resp, tc.Alerts{}, err)
+                                                       }
+                                               })
+                                       }
+                               }
+                       })
+               }
        })
 }
 
-func GetTestSteering(t *testing.T) {
-       if len(testData.SteeringTargets) < 1 {
-               t.Error("get steering: no steering target test data")
-       }
-       st := testData.SteeringTargets[0]
-       if st.DeliveryService == nil {
-               t.Error("get steering: test data missing ds")
-       }
-
-       steerings, _, err := TOSession.Steering()
-       if err != nil {
-               t.Errorf("steering get: getting steering: %v", err)
-       }
-
-       if len(steerings) != len(testData.SteeringTargets) {
-               t.Errorf("steering get: expected %v actual %v", 
len(testData.SteeringTargets), len(steerings))
-       }
-
-       if steerings[0].ClientSteering {
-               t.Errorf("steering get: ClientSteering expected %v actual %v", 
false, true)
-       }
-       if len(steerings[0].Targets) != 1 {
-               t.Errorf("steering get: Targets expected %v actual %v", 1, 
len(steerings[0].Targets))
-       }
-       if steerings[0].Targets[0].Order != 0 {
-               t.Errorf("steering get: Targets Order expected %v actual %v", 
0, steerings[0].Targets[0].Order)
-       }
-       if testData.SteeringTargets[0].Value != nil && 
steerings[0].Targets[0].Weight != int32(*testData.SteeringTargets[0].Value) {
-               t.Errorf("steering get: Targets Order expected %v actual %v", 
testData.SteeringTargets[0].Value, steerings[0].Targets[0].Weight)
-       }
-       if steerings[0].Targets[0].GeoOrder != nil {
-               t.Errorf("steering get: Targets Order expected %v actual %+v", 
nil, *steerings[0].Targets[0].GeoOrder)
-       }
-       if steerings[0].Targets[0].Longitude != nil {
-               t.Errorf("steering get: Targets Order expected %v actual %+v", 
nil, *steerings[0].Targets[0].Longitude)
-       }
-       if steerings[0].Targets[0].Latitude != nil {
-               t.Errorf("steering get: Targets Order expected %v actual %+v", 
nil, *steerings[0].Targets[0].Latitude)
+func validateSteeringTargetFields(expectedResp map[string]interface{}) 
utils.CkReqFunc {
+       return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ 
tc.Alerts, _ error) {
+               assert.RequireNotNil(t, resp, "Expected Steering response to 
not be nil.")
+               steeringResp := resp.([]tc.Steering)
+               for field, expected := range expectedResp {
+                       for _, steering := range steeringResp {
+                               switch field {
+                               case "TargetsLength":
+                                       assert.Equal(t, expected, 
len(steering.Targets), "Expected Targets Length to be %v, but got %d", 
expected, len(steering.Targets))
+                               case "TargetsOrder":
+                                       assert.RequireEqual(t, 1, 
len(steering.Targets), "Expected Targets Length to be %d, but got %d", 1, 
len(steering.Targets))
+                                       assert.Equal(t, expected, 
steering.Targets[0].Order, "Expected Targets Order to be %v, but got %d", 
expected, steering.Targets[0].Order)
+                               case "TargetsGeoOrderPtr":
+                                       assert.RequireEqual(t, 1, 
len(steering.Targets), "Expected Targets Length to be %d, but got %d", 1, 
len(steering.Targets))
+                                       assert.Equal(t, expected, 
steering.Targets[0].GeoOrder, "Expected Targets GeoOrder to be %v, but got %v", 
nil, steering.Targets[0].GeoOrder)
+                               case "TargetsLongitudePtr":
+                                       assert.RequireEqual(t, 1, 
len(steering.Targets), "Expected Targets Length to be %d, but got %d", 1, 
len(steering.Targets))
+                                       assert.Equal(t, expected, 
steering.Targets[0].Longitude, "Expected Targets Longitude to be %v, but got 
%v", nil, steering.Targets[0].Longitude)
+                               case "TargetsLatitudePtr":
+                                       assert.RequireEqual(t, 1, 
len(steering.Targets), "Expected Targets Length to be %d, but got %d", 1, 
len(steering.Targets))
+                                       assert.Equal(t, expected, 
steering.Targets[0].Latitude, "Expected Targets Latitude to be %v, but got %v", 
nil, steering.Targets[0].Latitude)
+                               case "TargetsWeight":
+                                       assert.RequireEqual(t, 1, 
len(steering.Targets), "Expected Targets Length to be %d, but got %d", 1, 
len(steering.Targets))
+                                       assert.Equal(t, expected, 
steering.Targets[0].Weight, "Expected Targets Weight to be %v, but got %v", 
expected, steering.Targets[0].Weight)
+                               default:
+                                       t.Errorf("Expected field: %v, does not 
exist in response", field)
+                               }
+                       }
+               }
        }
 }
diff --git a/traffic_ops/testing/api/v4/cdns_test.go 
b/traffic_ops/testing/api/v4/cdns_test.go
index 792eabc381..59f2ee5a58 100644
--- a/traffic_ops/testing/api/v4/cdns_test.go
+++ b/traffic_ops/testing/api/v4/cdns_test.go
@@ -33,7 +33,9 @@ import (
 )
 
 func TestCDNs(t *testing.T) {
-       WithObjs(t, []TCObj{CDNs, Parameters}, func() {
+       WithObjs(t, []TCObj{CDNs, Parameters, Tenants, Users}, func() {
+
+               readOnlyUserSession := utils.CreateV4Session(t, 
Config.TrafficOps.URL, "readonlyuser", "pa$$word", 
Config.Default.Session.TimeoutInSecs)
 
                currentTime := time.Now().UTC().Add(-15 * time.Second)
                currentTimeRFC := currentTime.Format(time.RFC1123)
@@ -132,6 +134,15 @@ func TestCDNs(t *testing.T) {
                                        },
                                        Expectations: 
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
                                },
+                               "FORBIDDEN when READ ONLY USER": {
+                                       ClientSession: readOnlyUserSession,
+                                       RequestBody: map[string]interface{}{
+                                               "name":          "readOnlyTest",
+                                               "dnssecEnabled": false,
+                                               "domainName":    "test.ro",
+                                       },
+                                       Expectations: 
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusForbidden)),
+                               },
                        },
                        "PUT": {
                                "OK when VALID request": {
diff --git a/traffic_ops/testing/api/v4/iso_test.go 
b/traffic_ops/testing/api/v4/iso_test.go
index 749089ae4f..d1f589f80e 100644
--- a/traffic_ops/testing/api/v4/iso_test.go
+++ b/traffic_ops/testing/api/v4/iso_test.go
@@ -23,6 +23,7 @@ import (
        "testing"
 
        "github.com/apache/trafficcontrol/lib/go-tc"
+       "github.com/apache/trafficcontrol/traffic_ops/testing/api/assert"
        client "github.com/apache/trafficcontrol/traffic_ops/v4-client"
 )
 
@@ -37,28 +38,20 @@ func TestGetOSVersions(t *testing.T) {
        }
 
        // Ensure request with an authenticated client returns expected data.
-       t.Run("authenticated", func(t *testing.T) {
+       t.Run("OK when AUTHENTICATED", func(t *testing.T) {
                got, _, err := TOSession.GetOSVersions(client.RequestOptions{})
-               if err != nil {
-                       t.Fatalf("unexpected error from authenticated 
GetOSVersions(): %v - alerts: %+v", err, got.Alerts)
-               }
+               assert.NoError(t, err, "Unexpected error from authenticated 
GetOSVersions(): %v - alerts: %+v", err, got.Alerts)
+               assert.RequireEqual(t, len(expected), len(got.Response), 
"Incorrect map length: got %d map entries, expected %d", len(expected), 
len(got.Response))
 
-               if lenGot, lenExp := len(got.Response), len(expected); lenGot 
!= lenExp {
-                       t.Fatalf("incorrect map length: got %d map entries, 
expected %d", lenGot, lenExp)
-               }
                for k, expectedVal := range expected {
-                       if gotVal := got.Response[k]; gotVal != expectedVal {
-                               t.Fatalf("incorrect map entry for key %q: got 
%q, expected %q", k, gotVal, expectedVal)
-                       }
+                       assert.RequireEqual(t, expectedVal, got.Response[k], 
"Incorrect map entry for key %q: got %q, expected %q", k, got.Response[k], 
expectedVal)
                }
        })
 
        // Ensure request with an un-authenticated client returns an error.
-       t.Run("un-authenticated", func(t *testing.T) {
+       t.Run("ERROR when UNAUTHENTICATED", func(t *testing.T) {
                _, _, err := 
NoAuthTOSession.GetOSVersions(client.RequestOptions{})
-               if err == nil {
-                       t.Fatal("expected error from unauthenticated 
GetOSVersions(), got: <nil>")
-               }
+               assert.Error(t, err, "Expected error from unauthenticated 
GetOSVersions(), got: <nil>")
        })
 
        // Update database with a Parameter entry. This should cause the 
endpoint
@@ -68,15 +61,15 @@ func TestGetOSVersions(t *testing.T) {
        // NOTE: This does not assume this test and TO are using the same 
filesystem, but
        // does make the reasonable assumption that 
`/DOES/NOT/EXIST/osversions.json` will not exist
        // on the TO host.
-       t.Run("parameter-invalid", func(t *testing.T) {
+       t.Run("ERROR when INVALID PARAMETER", func(t *testing.T) {
                p := tc.Parameter{
                        ConfigFile: "mkisofs",
                        Name:       "kickstart.files.location",
                        Value:      "/DOES/NOT/EXIST",
                }
-               if alerts, _, err := TOSession.CreateParameter(p, 
client.RequestOptions{}); err != nil {
-                       t.Fatalf("could not create Parameter: %v - alerts: 
%+v", err, alerts.Alerts)
-               }
+               alerts, _, err := TOSession.CreateParameter(p, 
client.RequestOptions{})
+               assert.RequireNoError(t, err, "Could not create Parameter: %v - 
alerts: %+v", err, alerts.Alerts)
+
                // Cleanup DB entry
                defer func() {
                        opts := client.NewRequestOptions()
@@ -84,21 +77,15 @@ func TestGetOSVersions(t *testing.T) {
                        opts.QueryParameters.Set("configFile", p.ConfigFile)
                        opts.QueryParameters.Set("value", p.Value)
                        resp, _, err := TOSession.GetParameters(opts)
-                       if err != nil {
-                               t.Fatalf("cannot GET Parameter by name '%s', 
configFile '%s' and value '%s': %v - alerts: %+v", p.Name, p.ConfigFile, 
p.Value, err, resp.Alerts)
-                       }
-                       if len(resp.Response) != 1 {
-                               t.Fatalf("unexpected response length %d", 
len(resp.Response))
-                       }
+                       assert.RequireNoError(t, err, "Cannot GET Parameter by 
name '%s', configFile '%s' and value '%s': %v - alerts: %+v", p.Name, 
p.ConfigFile, p.Value, err, resp.Alerts)
+                       assert.RequireEqual(t, 1, len(resp.Response), 
"Unexpected response length %d", len(resp.Response))
+
+                       delResp, _, err := 
TOSession.DeleteParameter(resp.Response[0].ID, client.RequestOptions{})
+                       assert.RequireNoError(t, err, "Cannot delete Parameter 
#%d: %v - alerts: %+v", resp.Response[0].ID, err, delResp.Alerts)
 
-                       if delResp, _, err := 
TOSession.DeleteParameter(resp.Response[0].ID, client.RequestOptions{}); err != 
nil {
-                               t.Fatalf("cannot delete Parameter #%d: %v - 
alerts: %+v", resp.Response[0].ID, err, delResp.Alerts)
-                       }
                }()
 
-               _, _, err := TOSession.GetOSVersions(client.RequestOptions{})
-               if err == nil {
-                       t.Fatal("expected error from GetOSVersions() after 
adding invalid Parameter DB entry, got: <nil>")
-               }
+               _, _, err = TOSession.GetOSVersions(client.RequestOptions{})
+               assert.Error(t, err, "Expected error from GetOSVersions() after 
adding invalid Parameter DB entry, got: <nil>")
        })
 }
diff --git a/traffic_ops/testing/api/v4/loginfail_test.go 
b/traffic_ops/testing/api/v4/loginfail_test.go
index cb4ccd8ecd..1eae5695ac 100644
--- a/traffic_ops/testing/api/v4/loginfail_test.go
+++ b/traffic_ops/testing/api/v4/loginfail_test.go
@@ -22,6 +22,7 @@ import (
        "testing"
        "time"
 
+       "github.com/apache/trafficcontrol/traffic_ops/testing/api/assert"
        "golang.org/x/net/publicsuffix"
 
        client "github.com/apache/trafficcontrol/traffic_ops/v4-client"
@@ -41,76 +42,54 @@ func TestLoginFail(t *testing.T) {
 func PostTestLoginFail(t *testing.T) {
        // This specifically tests a previous bug: auth failure returning a 
200, causing the client to think the request succeeded, and deserialize no 
matching fields successfully, and return an empty object.
 
-       userAgent := "to-api-v3-client-tests-loginfailtest"
+       userAgent := "to-api-v4-client-tests-loginfailtest"
        uninitializedTOClient, err := 
getUninitializedTOClient(Config.TrafficOps.Users.Admin, 
Config.TrafficOps.UserPassword, Config.TrafficOps.URL, userAgent, 
time.Second*time.Duration(Config.Default.Session.TimeoutInSecs))
-       if err != nil {
-               t.Fatalf("getting uninitialized client: %+v", err)
-       }
+       assert.RequireNoError(t, err, "Error getting uninitialized client: 
%+v", err)
+
+       assert.RequireGreaterOrEqual(t, len(testData.CDNs), 1, "cannot test 
login: must have at least 1 test data cdn")
 
-       if len(testData.CDNs) < 1 {
-               t.Fatal("cannot test login: must have at least 1 test data cdn")
-       }
        expectedCDN := testData.CDNs[0]
        opts := client.NewRequestOptions()
        opts.QueryParameters.Set("name", expectedCDN.Name)
        actualCDNs, _, err := uninitializedTOClient.GetCDNs(opts)
-       if err != nil {
-               t.Fatalf("failed to request CDN '%s': %v - alerts: %+v", 
expectedCDN.Name, err, actualCDNs.Alerts)
-       }
-       if len(actualCDNs.Response) < 1 {
-               t.Fatal("uninitialized client should have retried login 
(possibly login failed with a 200, so it didn't try again, and the CDN request 
returned an auth failure with a 200, which the client reasonably thought was 
success, and deserialized with no matching keys, resulting in an empty object); 
len(actualCDNs) expected >1, actual 0")
-       }
+       assert.RequireNoError(t, err, "Failed to request CDN '%s': %v - alerts: 
%+v", expectedCDN.Name, err, actualCDNs.Alerts)
+       assert.RequireGreaterOrEqual(t, len(actualCDNs.Response), 1, 
"Uninitialized client should have retried login (possibly login failed with a 
200, so it didn't try again, and the CDN request returned an auth failure with 
a 200, which the client reasonably thought was success, and deserialized with 
no matching keys, resulting in an empty object); len(actualCDNs) expected >1, 
actual 0")
+
        actualCDN := actualCDNs.Response[0]
-       if expectedCDN.Name != actualCDN.Name {
-               t.Fatalf("cdn.Name expected '%s' actual '%s'", 
expectedCDN.Name, actualCDN.Name)
-       }
+       assert.Equal(t, expectedCDN.Name, actualCDN.Name, "cdn.Name expected 
'%s' actual '%s'", expectedCDN.Name, actualCDN.Name)
 }
 
 func LoginWithEmptyCredentialsTest(t *testing.T) {
        userAgent := "to-api-v4-client-tests-loginfailtest"
        _, _, err := toclient.LoginWithAgent(Config.TrafficOps.URL, 
Config.TrafficOps.Users.Admin, "", true, userAgent, false, 
time.Second*time.Duration(Config.Default.Session.TimeoutInSecs))
-       if err == nil {
-               t.Fatal("expected error when logging in with empty credentials, 
actual nil")
-       }
+       assert.Error(t, err, "Expected error when logging in with empty 
credentials, actual nil")
 }
 
 func LoginWithTokenTest(t *testing.T) {
        db, err := OpenConnection()
-       if err != nil {
-               t.Fatalf("Failed to get database connection: %v", err)
-       }
+       assert.RequireNoError(t, err, "Failed to get database connection: %v", 
err)
 
        allowedToken := "test"
        disallowedToken := "quest"
 
-       if _, err = db.Exec(`UPDATE tm_user SET token=$1 WHERE id = (SELECT id 
FROM tm_user WHERE role != (SELECT id FROM role WHERE name='disallowed') LIMIT 
1)`, allowedToken); err != nil {
-               t.Fatalf("Failed to set allowed token: %v", err)
-       }
+       _, err = db.Exec(`UPDATE tm_user SET token=$1 WHERE id = (SELECT id 
FROM tm_user WHERE role != (SELECT id FROM role WHERE name='disallowed') LIMIT 
1)`, allowedToken)
+       assert.RequireNoError(t, err, "Failed to set allowed token: %v", err)
 
-       if _, err = db.Exec(`UPDATE tm_user SET token=$1 WHERE id = (SELECT id 
FROM tm_user WHERE role = (SELECT id FROM role WHERE name='disallowed') LIMIT 
1)`, disallowedToken); err != nil {
-               t.Fatalf("Failed to set disallowed token: %v", err)
-       }
+       _, err = db.Exec(`UPDATE tm_user SET token=$1 WHERE id = (SELECT id 
FROM tm_user WHERE role = (SELECT id FROM role WHERE name='disallowed') LIMIT 
1)`, disallowedToken)
+       assert.RequireNoError(t, err, "Failed to set disallowed token: %v", err)
 
-       userAgent := "to-api-v3-client-tests-loginfailtest"
+       userAgent := "to-api-v4-client-tests-loginfailtest"
        s, _, err := toclient.LoginWithToken(Config.TrafficOps.URL, 
allowedToken, true, userAgent, false, 
time.Second*time.Duration(Config.Default.Session.TimeoutInSecs))
-       if err != nil {
-               t.Errorf("unexpected error when logging in with a token: %v", 
err)
-       }
-       if s == nil {
-               t.Error("returned client was nil")
-       }
+       assert.NoError(t, err, "Unexpected error when logging in with a token: 
%v", err)
+       assert.NotNil(t, s, "returned client was nil")
 
        // disallowed token
        _, _, err = toclient.LoginWithToken(Config.TrafficOps.URL, 
disallowedToken, true, userAgent, false, 
time.Second*time.Duration(Config.Default.Session.TimeoutInSecs))
-       if err == nil {
-               t.Error("expected an error when logging in with a disallowed 
token, actual nil")
-       }
+       assert.Error(t, err, "Expected an error when logging in with a 
disallowed token, actual nil")
 
        // nonexistent token
        _, _, err = toclient.LoginWithToken(Config.TrafficOps.URL, 
"notarealtoken", true, userAgent, false, 
time.Second*time.Duration(Config.Default.Session.TimeoutInSecs))
-       if err == nil {
-               t.Error("expected an error when logging in with a nonexistent 
token, actual nil")
-       }
+       assert.Error(t, err, "expected an error when logging in with a 
nonexistent token, actual nil")
 }
 
 func getUninitializedTOClient(user, pass, uri, agent string, reqTimeout 
time.Duration) (*toclient.Session, error) {
diff --git a/traffic_ops/testing/api/v4/readonlycannotmodify_test.go 
b/traffic_ops/testing/api/v4/readonlycannotmodify_test.go
deleted file mode 100644
index 6db2e87a4f..0000000000
--- a/traffic_ops/testing/api/v4/readonlycannotmodify_test.go
+++ /dev/null
@@ -1,72 +0,0 @@
-package v4
-
-/*
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-   http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-*/
-
-import (
-       "strings"
-       "testing"
-       "time"
-
-       "github.com/apache/trafficcontrol/lib/go-tc"
-       client "github.com/apache/trafficcontrol/traffic_ops/v4-client"
-       toclient "github.com/apache/trafficcontrol/traffic_ops/v4-client"
-)
-
-func TestReadOnlyCannotModify(t *testing.T) {
-       WithObjs(t, []TCObj{CDNs, Types, Tenants, Parameters, Profiles, 
Statuses, Divisions, Regions, PhysLocations, CacheGroups, Servers, Topologies, 
ServiceCategories, DeliveryServices, Users}, func() {
-               CreateTestCDNWithReadOnlyUser(t)
-       })
-}
-
-func CreateTestCDNWithReadOnlyUser(t *testing.T) {
-       if len(testData.CDNs) == 0 {
-               t.Fatal("Can't test readonly user creating a cdns: test data 
has no cdns")
-       }
-
-       toReqTimeout := time.Second * 
time.Duration(Config.Default.Session.TimeoutInSecs)
-       readonlyTOClient, _, err := toclient.LoginWithAgent(TOSession.URL, 
"readonlyuser", "pa$$word", true, "to-api-v3-client-tests/readonlyuser", true, 
toReqTimeout)
-       if err != nil {
-               t.Fatalf("failed to get log in with readonlyuser: " + 
err.Error())
-       }
-
-       cdn := tc.CDN{
-               Name:       "cdn-test-readonly-create-failure",
-               DomainName: "createfailure.invalid",
-       }
-
-       alerts, _, err := readonlyTOClient.CreateCDN(cdn, 
client.RequestOptions{})
-
-       if err == nil {
-               t.Error("readonlyuser creating cdn error expected: not nil, 
actual: nil error")
-       }
-
-       if !strings.Contains(strings.ToLower(err.Error()), "forbidden") {
-               t.Errorf("readonlyuser creating cdn error expected: contains 
'forbidden', actual: '" + err.Error() + "'")
-       }
-
-       for _, alert := range alerts.Alerts {
-               if alert.Level == tc.SuccessLevel.String() {
-                       t.Errorf("readonlyuser creating cdn, alerts expected: 
no success alert, actual: got success alert '" + alert.Text + "'")
-               }
-       }
-
-       opts := client.NewRequestOptions()
-       opts.QueryParameters.Set("name", cdn.Name)
-       cdns, _, _ := TOSession.GetCDNs(opts)
-       if len(cdns.Response) > 0 {
-               t.Errorf("readonlyuser getting created cdn, len(cdns) expected: 
0, actual: %d", len(cdns.Response))
-       }
-}
diff --git a/traffic_ops/testing/api/v4/servercheckextension_test.go 
b/traffic_ops/testing/api/v4/servercheckextension_test.go
index a7e22635a8..3caaaa0d2d 100644
--- a/traffic_ops/testing/api/v4/servercheckextension_test.go
+++ b/traffic_ops/testing/api/v4/servercheckextension_test.go
@@ -16,11 +16,15 @@ package v4
 */
 
 import (
+       "encoding/json"
+       "net/http"
+       "strconv"
        "testing"
        "time"
 
        "github.com/apache/trafficcontrol/lib/go-tc"
-       "github.com/apache/trafficcontrol/lib/go-util"
+       "github.com/apache/trafficcontrol/traffic_ops/testing/api/assert"
+       "github.com/apache/trafficcontrol/traffic_ops/testing/api/utils"
        client "github.com/apache/trafficcontrol/traffic_ops/v4-client"
 )
 
@@ -30,119 +34,100 @@ var (
 
 func TestServerCheckExtensions(t *testing.T) {
        WithObjs(t, []TCObj{ServerCheckExtensions}, func() {
-               CreateTestInvalidServerCheckExtensions(t)
+
+               extensionUser := utils.CreateV4Session(t, 
Config.TrafficOps.URL, Config.TrafficOps.Users.Extension, 
Config.TrafficOps.UserPassword, Config.Default.Session.TimeoutInSecs)
+
+               methodTests := utils.V4TestCase{
+                       "POST": {
+                               "FORBIDDEN when NOT EXTENSION USER": {
+                                       ClientSession: TOSession,
+                                       RequestBody: map[string]interface{}{
+                                               "name":                   
"MEM_CHECKER",
+                                               "version":                
"3.0.3",
+                                               "info_url":               "-",
+                                               "script_file":            
"mem.py",
+                                               "isactive":               1,
+                                               "servercheck_short_name": "MC",
+                                               "type":                   
"CHECK_EXTENSION_MEM",
+                                       },
+                                       Expectations: 
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusForbidden)),
+                               },
+                               "BAD REQUEST when NO OPEN SLOTS": {
+                                       ClientSession: extensionUser,
+                                       RequestBody: map[string]interface{}{
+                                               "name":                   
"MEM_CHECKER",
+                                               "version":                
"3.0.3",
+                                               "info_url":               "-",
+                                               "script_file":            
"mem.py",
+                                               "isactive":               1,
+                                               "servercheck_short_name": "MC",
+                                               "type":                   
"CHECK_EXTENSION_NUM",
+                                       },
+                                       Expectations: 
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
+                               },
+                               "BAD REQUEST when INVALID TYPE": {
+                                       ClientSession: extensionUser,
+                                       RequestBody: map[string]interface{}{
+                                               "name":                   
"MEM_CHECKER",
+                                               "version":                
"3.0.3",
+                                               "info_url":               "-",
+                                               "script_file":            
"mem.py",
+                                               "isactive":               1,
+                                               "servercheck_short_name": "MC",
+                                               "type":                   
"INVALID_TYPE",
+                                       },
+                                       Expectations: 
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
+                               },
+                       },
+               }
+               for method, testCases := range methodTests {
+                       t.Run(method, func(t *testing.T) {
+                               for name, testCase := range testCases {
+                                       serverCheckExtension := 
tc.ServerCheckExtensionNullable{}
+
+                                       if testCase.RequestBody != nil {
+                                               dat, err := 
json.Marshal(testCase.RequestBody)
+                                               assert.NoError(t, err, "Error 
occurred when marshalling request body: %v", err)
+                                               err = json.Unmarshal(dat, 
&serverCheckExtension)
+                                               assert.NoError(t, err, "Error 
occurred when unmarshalling request body: %v", err)
+                                       }
+
+                                       switch method {
+                                       case "POST":
+                                               t.Run(name, func(t *testing.T) {
+                                                       alerts, reqInf, err := 
testCase.ClientSession.CreateServerCheckExtension(serverCheckExtension, 
testCase.RequestOpts)
+                                                       for _, check := range 
testCase.Expectations {
+                                                               check(t, 
reqInf, nil, alerts, err)
+                                                       }
+                                               })
+                                       }
+                               }
+                       })
+               }
        })
 }
 
 func CreateTestServerCheckExtensions(t *testing.T) {
-       SwitchSession(toReqTimeout, Config.TrafficOps.URL, 
Config.TrafficOps.Users.Admin, Config.TrafficOps.UserPassword, 
Config.TrafficOps.Users.Extension, Config.TrafficOps.UserPassword)
-
+       extensionUser := utils.CreateV4Session(t, Config.TrafficOps.URL, 
Config.TrafficOps.Users.Extension, Config.TrafficOps.UserPassword, 
Config.Default.Session.TimeoutInSecs)
        for _, ext := range testData.ServerCheckExtensions {
-               resp, _, err := TOSession.CreateServerCheckExtension(ext, 
client.RequestOptions{})
-               if err != nil {
-                       t.Errorf("could not create Servercheck Extension: %v - 
alerts: %+v", err, resp.Alerts)
-               }
-       }
-
-       SwitchSession(toReqTimeout, Config.TrafficOps.URL, 
Config.TrafficOps.Users.Extension, Config.TrafficOps.UserPassword, 
Config.TrafficOps.Users.Admin, Config.TrafficOps.UserPassword)
-}
-
-func CreateTestInvalidServerCheckExtensions(t *testing.T) {
-       if len(testData.ServerCheckExtensions) < 1 {
-               t.Fatal("Need at least one Servercheck Extension to test 
invalid Servercheck Extension creation")
+               resp, _, err := extensionUser.CreateServerCheckExtension(ext, 
client.RequestOptions{})
+               assert.NoError(t, err, "Could not create Servercheck Extension: 
%v - alerts: %+v", err, resp.Alerts)
        }
-       // Fail Attempt to Create ServerCheckExtension as non extension user
-       _, _, err := 
TOSession.CreateServerCheckExtension(testData.ServerCheckExtensions[0], 
client.RequestOptions{})
-       if err == nil {
-               t.Error("expected to receive error with non extension user")
-       }
-
-       SwitchSession(toReqTimeout, Config.TrafficOps.URL, 
Config.TrafficOps.Users.Admin, Config.TrafficOps.UserPassword, 
Config.TrafficOps.Users.Extension, Config.TrafficOps.UserPassword)
-
-       // Attempt to create another valid ServerCheckExtension and it should 
fail as there is no open slots
-       toExt := tc.ServerCheckExtensionNullable{
-               Name:                 util.StrPtr("MEM_CHECKER"),
-               Version:              util.StrPtr("3.0.3"),
-               InfoURL:              util.StrPtr("-"),
-               ScriptFile:           util.StrPtr("mem.py"),
-               ServercheckShortName: util.StrPtr("MC"),
-               Type:                 util.StrPtr("CHECK_EXTENSION_MEM"),
-       }
-       _, _, err = TOSession.CreateServerCheckExtension(toExt, 
client.RequestOptions{})
-       if err == nil {
-               t.Error("expected to receive error with no open slots left")
-       }
-
-       // Attempt to create a TO Extension with an invalid type
-       toExt.Type = util.StrPtr("INVALID_TYPE")
-       _, _, err = TOSession.CreateServerCheckExtension(toExt, 
client.RequestOptions{})
-       if err == nil {
-               t.Error("expected to receive error with invalid TO extension 
type")
-       }
-       SwitchSession(toReqTimeout, Config.TrafficOps.URL, 
Config.TrafficOps.Users.Extension, Config.TrafficOps.UserPassword, 
Config.TrafficOps.Users.Admin, Config.TrafficOps.UserPassword)
-
 }
 
 func DeleteTestServerCheckExtensions(t *testing.T) {
-       SwitchSession(toReqTimeout, Config.TrafficOps.URL, 
Config.TrafficOps.Users.Admin, Config.TrafficOps.UserPassword, 
Config.TrafficOps.Users.Extension, Config.TrafficOps.UserPassword)
-
+       extensionUser := utils.CreateV4Session(t, Config.TrafficOps.URL, 
Config.TrafficOps.Users.Extension, Config.TrafficOps.UserPassword, 
Config.Default.Session.TimeoutInSecs)
        extensions, _, err := 
TOSession.GetServerCheckExtensions(client.RequestOptions{})
-       if err != nil {
-               t.Fatalf("could not get Servercheck Extensions: %v - alerts: 
%+v", err, extensions.Alerts)
+       assert.RequireNoError(t, err, "Could not get Servercheck Extensions: %v 
- alerts: %+v", err, extensions.Alerts)
+
+       for _, extension := range extensions.Response {
+               alerts, _, err := 
extensionUser.DeleteServerCheckExtension(*extension.ID, client.RequestOptions{})
+               assert.NoError(t, err, "Unexpected error deleting Servercheck 
Extension '%s' (#%d): %v - alerts: %+v", *extension.Name, *extension.ID, err, 
alerts.Alerts)
+               // Retrieve the Server Extension to see if it got deleted
+               opts := client.NewRequestOptions()
+               opts.QueryParameters.Set("id", strconv.Itoa(*extension.ID))
+               getExtension, _, err := TOSession.GetServerCheckExtensions(opts)
+               assert.NoError(t, err, "Error getting Servercheck Extension 
'%s' after deletion: %v - alerts: %+v", *extension.Name, err, 
getExtension.Alerts)
+               assert.Equal(t, 0, len(getExtension.Response), "Expected 
Servercheck Extension '%s' to be deleted, but it was found in Traffic Ops", 
*extension.Name)
        }
-
-       ids := []int{}
-       for _, ext := range testData.ServerCheckExtensions {
-               if ext.Name == nil {
-                       t.Errorf("Found Servercheck Extension in the testing 
data with null or undefined Name")
-               }
-               found := false
-               for _, respTOExt := range extensions.Response {
-                       if respTOExt.Name == nil || respTOExt.ID == nil {
-                               t.Error("Traffic Ops returned a representation 
for a Servercheck Extension with null or undefined ID and/or name")
-                               continue
-                       }
-                       if *ext.Name == *respTOExt.Name {
-                               ids = append(ids, *respTOExt.ID)
-                               found = true
-                               continue
-                       }
-               }
-               if !found {
-                       t.Errorf("expected to find to_extension %v", *ext.Name)
-               }
-       }
-
-       for _, id := range ids {
-               resp, _, err := TOSession.DeleteServerCheckExtension(id, 
client.RequestOptions{})
-               if err != nil {
-                       t.Errorf("cannot delete Servercheck Extension #%d: %v - 
alerts: %+v", id, err, resp.Alerts)
-               }
-       }
-       extensions, _, err = 
TOSession.GetServerCheckExtensions(client.RequestOptions{})
-       if err != nil {
-               t.Fatalf("could not get to_extensions: %v", err)
-       }
-
-       for _, ext := range testData.ServerCheckExtensions {
-               if ext.Name == nil {
-                       t.Errorf("Found Servercheck Extension in the testing 
data with null or undefined Name")
-               }
-               found := false
-               for _, respTOExt := range extensions.Response {
-                       if respTOExt.Name == nil {
-                               t.Error("Traffic Ops returned a representation 
for a Servercheck Extension with null or undefined name")
-                               continue
-                       }
-                       if *ext.Name == *respTOExt.Name {
-                               found = true
-                               continue
-                       }
-               }
-               if found {
-                       t.Errorf("to_extension %v should have been deleted", 
*ext.Name)
-               }
-       }
-
-       SwitchSession(toReqTimeout, Config.TrafficOps.URL, 
Config.TrafficOps.Users.Extension, Config.TrafficOps.UserPassword, 
Config.TrafficOps.Users.Admin, Config.TrafficOps.UserPassword)
 }
diff --git a/traffic_ops/testing/api/v4/steering_test.go 
b/traffic_ops/testing/api/v4/steering_test.go
index e8063c3ed9..2925060826 100644
--- a/traffic_ops/testing/api/v4/steering_test.go
+++ b/traffic_ops/testing/api/v4/steering_test.go
@@ -16,55 +16,73 @@ package v4
 */
 
 import (
+       "net/http"
        "testing"
 
-       client "github.com/apache/trafficcontrol/traffic_ops/v4-client"
+       "github.com/apache/trafficcontrol/lib/go-tc"
+       "github.com/apache/trafficcontrol/traffic_ops/testing/api/assert"
+       "github.com/apache/trafficcontrol/traffic_ops/testing/api/utils"
+       "github.com/apache/trafficcontrol/traffic_ops/toclientlib"
 )
 
 func TestSteering(t *testing.T) {
        WithObjs(t, []TCObj{CDNs, Types, Tenants, Parameters, Profiles, 
Statuses, Divisions, Regions, PhysLocations, CacheGroups, Servers, Topologies, 
ServiceCategories, DeliveryServices, Users, SteeringTargets}, func() {
-               GetTestSteering(t)
+               methodTests := utils.V4TestCase{
+                       "GET": {
+                               "OK when VALID request": {
+                                       ClientSession: TOSession,
+                                       Expectations: 
utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK), 
utils.ResponseHasLength(1),
+                                               
validateSteeringTargetFields(map[string]interface{}{"TargetsLength": 1, 
"TargetsOrder": int32(0),
+                                                       "TargetsGeoOrderPtr": 
(*int)(nil), "TargetsLongitudePtr": (*float64)(nil), "TargetsLatitudePtr": 
(*float64)(nil), "TargetsWeight": int32(42)})),
+                               },
+                       },
+               }
+               for method, testCases := range methodTests {
+                       t.Run(method, func(t *testing.T) {
+                               for name, testCase := range testCases {
+                                       switch method {
+                                       case "GET":
+                                               t.Run(name, func(t *testing.T) {
+                                                       resp, reqInf, err := 
testCase.ClientSession.Steering(testCase.RequestOpts)
+                                                       for _, check := range 
testCase.Expectations {
+                                                               check(t, 
reqInf, resp.Response, resp.Alerts, err)
+                                                       }
+                                               })
+                                       }
+                               }
+                       })
+               }
        })
 }
 
-func GetTestSteering(t *testing.T) {
-       if len(testData.SteeringTargets) < 1 {
-               t.Fatal("get steering: no steering target test data")
-       }
-       st := testData.SteeringTargets[0]
-       if st.DeliveryService == nil {
-               t.Fatal("get steering: test data missing ds")
-       }
-
-       resp, _, err := TOSession.Steering(client.RequestOptions{})
-       if err != nil {
-               t.Errorf("steering get: getting steering: %v - alerts: %+v", 
err, resp.Alerts)
-       }
-
-       if len(resp.Response) != len(testData.SteeringTargets) {
-               t.Fatalf("steering get: expected %d actual %d", 
len(testData.SteeringTargets), len(resp.Response))
-       }
-       steerings := resp.Response
-
-       if steerings[0].ClientSteering {
-               t.Error("steering get: ClientSteering expected: true actual: 
false")
-       }
-       if len(steerings[0].Targets) != 1 {
-               t.Fatalf("steering get: Targets expected %d actual %d", 1, 
len(steerings[0].Targets))
-       }
-       if steerings[0].Targets[0].Order != 0 {
-               t.Errorf("steering get: Targets Order expected %d actual %d", 
0, steerings[0].Targets[0].Order)
-       }
-       if steerings[0].Targets[0].GeoOrder != nil {
-               t.Errorf("steering get: Targets Order expected %v actual %d", 
nil, *steerings[0].Targets[0].GeoOrder)
-       }
-       if steerings[0].Targets[0].Longitude != nil {
-               t.Errorf("steering get: Targets Order expected %v actual %f", 
nil, *steerings[0].Targets[0].Longitude)
-       }
-       if steerings[0].Targets[0].Latitude != nil {
-               t.Errorf("steering get: Targets Order expected %v actual %f", 
nil, *steerings[0].Targets[0].Latitude)
-       }
-       if testData.SteeringTargets[0].Value != nil && 
steerings[0].Targets[0].Weight != int32(*testData.SteeringTargets[0].Value) {
-               t.Errorf("steering get: Targets Order expected %v actual %v", 
testData.SteeringTargets[0].Value, steerings[0].Targets[0].Weight)
+func validateSteeringTargetFields(expectedResp map[string]interface{}) 
utils.CkReqFunc {
+       return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ 
tc.Alerts, _ error) {
+               assert.RequireNotNil(t, resp, "Expected Steering response to 
not be nil.")
+               steeringResp := resp.([]tc.Steering)
+               for field, expected := range expectedResp {
+                       for _, steering := range steeringResp {
+                               switch field {
+                               case "TargetsLength":
+                                       assert.Equal(t, expected, 
len(steering.Targets), "Expected Targets Length to be %v, but got %d", 
expected, len(steering.Targets))
+                               case "TargetsOrder":
+                                       assert.RequireEqual(t, 1, 
len(steering.Targets), "Expected Targets Length to be %d, but got %d", 1, 
len(steering.Targets))
+                                       assert.Equal(t, expected, 
steering.Targets[0].Order, "Expected Targets Order to be %v, but got %d", 
expected, steering.Targets[0].Order)
+                               case "TargetsGeoOrderPtr":
+                                       assert.RequireEqual(t, 1, 
len(steering.Targets), "Expected Targets Length to be %d, but got %d", 1, 
len(steering.Targets))
+                                       assert.Equal(t, expected, 
steering.Targets[0].GeoOrder, "Expected Targets GeoOrder to be %v, but got %v", 
nil, steering.Targets[0].GeoOrder)
+                               case "TargetsLongitudePtr":
+                                       assert.RequireEqual(t, 1, 
len(steering.Targets), "Expected Targets Length to be %d, but got %d", 1, 
len(steering.Targets))
+                                       assert.Equal(t, expected, 
steering.Targets[0].Longitude, "Expected Targets Longitude to be %v, but got 
%v", nil, steering.Targets[0].Longitude)
+                               case "TargetsLatitudePtr":
+                                       assert.RequireEqual(t, 1, 
len(steering.Targets), "Expected Targets Length to be %d, but got %d", 1, 
len(steering.Targets))
+                                       assert.Equal(t, expected, 
steering.Targets[0].Latitude, "Expected Targets Latitude to be %v, but got %v", 
nil, steering.Targets[0].Latitude)
+                               case "TargetsWeight":
+                                       assert.RequireEqual(t, 1, 
len(steering.Targets), "Expected Targets Length to be %d, but got %d", 1, 
len(steering.Targets))
+                                       assert.Equal(t, expected, 
steering.Targets[0].Weight, "Expected Targets Weight to be %v, but got %v", 
expected, steering.Targets[0].Weight)
+                               default:
+                                       t.Errorf("Expected field: %v, does not 
exist in response", field)
+                               }
+                       }
+               }
        }
 }

Reply via email to