blob: a4e1220ab896ae39fd8148d22755f1fb05b3a26a [file] [log] [blame]
package origin
/*
* 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.
*/
import (
"errors"
"net/http"
"reflect"
"strings"
"testing"
"time"
"github.com/apache/trafficcontrol/lib/go-tc"
"github.com/apache/trafficcontrol/lib/go-util"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/auth"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/test"
"github.com/jmoiron/sqlx"
"gopkg.in/DATA-DOG/go-sqlmock.v1"
)
func getTestOrigins() []tc.Origin {
origins := []tc.Origin{}
testOrigin := tc.Origin{
Cachegroup: util.StrPtr("Cachegroup"),
CachegroupID: util.IntPtr(1),
Coordinate: util.StrPtr("originCoordinate"),
CoordinateID: util.IntPtr(1),
DeliveryService: util.StrPtr("testDS"),
DeliveryServiceID: util.IntPtr(1),
FQDN: util.StrPtr("origin.cdn.net"),
ID: util.IntPtr(1),
IP6Address: util.StrPtr("dead:beef:cafe::42"),
IPAddress: util.StrPtr("10.2.3.4"),
IsPrimary: util.BoolPtr(false),
LastUpdated: tc.NewTimeNoMod(),
Name: util.StrPtr("originName"),
Port: util.IntPtr(443),
Profile: util.StrPtr("profile"),
ProfileID: util.IntPtr(1),
Protocol: util.StrPtr("https"),
Tenant: util.StrPtr("tenantName"),
TenantID: util.IntPtr(1),
}
origins = append(origins, testOrigin)
testOrigin2 := testOrigin
testOrigin2.FQDN = util.StrPtr("origin2.cdn.com")
testOrigin2.Name = util.StrPtr("origin2")
origins = append(origins, testOrigin2)
testOrigin3 := testOrigin
testOrigin3.FQDN = util.StrPtr("origin3.cdn.org")
testOrigin3.Name = util.StrPtr("origin3")
origins = append(origins, testOrigin3)
return origins
}
func TestReadOrigins(t *testing.T) {
mockDB, mock, err := sqlmock.New()
if err != nil {
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
}
defer mockDB.Close()
db := sqlx.NewDb(mockDB, "sqlmock")
defer db.Close()
testOrigins := getTestOrigins()
cols := test.ColsFromStructByTag("db", tc.Origin{})
originRows := sqlmock.NewRows(cols)
for _, to := range testOrigins {
originRows = originRows.AddRow(
to.Cachegroup,
to.CachegroupID,
to.Coordinate,
to.CoordinateID,
to.DeliveryService,
to.DeliveryServiceID,
to.FQDN,
to.ID,
to.IP6Address,
to.IPAddress,
to.IsPrimary,
to.LastUpdated,
to.Name,
to.Port,
to.Profile,
to.ProfileID,
to.Protocol,
to.Tenant,
to.TenantID,
)
}
tenantRows := sqlmock.NewRows([]string{"id"})
tenantRows.AddRow(1)
mock.ExpectBegin()
mock.ExpectQuery("WITH").WillReturnRows(tenantRows)
mock.ExpectQuery("SELECT").WillReturnRows(originRows)
v := map[string]string{}
testUser := auth.CurrentUser{TenantID: 1}
origins, userErr, sysErr, errCode, _ := getOrigins(nil, v, db.MustBegin(), &testUser, false)
if userErr != nil || sysErr != nil {
t.Errorf("getOrigins expected: no errors, actual: %v %v with status: %s", userErr, sysErr, http.StatusText(errCode))
}
if len(origins) != 3 {
t.Errorf("getOrigins expected: len(origins) == 3, actual: %v", len(origins))
}
}
func TestFuncs(t *testing.T) {
if strings.Index(selectQuery(), "SELECT") != 0 {
t.Errorf("expected selectQuery to start with SELECT")
}
if strings.Index(insertQuery(), "INSERT") != 0 {
t.Errorf("expected insertQuery to start with INSERT")
}
if strings.Index(updateQuery(), "UPDATE") != 0 {
t.Errorf("expected updateQuery to start with UPDATE")
}
if strings.Index(deleteQuery(), "DELETE") != 0 {
t.Errorf("expected deleteQuery to start with DELETE")
}
}
func TestInterfaces(t *testing.T) {
var i interface{}
i = &TOOrigin{}
if _, ok := i.(api.Creator); !ok {
t.Errorf("origin must be creator")
}
if _, ok := i.(api.Reader); !ok {
t.Errorf("origin must be reader")
}
if _, ok := i.(api.Updater); !ok {
t.Errorf("origin must be updater")
}
if _, ok := i.(api.Deleter); !ok {
t.Errorf("origin must be deleter")
}
if _, ok := i.(api.Identifier); !ok {
t.Errorf("origin must be Identifier")
}
if _, ok := i.(api.Tenantable); !ok {
t.Errorf("origin must be tenantable")
}
}
func TestValidate(t *testing.T) {
const portErr = `'port' must be a valid integer between 1 and 65535`
const protoErr = `'protocol' must be http or https`
const fqdnErr = `'fqdn' must be a valid DNS name`
const ipErr = `'ipAddress' must be a valid IPv4 address`
const ip6Err = `'ip6Address' must be a valid IPv6 address`
// verify that non-null fields are invalid
c := TOOrigin{Origin: tc.Origin{ID: nil,
Name: nil,
DeliveryServiceID: nil,
FQDN: nil,
Protocol: nil,
}}
err, _ := c.Validate()
errs := util.JoinErrsStr(test.SortErrors(test.SplitErrors(err)))
expectedErrs := util.JoinErrsStr([]error{
errors.New(`'deliveryServiceId' is required`),
errors.New(`'fqdn' cannot be blank`),
errors.New(`'name' cannot be blank`),
errors.New(`'protocol' cannot be blank`),
})
if !reflect.DeepEqual(expectedErrs, errs) {
t.Errorf("expected %s, got %s", expectedErrs, errs)
}
// all valid fields
id := 1
nm := "validname"
fqdn := "is.a.valid.hostname"
ip6 := "dead:beef::42"
ip := "1.2.3.4"
port := 65535
pro := "http"
lu := tc.TimeNoMod{Time: time.Now()}
c = TOOrigin{Origin: tc.Origin{ID: &id,
Name: &nm,
DeliveryServiceID: &id,
FQDN: &fqdn,
IP6Address: &ip6,
IPAddress: &ip,
Port: &port,
Protocol: &pro,
LastUpdated: &lu,
}}
err, _ = c.Validate()
if err != nil {
t.Errorf("expected nil, got %s", err)
}
type testCase struct {
Int int
Str string
ExpectedErrors []error
}
type typedTestCases struct {
Type string
TestCases []testCase
}
nameTestCases := typedTestCases{
"name",
[]testCase{
{Str: "", ExpectedErrors: []error{errors.New(`'name' cannot be blank`)}},
{Str: "invalid name", ExpectedErrors: []error{errors.New(`'name' cannot contain spaces`)}},
{Str: "valid-name", ExpectedErrors: []error{}},
},
}
portTestCases := typedTestCases{
"port",
[]testCase{
{Int: -1, ExpectedErrors: []error{errors.New(portErr)}},
{Int: 0, ExpectedErrors: []error{errors.New(portErr)}},
{Int: 1, ExpectedErrors: []error{}},
},
}
protoTestCases := typedTestCases{
"protocol",
[]testCase{
{Str: "foo", ExpectedErrors: []error{errors.New(protoErr)}},
{Str: "", ExpectedErrors: []error{errors.New(`'protocol' cannot be blank`)}},
{Str: "http", ExpectedErrors: []error{}},
{Str: "https", ExpectedErrors: []error{}},
},
}
fqdnTestCases := typedTestCases{
"fqdn",
[]testCase{
{Str: "not.@.v@lid.#()stn@me", ExpectedErrors: []error{errors.New(fqdnErr)}},
{Str: "dead:beef::42", ExpectedErrors: []error{errors.New(fqdnErr)}},
{Str: "valid.hostname.net", ExpectedErrors: []error{}},
},
}
ipTestCases := typedTestCases{
"ip",
[]testCase{
{Str: "not.@.v@lid.#()stn@me", ExpectedErrors: []error{errors.New(ipErr)}},
{Str: "dead:beef::42", ExpectedErrors: []error{errors.New(ipErr)}},
{Str: "1.2.3", ExpectedErrors: []error{errors.New(ipErr)}},
{Str: "", ExpectedErrors: []error{errors.New(`'ipAddress' cannot be blank`)}},
{Str: "1.2.3.4", ExpectedErrors: []error{}},
},
}
ip6TestCases := typedTestCases{
"ip6",
[]testCase{
{Str: "not.@.v@lid.#()stn@me", ExpectedErrors: []error{errors.New(ip6Err)}},
{Str: "1.2.3.4", ExpectedErrors: []error{errors.New(ip6Err)}},
{Str: "beef", ExpectedErrors: []error{errors.New(ip6Err)}},
{Str: "", ExpectedErrors: []error{errors.New(`'ip6Address' cannot be blank`)}},
{Str: "dead:beef::42", ExpectedErrors: []error{}},
},
}
for _, ttc := range []typedTestCases{
nameTestCases,
portTestCases,
protoTestCases,
fqdnTestCases,
ipTestCases,
ip6TestCases,
} {
for _, tc := range ttc.TestCases {
var value interface{}
switch ttc.Type {
case "name":
c.Name = &tc.Str
value = tc.Str
case "port":
c.Port = &tc.Int
value = tc.Int
case "protocol":
c.Protocol = &tc.Str
value = tc.Str
case "fqdn":
c.FQDN = &tc.Str
value = tc.Str
case "ip":
c.IPAddress = &tc.Str
value = tc.Str
case "ip6":
c.IP6Address = &tc.Str
value = tc.Str
}
err, _ = c.Validate()
errStr := util.JoinErrsStr(test.SortErrors(test.SplitErrors(err)))
if !reflect.DeepEqual(util.JoinErrsStr(tc.ExpectedErrors), errStr) {
t.Errorf("given: '%v', expected %s, got %s", value, tc.ExpectedErrors, errStr)
}
}
}
}