You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@trafficcontrol.apache.org by GitBox <gi...@apache.org> on 2022/06/17 21:03:45 UTC

[GitHub] [trafficcontrol] rimashah25 opened a new pull request, #6911: Refactor/types test

rimashah25 opened a new pull request, #6911:
URL: https://github.com/apache/trafficcontrol/pull/6911

   <!--
   Thank you for contributing! Please be sure to read our contribution guidelines: https://github.com/apache/trafficcontrol/blob/master/CONTRIBUTING.md
   If this closes or relates to an existing issue, please reference it using one of the following:
   
   Closes: #ISSUE
   Related: #ISSUE
   
   If this PR fixes a security vulnerability, DO NOT submit! Instead, contact
   the Apache Traffic Control Security Team at security@trafficcontrol.apache.org and follow the
   guidelines at https://apache.org/security regarding vulnerability disclosure.
   -->
   
   This PR refactors v4/types_test.go, v3/types_test.go, in order to make tests more granular, providing transparency on what is being tested and reducing the need to dig through test code to confirm what was tested. In addition this new test structure should also make it easier for new tests to be added as well as reduce redundant code.
   
   - Adds data driven testing
      - Descriptive test names 
   - Sub tests for organization
      - Use GoLangs test runner
   - Use assert functionality
       - Provides fundamental assertions
       - Streamlines code
       - Reduces nested conditionals
   - Adds expectation functions
      - Test specific expectations in a clear concise way
      - Reusable expectations
   
   
   <!-- **^ Add meaningful description above** --><hr/>
   
   ## Which Traffic Control components are affected by this PR?
   <!-- Please delete all components from this list that are NOT affected by this PR.
   Feel free to add the name of a tool or script that is affected but not on the list.
   -->
   - Traffic Ops
   
   ## What is the best way to verify this PR?
   <!-- Please include here ALL the steps necessary to test your PR.
   If your PR has tests (and most should), provide the steps needed to run the tests.
   If not, please provide step-by-step instructions to test the PR manually and explain why your PR does not need tests. -->
   
   Run TO Integration Tests
   
   ## If this is a bugfix, which Traffic Control versions contained the bug?
   <!-- Delete this section if the PR is not a bugfix, or if the bug is only in the master branch.
   Examples:
   - 5.1.2
   - 5.1.3 (RC1)
    -->
   
   
   ## PR submission checklist
   - [x] This PR has tests <!-- If not, please delete this text and explain why this PR does not need tests. -->
   - [x] This PR has documentation <!-- If not, please delete this text and explain why this PR does not need documentation. -->
   - [ ] This PR has a CHANGELOG.md entry <!-- A fix for a bug from an ATC release, an improvement, or a new feature should have a changelog entry. -->
   - [ ] This PR **DOES NOT FIX A SERIOUS SECURITY VULNERABILITY** (see [the Apache Software Foundation's security guidelines](https://apache.org/security) for details)
   
   <!--
   Licensed to the Apache Software Foundation (ASF) under one
   or more contributor license agreements.  See the NOTICE file
   distributed with this work for additional information
   regarding copyright ownership.  The ASF licenses this file
   to you 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.
   -->
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@trafficcontrol.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [trafficcontrol] ocket8888 merged pull request #6911: Refactor/types test

Posted by GitBox <gi...@apache.org>.
ocket8888 merged PR #6911:
URL: https://github.com/apache/trafficcontrol/pull/6911


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@trafficcontrol.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [trafficcontrol] ericholguin commented on a diff in pull request #6911: Refactor/types test

Posted by GitBox <gi...@apache.org>.
ericholguin commented on code in PR #6911:
URL: https://github.com/apache/trafficcontrol/pull/6911#discussion_r900502334


##########
traffic_ops/testing/api/v3/types_test.go:
##########
@@ -28,224 +34,255 @@ import (
 
 func TestTypes(t *testing.T) {
 	WithObjs(t, []TCObj{Parameters, Types}, func() {
-		GetTestTypesIMS(t)
-		currentTime := time.Now().UTC().Add(-5 * time.Second)
-		time := currentTime.Format(time.RFC1123)
-		var header http.Header
-		header = make(map[string][]string)
-		header.Set(rfc.IfModifiedSince, time)
-		SortTestTypes(t)
-		UpdateTestTypes(t)
-		GetTestTypes(t)
-		GetTestTypesIMSAfterChange(t, header)
+
+		currentTime := time.Now().UTC().Add(-15 * time.Second)
+		currentTimeRFC := currentTime.Format(time.RFC1123)
+		tomorrow := currentTime.AddDate(0, 0, 1).Format(time.RFC1123)
+
+		methodTests := utils.V3TestCase{
+			"GET": {
+				"NOT MODIFIED when NO CHANGES made": {
+					ClientSession:  TOSession,
+					RequestHeaders: http.Header{rfc.IfModifiedSince: {tomorrow}},
+					Expectations:   utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusNotModified)),
+				},
+				"OK when VALID request": {
+					ClientSession: TOSession,
+					Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK), utils.ResponseLengthGreaterOrEqual(1),
+						validateTypeSort()),
+				},
+				"OK when VALID NAME parameter": {
+					ClientSession: TOSession,
+					RequestParams: url.Values{"name": {"ORG"}},
+					Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK), utils.ResponseHasLength(1),
+						validateTypeFields(map[string]interface{}{"Name": "ORG"})),
+				},
+			},
+			"POST": {
+				"BAD REQUEST when NOT in server table": {
+					ClientSession: TOSession,
+					RequestBody: map[string]interface{}{
+						"description": "Host header regular expression-Test",
+						"name":        "TEST_1",
+						"useInTable":  "regex",
+					},
+					Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
+				},
+				"OK when VALID request in SERVER table": {
+					ClientSession: TOSession,
+					RequestBody: map[string]interface{}{
+						"description": "Host header regular expression-Test",
+						"lastUpdated": "2022-06-17T19:13:46.802044+00:00",
+						"name":        "TEST_4",
+						"useInTable":  "server",
+					},
+					Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK),
+						validateTypeUpdateCreateFields("TEST_4", map[string]interface{}{"Name": "TEST_4"})),
+				},
+			},
+			"PUT": {
+				"BAD REQUEST when NOT in server table": {
+					EndpointId:    GetTypeID(t, "ACTIVE_DIRECTORY"),
+					ClientSession: TOSession,
+					RequestBody: map[string]interface{}{
+						"description": "Active Directory User",
+						"name":        "TEST_3",
+						"useInTable":  "cachegroup",
+					},
+					Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
+				},
+				"OK when VALID request in SERVER table": {
+					EndpointId:    GetTypeID(t, "RIAK"),
+					ClientSession: TOSession,
+					RequestBody: map[string]interface{}{
+						"description": "riak type",
+						"name":        "TEST_5",
+						"useInTable":  "server",
+					},
+					Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK),
+						validateTypeUpdateCreateFields("TEST_5", map[string]interface{}{"Name": "TEST_5"})),
+				},
+			},
+			"DELETE": {
+				"OK when VALID request": {
+					EndpointId:    GetTypeID(t, "INFLUXDB"),
+					ClientSession: TOSession,
+					Expectations:  utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK)),
+				},
+			},
+			"GET AFTER CHANGES": {
+				"OK when CHANGES made": {
+					ClientSession:  TOSession,
+					RequestHeaders: http.Header{rfc.IfModifiedSince: {currentTimeRFC}},
+					Expectations:   utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK)),
+				},
+			},
+		}
+
+		for method, testCases := range methodTests {
+			t.Run(method, func(t *testing.T) {
+				for name, testCase := range testCases {
+					typ := tc.Type{}
+					params := make(map[string]string)
+					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, &typ)
+						assert.NoError(t, err, "Error occurred when unmarshalling request body: %v", err)
+					}
+
+					if testCase.RequestParams != nil {
+						for k, v := range testCase.RequestParams {
+							params[k] = v[0]
+						}
+					}
+
+					switch method {
+					case "GET", "GET AFTER CHANGES":
+						t.Run(name, func(t *testing.T) {
+							var resp []tc.Type
+							var reqInf toclientlib.ReqInf
+							var err error
+							if len(params) != 0 {
+								resp, reqInf, err = testCase.ClientSession.GetTypeByNameWithHdr(params["name"], testCase.RequestHeaders)
+							} else {
+								resp, reqInf, err = testCase.ClientSession.GetTypesWithHdr(testCase.RequestHeaders)
+							}
+							for _, check := range testCase.Expectations {
+								check(t, reqInf, resp, tc.Alerts{}, err)
+							}
+						})
+					case "POST":
+						t.Run(name, func(t *testing.T) {
+							alerts, reqInf, err := testCase.ClientSession.CreateType(typ)
+							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.UpdateTypeByIDWithHdr(testCase.EndpointId(), typ, testCase.RequestHeaders)
+							for _, check := range testCase.Expectations {
+								check(t, reqInf, nil, alerts, err)
+							}
+						})
+					case "DELETE":
+						t.Run(name, func(t *testing.T) {
+							alerts, reqInf, err := testCase.ClientSession.DeleteTypeByID(testCase.EndpointId())
+							for _, check := range testCase.Expectations {
+								check(t, reqInf, nil, alerts, err)
+							}
+						})
+					}
+				}
+			})
+		}
 	})
 }
 
