blob: b2d791fbd60e5d004b9d887af8553b769ad8e902 [file] [log] [blame]
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 (
"net/http"
"net/url"
"reflect"
"sort"
"strconv"
"strings"
"testing"
"time"
"github.com/apache/trafficcontrol/lib/go-rfc"
client "github.com/apache/trafficcontrol/traffic_ops/v4-client"
)
func TestRegions(t *testing.T) {
WithObjs(t, []TCObj{Parameters, Divisions, Regions}, func() {
GetTestRegionsIMS(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)
header.Set(rfc.IfUnmodifiedSince, time)
SortTestRegions(t)
SortTestRegionsDesc(t)
UpdateTestRegions(t)
UpdateTestRegionsWithHeaders(t, header)
GetTestRegions(t)
GetTestRegionsIMSAfterChange(t, header)
DeleteTestRegionsByName(t)
VerifyPaginationSupportRegion(t)
header = make(map[string][]string)
etag := rfc.ETag(currentTime)
header.Set(rfc.IfMatch, etag)
UpdateTestRegionsWithHeaders(t, header)
DeleteTestRegionsByInvalidId(t)
DeleteTestRegionsByInvalidName(t)
GetTestRegionByInvalidId(t)
GetTestRegionByInvalidName(t)
GetTestRegionByDivision(t)
GetTestRegionByInvalidDivision(t)
CreateTestRegionsInvalidDivision(t)
})
}
func UpdateTestRegionsWithHeaders(t *testing.T, header http.Header) {
if len(testData.Regions) < 1 {
t.Fatal("Need at least one Region to test updating Regions with HTTP headers")
}
firstRegion := testData.Regions[0]
// Retrieve the Region by region so we can get the id for the Update
opts := client.NewRequestOptions()
opts.QueryParameters.Set("name", firstRegion.Name)
opts.Header = header
resp, _, err := TOSession.GetRegions(opts)
if err != nil {
t.Errorf("cannot get Region '%s' by name: %v - alerts: %+v", firstRegion.Name, err, resp.Alerts)
}
if len(resp.Response) != 1 {
t.Fatalf("Expected exactly one Region to exist with name '%s', found: %d", firstRegion.Name, len(resp.Response))
}
remoteRegion := resp.Response[0]
remoteRegion.Name = "OFFLINE-TEST"
opts.QueryParameters.Del("name")
_, reqInf, err := TOSession.UpdateRegion(remoteRegion.ID, remoteRegion, opts)
if err == nil {
t.Errorf("Expected error about precondition failed, but got none")
}
if reqInf.StatusCode != http.StatusPreconditionFailed {
t.Errorf("Expected status code 412, got %v", reqInf.StatusCode)
}
}
func GetTestRegionsIMS(t *testing.T) {
futureTime := time.Now().AddDate(0, 0, 1)
time := futureTime.Format(time.RFC1123)
opts := client.NewRequestOptions()
opts.Header.Set(rfc.IfModifiedSince, time)
for _, region := range testData.Regions {
opts.QueryParameters.Set("name", region.Name)
resp, reqInf, err := TOSession.GetRegions(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 GetTestRegionsIMSAfterChange(t *testing.T, header http.Header) {
opts := client.NewRequestOptions()
opts.Header = header
for _, region := range testData.Regions {
opts.QueryParameters.Set("name", region.Name)
resp, reqInf, err := TOSession.GetRegions(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)
}
}
currentTime := time.Now().UTC()
currentTime = currentTime.Add(1 * time.Second)
timeStr := currentTime.Format(time.RFC1123)
opts.Header.Set(rfc.IfModifiedSince, timeStr)
for _, region := range testData.Regions {
opts.QueryParameters.Set("name", region.Name)
resp, reqInf, err := TOSession.GetRegions(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 GetTestRegions(t *testing.T) {
opts := client.NewRequestOptions()
for _, region := range testData.Regions {
opts.QueryParameters.Set("name", region.Name)
resp, _, err := TOSession.GetRegions(opts)
if err != nil {
t.Errorf("cannot get Region '%s' by name: %v - alerts: %+v", region.Name, err, resp.Alerts)
}
}
}
func CreateTestRegions(t *testing.T) {
for _, region := range testData.Regions {
resp, _, err := TOSession.CreateRegion(region, client.RequestOptions{})
if err != nil {
t.Errorf("could not create Region '%s': %v - alerts: %+v", region.Name, err, resp.Alerts)
}
}
}
func SortTestRegions(t *testing.T) {
resp, _, err := TOSession.GetRegions(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 _, region := range resp.Response {
sortedList = append(sortedList, region.Name)
}
res := sort.SliceIsSorted(sortedList, func(p, q int) bool {
return sortedList[p] < sortedList[q]
})
if !res {
t.Errorf("list is not sorted by their names: %v", sortedList)
}
}
func SortTestRegionsDesc(t *testing.T) {
resp, _, err := TOSession.GetRegions(client.RequestOptions{})
if err != nil {
t.Errorf("Expected no error, but got error in Regions with default ordering: %v - alerts: %+v", err, resp.Alerts)
}
respAsc := resp.Response
if len(respAsc) < 1 {
t.Fatal("Need at least one Region in Traffic Ops to test Regions sort ordering")
}
opts := client.NewRequestOptions()
opts.QueryParameters.Set("sortOrder", "desc")
resp, _, err = TOSession.GetRegions(opts)
if err != nil {
t.Errorf("Expected no error, but got error in Regions with Descending ordering: %v - alerts: %+v", err, resp.Alerts)
}
respDesc := resp.Response
if len(respDesc) < 1 {
t.Fatal("Need at least one Region in Traffic Ops to test Regions sort ordering")
}
if len(respAsc) != len(respDesc) {
t.Fatalf("Traffic Ops returned %d Regions using default sort order, but %d Regions when sort order was explicitly set to descending", len(respAsc), len(respDesc))
}
// reverse the descending-sorted response and compare it to the ascending-sorted one
// TODO ensure at least two in each slice? A list of length one is
// trivially sorted both ascending and descending.
for start, end := 0, len(respDesc)-1; start < end; start, end = start+1, end-1 {
respDesc[start], respDesc[end] = respDesc[end], respDesc[start]
}
if respDesc[0].Name != respAsc[0].Name {
t.Errorf("Regions responses are not equal after reversal: Asc: %s - Desc: %s", respDesc[0].Name, respAsc[0].Name)
}
}
func UpdateTestRegions(t *testing.T) {
if len(testData.Regions) < 1 {
t.Fatal("Need at least one Region to test updating a Region")
}
firstRegion := testData.Regions[0]
// Retrieve the Region by region so we can get the id for the Update
opts := client.NewRequestOptions()
opts.QueryParameters.Set("name", firstRegion.Name)
resp, _, err := TOSession.GetRegions(opts)
if err != nil {
t.Errorf("cannot get Region '%s' by name: %v - alerts: %+v", firstRegion.Name, err, resp.Alerts)
}
if len(resp.Response) != 1 {
t.Fatalf("Expected exactly one Region to exist with name '%s', found: %d", firstRegion.Name, len(resp.Response))
}
remoteRegion := resp.Response[0]
expectedRegion := "OFFLINE-TEST"
remoteRegion.Name = expectedRegion
alert, _, err := TOSession.UpdateRegion(remoteRegion.ID, remoteRegion, client.RequestOptions{})
if err != nil {
t.Errorf("cannot update Region: %v - alerts: %+v", err, alert.Alerts)
}
// Retrieve the Region to check region got updated
opts.QueryParameters.Del("name")
opts.QueryParameters.Set("id", strconv.Itoa(remoteRegion.ID))
resp, _, err = TOSession.GetRegions(opts)
if err != nil {
t.Errorf("cannot get Region '%s' (#%d) by ID: %v - alerts: %+v", firstRegion.Name, remoteRegion.ID, err, resp.Alerts)
}
if len(resp.Response) != 1 {
t.Fatalf("Expected exactly one Region to exist with ID %d, found: %d", remoteRegion.ID, len(resp.Response))
}
respRegion := resp.Response[0]
if respRegion.Name != expectedRegion {
t.Errorf("results do not match actual: %s, expected: %s", respRegion.Name, expectedRegion)
}
// Set the name back to the fixture value so we can delete it after
remoteRegion.Name = firstRegion.Name
alert, _, err = TOSession.UpdateRegion(remoteRegion.ID, remoteRegion, client.RequestOptions{})
if err != nil {
t.Errorf("cannot update Region: %v - alerts: %+v", err, alert.Alerts)
}
}
func VerifyPaginationSupportRegion(t *testing.T) {
opts := client.NewRequestOptions()
opts.QueryParameters.Set("orderby", "id")
resp, _, err := TOSession.GetRegions(opts)
if err != nil {
t.Fatalf("cannot get Regions: %v - alerts: %+v", err, resp.Alerts)
}
regions := resp.Response
if len(regions) < 2 {
t.Fatalf("Need at least 2 Regions in Traffic Ops to test pagination support, found: %d", len(regions))
}
opts.QueryParameters = url.Values{}
opts.QueryParameters.Set("orderby", "id")
opts.QueryParameters.Set("limit", "1")
regionsWithLimit, _, err := TOSession.GetRegions(opts)
if err == nil {
if !reflect.DeepEqual(regions[:1], regionsWithLimit.Response) {
t.Error("expected GET Regions with limit = 1 to return first result")
}
} else {
t.Error("Error in getting regions by limit")
}
opts.QueryParameters = url.Values{}
opts.QueryParameters.Set("orderby", "id")
opts.QueryParameters.Set("limit", "1")
opts.QueryParameters.Set("offset", "1")
regionsWithOffset, _, err := TOSession.GetRegions(opts)
if err == nil {
if !reflect.DeepEqual(regions[1:2], regionsWithOffset.Response) {
t.Error("expected GET Regions with limit = 1, offset = 1 to return second result")
}
} else {
t.Error("Error in getting regions by limit and offset")
}
opts.QueryParameters = url.Values{}
opts.QueryParameters.Set("orderby", "id")
opts.QueryParameters.Set("limit", "1")
opts.QueryParameters.Set("page", "2")
regionsWithPage, _, err := TOSession.GetRegions(opts)
if err == nil {
if !reflect.DeepEqual(regions[1:2], regionsWithPage.Response) {
t.Error("expected GET Regions with limit = 1, page = 2 to return second result")
}
} else {
t.Error("Error in getting regions by limit and page")
}
opts.QueryParameters = url.Values{}
opts.QueryParameters.Set("limit", "-2")
resp, _, err = TOSession.GetRegions(opts)
if err == nil {
t.Error("expected GET Regions to return an error when limit is not bigger than -1")
} else if !alertsHaveError(resp.Alerts.Alerts, "must be bigger than -1") {
t.Errorf("expected GET Regions to return an error for limit is not bigger than -1, actual error: " + err.Error())
}
opts.QueryParameters = url.Values{}
opts.QueryParameters.Set("limit", "1")
opts.QueryParameters.Set("offset", "0")
resp, _, err = TOSession.GetRegions(opts)
if err == nil {
t.Error("expected GET Regions to return an error when offset is not a positive integer")
} else if !alertsHaveError(resp.Alerts.Alerts, "must be a positive integer") {
t.Errorf("expected GET Regions to return an error for offset is not a positive integer, actual error: " + err.Error())
}
opts.QueryParameters = url.Values{}
opts.QueryParameters.Set("limit", "1")
opts.QueryParameters.Set("page", "0")
resp, _, err = TOSession.GetRegions(opts)
if err == nil {
t.Error("expected GET Regions to return an error when page is not a positive integer")
} else if !alertsHaveError(resp.Alerts.Alerts, "must be a positive integer") {
t.Errorf("expected GET Regions to return an error for page is not a positive integer, actual error: " + err.Error())
}
}
func DeleteTestRegionsByName(t *testing.T) {
opts := client.NewRequestOptions()
for _, region := range testData.Regions {
delResp, _, err := TOSession.DeleteRegion(region.Name, client.RequestOptions{})
if err != nil {
t.Errorf("cannot delete Region '%s': %v - alerts: %+v", region.Name, err, delResp.Alerts)
}
opts.QueryParameters.Set("limit", "1")
deleteLog, _, err := TOSession.GetLogs(opts)
opts.QueryParameters.Del("limit")
if err != nil {
t.Fatalf("unable to get latest audit log entry")
}
if len(deleteLog.Response) != 1 {
t.Fatalf("log entry length - expected: 1, actual: %d", len(deleteLog.Response))
}
if deleteLog.Response[0].Message == nil {
t.Fatal("Traffic Ops returned a representation for a log entry with null or undefined message")
}
if !strings.Contains(*deleteLog.Response[0].Message, region.Name) {
t.Errorf("region deletion audit log entry - expected: message containing region name '%s', actual: %s", region.Name, *deleteLog.Response[0].Message)
}
// Retrieve the Region to see if it got deleted
opts.QueryParameters.Set("name", region.Name)
regionResp, _, err := TOSession.GetRegions(opts)
opts.QueryParameters.Del("name")
if err != nil {
t.Errorf("error deleting Region '%s': %v - alerts: %+v", region.Name, err, regionResp.Alerts)
}
if len(regionResp.Response) > 0 {
t.Errorf("expected Region '%s' to be deleted, but it was found in Traffic Ops", region.Name)
}
}
CreateTestRegions(t)
}
func DeleteTestRegions(t *testing.T) {
opts := client.NewRequestOptions()
for _, region := range testData.Regions {
// Retrieve the Region by name so we can get the id
opts.QueryParameters.Set("name", region.Name)
resp, _, err := TOSession.GetRegions(opts)
opts.QueryParameters.Del("name")
if err != nil {
t.Errorf("cannot get Region '%s' by name: %v - alerts: %+v", region.Name, err, resp.Alerts)
}
if len(resp.Response) != 1 {
t.Errorf("Expected exactly one Region to exist with name '%s', found: %d", region.Name, len(resp.Response))
}
respRegion := resp.Response[0]
opts.QueryParameters.Set("id", strconv.Itoa(respRegion.ID))
delResp, _, err := TOSession.DeleteRegion("", opts)
opts.QueryParameters.Del("id")
if err != nil {
t.Errorf("cannot delete Region: %v - alerts: %+v", err, delResp.Alerts)
}
// Retrieve the Region to see if it got deleted
opts.QueryParameters.Set("name", region.Name)
regionResp, _, err := TOSession.GetRegions(opts)
if err != nil {
t.Errorf("error fetching Region '%s' after deletion: %v - alerts: %+v", region.Name, err, regionResp.Alerts)
}
if len(regionResp.Response) > 0 {
t.Errorf("expected Region '%s' to be deleted, but it was found in Traffic Ops", region.Name)
}
}
}
func DeleteTestRegionsByInvalidId(t *testing.T) {
opts := client.NewRequestOptions()
opts.QueryParameters.Set("id", "10000")
delResp, _, err := TOSession.DeleteRegion("", opts)
if err == nil {
t.Errorf("cannot delete Regions by invalid ID: %v - alerts: %+v", err, delResp.Alerts)
}
}
func DeleteTestRegionsByInvalidName(t *testing.T) {
delResp, _, err := TOSession.DeleteRegion("invalid", client.RequestOptions{})
if err == nil {
t.Errorf("cannot delete Regions by invalid name: %v - alerts: %+v", err, delResp.Alerts)
}
}
func GetTestRegionByInvalidId(t *testing.T) {
opts := client.NewRequestOptions()
opts.QueryParameters.Set("id", "10000")
regionResp, _, err := TOSession.GetRegions(opts)
if err != nil {
t.Errorf("Unexpected error getting Regions filtered by presumably non-existent ID: %v - alerts: %+v", err, regionResp.Alerts)
}
if len(regionResp.Response) >= 1 {
t.Errorf("Didn't expect to find any Regions with presumably non-existent ID, found: %d", len(regionResp.Response))
}
}
func GetTestRegionByInvalidName(t *testing.T) {
opts := client.NewRequestOptions()
opts.QueryParameters.Set("name", "abcd")
regionResp, _, err := TOSession.GetRegions(opts)
if err != nil {
t.Errorf("Unexpected error getting Regions filtered by presumably non-existent name: %v - alerts: %+v", err, regionResp.Alerts)
}
if len(regionResp.Response) >= 1 {
t.Errorf("Didn't expect to find any Regions with presumably non-existent name, found: %d", len(regionResp.Response))
}
}
func GetTestRegionByDivision(t *testing.T) {
opts := client.NewRequestOptions()
for _, region := range testData.Regions {
opts.QueryParameters.Set("name", region.DivisionName)
resp, _, err := TOSession.GetDivisions(opts)
opts.QueryParameters.Del("name")
if err != nil {
t.Errorf("cannot get Division '%s' by name: %v - alerts: %+v", region.DivisionName, err, resp.Alerts)
}
if len(resp.Response) != 1 {
t.Errorf("Expected exactly one Division to exist with name '%s', found: %d", region.DivisionName, len(resp.Response))
continue
}
respDivision := resp.Response[0]
opts.QueryParameters.Set("division", strconv.Itoa(respDivision.ID))
regionsResp, reqInf, err := TOSession.GetRegions(opts)
opts.QueryParameters.Del("division")
if err != nil {
t.Fatalf("Expected no error, but got: %v - alerts: %+v", err, regionsResp.Alerts)
}
if reqInf.StatusCode != http.StatusOK {
t.Fatalf("Expected 200 status code, got %v", reqInf.StatusCode)
}
}
}
func GetTestRegionByInvalidDivision(t *testing.T) {
opts := client.NewRequestOptions()
opts.QueryParameters.Set("division", "100000")
regionResp, _, err := TOSession.GetRegions(opts)
if err != nil {
t.Errorf("Unexpected error getting Regions filtered by presumably non-existent Division ID: %v - alerts: %+v", err, regionResp.Alerts)
}
if len(regionResp.Response) >= 1 {
t.Errorf("Didn't expect to find any Regions in presumably non-existent Division, found: %d", len(regionResp.Response))
}
}
func CreateTestRegionsInvalidDivision(t *testing.T) {
if len(testData.Regions) < 1 {
t.Fatal("Need at least one Region to test creating an invalid Region")
}
firstRegion := testData.Regions[0]
firstRegion.Division = 100
firstRegion.Name = "abcd"
_, _, err := TOSession.CreateRegion(firstRegion, client.RequestOptions{})
if err == nil {
t.Error("Expected an error creating a presumably invalid Region (name: 'abcd', Division ID: 100), but didn't get one")
}
}