-func GetTestTypesIMSAfterChange(t *testing.T, header http.Header) {
-	for _, typ := range testData.Types {
-		_, reqInf, err := TOSession.GetTypeByNameWithHdr(typ.Name, header)
-		if err != nil {
-			t.Fatalf("Expected no error, but got %v", err.Error())
-		}
-		if reqInf.StatusCode != http.StatusOK {
-			t.Fatalf("Expected 200 status code, got %v", reqInf.StatusCode)
+func validateTypeSort() utils.CkReqFunc {
+	return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, alerts tc.Alerts, _ error) {
+		assert.RequireNotNil(t, resp, "Expected Type response to not be nil.")
+		var typeNames []string
+		typeResp := resp.([]tc.Type)
+		for _, typ := range typeResp {
+			typeNames = append(typeNames, typ.Name)
 		}
+		assert.Equal(t, true, sort.StringsAreSorted(typeNames), "List is not sorted by their names: %v", typeNames)
 	}
-	currentTime := time.Now().UTC()
-	currentTime = currentTime.Add(1 * time.Second)
-	timeStr := currentTime.Format(time.RFC1123)
-	header.Set(rfc.IfModifiedSince, timeStr)
-	for _, typ := range testData.Types {
-		_, reqInf, err := TOSession.GetTypeByNameWithHdr(typ.Name, header)
-		if err != nil {
-			t.Fatalf("Expected no error, but got %v", err.Error())
-		}
-		if reqInf.StatusCode != http.StatusNotModified {
-			t.Fatalf("Expected 304 status code, got %v", reqInf.StatusCode)
+}
+
+func validateTypeFields(expectedResp map[string]interface{}) utils.CkReqFunc {
+	return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ tc.Alerts, _ error) {
+		assert.RequireNotNil(t, resp, "Expected Type response to not be nil.")
+		typeResp := resp.([]tc.Type)
+		for field, expected := range expectedResp {
+			for _, typ := range typeResp {
+				switch field {
+				case "Name":
+					assert.Equal(t, expected, typ.Name, "Expected Name to be %v, but got %s", expected, typ.Name)
+				case "UseInTable":
+					assert.Equal(t, expected, typ.UseInTable, "Expected UseInTable to be %v, but got %s", expected, typ.UseInTable)
+				default:
+					t.Errorf("Expected field: %v, does not exist in response", field)
+				}
+			}
 		}
 	}
 }
 
-func GetTestTypesIMS(t *testing.T) {
-	var header http.Header
-	header = make(map[string][]string)
-	futureTime := time.Now().AddDate(0, 0, 1)
-	time := futureTime.Format(time.RFC1123)
-	header.Set(rfc.IfModifiedSince, time)
-	t.Log("---- GetTestTypes ----")
+func validateTypeUpdateCreateFields(name string, expectedResp map[string]interface{}) utils.CkReqFunc {
+	return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ tc.Alerts, _ error) {
+		typ, _, err := TOSession.GetTypeByNameWithHdr(name, nil)
+		assert.RequireNoError(t, err, "Error getting Types: %v", err)
+		assert.RequireEqual(t, 1, len(typ), "Expected one Type returned, Got: %d", len(typ))
+		validateTypeFields(expectedResp)(t, toclientlib.ReqInf{}, typ, tc.Alerts{}, nil)
+	}
+}
 
-	for _, typ := range testData.Types {
-		_, reqInf, err := TOSession.GetTypeByNameWithHdr(typ.Name, header)
-		if err != nil {
-			t.Fatalf("Expected no error, but got %v", err.Error())
-		}
-		if reqInf.StatusCode != http.StatusNotModified {
-			t.Fatalf("Expected 304 status code, got %v", reqInf.StatusCode)
-		}
+func GetTypeID(t *testing.T, typeName string) func() int {
+	return func() int {
+		resp, _, err := TOSession.GetTypeByNameWithHdr(typeName, nil)
+		assert.RequireNoError(t, err, "Get Types Request failed with error: %v", err)
+		assert.RequireEqual(t, 1, len(resp), "Expected response object length 1, but got %d", len(resp))
+
+		return resp[0].ID
 	}
 }
 
 func CreateTestTypes(t *testing.T) {
-	t.Log("---- CreateTestTypes ----")
-
 	db, err := OpenConnection()
-	if err != nil {
-		t.Fatal("cannot open db")
-	}
+	assert.RequireNoError(t, err, "cannot open db")
+
 	defer func() {
 		err := db.Close()
-		if err != nil {
-			t.Errorf("unable to close connection to db, error: %v", err)
-		}
+		assert.NoError(t, err, "unable to close connection to db, error: %v", err)
 	}()
-	dbQueryTemplate := "INSERT INTO type (name, description, use_in_table) VALUES ('%v', '%v', '%v');"
+	dbQueryTemplate := "INSERT INTO type (name, description, use_in_table) VALUES ('%s', '%s', '%s');"
 
+	//opts := client.NewRequestOptions()

Review Comment:
   Can remove comment



##########
traffic_ops/testing/api/v4/types_test.go:
##########
@@ -16,243 +16,264 @@ package v4
 */
 
 import (
+	"encoding/json"
 	"fmt"
+	"github.com/apache/trafficcontrol/traffic_ops/testing/api/assert"
+	"github.com/apache/trafficcontrol/traffic_ops/toclientlib"
 	"net/http"
+	"net/url"
 	"sort"
-	"strconv"
 	"testing"
 	"time"
 
 	"github.com/apache/trafficcontrol/lib/go-rfc"
 	tc "github.com/apache/trafficcontrol/lib/go-tc"
+	"github.com/apache/trafficcontrol/traffic_ops/testing/api/utils"
 	client "github.com/apache/trafficcontrol/traffic_ops/v4-client"
 )
 
 func TestTypes(t *testing.T) {
 	WithObjs(t, []TCObj{Parameters, Types}, func() {
-		GetTestTypesIMS(t)
-		currentTime := time.Now().UTC().Add(-5 * time.Second)
-		time := currentTime.Format(time.RFC1123)
-		var header http.Header
-		header = make(map[string][]string)
-		header.Set(rfc.IfModifiedSince, time)
-		SortTestTypes(t)
-		UpdateTestTypes(t)
-		GetTestTypes(t)
-		GetTestTypesIMSAfterChange(t, header)
+
+		currentTime := time.Now().UTC().Add(-15 * time.Second)
+		currentTimeRFC := currentTime.Format(time.RFC1123)
+		tomorrow := currentTime.AddDate(0, 0, 1).Format(time.RFC1123)
+
+		methodTests := utils.V4TestCase{
+			"GET": {
+				"NOT MODIFIED when NO CHANGES made": {
+					ClientSession: TOSession,
+					RequestOpts:   client.RequestOptions{Header: http.Header{rfc.IfModifiedSince: {tomorrow}}},
+					Expectations:  utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusNotModified)),
+				},
+				"OK when VALID request": {
+					ClientSession: TOSession,
+					Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK), utils.ResponseLengthGreaterOrEqual(1),
+						validateTypeSort()),
+				},
+				"OK when VALID NAME parameter": {
+					ClientSession: TOSession,
+					RequestOpts:   client.RequestOptions{QueryParameters: url.Values{"name": {"ORG"}}},
+					Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK), utils.ResponseHasLength(1),
+						validateTypeFields(map[string]interface{}{"Name": "ORG"})),
+				},
+			},
+			"POST": {
+				"BAD REQUEST when NOT in server table": {
+					ClientSession: TOSession,
+					RequestBody: map[string]interface{}{
+						"description": "Host header regular expression-Test",
+						"name":        "TEST_1",
+						"useInTable":  "regex",
+					},
+					Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
+				},
+				"OK when VALID request in SERVER table": {
+					ClientSession: TOSession,
+					RequestBody: map[string]interface{}{
+						"description": "Host header regular expression-Test",
+						"name":        "TEST_4",
+						"useInTable":  "server",
+					},
+					Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK),
+						validateTypeUpdateCreateFields("TEST_4", map[string]interface{}{"Name": "TEST_4"})),
+				},
+			},
+			"PUT": {
+				"BAD REQUEST when NOT in server table": {
+					EndpointId:    GetTypeID(t, "ACTIVE_DIRECTORY"),
+					ClientSession: TOSession,
+					RequestBody: map[string]interface{}{
+						"description": "Active Directory User",
+						"name":        "TEST_3",
+						"useInTable":  "cachegroup",
+					},
+					Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
+				},
+				"OK when VALID request in SERVER table": {
+					EndpointId:    GetTypeID(t, "RIAK"),
+					ClientSession: TOSession,
+					RequestBody: map[string]interface{}{
+						"description": "riak type",
+						"name":        "TEST_5",
+						"useInTable":  "server",
+					},
+					Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK),
+						validateTypeUpdateCreateFields("TEST_5", map[string]interface{}{"Name": "TEST_5"})),
+				},
+			},
+			"DELETE": {
+				"OK when VALID request": {
+					EndpointId:    GetTypeID(t, "INFLUXDB"),
+					ClientSession: TOSession,
+					Expectations:  utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK)),
+				},
+			},
+			"GET AFTER CHANGES": {
+				"OK when CHANGES made": {
+					ClientSession: TOSession,
+					RequestOpts:   client.RequestOptions{Header: http.Header{rfc.IfModifiedSince: {currentTimeRFC}}},
+					Expectations:  utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK)),
+				},
+			},
+		}
+
+		for method, testCases := range methodTests {
+			t.Run(method, func(t *testing.T) {
+				for name, testCase := range testCases {
+					typ := tc.Type{}
+
+					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, &typ)
+						assert.NoError(t, err, "Error occurred when unmarshalling request body: %v", err)
+					}
+
+					switch method {
+					case "GET", "GET AFTER CHANGES":
+						t.Run(name, func(t *testing.T) {
+							resp, reqInf, err := testCase.ClientSession.GetTypes(testCase.RequestOpts)
+							for _, check := range testCase.Expectations {
+								check(t, reqInf, resp.Response, resp.Alerts, err)
+							}
+						})
+					case "POST":
+						t.Run(name, func(t *testing.T) {
+							alerts, reqInf, err := testCase.ClientSession.CreateType(typ, client.RequestOptions{})
+							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.UpdateType(testCase.EndpointId(), typ, testCase.RequestOpts)
+							for _, check := range testCase.Expectations {
+								check(t, reqInf, nil, alerts, err)
+							}
+						})
+					case "DELETE":
+						t.Run(name, func(t *testing.T) {
+							alerts, reqInf, err := testCase.ClientSession.DeleteType(testCase.EndpointId(), testCase.RequestOpts)
+							for _, check := range testCase.Expectations {
+								check(t, reqInf, nil, alerts, err)
+							}
+						})
+					}
+				}
+			})
+		}
 	})
 }
 
-func GetTestTypesIMSAfterChange(t *testing.T, header http.Header) {
-	opts := client.NewRequestOptions()
-	opts.Header = header
-	for _, typ := range testData.Types {
-		opts.QueryParameters.Set("name", typ.Name)
-		resp, reqInf, err := TOSession.GetTypes(opts)
-		if err != nil {
-			t.Fatalf("Expected no error, but got: %v - alerts: %+v", err, resp.Alerts)
-		}
-		if reqInf.StatusCode != http.StatusOK {
-			t.Fatalf("Expected 200 status code, got %v", reqInf.StatusCode)
+func validateTypeSort() utils.CkReqFunc {
+	return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, alerts tc.Alerts, _ error) {
+		assert.RequireNotNil(t, resp, "Expected Type response to not be nil.")
+		var typeNames []string
+		typeResp := resp.([]tc.Type)
+		for _, typ := range typeResp {
+			typeNames = append(typeNames, typ.Name)
 		}
+		assert.Equal(t, true, sort.StringsAreSorted(typeNames), "List is not sorted by their names: %v", typeNames)
 	}
+}
 
-	currentTime := time.Now().UTC()
-	currentTime = currentTime.Add(1 * time.Second)
-	timeStr := currentTime.Format(time.RFC1123)
-	opts.Header.Set(rfc.IfModifiedSince, timeStr)
-
-	for _, typ := range testData.Types {
-		opts.QueryParameters.Set("name", typ.Name)
-		resp, reqInf, err := TOSession.GetTypes(opts)
-		if err != nil {
-			t.Fatalf("Expected no error, but got: %v - alerts: %+v", err, resp.Alerts)
-		}
-		if reqInf.StatusCode != http.StatusNotModified {
-			t.Fatalf("Expected 304 status code, got %v", reqInf.StatusCode)
+func validateTypeFields(expectedResp map[string]interface{}) utils.CkReqFunc {
+	return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ tc.Alerts, _ error) {
+		assert.RequireNotNil(t, resp, "Expected Type response to not be nil.")
+		typeResp := resp.([]tc.Type)
+		for field, expected := range expectedResp {
+			for _, typ := range typeResp {
+				switch field {
+				case "Name":
+					assert.Equal(t, expected, typ.Name, "Expected Name to be %v, but got %s", expected, typ.Name)
+				case "UseInTable":
+					assert.Equal(t, expected, typ.UseInTable, "Expected UseInTable to be %v, but got %s", expected, typ.UseInTable)
+				default:
+					t.Errorf("Expected field: %v, does not exist in response", field)
+				}
+			}
 		}
 	}
 }
 
-func GetTestTypesIMS(t *testing.T) {
-	futureTime := time.Now().AddDate(0, 0, 1)
-	time := futureTime.Format(time.RFC1123)
+func validateTypeUpdateCreateFields(name string, expectedResp map[string]interface{}) utils.CkReqFunc {
+	return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ tc.Alerts, _ error) {
+		opts := client.NewRequestOptions()
+		opts.QueryParameters.Set("name", name)
+		typ, _, err := TOSession.GetTypes(opts)
+		assert.RequireNoError(t, err, "Error getting Types: %v - alerts: %+v", err, typ.Alerts)
+		assert.RequireEqual(t, 1, len(typ.Response), "Expected one Type returned, Got: %d", len(typ.Response))
+		validateTypeFields(expectedResp)(t, toclientlib.ReqInf{}, typ.Response, tc.Alerts{}, nil)
+	}
+}
 
-	opts := client.NewRequestOptions()
-	opts.Header.Set(rfc.IfModifiedSince, time)
+func GetTypeID(t *testing.T, typeName string) func() int {
+	return func() int {
+		opts := client.NewRequestOptions()
+		opts.QueryParameters.Set("name", typeName)
+		resp, _, err := TOSession.GetTypes(opts)
 
-	for _, typ := range testData.Types {
-		opts.QueryParameters.Set("name", typ.Name)
-		resp, reqInf, err := TOSession.GetTypes(opts)
-		if err != nil {
-			t.Fatalf("Expected no error, but got: %v - alerts: %+v", err, resp.Alerts)
-		}
-		if reqInf.StatusCode != http.StatusNotModified {
-			t.Fatalf("Expected 304 status code, got %v", reqInf.StatusCode)
-		}
+		assert.RequireNoError(t, err, "Get Types Request failed with error: %v", err)
+		assert.RequireEqual(t, 1, len(resp.Response), "Expected response object length 1, but got %d", len(resp.Response))
+
+		return resp.Response[0].ID
 	}
 }
 
 func CreateTestTypes(t *testing.T) {
 	db, err := OpenConnection()
-	if err != nil {
-		t.Fatal("cannot open db")
-	}
+	assert.RequireNoError(t, err, "cannot open db")
+
 	defer func() {
 		err := db.Close()
-		if err != nil {
-			t.Errorf("unable to close connection to db, error: %v", err)
-		}
+		assert.NoError(t, err, "unable to close connection to db, error: %v", err)
 	}()
 	dbQueryTemplate := "INSERT INTO type (name, description, use_in_table) VALUES ('%s', '%s', '%s');"
 
-	opts := client.NewRequestOptions()
+	//opts := client.NewRequestOptions()

Review Comment:
   Can remove comment



##########
traffic_ops/testing/api/v3/types_test.go:
##########
@@ -28,224 +34,255 @@ import (
 
 func TestTypes(t *testing.T) {
 	WithObjs(t, []TCObj{Parameters, Types}, func() {
-		GetTestTypesIMS(t)
-		currentTime := time.Now().UTC().Add(-5 * time.Second)
-		time := currentTime.Format(time.RFC1123)
-		var header http.Header
-		header = make(map[string][]string)
-		header.Set(rfc.IfModifiedSince, time)
-		SortTestTypes(t)
-		UpdateTestTypes(t)
-		GetTestTypes(t)
-		GetTestTypesIMSAfterChange(t, header)
+
+		currentTime := time.Now().UTC().Add(-15 * time.Second)
+		currentTimeRFC := currentTime.Format(time.RFC1123)
+		tomorrow := currentTime.AddDate(0, 0, 1).Format(time.RFC1123)
+
+		methodTests := utils.V3TestCase{
+			"GET": {
+				"NOT MODIFIED when NO CHANGES made": {
+					ClientSession:  TOSession,
+					RequestHeaders: http.Header{rfc.IfModifiedSince: {tomorrow}},
+					Expectations:   utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusNotModified)),
+				},
+				"OK when VALID request": {
+					ClientSession: TOSession,
+					Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK), utils.ResponseLengthGreaterOrEqual(1),
+						validateTypeSort()),
+				},
+				"OK when VALID NAME parameter": {
+					ClientSession: TOSession,
+					RequestParams: url.Values{"name": {"ORG"}},
+					Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK), utils.ResponseHasLength(1),
+						validateTypeFields(map[string]interface{}{"Name": "ORG"})),
+				},
+			},
+			"POST": {
+				"BAD REQUEST when NOT in server table": {
+					ClientSession: TOSession,
+					RequestBody: map[string]interface{}{
+						"description": "Host header regular expression-Test",
+						"name":        "TEST_1",
+						"useInTable":  "regex",
+					},
+					Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
+				},
+				"OK when VALID request in SERVER table": {
+					ClientSession: TOSession,
+					RequestBody: map[string]interface{}{
+						"description": "Host header regular expression-Test",
+						"lastUpdated": "2022-06-17T19:13:46.802044+00:00",
+						"name":        "TEST_4",
+						"useInTable":  "server",
+					},
+					Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK),
+						validateTypeUpdateCreateFields("TEST_4", map[string]interface{}{"Name": "TEST_4"})),
+				},
+			},
+			"PUT": {
+				"BAD REQUEST when NOT in server table": {

Review Comment:
   nit: Can we change this to: "BAD REQUEST when USEINTABLE NOT server". Same for POST and v4



##########
traffic_ops/testing/api/v3/types_test.go:
##########
@@ -28,224 +34,255 @@ import (
 
 func TestTypes(t *testing.T) {
 	WithObjs(t, []TCObj{Parameters, Types}, func() {
-		GetTestTypesIMS(t)
-		currentTime := time.Now().UTC().Add(-5 * time.Second)
-		time := currentTime.Format(time.RFC1123)
-		var header http.Header
-		header = make(map[string][]string)
-		header.Set(rfc.IfModifiedSince, time)
-		SortTestTypes(t)
-		UpdateTestTypes(t)
-		GetTestTypes(t)
-		GetTestTypesIMSAfterChange(t, header)
+
+		currentTime := time.Now().UTC().Add(-15 * time.Second)
+		currentTimeRFC := currentTime.Format(time.RFC1123)
+		tomorrow := currentTime.AddDate(0, 0, 1).Format(time.RFC1123)
+
+		methodTests := utils.V3TestCase{
+			"GET": {
+				"NOT MODIFIED when NO CHANGES made": {
+					ClientSession:  TOSession,
+					RequestHeaders: http.Header{rfc.IfModifiedSince: {tomorrow}},
+					Expectations:   utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusNotModified)),
+				},
+				"OK when VALID request": {
+					ClientSession: TOSession,
+					Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK), utils.ResponseLengthGreaterOrEqual(1),
+						validateTypeSort()),
+				},
+				"OK when VALID NAME parameter": {
+					ClientSession: TOSession,
+					RequestParams: url.Values{"name": {"ORG"}},
+					Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK), utils.ResponseHasLength(1),
+						validateTypeFields(map[string]interface{}{"Name": "ORG"})),
+				},
+			},
+			"POST": {
+				"BAD REQUEST when NOT in server table": {
+					ClientSession: TOSession,
+					RequestBody: map[string]interface{}{
+						"description": "Host header regular expression-Test",
+						"name":        "TEST_1",
+						"useInTable":  "regex",
+					},
+					Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
+				},
+				"OK when VALID request in SERVER table": {
+					ClientSession: TOSession,
+					RequestBody: map[string]interface{}{
+						"description": "Host header regular expression-Test",
+						"lastUpdated": "2022-06-17T19:13:46.802044+00:00",
+						"name":        "TEST_4",
+						"useInTable":  "server",
+					},
+					Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK),
+						validateTypeUpdateCreateFields("TEST_4", map[string]interface{}{"Name": "TEST_4"})),
+				},
+			},
+			"PUT": {
+				"BAD REQUEST when NOT in server table": {
+					EndpointId:    GetTypeID(t, "ACTIVE_DIRECTORY"),
+					ClientSession: TOSession,
+					RequestBody: map[string]interface{}{
+						"description": "Active Directory User",
+						"name":        "TEST_3",
+						"useInTable":  "cachegroup",
+					},
+					Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
+				},
+				"OK when VALID request in SERVER table": {

Review Comment:
   nit: Can we change this to: "OK when VALID request when USEINTABLE=server". Same for POST and v4



##########
traffic_ops/testing/api/v4/types_test.go:
##########
@@ -16,243 +16,264 @@ package v4
 */
 
 import (
+	"encoding/json"
 	"fmt"
+	"github.com/apache/trafficcontrol/traffic_ops/testing/api/assert"
+	"github.com/apache/trafficcontrol/traffic_ops/toclientlib"
 	"net/http"
+	"net/url"
 	"sort"
-	"strconv"
 	"testing"
 	"time"
 
 	"github.com/apache/trafficcontrol/lib/go-rfc"
 	tc "github.com/apache/trafficcontrol/lib/go-tc"
+	"github.com/apache/trafficcontrol/traffic_ops/testing/api/utils"
 	client "github.com/apache/trafficcontrol/traffic_ops/v4-client"
 )
 
 func TestTypes(t *testing.T) {
 	WithObjs(t, []TCObj{Parameters, Types}, func() {
-		GetTestTypesIMS(t)
-		currentTime := time.Now().UTC().Add(-5 * time.Second)
-		time := currentTime.Format(time.RFC1123)
-		var header http.Header
-		header = make(map[string][]string)
-		header.Set(rfc.IfModifiedSince, time)
-		SortTestTypes(t)
-		UpdateTestTypes(t)
-		GetTestTypes(t)
-		GetTestTypesIMSAfterChange(t, header)
+
+		currentTime := time.Now().UTC().Add(-15 * time.Second)
+		currentTimeRFC := currentTime.Format(time.RFC1123)
+		tomorrow := currentTime.AddDate(0, 0, 1).Format(time.RFC1123)
+
+		methodTests := utils.V4TestCase{
+			"GET": {
+				"NOT MODIFIED when NO CHANGES made": {
+					ClientSession: TOSession,
+					RequestOpts:   client.RequestOptions{Header: http.Header{rfc.IfModifiedSince: {tomorrow}}},
+					Expectations:  utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusNotModified)),
+				},
+				"OK when VALID request": {
+					ClientSession: TOSession,
+					Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK), utils.ResponseLengthGreaterOrEqual(1),
+						validateTypeSort()),
+				},
+				"OK when VALID NAME parameter": {
+					ClientSession: TOSession,
+					RequestOpts:   client.RequestOptions{QueryParameters: url.Values{"name": {"ORG"}}},
+					Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK), utils.ResponseHasLength(1),
+						validateTypeFields(map[string]interface{}{"Name": "ORG"})),
+				},
+			},
+			"POST": {
+				"BAD REQUEST when NOT in server table": {
+					ClientSession: TOSession,
+					RequestBody: map[string]interface{}{
+						"description": "Host header regular expression-Test",
+						"name":        "TEST_1",
+						"useInTable":  "regex",
+					},
+					Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
+				},
+				"OK when VALID request in SERVER table": {
+					ClientSession: TOSession,
+					RequestBody: map[string]interface{}{
+						"description": "Host header regular expression-Test",
+						"name":        "TEST_4",
+						"useInTable":  "server",
+					},
+					Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK),
+						validateTypeUpdateCreateFields("TEST_4", map[string]interface{}{"Name": "TEST_4"})),
+				},
+			},
+			"PUT": {
+				"BAD REQUEST when NOT in server table": {
+					EndpointId:    GetTypeID(t, "ACTIVE_DIRECTORY"),
+					ClientSession: TOSession,
+					RequestBody: map[string]interface{}{
+						"description": "Active Directory User",
+						"name":        "TEST_3",
+						"useInTable":  "cachegroup",
+					},
+					Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
+				},
+				"OK when VALID request in SERVER table": {
+					EndpointId:    GetTypeID(t, "RIAK"),
+					ClientSession: TOSession,
+					RequestBody: map[string]interface{}{
+						"description": "riak type",
+						"name":        "TEST_5",
+						"useInTable":  "server",
+					},
+					Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK),
+						validateTypeUpdateCreateFields("TEST_5", map[string]interface{}{"Name": "TEST_5"})),
+				},
+			},
+			"DELETE": {
+				"OK when VALID request": {
+					EndpointId:    GetTypeID(t, "INFLUXDB"),
+					ClientSession: TOSession,
+					Expectations:  utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK)),
+				},
+			},
+			"GET AFTER CHANGES": {
+				"OK when CHANGES made": {
+					ClientSession: TOSession,
+					RequestOpts:   client.RequestOptions{Header: http.Header{rfc.IfModifiedSince: {currentTimeRFC}}},
+					Expectations:  utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK)),
+				},
+			},
+		}
+
+		for method, testCases := range methodTests {
+			t.Run(method, func(t *testing.T) {
+				for name, testCase := range testCases {
+					typ := tc.Type{}
+
+					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, &typ)
+						assert.NoError(t, err, "Error occurred when unmarshalling request body: %v", err)
+					}
+
+					switch method {
+					case "GET", "GET AFTER CHANGES":
+						t.Run(name, func(t *testing.T) {
+							resp, reqInf, err := testCase.ClientSession.GetTypes(testCase.RequestOpts)
+							for _, check := range testCase.Expectations {
+								check(t, reqInf, resp.Response, resp.Alerts, err)
+							}
+						})
+					case "POST":
+						t.Run(name, func(t *testing.T) {
+							alerts, reqInf, err := testCase.ClientSession.CreateType(typ, client.RequestOptions{})
+							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.UpdateType(testCase.EndpointId(), typ, testCase.RequestOpts)
+							for _, check := range testCase.Expectations {
+								check(t, reqInf, nil, alerts, err)
+							}
+						})
+					case "DELETE":
+						t.Run(name, func(t *testing.T) {
+							alerts, reqInf, err := testCase.ClientSession.DeleteType(testCase.EndpointId(), testCase.RequestOpts)
+							for _, check := range testCase.Expectations {
+								check(t, reqInf, nil, alerts, err)
+							}
+						})
+					}
+				}
+			})
+		}
 	})
 }
 
-func GetTestTypesIMSAfterChange(t *testing.T, header http.Header) {
-	opts := client.NewRequestOptions()
-	opts.Header = header
-	for _, typ := range testData.Types {
-		opts.QueryParameters.Set("name", typ.Name)
-		resp, reqInf, err := TOSession.GetTypes(opts)
-		if err != nil {
-			t.Fatalf("Expected no error, but got: %v - alerts: %+v", err, resp.Alerts)
-		}
-		if reqInf.StatusCode != http.StatusOK {
-			t.Fatalf("Expected 200 status code, got %v", reqInf.StatusCode)
+func validateTypeSort() utils.CkReqFunc {
+	return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, alerts tc.Alerts, _ error) {
+		assert.RequireNotNil(t, resp, "Expected Type response to not be nil.")
+		var typeNames []string
+		typeResp := resp.([]tc.Type)
+		for _, typ := range typeResp {
+			typeNames = append(typeNames, typ.Name)
 		}
+		assert.Equal(t, true, sort.StringsAreSorted(typeNames), "List is not sorted by their names: %v", typeNames)
 	}
+}
 
-	currentTime := time.Now().UTC()
-	currentTime = currentTime.Add(1 * time.Second)
-	timeStr := currentTime.Format(time.RFC1123)
-	opts.Header.Set(rfc.IfModifiedSince, timeStr)
-
-	for _, typ := range testData.Types {
-		opts.QueryParameters.Set("name", typ.Name)
-		resp, reqInf, err := TOSession.GetTypes(opts)
-		if err != nil {
-			t.Fatalf("Expected no error, but got: %v - alerts: %+v", err, resp.Alerts)
-		}
-		if reqInf.StatusCode != http.StatusNotModified {
-			t.Fatalf("Expected 304 status code, got %v", reqInf.StatusCode)
+func validateTypeFields(expectedResp map[string]interface{}) utils.CkReqFunc {
+	return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ tc.Alerts, _ error) {
+		assert.RequireNotNil(t, resp, "Expected Type response to not be nil.")
+		typeResp := resp.([]tc.Type)
+		for field, expected := range expectedResp {
+			for _, typ := range typeResp {
+				switch field {
+				case "Name":
+					assert.Equal(t, expected, typ.Name, "Expected Name to be %v, but got %s", expected, typ.Name)
+				case "UseInTable":
+					assert.Equal(t, expected, typ.UseInTable, "Expected UseInTable to be %v, but got %s", expected, typ.UseInTable)
+				default:
+					t.Errorf("Expected field: %v, does not exist in response", field)
+				}
+			}
 		}
 	}
 }
 
-func GetTestTypesIMS(t *testing.T) {
-	futureTime := time.Now().AddDate(0, 0, 1)
-	time := futureTime.Format(time.RFC1123)
+func validateTypeUpdateCreateFields(name string, expectedResp map[string]interface{}) utils.CkReqFunc {
+	return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ tc.Alerts, _ error) {
+		opts := client.NewRequestOptions()
+		opts.QueryParameters.Set("name", name)
+		typ, _, err := TOSession.GetTypes(opts)
+		assert.RequireNoError(t, err, "Error getting Types: %v - alerts: %+v", err, typ.Alerts)
+		assert.RequireEqual(t, 1, len(typ.Response), "Expected one Type returned, Got: %d", len(typ.Response))
+		validateTypeFields(expectedResp)(t, toclientlib.ReqInf{}, typ.Response, tc.Alerts{}, nil)
+	}
+}
 
-	opts := client.NewRequestOptions()
-	opts.Header.Set(rfc.IfModifiedSince, time)
+func GetTypeID(t *testing.T, typeName string) func() int {
+	return func() int {
+		opts := client.NewRequestOptions()
+		opts.QueryParameters.Set("name", typeName)
+		resp, _, err := TOSession.GetTypes(opts)
 
-	for _, typ := range testData.Types {
-		opts.QueryParameters.Set("name", typ.Name)
-		resp, reqInf, err := TOSession.GetTypes(opts)
-		if err != nil {
-			t.Fatalf("Expected no error, but got: %v - alerts: %+v", err, resp.Alerts)
-		}
-		if reqInf.StatusCode != http.StatusNotModified {
-			t.Fatalf("Expected 304 status code, got %v", reqInf.StatusCode)
-		}
+		assert.RequireNoError(t, err, "Get Types Request failed with error: %v", err)
+		assert.RequireEqual(t, 1, len(resp.Response), "Expected response object length 1, but got %d", len(resp.Response))
+
+		return resp.Response[0].ID
 	}
 }
 
 func CreateTestTypes(t *testing.T) {
 	db, err := OpenConnection()
-	if err != nil {
-		t.Fatal("cannot open db")
-	}
+	assert.RequireNoError(t, err, "cannot open db")
+
 	defer func() {
 		err := db.Close()
-		if err != nil {
-			t.Errorf("unable to close connection to db, error: %v", err)
-		}
+		assert.NoError(t, err, "unable to close connection to db, error: %v", err)
 	}()
 	dbQueryTemplate := "INSERT INTO type (name, description, use_in_table) VALUES ('%s', '%s', '%s');"
 
-	opts := client.NewRequestOptions()
+	//opts := client.NewRequestOptions()
 	for _, typ := range testData.Types {
-		opts.QueryParameters.Set("name", typ.Name)
-		foundTypes, _, err := TOSession.GetTypes(opts)
-		if err == nil && len(foundTypes.Response) > 0 {
-			t.Logf("Type %v already exists (%v match(es))", typ.Name, len(foundTypes.Response))
-			continue
-		}
-
-		var alerts tc.Alerts
 		if typ.UseInTable != "server" {
 			err = execSQL(db, fmt.Sprintf(dbQueryTemplate, typ.Name, typ.Description, typ.UseInTable))
+			assert.RequireNoError(t, err, "could not create Type using database operations: %v", err)
 		} else {
-			alerts, _, err = TOSession.CreateType(typ, client.RequestOptions{})
-		}
-
-		if err != nil {
-			t.Fatalf("could not create Type: %v - alerts: %+v", err, alerts.Alerts)
-		}
-	}
-}
-
-func SortTestTypes(t *testing.T) {
-	resp, _, err := TOSession.GetTypes(client.RequestOptions{})
-	if err != nil {
-		t.Fatalf("Expected no error, but got: %v - alerts: %+v", err, resp.Alerts)
-	}
-
-	sortedList := make([]string, 0, len(resp.Response))
-	for _, typ := range resp.Response {
-		sortedList = append(sortedList, typ.Name)
-	}
-
-	if !sort.StringsAreSorted(sortedList) {
-		t.Errorf("list is not sorted by their names: %v", sortedList)
-	}
-}
-
-func UpdateTestTypes(t *testing.T) {
-
-	for i, typ := range testData.Types {
-		expectedTypeName := fmt.Sprintf("testType%v", i)
-		originalType := typ
-
-		opts := client.NewRequestOptions()
-		opts.QueryParameters.Set("name", originalType.Name)
-		resp, _, err := TOSession.GetTypes(opts)
-		if err != nil {
-			t.Fatalf("cannot get Types filtered by name '%s': %v - alerts: %+v", originalType.Name, err, resp.Alerts)
-		}
-		if len(resp.Response) < 1 {
-			t.Fatalf("no Types exist by name '%s'", originalType.Name)
-		}
-
-		remoteType := resp.Response[0]
-		remoteType.Name = expectedTypeName
-		// Ensure TO checks DB for UseInTable value
-		remoteType.UseInTable = "server"
-
-		alert, _, err := TOSession.UpdateType(remoteType.ID, remoteType, client.RequestOptions{})
-		if originalType.UseInTable != "server" {
-			if err == nil {
-				t.Fatalf("expected update on Type #%d to fail", remoteType.ID)
-			}
-			continue
-		} else if err != nil {
-			t.Fatalf("cannot update Type: %v - alerts: %+v", err, alert.Alerts)
-		}
-
-		// Retrieve the Type to check Type name got updated
-		opts.QueryParameters.Del("name")
-		opts.QueryParameters.Set("id", strconv.Itoa(remoteType.ID))
-		resp, _, err = TOSession.GetTypes(opts)
-		opts.QueryParameters.Del("id")
-		if err != nil {
-			t.Fatalf("cannot get Type by ID %d: %v - alerts: %+v", originalType.ID, err, resp.Alerts)
-		}
-		respType := resp.Response[0]
-		if respType.Name != expectedTypeName {
-			t.Fatalf("results do not match actual: %s, expected: %s", respType.Name, expectedTypeName)
-		}
-		if respType.UseInTable != originalType.UseInTable {
-			t.Fatalf("use in table should never be updated, got: %v, expected %v", respType.UseInTable, originalType.UseInTable)
-		}
-
-		// Revert name change
-		respType.Name = originalType.Name
-		alert, _, err = TOSession.UpdateType(respType.ID, respType, client.RequestOptions{})
-		if err != nil {
-			t.Fatalf("cannot restore/update Type: %v - %+v", err, alert.Alerts)
-		}
-	}
-}
-
-func GetTestTypes(t *testing.T) {
-	opts := client.NewRequestOptions()
-	for _, typ := range testData.Types {
-		opts.QueryParameters.Set("name", typ.Name)
-		resp, _, err := TOSession.GetTypes(opts)
-		if err != nil {
-			t.Errorf("cannot get Type: %v - alerts: %+v", err, resp.Alerts)
+			alerts, _, err := TOSession.CreateType(typ, client.RequestOptions{})
+			assert.RequireNoError(t, err, "could not create Type: %v - alerts: %+v", err, alerts.Alerts)
 		}
 	}
 }
 
 func DeleteTestTypes(t *testing.T) {
 	db, err := OpenConnection()
-	if err != nil {
-		t.Fatal("cannot open db")
-	}
+	assert.RequireNoError(t, err, "cannot open db")
+
 	defer func() {
 		err := db.Close()
-		if err != nil {
-			t.Errorf("unable to close connection to db, error: %v", err)
-		}
+		assert.NoError(t, err, "unable to close connection to db, error: %v", err)
 	}()
 	dbDeleteTemplate := "DELETE FROM type WHERE name='%s';"
 
-	opts := client.NewRequestOptions()
-	for _, typ := range testData.Types {
-		// Retrieve the Type by name so we can get the id for the Update
-		opts.QueryParameters.Set("name", typ.Name)
-		resp, _, err := TOSession.GetTypes(opts)
-		if err != nil || len(resp.Response) == 0 {
-			t.Fatalf("cannot get Types filtered by name '%s': %v - alerts: %+v", typ.Name, err, resp.Alerts)
+	types, _, err := TOSession.GetTypes(client.RequestOptions{})
+	assert.NoError(t, err, "Cannot get Types: %v - alerts: %+v", err, types.Alerts)
+
+	for _, typ := range types.Response {
+		if typ.Name == "CHECK_EXTENSION_BOOL" || typ.Name == "CHECK_EXTENSION_NUM" || typ.Name == "CHECK_EXTENSION_OPEN_SLOT" {
+			continue
 		}
-		respType := resp.Response[0]
 
-		if respType.UseInTable != "server" {
-			err := execSQL(db, fmt.Sprintf(dbDeleteTemplate, respType.Name))
-			if err != nil {
-				t.Fatalf("cannot delete Type using database operations: %v", err)
-			}
+		if typ.UseInTable != "server" {
+			err := execSQL(db, fmt.Sprintf(dbDeleteTemplate, typ.Name))
+			assert.RequireNoError(t, err, "cannot delete Type using database operations: %v", err)
 		} else {
-			delResp, _, err := TOSession.DeleteType(respType.ID, client.RequestOptions{})
-			if err != nil {
-				t.Fatalf("cannot delete Type using the API: %v - alerts: %+v", err, delResp.Alerts)
-			}
+			delResp, _, err := TOSession.DeleteType(typ.ID, client.RequestOptions{})
+			assert.RequireNoError(t, err, "cannot delete Type using the API: %v - alerts: %+v", err, delResp.Alerts)
 		}
 
-		// Retrieve the Type to see if it got deleted
+		// Retrieve the Type by name so we can get the id for the Update

Review Comment:
   Comment should be updated to: // Retrieve the Type to see if it got deleted



##########
traffic_ops/testing/api/v3/types_test.go:
##########
@@ -28,224 +34,255 @@ import (
 
 func TestTypes(t *testing.T) {
 	WithObjs(t, []TCObj{Parameters, Types}, func() {
-		GetTestTypesIMS(t)
-		currentTime := time.Now().UTC().Add(-5 * time.Second)
-		time := currentTime.Format(time.RFC1123)
-		var header http.Header
-		header = make(map[string][]string)
-		header.Set(rfc.IfModifiedSince, time)
-		SortTestTypes(t)
-		UpdateTestTypes(t)
-		GetTestTypes(t)
-		GetTestTypesIMSAfterChange(t, header)
+
+		currentTime := time.Now().UTC().Add(-15 * time.Second)
+		currentTimeRFC := currentTime.Format(time.RFC1123)
+		tomorrow := currentTime.AddDate(0, 0, 1).Format(time.RFC1123)
+
+		methodTests := utils.V3TestCase{
+			"GET": {
+				"NOT MODIFIED when NO CHANGES made": {
+					ClientSession:  TOSession,
+					RequestHeaders: http.Header{rfc.IfModifiedSince: {tomorrow}},
+					Expectations:   utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusNotModified)),
+				},
+				"OK when VALID request": {
+					ClientSession: TOSession,
+					Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK), utils.ResponseLengthGreaterOrEqual(1),
+						validateTypeSort()),
+				},
+				"OK when VALID NAME parameter": {
+					ClientSession: TOSession,
+					RequestParams: url.Values{"name": {"ORG"}},
+					Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK), utils.ResponseHasLength(1),
+						validateTypeFields(map[string]interface{}{"Name": "ORG"})),
+				},
+			},
+			"POST": {
+				"BAD REQUEST when NOT in server table": {
+					ClientSession: TOSession,
+					RequestBody: map[string]interface{}{
+						"description": "Host header regular expression-Test",
+						"name":        "TEST_1",
+						"useInTable":  "regex",
+					},
+					Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
+				},
+				"OK when VALID request in SERVER table": {
+					ClientSession: TOSession,
+					RequestBody: map[string]interface{}{
+						"description": "Host header regular expression-Test",
+						"lastUpdated": "2022-06-17T19:13:46.802044+00:00",
+						"name":        "TEST_4",
+						"useInTable":  "server",
+					},
+					Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK),
+						validateTypeUpdateCreateFields("TEST_4", map[string]interface{}{"Name": "TEST_4"})),
+				},
+			},
+			"PUT": {
+				"BAD REQUEST when NOT in server table": {
+					EndpointId:    GetTypeID(t, "ACTIVE_DIRECTORY"),
+					ClientSession: TOSession,
+					RequestBody: map[string]interface{}{
+						"description": "Active Directory User",
+						"name":        "TEST_3",
+						"useInTable":  "cachegroup",
+					},
+					Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
+				},
+				"OK when VALID request in SERVER table": {
+					EndpointId:    GetTypeID(t, "RIAK"),
+					ClientSession: TOSession,
+					RequestBody: map[string]interface{}{
+						"description": "riak type",
+						"name":        "TEST_5",
+						"useInTable":  "server",
+					},
+					Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK),
+						validateTypeUpdateCreateFields("TEST_5", map[string]interface{}{"Name": "TEST_5"})),
+				},
+			},
+			"DELETE": {
+				"OK when VALID request": {
+					EndpointId:    GetTypeID(t, "INFLUXDB"),
+					ClientSession: TOSession,
+					Expectations:  utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK)),
+				},
+			},
+			"GET AFTER CHANGES": {
+				"OK when CHANGES made": {
+					ClientSession:  TOSession,
+					RequestHeaders: http.Header{rfc.IfModifiedSince: {currentTimeRFC}},
+					Expectations:   utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK)),
+				},
+			},
+		}
+
+		for method, testCases := range methodTests {
+			t.Run(method, func(t *testing.T) {
+				for name, testCase := range testCases {
+					typ := tc.Type{}
+					params := make(map[string]string)
+					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, &typ)
+						assert.NoError(t, err, "Error occurred when unmarshalling request body: %v", err)
+					}
+
+					if testCase.RequestParams != nil {
+						for k, v := range testCase.RequestParams {
+							params[k] = v[0]
+						}
+					}
+
+					switch method {
+					case "GET", "GET AFTER CHANGES":
+						t.Run(name, func(t *testing.T) {
+							var resp []tc.Type
+							var reqInf toclientlib.ReqInf
+							var err error
+							if len(params) != 0 {
+								resp, reqInf, err = testCase.ClientSession.GetTypeByNameWithHdr(params["name"], testCase.RequestHeaders)
+							} else {
+								resp, reqInf, err = testCase.ClientSession.GetTypesWithHdr(testCase.RequestHeaders)
+							}
+							for _, check := range testCase.Expectations {
+								check(t, reqInf, resp, tc.Alerts{}, err)
+							}
+						})
+					case "POST":
+						t.Run(name, func(t *testing.T) {
+							alerts, reqInf, err := testCase.ClientSession.CreateType(typ)
+							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.UpdateTypeByIDWithHdr(testCase.EndpointId(), typ, testCase.RequestHeaders)
+							for _, check := range testCase.Expectations {
+								check(t, reqInf, nil, alerts, err)
+							}
+						})
+					case "DELETE":
+						t.Run(name, func(t *testing.T) {
+							alerts, reqInf, err := testCase.ClientSession.DeleteTypeByID(testCase.EndpointId())
+							for _, check := range testCase.Expectations {
+								check(t, reqInf, nil, alerts, err)
+							}
+						})
+					}
+				}
+			})
+		}
 	})
 }
 
-func GetTestTypesIMSAfterChange(t *testing.T, header http.Header) {
-	for _, typ := range testData.Types {
-		_, reqInf, err := TOSession.GetTypeByNameWithHdr(typ.Name, header)
-		if err != nil {
-			t.Fatalf("Expected no error, but got %v", err.Error())
-		}
-		if reqInf.StatusCode != http.StatusOK {
-			t.Fatalf("Expected 200 status code, got %v", reqInf.StatusCode)
+func validateTypeSort() utils.CkReqFunc {
+	return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, alerts tc.Alerts, _ error) {
+		assert.RequireNotNil(t, resp, "Expected Type response to not be nil.")
+		var typeNames []string
+		typeResp := resp.([]tc.Type)
+		for _, typ := range typeResp {
+			typeNames = append(typeNames, typ.Name)
 		}
+		assert.Equal(t, true, sort.StringsAreSorted(typeNames), "List is not sorted by their names: %v", typeNames)
 	}
-	currentTime := time.Now().UTC()
-	currentTime = currentTime.Add(1 * time.Second)
-	timeStr := currentTime.Format(time.RFC1123)
-	header.Set(rfc.IfModifiedSince, timeStr)
-	for _, typ := range testData.Types {
-		_, reqInf, err := TOSession.GetTypeByNameWithHdr(typ.Name, header)
-		if err != nil {
-			t.Fatalf("Expected no error, but got %v", err.Error())
-		}
-		if reqInf.StatusCode != http.StatusNotModified {
-			t.Fatalf("Expected 304 status code, got %v", reqInf.StatusCode)
+}
+
+func validateTypeFields(expectedResp map[string]interface{}) utils.CkReqFunc {
+	return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ tc.Alerts, _ error) {
+		assert.RequireNotNil(t, resp, "Expected Type response to not be nil.")
+		typeResp := resp.([]tc.Type)
+		for field, expected := range expectedResp {
+			for _, typ := range typeResp {
+				switch field {
+				case "Name":
+					assert.Equal(t, expected, typ.Name, "Expected Name to be %v, but got %s", expected, typ.Name)
+				case "UseInTable":
+					assert.Equal(t, expected, typ.UseInTable, "Expected UseInTable to be %v, but got %s", expected, typ.UseInTable)
+				default:
+					t.Errorf("Expected field: %v, does not exist in response", field)
+				}
+			}
 		}
 	}
 }
 
-func GetTestTypesIMS(t *testing.T) {
-	var header http.Header
-	header = make(map[string][]string)
-	futureTime := time.Now().AddDate(0, 0, 1)
-	time := futureTime.Format(time.RFC1123)
-	header.Set(rfc.IfModifiedSince, time)
-	t.Log("---- GetTestTypes ----")
+func validateTypeUpdateCreateFields(name string, expectedResp map[string]interface{}) utils.CkReqFunc {
+	return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ tc.Alerts, _ error) {
+		typ, _, err := TOSession.GetTypeByNameWithHdr(name, nil)
+		assert.RequireNoError(t, err, "Error getting Types: %v", err)
+		assert.RequireEqual(t, 1, len(typ), "Expected one Type returned, Got: %d", len(typ))
+		validateTypeFields(expectedResp)(t, toclientlib.ReqInf{}, typ, tc.Alerts{}, nil)
+	}
+}
 
-	for _, typ := range testData.Types {
-		_, reqInf, err := TOSession.GetTypeByNameWithHdr(typ.Name, header)
-		if err != nil {
-			t.Fatalf("Expected no error, but got %v", err.Error())
-		}
-		if reqInf.StatusCode != http.StatusNotModified {
-			t.Fatalf("Expected 304 status code, got %v", reqInf.StatusCode)
-		}
+func GetTypeID(t *testing.T, typeName string) func() int {
+	return func() int {
+		resp, _, err := TOSession.GetTypeByNameWithHdr(typeName, nil)
+		assert.RequireNoError(t, err, "Get Types Request failed with error: %v", err)
+		assert.RequireEqual(t, 1, len(resp), "Expected response object length 1, but got %d", len(resp))
+
+		return resp[0].ID
 	}
 }
 
 func CreateTestTypes(t *testing.T) {
-	t.Log("---- CreateTestTypes ----")
-
 	db, err := OpenConnection()
-	if err != nil {
-		t.Fatal("cannot open db")
-	}
+	assert.RequireNoError(t, err, "cannot open db")
+
 	defer func() {
 		err := db.Close()
-		if err != nil {
-			t.Errorf("unable to close connection to db, error: %v", err)
-		}
+		assert.NoError(t, err, "unable to close connection to db, error: %v", err)
 	}()
-	dbQueryTemplate := "INSERT INTO type (name, description, use_in_table) VALUES ('%v', '%v', '%v');"
+	dbQueryTemplate := "INSERT INTO type (name, description, use_in_table) VALUES ('%s', '%s', '%s');"
 
+	//opts := client.NewRequestOptions()
 	for _, typ := range testData.Types {
-		foundTypes, _, err := TOSession.GetTypeByName(typ.Name)
-		if err == nil && len(foundTypes) > 0 {
-			t.Logf("Type %v already exists (%v match(es))", typ.Name, len(foundTypes))
-			continue
-		}
-
 		if typ.UseInTable != "server" {
 			err = execSQL(db, fmt.Sprintf(dbQueryTemplate, typ.Name, typ.Description, typ.UseInTable))
+			assert.RequireNoError(t, err, "could not create Type using database operations: %v", err)
 		} else {
-			_, _, err = TOSession.CreateType(typ)
-		}
-
-		if err != nil {
-			t.Fatalf("could not CREATE types: %v", err)
-		}
-	}
-}
-
-func SortTestTypes(t *testing.T) {
-	var header http.Header
-	var sortedList []string
-	resp, _, err := TOSession.GetTypesWithHdr(header)
-	if err != nil {
-		t.Fatalf("Expected no error, but got %v", err.Error())
-	}
-	for i, _ := range resp {
-		sortedList = append(sortedList, resp[i].Name)
-	}
-
-	res := sort.SliceIsSorted(sortedList, func(p, q int) bool {
-		return sortedList[p] < sortedList[q]
-	})
-	if res != true {
-		t.Errorf("list is not sorted by their names: %v", sortedList)
-	}
-}
-
-func UpdateTestTypes(t *testing.T) {
-	t.Log("---- UpdateTestTypes ----")
-
-	for i, typ := range testData.Types {
-		expectedTypeName := fmt.Sprintf("testType%v", i)
-		originalType := typ
-		resp, _, err := TOSession.GetTypeByName(originalType.Name)
-		if err != nil {
-			t.Fatalf("cannot GET Type by name: %v - %v", originalType.Name, err)
-		}
-		if len(resp) < 1 {
-			t.Fatalf("no types by name: %v", originalType.Name)
-		}
-
-		remoteType := resp[0]
-		remoteType.Name = expectedTypeName
-		// Ensure TO checks DB for UseInTable value
-		remoteType.UseInTable = "server"
-
-		var alert tc.Alerts
-		alert, _, err = TOSession.UpdateTypeByID(remoteType.ID, remoteType)
-		if originalType.UseInTable != "server" {
-			if err == nil {
-				t.Fatalf("expected UPDATE on type %v to fail", remoteType.ID)
-			}
-			continue
-		} else if err != nil {
-			t.Fatalf("cannot UPDATE Type by id: %v - %v", err, alert)
-		}
-
-		// Retrieve the Type to check Type name got updated
-		resp, _, err = TOSession.GetTypeByID(remoteType.ID)
-		if err != nil {
-			t.Fatalf("cannot GET Type by ID: %v - %v", originalType.ID, err)
-		}
-		respType := resp[0]
-		if respType.Name != expectedTypeName {
-			t.Fatalf("results do not match actual: %s, expected: %s", respType.Name, expectedTypeName)
-		}
-		if respType.UseInTable != originalType.UseInTable {
-			t.Fatalf("use in table should never be updated, got: %v, expected %v", respType.UseInTable, originalType.UseInTable)
-		}
-
-		// Revert name change
-		respType.Name = originalType.Name
-		alert, _, err = TOSession.UpdateTypeByID(respType.ID, respType)
-		if err != nil {
-			t.Fatalf("cannot restore UPDATE Type by id: %v - %v", err, alert)
-		}
-	}
-}
-
-func GetTestTypes(t *testing.T) {
-	t.Log("---- GetTestTypes ----")
-
-	for _, typ := range testData.Types {
-		resp, _, err := TOSession.GetTypeByName(typ.Name)
-		if err != nil {
-			t.Errorf("cannot GET Type by name: %v - %v", err, resp)
-
+			alerts, _, err := TOSession.CreateType(typ)
+			assert.RequireNoError(t, err, "could not create Type: %v - alerts: %+v", err, alerts.Alerts)
 		}
-
-		t.Log("Response: ", resp)
 	}
 }
 
 func DeleteTestTypes(t *testing.T) {
-	t.Log("---- DeleteTestTypes ----")
-
 	db, err := OpenConnection()
-	if err != nil {
-		t.Fatal("cannot open db")
-	}
+	assert.RequireNoError(t, err, "cannot open db")
+
 	defer func() {
 		err := db.Close()
-		if err != nil {
-			t.Errorf("unable to close connection to db, error: %v", err)
-		}
+		assert.NoError(t, err, "unable to close connection to db, error: %v", err)
 	}()
-	dbDeleteTemplate := "DELETE FROM type WHERE name='%v';"
+	dbDeleteTemplate := "DELETE FROM type WHERE name='%s';"
 
-	for _, typ := range testData.Types {
-		// Retrieve the Type by name so we can get the id for the Update
-		resp, _, err := TOSession.GetTypeByName(typ.Name)
-		if err != nil || len(resp) == 0 {
-			t.Fatalf("cannot GET Type by name: %v - %v", typ.Name, err)
+	types, _, err := TOSession.GetTypesWithHdr(nil)
+	assert.NoError(t, err, "Cannot get Types: %v: %+v", err)
+
+	for _, typ := range types {
+		if typ.Name == "CHECK_EXTENSION_BOOL" || typ.Name == "CHECK_EXTENSION_NUM" || typ.Name == "CHECK_EXTENSION_OPEN_SLOT" {
+			continue
 		}
-		respType := resp[0]
 
-		if respType.UseInTable != "server" {
-			err := execSQL(db, fmt.Sprintf(dbDeleteTemplate, respType.Name))
-			if err != nil {
-				t.Fatalf("cannot DELETE Type by name: %v", err)
-			}
+		if typ.UseInTable != "server" {
+			err := execSQL(db, fmt.Sprintf(dbDeleteTemplate, typ.Name))
+			assert.RequireNoError(t, err, "cannot delete Type using database operations: %v", err)
 		} else {
-			delResp, _, err := TOSession.DeleteTypeByID(respType.ID)
-			if err != nil {
-				t.Fatalf("cannot DELETE Type by name: %v - %v", err, delResp)
-			}
+			delResp, _, err := TOSession.DeleteTypeByID(typ.ID)
+			assert.RequireNoError(t, err, "cannot delete Type using the API: %v - alerts: %+v", err, delResp.Alerts)
 		}
 
-		// Retrieve the Type to see if it got deleted
-		types, _, err := TOSession.GetTypeByName(typ.Name)
-		if err != nil {
-			t.Errorf("error deleting Type name: %v", err)
-		}
-		if len(types) > 0 {
-			t.Errorf("expected Type name: %s to be deleted", typ.Name)
-		}
+		// Retrieve the Type by name so we can get the id for the Update

Review Comment:
   Comment should be updated to: // Retrieve the Type to see if it got deleted



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@trafficcontrol.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org