You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by zr...@apache.org on 2023/03/14 15:01:34 UTC
[trafficcontrol] branch master updated: Add unit tests for deliveryservice folder in Traffic Ops (#7390)
This is an automated email from the ASF dual-hosted git repository.
zrhoffman 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 da0dc68067 Add unit tests for deliveryservice folder in Traffic Ops (#7390)
da0dc68067 is described below
commit da0dc6806798b30fcf2b3843d1054f00fd6bea10
Author: Srijeet Chatterjee <30...@users.noreply.github.com>
AuthorDate: Tue Mar 14 09:01:27 2023 -0600
Add unit tests for deliveryservice folder in Traffic Ops (#7390)
* wip
* wip
* Adding tests for ds folder
* sort imports
* go fmt
* license
---
.../deliveryservice/acme_test.go | 169 ++++++++
.../deliveryservice/gencert_test.go | 31 ++
.../deliveryservice/request/requests_test.go | 465 +++++++++++++++++++++
.../deliveryservice/request/validate_test.go | 278 ++++++++++++
.../deliveryservice/safe_test.go | 70 ++++
.../deliveryservice/servers/delete_test.go | 119 ++++++
.../deliveryservice/servers/servers.go | 2 +-
.../deliveryservice/servers/servers_test.go | 339 +++++++++++++++
8 files changed, 1472 insertions(+), 1 deletion(-)
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/acme_test.go b/traffic_ops/traffic_ops_golang/deliveryservice/acme_test.go
new file mode 100644
index 0000000000..24089cd643
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/acme_test.go
@@ -0,0 +1,169 @@
+package deliveryservice
+
+/*
+ * 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 (
+ "bytes"
+ "crypto/rand"
+ "crypto/rsa"
+ "crypto/sha256"
+ "crypto/x509"
+ "encoding/base64"
+ "encoding/pem"
+ "testing"
+
+ "github.com/apache/trafficcontrol/lib/go-tc"
+ "github.com/apache/trafficcontrol/lib/go-util"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/config"
+ "github.com/go-acme/lego/challenge/dns01"
+
+ "github.com/jmoiron/sqlx"
+ "gopkg.in/DATA-DOG/go-sqlmock.v1"
+)
+
+func TestGetStoredAcmeAccountInfo(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()
+
+ priv, err := rsa.GenerateKey(rand.Reader, 2048)
+ if err != nil {
+ t.Fatalf("expected no error while generating key, but got %v", err)
+ }
+ keyBuf := bytes.Buffer{}
+ keyDer := x509.MarshalPKCS1PrivateKey(priv)
+ err = pem.Encode(&keyBuf, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: keyDer})
+ if err != nil {
+ t.Fatalf("expected no error while encoding key, but got %v", err)
+ }
+
+ mock.ExpectBegin()
+ rows := sqlmock.NewRows([]string{"email", "private_key", "uri"})
+ rows.AddRow("testuser@blah.com", keyBuf.Bytes(), "https://uri.com")
+ mock.ExpectQuery("SELECT email, private_key, uri").WithArgs("testuser@blah.com", "Lets Encrypt").WillReturnRows(rows)
+
+ info, err := getStoredAcmeAccountInfo(db.MustBegin().Tx, "testuser@blah.com", "Lets Encrypt")
+ if err != nil {
+ t.Errorf("expected no error while getting stored acme account into, but got %v", err)
+ }
+ if info == nil {
+ t.Fatalf("expected valid acme account info in response, but got nothing")
+ }
+ if info.Email != "testuser@blah.com" {
+ t.Errorf("expected email to be testuser@blah.com, but got %s", info.Email)
+ }
+ if info.Key != string(keyBuf.Bytes()) {
+ t.Errorf("expected key to be %s, but got %s", string(keyBuf.Bytes()), info.Key)
+ }
+ if info.URI != "https://uri.com" {
+ t.Errorf("expected uri to be https://uri.com, but got %s", info.URI)
+ }
+}
+
+func TestGetAcmeAccountConfig(t *testing.T) {
+ cfgAcmeAccounts := make([]config.ConfigAcmeAccount, 0)
+ cfg := config.Config{
+ AcmeAccounts: cfgAcmeAccounts,
+ ConfigLetsEncrypt: config.ConfigLetsEncrypt{
+ Email: "testuser@apache.org",
+ Environment: "production",
+ },
+ }
+ c := GetAcmeAccountConfig(&cfg, tc.LetsEncryptAuthType)
+ if c == nil {
+ t.Fatalf("expected a valid Acme Account Config in response, but got nothing")
+ }
+ if c.UserEmail != cfg.Email {
+ t.Errorf("expected user email to be %s, but got %s", cfg.Email, c.UserEmail)
+ }
+ if c.AcmeUrl != "https://acme-v02.api.letsencrypt.org/directory" {
+ t.Errorf("expected AcmeProvider to be https://acme-v02.api.letsencrypt.org/directory, but got %s", c.AcmeUrl)
+ }
+ if c.AcmeProvider != tc.LetsEncryptAuthType {
+ t.Errorf("expected AcmeProvider to be Lets Encrypt, but got %s", c.AcmeProvider)
+ }
+}
+
+func TestDNSProviderTrafficRouter_Present(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()
+
+ d := DNSProviderTrafficRouter{
+ db: db,
+ xmlId: util.Ptr("dsXMLID"),
+ }
+ keyAuthShaBytes := sha256.Sum256([]byte("blah"))
+ value := base64.RawURLEncoding.EncodeToString(keyAuthShaBytes[:sha256.Size])
+ mock.ExpectBegin()
+ mock.ExpectExec("INSERT INTO dnschallenges").WithArgs("_acme-challenge.test.", value, *d.xmlId).WillReturnResult(sqlmock.NewResult(1, 1))
+ mock.ExpectCommit()
+ err = d.Present("test", "token", "blah")
+ if err != nil {
+ t.Errorf("expected no error, but got %v", err)
+ }
+}
+
+func TestDNSProviderTrafficRouter_Cleanup(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()
+
+ d := DNSProviderTrafficRouter{
+ db: db,
+ xmlId: util.Ptr("dsXMLID"),
+ }
+ keyAuthShaBytes := sha256.Sum256([]byte("blah"))
+ value := base64.RawURLEncoding.EncodeToString(keyAuthShaBytes[:sha256.Size])
+ mock.ExpectBegin()
+ mock.ExpectExec("DELETE FROM dnschallenges").WithArgs("_acme-challenge.test.", value).WillReturnResult(sqlmock.NewResult(1, 1))
+ mock.ExpectCommit()
+ err = d.CleanUp("test", "token", "blah")
+ if err != nil {
+ t.Errorf("expected no error, but got %v", err)
+ }
+}
+
+func TestGetRecord(t *testing.T) {
+ fqdn, val := dns01.GetRecord("test", "blah")
+ keyAuthShaBytes := sha256.Sum256([]byte("blah"))
+ value := base64.RawURLEncoding.EncodeToString(keyAuthShaBytes[:sha256.Size])
+ if fqdn != "_acme-challenge.test." {
+ t.Errorf("expected fqdn to be _acme-challenge.test., but got %s", fqdn)
+ }
+ if val != value {
+ t.Errorf("expected returned value to be %s, but got %s", value, val)
+ }
+}
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/gencert_test.go b/traffic_ops/traffic_ops_golang/deliveryservice/gencert_test.go
new file mode 100644
index 0000000000..724c9d2e86
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/gencert_test.go
@@ -0,0 +1,31 @@
+package deliveryservice
+
+/*
+ * 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 (
+ "testing"
+)
+
+func TestGenerateCert(t *testing.T) {
+ _, _, _, err := GenerateCert("localhost", "US", "Denver", "CO", "Comcast", "IPCDN")
+ if err != nil {
+ t.Errorf("expected no error, but got %v", err)
+ }
+}
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/request/requests_test.go b/traffic_ops/traffic_ops_golang/deliveryservice/request/requests_test.go
index 7e403fe3b6..388288d008 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/request/requests_test.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/request/requests_test.go
@@ -20,12 +20,477 @@ package request
*/
import (
+ "net/http"
"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/jmoiron/sqlx"
"gopkg.in/DATA-DOG/go-sqlmock.v1"
)
+func TestInsert(t *testing.T) {
+ mockDB, mock, err := sqlmock.New()
+ if err != nil {
+ t.Fatalf("opening mock database: %v", err)
+ }
+ defer mockDB.Close()
+
+ db := sqlx.NewDb(mockDB, "sqlmock")
+ defer db.Close()
+
+ mock.ExpectBegin()
+ inf := api.APIInfo{
+ Params: nil,
+ IntParams: nil,
+ User: &auth.CurrentUser{
+ UserName: "testUser",
+ ID: 1,
+ PrivLevel: 10,
+ TenantID: 1,
+ Role: 1,
+ RoleName: "testRole",
+ Capabilities: nil,
+ UCDN: "",
+ },
+ ReqID: 0,
+ Version: nil,
+ Tx: db.MustBegin(),
+ CancelTx: nil,
+ Vault: nil,
+ Config: nil,
+ }
+ dsr := tc.DeliveryServiceRequestV5{
+ Assignee: util.StrPtr("assignee"),
+ AssigneeID: util.IntPtr(25),
+ Author: "test",
+ AuthorID: util.IntPtr(35),
+ ChangeType: tc.DSRChangeTypeUpdate,
+ CreatedAt: time.Now(),
+ LastEditedBy: "test",
+ LastEditedByID: util.IntPtr(35),
+ LastUpdated: time.Now(),
+ Original: nil,
+ Requested: &tc.DeliveryServiceV5{},
+ Status: tc.RequestStatusDraft,
+ XMLID: "dsXMLID",
+ }
+
+ rows := sqlmock.NewRows([]string{"id", "last_updated", "created_at"})
+ rows.AddRow(1, time.Now(), time.Now())
+ mock.ExpectQuery("INSERT INTO deliveryservice_request*").WillReturnRows(rows)
+
+ rows2 := sqlmock.NewRows([]string{"active", "anonymous_blocking_enabled", "ccr_dns_ttl", "cdn_id", "cdnname", "check_path",
+ "consistent_hash_regex", "deep_caching_type", "display_name", "dns_bypass_cname", "dns_bypass_ip", "dns_bypass_ip6",
+ "dns_bypass_ttl", "dscp", "ecs_enabled", "edge_header_rewrite", "first_header_rewrite", "geolimit_redirect_url",
+ "geo_limit", "geo_limit_countries", "geo_provider", "global_max_mbps", "global_max_tps", "fq_pacing_rate", "http_bypass_fqdn",
+ "id", "info_url", "initial_dispersion", "inner_header_rewrite", "ipv6_routing_enabled", "last_header_rewrite", "last_updated",
+ "logs_enabled", "long_desc", "long_desc_1", "long_desc_2", "max_dns_answers", "max_origin_connections", "max_request_header_bytes",
+ "mid_header_rewrite", "miss_lat", "miss_long", "multi_site_origin", "org_server_fqdn ", "origin_shield", "profileid", "profile_name",
+ "profile_description", "protocol", "qstring_ignore", "query_keys", "range_request_handling", "regex_remap", "regional", "regional_geo_blocking",
+ "remap_text", "required_capabilities", "routing_name", "service_category", "signing_algorithm", "range_slice_block_size", "ssl_key_version", "tenant_id",
+ "name", "tls_versions", "topology", "tr_request_headers", "tr_response_headers", "name", "type_id", "xml_id", "cdn_domain",
+ })
+ ds := tc.DeliveryServiceV5{
+ Active: tc.DeliveryServiceActiveState("PRIMED"),
+ AnonymousBlockingEnabled: false,
+ CCRDNSTTL: util.IntPtr(20),
+ CDNID: 11,
+ CDNName: util.StrPtr("testCDN"),
+ CheckPath: util.StrPtr("blah"),
+ ConsistentHashRegex: nil,
+ DeepCachingType: tc.DeepCachingTypeNever,
+ DisplayName: "ds",
+ DNSBypassCNAME: nil,
+ DNSBypassIP: nil,
+ DNSBypassIP6: nil,
+ DNSBypassTTL: nil,
+ DSCP: 0,
+ EcsEnabled: false,
+ EdgeHeaderRewrite: nil,
+ FirstHeaderRewrite: nil,
+ GeoLimitRedirectURL: nil,
+ GeoLimit: 0,
+ GeoLimitCountries: nil,
+ GeoProvider: 0,
+ GlobalMaxMBPS: nil,
+ GlobalMaxTPS: nil,
+ FQPacingRate: nil,
+ HTTPBypassFQDN: nil,
+ ID: util.IntPtr(1),
+ InfoURL: nil,
+ InitialDispersion: util.IntPtr(1),
+ InnerHeaderRewrite: nil,
+ IPV6RoutingEnabled: util.BoolPtr(true),
+ LastHeaderRewrite: nil,
+ LastUpdated: time.Now(),
+ LogsEnabled: true,
+ LongDesc: "",
+ MaxDNSAnswers: util.IntPtr(5),
+ MaxOriginConnections: util.IntPtr(2),
+ MaxRequestHeaderBytes: util.IntPtr(0),
+ MidHeaderRewrite: nil,
+ MissLat: util.FloatPtr(0.0),
+ MissLong: util.FloatPtr(0.0),
+ MultiSiteOrigin: false,
+ OrgServerFQDN: util.StrPtr("http://1.2.3.4"),
+ OriginShield: nil,
+ ProfileID: util.IntPtr(99),
+ ProfileName: util.StrPtr("profile99"),
+ ProfileDesc: nil,
+ Protocol: util.IntPtr(1),
+ QStringIgnore: nil,
+ RangeRequestHandling: nil,
+ RegexRemap: nil,
+ Regional: false,
+ RegionalGeoBlocking: false,
+ RemapText: nil,
+ RequiredCapabilities: nil,
+ RoutingName: "",
+ ServiceCategory: nil,
+ SigningAlgorithm: nil,
+ RangeSliceBlockSize: nil,
+ SSLKeyVersion: nil,
+ TenantID: 100,
+ Tenant: util.StrPtr("tenant100"),
+ TLSVersions: nil,
+ Topology: nil,
+ TRRequestHeaders: nil,
+ TRResponseHeaders: nil,
+ Type: util.StrPtr("type101"),
+ TypeID: 101,
+ XMLID: "dsXMLID",
+ }
+
+ rows2.AddRow(
+ ds.Active,
+ ds.AnonymousBlockingEnabled,
+ ds.CCRDNSTTL,
+ ds.CDNID,
+ ds.CDNName,
+ ds.CheckPath,
+ ds.ConsistentHashRegex,
+ ds.DeepCachingType,
+ ds.DisplayName,
+ ds.DNSBypassCNAME,
+ ds.DNSBypassIP,
+ ds.DNSBypassIP6,
+ ds.DNSBypassTTL,
+ ds.DSCP,
+ ds.EcsEnabled,
+ ds.EdgeHeaderRewrite,
+ ds.FirstHeaderRewrite,
+ ds.GeoLimitRedirectURL,
+ ds.GeoLimit,
+ nil,
+ ds.GeoProvider,
+ ds.GlobalMaxMBPS,
+ ds.GlobalMaxTPS,
+ ds.FQPacingRate,
+ ds.HTTPBypassFQDN,
+ ds.ID,
+ ds.InfoURL,
+ ds.InitialDispersion,
+ ds.InnerHeaderRewrite,
+ ds.IPV6RoutingEnabled,
+ ds.LastHeaderRewrite,
+ ds.LastUpdated,
+ ds.LogsEnabled,
+ ds.LongDesc,
+ ds.LongDesc,
+ ds.LongDesc,
+ ds.MaxDNSAnswers,
+ ds.MaxOriginConnections,
+ ds.MaxRequestHeaderBytes,
+ ds.MidHeaderRewrite,
+ ds.MissLat,
+ ds.MissLong,
+ ds.MultiSiteOrigin,
+ ds.OrgServerFQDN,
+ ds.OriginShield,
+ ds.ProfileID,
+ ds.ProfileName,
+ ds.ProfileDesc,
+ ds.Protocol,
+ ds.QStringIgnore,
+ nil,
+ ds.RangeRequestHandling,
+ ds.RegexRemap,
+ ds.Regional,
+ ds.RegionalGeoBlocking,
+ ds.RemapText,
+ nil,
+ ds.RoutingName,
+ ds.ServiceCategory,
+ ds.SigningAlgorithm,
+ ds.RangeSliceBlockSize,
+ ds.SSLKeyVersion,
+ ds.TenantID,
+ ds.Tenant,
+ nil,
+ ds.Topology,
+ ds.TRRequestHeaders,
+ ds.TRResponseHeaders,
+ ds.Type,
+ ds.TypeID,
+ ds.XMLID,
+ "cdn_domain_name")
+
+ mock.ExpectQuery("SELECT ds.active*").WillReturnRows(rows2)
+
+ rows3 := sqlmock.NewRows([]string{
+ "ds_name",
+ "type",
+ "pattern",
+ "set_number",
+ })
+ rows3.AddRow(
+ "dsXMLID",
+ "HOST_REGEXP",
+ ".*\\.dsXMLID\\..*",
+ 0)
+ mock.ExpectQuery("SELECT ds.xml_id as ds_name*").WillReturnRows(rows3)
+
+ sc, userErr, sysErr := insert(&dsr, &inf)
+
+ if userErr != nil || sysErr != nil {
+ t.Fatalf("expected no error, but got userErr: %v, sysErr: %v", userErr, sysErr)
+ }
+ if sc != http.StatusOK {
+ t.Fatalf("expected a 200 status code, but got %d", sc)
+ }
+ if dsr.Original == nil {
+ t.Fatalf("expected original to be a valid delivery service, but got nothing")
+ }
+ if dsr.Original.XMLID != "dsXMLID" {
+ t.Fatalf("expected original to have a DS with XMLID 'dsXMLID', but got %s", dsr.Original.XMLID)
+ }
+
+}
+func TestGetOriginals(t *testing.T) {
+ mockDB, mock, err := sqlmock.New()
+ if err != nil {
+ t.Fatalf("opening mock database: %v", err)
+ }
+ defer mockDB.Close()
+
+ db := sqlx.NewDb(mockDB, "sqlmock")
+ defer db.Close()
+
+ mock.ExpectBegin()
+ ID := 66
+ ids := []int{ID}
+ needOriginals := make(map[int][]*tc.DeliveryServiceRequestV5)
+ dsr := tc.DeliveryServiceRequestV5{
+ Assignee: util.StrPtr("assignee"),
+ AssigneeID: util.IntPtr(25),
+ Author: "test",
+ AuthorID: util.IntPtr(35),
+ ChangeType: tc.DSRChangeTypeUpdate,
+ CreatedAt: time.Now(),
+ ID: util.IntPtr(1),
+ LastEditedBy: "test",
+ LastEditedByID: util.IntPtr(35),
+ LastUpdated: time.Now(),
+ Original: nil,
+ Requested: nil,
+ Status: tc.RequestStatusDraft,
+ XMLID: "dsXMLID",
+ }
+ needOriginals[ID] = []*tc.DeliveryServiceRequestV5{&dsr}
+
+ rows := sqlmock.NewRows([]string{"active", "anonymous_blocking_enabled", "ccr_dns_ttl", "cdn_id", "cdnname", "check_path",
+ "consistent_hash_regex", "deep_caching_type", "display_name", "dns_bypass_cname", "dns_bypass_ip", "dns_bypass_ip6",
+ "dns_bypass_ttl", "dscp", "ecs_enabled", "edge_header_rewrite", "first_header_rewrite", "geolimit_redirect_url",
+ "geo_limit", "geo_limit_countries", "geo_provider", "global_max_mbps", "global_max_tps", "fq_pacing_rate", "http_bypass_fqdn",
+ "id", "info_url", "initial_dispersion", "inner_header_rewrite", "ipv6_routing_enabled", "last_header_rewrite", "last_updated",
+ "logs_enabled", "long_desc", "long_desc_1", "long_desc_2", "max_dns_answers", "max_origin_connections", "max_request_header_bytes",
+ "mid_header_rewrite", "miss_lat", "miss_long", "multi_site_origin", "org_server_fqdn ", "origin_shield", "profileid", "profile_name",
+ "profile_description", "protocol", "qstring_ignore", "query_keys", "range_request_handling", "regex_remap", "regional", "regional_geo_blocking",
+ "remap_text", "required_capabilities", "routing_name", "service_category", "signing_algorithm", "range_slice_block_size", "ssl_key_version", "tenant_id",
+ "name", "tls_versions", "topology", "tr_request_headers", "tr_response_headers", "name", "type_id", "xml_id", "cdn_domain",
+ })
+ ds := tc.DeliveryServiceV5{
+ Active: tc.DeliveryServiceActiveState("PRIMED"),
+ AnonymousBlockingEnabled: false,
+ CCRDNSTTL: util.IntPtr(20),
+ CDNID: 11,
+ CDNName: util.StrPtr("testCDN"),
+ CheckPath: util.StrPtr("blah"),
+ ConsistentHashRegex: nil,
+ DeepCachingType: tc.DeepCachingTypeNever,
+ DisplayName: "ds",
+ DNSBypassCNAME: nil,
+ DNSBypassIP: nil,
+ DNSBypassIP6: nil,
+ DNSBypassTTL: nil,
+ DSCP: 0,
+ EcsEnabled: false,
+ EdgeHeaderRewrite: nil,
+ FirstHeaderRewrite: nil,
+ GeoLimitRedirectURL: nil,
+ GeoLimit: 0,
+ GeoLimitCountries: nil,
+ GeoProvider: 0,
+ GlobalMaxMBPS: nil,
+ GlobalMaxTPS: nil,
+ FQPacingRate: nil,
+ HTTPBypassFQDN: nil,
+ ID: util.IntPtr(ID),
+ InfoURL: nil,
+ InitialDispersion: util.IntPtr(1),
+ InnerHeaderRewrite: nil,
+ IPV6RoutingEnabled: util.BoolPtr(true),
+ LastHeaderRewrite: nil,
+ LastUpdated: time.Now(),
+ LogsEnabled: true,
+ LongDesc: "",
+ MaxDNSAnswers: util.IntPtr(5),
+ MaxOriginConnections: util.IntPtr(2),
+ MaxRequestHeaderBytes: util.IntPtr(0),
+ MidHeaderRewrite: nil,
+ MissLat: util.FloatPtr(0.0),
+ MissLong: util.FloatPtr(0.0),
+ MultiSiteOrigin: false,
+ OrgServerFQDN: util.StrPtr("http://1.2.3.4"),
+ OriginShield: nil,
+ ProfileID: util.IntPtr(99),
+ ProfileName: util.StrPtr("profile99"),
+ ProfileDesc: nil,
+ Protocol: util.IntPtr(1),
+ QStringIgnore: nil,
+ RangeRequestHandling: nil,
+ RegexRemap: nil,
+ Regional: false,
+ RegionalGeoBlocking: false,
+ RemapText: nil,
+ RequiredCapabilities: nil,
+ RoutingName: "",
+ ServiceCategory: nil,
+ SigningAlgorithm: nil,
+ RangeSliceBlockSize: nil,
+ SSLKeyVersion: nil,
+ TenantID: 100,
+ Tenant: util.StrPtr("tenant100"),
+ TLSVersions: nil,
+ Topology: nil,
+ TRRequestHeaders: nil,
+ TRResponseHeaders: nil,
+ Type: util.StrPtr("type101"),
+ TypeID: 101,
+ XMLID: "dsXMLID",
+ }
+ rows.AddRow(
+ ds.Active,
+ ds.AnonymousBlockingEnabled,
+ ds.CCRDNSTTL,
+ ds.CDNID,
+ ds.CDNName,
+ ds.CheckPath,
+ ds.ConsistentHashRegex,
+ ds.DeepCachingType,
+ ds.DisplayName,
+ ds.DNSBypassCNAME,
+ ds.DNSBypassIP,
+ ds.DNSBypassIP6,
+ ds.DNSBypassTTL,
+ ds.DSCP,
+ ds.EcsEnabled,
+ ds.EdgeHeaderRewrite,
+ ds.FirstHeaderRewrite,
+ ds.GeoLimitRedirectURL,
+ ds.GeoLimit,
+ nil,
+ ds.GeoProvider,
+ ds.GlobalMaxMBPS,
+ ds.GlobalMaxTPS,
+ ds.FQPacingRate,
+ ds.HTTPBypassFQDN,
+ ds.ID,
+ ds.InfoURL,
+ ds.InitialDispersion,
+ ds.InnerHeaderRewrite,
+ ds.IPV6RoutingEnabled,
+ ds.LastHeaderRewrite,
+ ds.LastUpdated,
+ ds.LogsEnabled,
+ ds.LongDesc,
+ ds.LongDesc,
+ ds.LongDesc,
+ ds.MaxDNSAnswers,
+ ds.MaxOriginConnections,
+ ds.MaxRequestHeaderBytes,
+ ds.MidHeaderRewrite,
+ ds.MissLat,
+ ds.MissLong,
+ ds.MultiSiteOrigin,
+ ds.OrgServerFQDN,
+ ds.OriginShield,
+ ds.ProfileID,
+ ds.ProfileName,
+ ds.ProfileDesc,
+ ds.Protocol,
+ ds.QStringIgnore,
+ nil,
+ ds.RangeRequestHandling,
+ ds.RegexRemap,
+ ds.Regional,
+ ds.RegionalGeoBlocking,
+ ds.RemapText,
+ nil,
+ ds.RoutingName,
+ ds.ServiceCategory,
+ ds.SigningAlgorithm,
+ ds.RangeSliceBlockSize,
+ ds.SSLKeyVersion,
+ ds.TenantID,
+ ds.Tenant,
+ nil,
+ ds.Topology,
+ ds.TRRequestHeaders,
+ ds.TRResponseHeaders,
+ ds.Type,
+ ds.TypeID,
+ ds.XMLID,
+ "cdn_domain_name")
+
+ mock.ExpectQuery("SELECT ds.active*").WillReturnRows(rows)
+
+ rows2 := sqlmock.NewRows([]string{
+ "ds_name",
+ "type",
+ "pattern",
+ "set_number",
+ })
+ rows2.AddRow(
+ "dsXMLID",
+ "HOST_REGEXP",
+ ".*\\.dsXMLID\\..*",
+ 0)
+ mock.ExpectQuery("SELECT ds.xml_id as ds_name*").WillReturnRows(rows2)
+
+ if needOriginals[ID][0].Original != nil {
+ t.Errorf("expected original to be initially empty")
+ }
+ sc, userErr, sysErr := getOriginals(ids, db.MustBegin(), needOriginals)
+ if userErr != nil || sysErr != nil {
+ t.Fatalf("expected no error, but got userErr: %v, sysErr: %v", userErr, sysErr)
+ }
+ if sc != http.StatusOK {
+ t.Fatalf("expected a 200 status code, but got %d", sc)
+ }
+ if needOriginals[ID][0].Original == nil {
+ t.Fatalf("expected original to be a valid delivery service, but got nothing")
+ }
+ if needOriginals[ID][0].Original.XMLID != "dsXMLID" {
+ t.Fatalf("expected original to have a DS with XMLID 'dsXMLID', but got %s", needOriginals[ID][0].Original.XMLID)
+ }
+}
+
func TestGetAssignee(t *testing.T) {
req := assignmentRequest{
AssigneeID: nil,
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/request/validate_test.go b/traffic_ops/traffic_ops_golang/deliveryservice/request/validate_test.go
new file mode 100644
index 0000000000..60012cb70b
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/request/validate_test.go
@@ -0,0 +1,278 @@
+package request
+
+/*
+ * 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 (
+ "testing"
+ "time"
+
+ "github.com/apache/trafficcontrol/lib/go-tc"
+ "github.com/apache/trafficcontrol/lib/go-util"
+ "github.com/jmoiron/sqlx"
+
+ "gopkg.in/DATA-DOG/go-sqlmock.v1"
+)
+
+var ds = tc.DeliveryServiceV5{
+ Active: tc.DeliveryServiceActiveState("PRIMED"),
+ AnonymousBlockingEnabled: false,
+ CCRDNSTTL: util.IntPtr(20),
+ CDNID: 11,
+ CDNName: util.StrPtr("testCDN"),
+ CheckPath: util.StrPtr("blah"),
+ ConsistentHashRegex: nil,
+ DeepCachingType: tc.DeepCachingTypeNever,
+ DisplayName: "ds",
+ DNSBypassCNAME: nil,
+ DNSBypassIP: nil,
+ DNSBypassIP6: nil,
+ DNSBypassTTL: nil,
+ DSCP: 0,
+ EcsEnabled: false,
+ EdgeHeaderRewrite: nil,
+ FirstHeaderRewrite: nil,
+ GeoLimitRedirectURL: nil,
+ GeoLimit: 0,
+ GeoLimitCountries: nil,
+ GeoProvider: 0,
+ GlobalMaxMBPS: nil,
+ GlobalMaxTPS: nil,
+ FQPacingRate: nil,
+ HTTPBypassFQDN: nil,
+ ID: util.IntPtr(1),
+ InfoURL: nil,
+ InitialDispersion: util.IntPtr(1),
+ InnerHeaderRewrite: nil,
+ IPV6RoutingEnabled: util.BoolPtr(true),
+ LastHeaderRewrite: nil,
+ LastUpdated: time.Now(),
+ LogsEnabled: true,
+ LongDesc: "",
+ MaxDNSAnswers: util.IntPtr(5),
+ MaxOriginConnections: util.IntPtr(2),
+ MaxRequestHeaderBytes: util.IntPtr(0),
+ MidHeaderRewrite: nil,
+ MissLat: util.FloatPtr(0.0),
+ MissLong: util.FloatPtr(0.0),
+ MultiSiteOrigin: false,
+ OrgServerFQDN: util.StrPtr("http://1.2.3.4"),
+ OriginShield: nil,
+ ProfileID: util.IntPtr(99),
+ ProfileName: util.StrPtr("profile99"),
+ ProfileDesc: nil,
+ Protocol: util.IntPtr(1),
+ QStringIgnore: nil,
+ RangeRequestHandling: nil,
+ RegexRemap: nil,
+ Regional: false,
+ RegionalGeoBlocking: false,
+ RemapText: nil,
+ RequiredCapabilities: nil,
+ RoutingName: "",
+ ServiceCategory: nil,
+ SigningAlgorithm: nil,
+ RangeSliceBlockSize: nil,
+ SSLKeyVersion: nil,
+ TenantID: 100,
+ Tenant: util.StrPtr("tenant100"),
+ TLSVersions: nil,
+ Topology: nil,
+ TRRequestHeaders: nil,
+ TRResponseHeaders: nil,
+ Type: util.StrPtr("type101"),
+ TypeID: 101,
+ XMLID: "dsXMLID",
+}
+
+var dsr = tc.DeliveryServiceRequestNullable{
+ AssigneeID: nil,
+ Assignee: nil,
+ AuthorID: nil,
+ Author: nil,
+ ChangeType: nil,
+ CreatedAt: nil,
+ ID: nil,
+ LastEditedBy: nil,
+ LastEditedByID: nil,
+ LastUpdated: nil,
+ DeliveryService: nil,
+ Status: util.Ptr(tc.RequestStatusDraft),
+ XMLID: util.StrPtr("dsXMLID"),
+}
+
+func TestValidateV5(t *testing.T) {
+ mockDB, mock, err := sqlmock.New()
+ if err != nil {
+ t.Fatalf("opening mock database: %v", err)
+ }
+ defer mockDB.Close()
+
+ db := sqlx.NewDb(mockDB, "sqlmock")
+ defer db.Close()
+
+ mock.ExpectBegin()
+
+ dsrV5 := dsr.Upgrade().Upgrade().Upgrade()
+ userErr, sysErr := validateV5(dsrV5, db.MustBegin().Tx)
+
+ if sysErr != nil {
+ t.Fatalf("expected no error, but got sysErr: %v", sysErr)
+ }
+ if userErr == nil {
+ t.Fatalf("expected userErr because change type is absent, but got nothing")
+ }
+
+ dsrV5.ChangeType = tc.DSRChangeTypeCreate
+ mock.ExpectBegin()
+ userErr, sysErr = validateV5(dsrV5, db.MustBegin().Tx)
+ if sysErr != nil {
+ t.Fatalf("expected no error, but got sysErr: %v", sysErr)
+ }
+ if userErr == nil {
+ t.Fatalf("expected userErr because requested is absent for changetype 'change', but got nothing")
+ }
+
+ dsrV5.Requested = &ds
+ mock.ExpectBegin()
+ rows := sqlmock.NewRows([]string{
+ "name",
+ "use_in_table",
+ })
+ rows.AddRow("type101", "server")
+ mock.ExpectQuery("SELECT name, use_in_table*").WillReturnRows(rows)
+ userErr, sysErr = validateV5(dsrV5, db.MustBegin().Tx)
+ if sysErr != nil {
+ t.Fatalf("expected no error, but got sysErr: %v", sysErr)
+ }
+ if userErr == nil {
+ t.Fatalf("expected userErr because use_in_table is not deliveryservice, but got nothing")
+ }
+
+ mock.ExpectBegin()
+ rows = sqlmock.NewRows([]string{
+ "name",
+ "use_in_table",
+ })
+ rows.AddRow("type101", "deliveryservice")
+ mock.ExpectQuery("SELECT name, use_in_table*").WillReturnRows(rows)
+ userErr, sysErr = validateV5(dsrV5, db.MustBegin().Tx)
+ if userErr != nil || sysErr != nil {
+ t.Fatalf("no error expected, but got usererr: %v, sysErr: %v", userErr, sysErr)
+ }
+
+ dsrV5.ChangeType = tc.DSRChangeTypeDelete
+ mock.ExpectBegin()
+ rows = sqlmock.NewRows([]string{
+ "name",
+ "use_in_table",
+ })
+ rows.AddRow("type101", "deliveryservice")
+ userErr, sysErr = validateV5(dsrV5, db.MustBegin().Tx)
+ if sysErr != nil {
+ t.Fatalf("expected no error, but got sysErr: %v", sysErr)
+ }
+ if userErr == nil {
+ t.Fatalf("expected userErr because original is not present for changetype 'delete', but got nothing")
+ }
+
+ dsrV5.Requested = nil
+ dsrV5.Original = &ds
+ mock.ExpectBegin()
+ userErr, sysErr = validateV5(dsrV5, db.MustBegin().Tx)
+ if userErr != nil || sysErr != nil {
+ t.Fatalf("no error expected, but got usererr: %v, sysErr: %v", userErr, sysErr)
+ }
+
+ dsrV5.Assignee = util.StrPtr("testUser")
+ mock.ExpectBegin()
+ rows = sqlmock.NewRows([]string{
+ "id",
+ })
+ rows.AddRow(10)
+ mock.ExpectQuery("SELECT id FROM tm_user*").WillReturnRows(rows)
+ userErr, sysErr = validateV5(dsrV5, db.MustBegin().Tx)
+ if userErr != nil || sysErr != nil {
+ t.Fatalf("no error expected, but got usererr: %v, sysErr: %v", userErr, sysErr)
+ }
+}
+
+func TestValidateLegacy(t *testing.T) {
+ mockDB, mock, err := sqlmock.New()
+ if err != nil {
+ t.Fatalf("opening mock database: %v", err)
+ }
+ defer mockDB.Close()
+
+ db := sqlx.NewDb(mockDB, "sqlmock")
+ defer db.Close()
+
+ mock.ExpectBegin()
+
+ dsV30 := ds.Downgrade().DowngradeToV31()
+ dsr.DeliveryService = &dsV30
+ // expect error because ChangeType is absent
+ userErr, sysErr := validateLegacy(dsr, db.MustBegin().Tx)
+ if sysErr != nil {
+ t.Fatalf("expected no error, but got sysErr: %v", sysErr)
+ }
+ if userErr == nil {
+ t.Fatalf("expected userErr because change type is absent, but got nothing")
+ }
+ dsr.ChangeType = util.StrPtr(string(tc.DSRChangeTypeCreate))
+ mock.ExpectBegin()
+ rows := sqlmock.NewRows([]string{
+ "name",
+ "use_in_table",
+ })
+ rows.AddRow("type101", "server")
+ mock.ExpectQuery("SELECT name, use_in_table*").WillReturnRows(rows)
+ userErr, sysErr = validateLegacy(dsr, db.MustBegin().Tx)
+ if sysErr != nil {
+ t.Fatalf("expected no error, but got sysErr: %v", sysErr)
+ }
+ if userErr == nil {
+ t.Fatalf("expected userErr because use_in_table is not deliveryservice, but got nothing")
+ }
+
+ mock.ExpectBegin()
+ rows.AddRow("type101", "deliveryservice")
+ mock.ExpectQuery("SELECT name, use_in_table*").WillReturnRows(rows)
+ userErr, sysErr = validateLegacy(dsr, db.MustBegin().Tx)
+ if userErr != nil || sysErr != nil {
+ t.Fatalf("no error expected, but got usererr: %v, sysErr: %v", userErr, sysErr)
+ }
+
+ dsr.ID = util.IntPtr(1)
+ dsr.Status = util.Ptr(tc.RequestStatusSubmitted)
+ mock.ExpectBegin()
+
+ rows2 := sqlmock.NewRows([]string{
+ "status",
+ })
+ rows2.AddRow([]byte("submitted"))
+ mock.ExpectQuery("SELECT status*").WillReturnRows(rows2)
+
+ rows.AddRow("type101", "deliveryservice")
+ mock.ExpectQuery("SELECT name, use_in_table*").WillReturnRows(rows)
+ userErr, sysErr = validateLegacy(dsr, db.MustBegin().Tx)
+ if userErr != nil || sysErr != nil {
+ t.Fatalf("no error expected, but got usererr: %v, sysErr: %v", userErr, sysErr)
+ }
+}
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/safe_test.go b/traffic_ops/traffic_ops_golang/deliveryservice/safe_test.go
new file mode 100644
index 0000000000..9cdea41527
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/safe_test.go
@@ -0,0 +1,70 @@
+package deliveryservice
+
+/*
+ * 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 (
+ "testing"
+
+ "github.com/apache/trafficcontrol/lib/go-tc"
+ "github.com/apache/trafficcontrol/lib/go-util"
+ "github.com/jmoiron/sqlx"
+
+ "gopkg.in/DATA-DOG/go-sqlmock.v1"
+)
+
+func TestUpdateDSSafe(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()
+
+ // test with a DS that exists
+ mock.ExpectBegin()
+ dsID := 1
+ dsr := tc.DeliveryServiceSafeUpdateRequest{
+ DisplayName: util.Ptr("displayName"),
+ InfoURL: util.Ptr("http://blah.com"),
+ LongDesc: util.Ptr("longdesc"),
+ LongDesc1: util.Ptr("longdesc1"),
+ }
+ mock.ExpectExec("UPDATE deliveryservice").WithArgs(*dsr.DisplayName, *dsr.InfoURL, *dsr.LongDesc, *dsr.LongDesc1, dsID).WillReturnResult(sqlmock.NewResult(int64(dsID), 1))
+ exists, err := updateDSSafe(db.MustBegin().Tx, dsID, dsr, false)
+ if err != nil {
+ t.Errorf("expected no error, but got: %v", err)
+ }
+ if !exists {
+ t.Errorf("expected DS with id 1 to exist")
+ }
+
+ // test with a DS that doesn't exist
+ mock.ExpectBegin()
+ mock.ExpectExec("UPDATE deliveryservice").WithArgs(*dsr.DisplayName, *dsr.InfoURL, *dsr.LongDesc, *dsr.LongDesc1, 2).WillReturnResult(sqlmock.NewResult(2, 0))
+ exists, err = updateDSSafe(db.MustBegin().Tx, 2, dsr, false)
+ if err != nil {
+ t.Errorf("expected no error, but got: %v", err)
+ }
+ if exists {
+ t.Errorf("expected DS with id 2 to not exist")
+ }
+}
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/servers/delete_test.go b/traffic_ops/traffic_ops_golang/deliveryservice/servers/delete_test.go
new file mode 100644
index 0000000000..5f08e11c98
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/servers/delete_test.go
@@ -0,0 +1,119 @@
+package servers
+
+/*
+ * 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 (
+ "net/http"
+ "testing"
+
+ "github.com/jmoiron/sqlx"
+
+ "gopkg.in/DATA-DOG/go-sqlmock.v1"
+)
+
+func TestCheckLastAvailableEdgeOrOrigin(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()
+
+ // check DS with no topology or mso
+ mock.ExpectBegin()
+ rows := sqlmock.NewRows([]string{"available", "available"})
+ rows.AddRow(true, true)
+ mock.ExpectQuery("SELECT").WithArgs(1, 2).WillReturnRows(rows)
+ sc, userErr, sysErr := checkLastAvailableEdgeOrOrigin(1, 2, false, false, db.MustBegin().Tx)
+ if sysErr != nil {
+ t.Errorf("expected no system error, but got %v", sysErr)
+ }
+ if userErr == nil {
+ t.Errorf("expected error because removing the given server would result in active DS with no REPORTED/ ONLINE EDGE servers, but got nothing")
+ }
+ if sc != http.StatusConflict {
+ t.Errorf("expected 409 status code, but got %d", sc)
+ }
+
+ // check DS with topology, but no MSO
+ mock.ExpectBegin()
+ rows = sqlmock.NewRows([]string{"available", "available"})
+ rows.AddRow(true, true)
+ mock.ExpectQuery("SELECT").WithArgs(1, 2).WillReturnRows(rows)
+ sc, userErr, sysErr = checkLastAvailableEdgeOrOrigin(1, 2, false, true, db.MustBegin().Tx)
+ if userErr != nil || sysErr != nil {
+ t.Errorf("expected no error, but got userErr: %v, sysErr: %v", userErr, sysErr)
+ }
+ if sc != http.StatusOK {
+ t.Errorf("ecpected status code 200, but got %d", sc)
+ }
+
+ // check DS with MSO, but no topology
+ mock.ExpectBegin()
+ rows = sqlmock.NewRows([]string{"available", "available"})
+ rows.AddRow(false, true)
+ mock.ExpectQuery("SELECT").WithArgs(1, 2).WillReturnRows(rows)
+ sc, userErr, sysErr = checkLastAvailableEdgeOrOrigin(1, 2, true, false, db.MustBegin().Tx)
+ if sysErr != nil {
+ t.Errorf("expected no system error, but got %v", sysErr)
+ }
+ if userErr == nil {
+ t.Errorf("expected error because removing the given server would result in active DS with no REPORTED/ ONLINE EDGE servers, but got nothing")
+ }
+ if sc != http.StatusConflict {
+ t.Errorf("expected 409 status code, but got %d", sc)
+ }
+}
+
+func TestDeleteDSServer(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()
+
+ mock.ExpectBegin()
+ rows := sqlmock.NewRows([]string{"server"})
+ mock.ExpectQuery("DELETE").WithArgs(1, 2).WillReturnRows(rows)
+ exists, err := deleteDSServer(db.MustBegin().Tx, 1, 2)
+ if err != nil {
+ t.Errorf("expected no error, but got %v", err)
+ }
+ if exists {
+ t.Errorf("expected exists to be false, but got true")
+ }
+
+ rows = sqlmock.NewRows([]string{"server"})
+ rows.AddRow(2)
+ mock.ExpectBegin()
+ mock.ExpectQuery("DELETE").WithArgs(1, 2).WillReturnRows(rows)
+ exists, err = deleteDSServer(db.MustBegin().Tx, 1, 2)
+ if err != nil {
+ t.Errorf("expected no error, but got %v", err)
+ }
+ if !exists {
+ t.Errorf("expected exists to be true, but got false")
+ }
+}
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers.go b/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers.go
index e5b4463c7b..0dc7c0bd13 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers.go
@@ -78,7 +78,7 @@ func (dss TODeliveryServiceServer) GetKeys() (map[string]interface{}, bool) {
}
func (dss *TODeliveryServiceServer) GetAuditName() string {
- if dss.DeliveryService != nil {
+ if dss.DeliveryService != nil && dss.Server != nil {
return strconv.Itoa(*dss.DeliveryService) + "-" + strconv.Itoa(*dss.Server)
}
return "unknown"
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers_test.go b/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers_test.go
index eed56f1613..d3e077cd59 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers_test.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers_test.go
@@ -21,19 +21,358 @@ package servers
import (
"fmt"
+ "net/http"
"strconv"
"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/jmoiron/sqlx"
+ "github.com/lib/pq"
"gopkg.in/DATA-DOG/go-sqlmock.v1"
)
+func getTestDeliveryServiceServer() TODeliveryServiceServer {
+ return TODeliveryServiceServer{
+ APIInfoImpl: api.APIInfoImpl{},
+ DeliveryServiceServer: tc.DeliveryServiceServer{
+ Server: nil,
+ DeliveryService: nil,
+ LastUpdated: nil,
+ },
+ TenantIDs: nil,
+ DeliveryServiceIDs: nil,
+ ServerIDs: nil,
+ CDN: "",
+ }
+}
+
func TestValidateDSSAssignments(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()
+ mock.ExpectBegin()
+
+ serverInfo := make([]tc.ServerInfo, 0)
+ s := tc.ServerInfo{
+ Cachegroup: "cg1",
+ CachegroupID: 20,
+ CDNID: 10,
+ DomainName: "test",
+ HostName: "blah",
+ ID: 100,
+ Status: "ONLINE",
+ Type: "EDGE",
+ }
+ serverInfo = append(serverInfo, s)
+ s2 := s
+ s2.ID = 200
+ s2.HostName = "blah2"
+ serverInfo = append(serverInfo, s2)
+
+ dsInfo := DSInfo{Active: true,
+ ID: 1,
+ Name: "ds1",
+ Type: tc.DSTypeDNS,
+ EdgeHeaderRewrite: nil,
+ MidHeaderRewrite: nil,
+ RegexRemap: nil,
+ SigningAlgorithm: nil,
+ CacheURL: nil,
+ MaxOriginConnections: nil,
+ Topology: util.Ptr("topology1"),
+ CDNID: util.Ptr(10),
+ UseMultiSiteOrigin: false}
+
+ // Try to assign non-ORG servers to a topology based DS (with required capabilities)
+ userErr, sysErr, sc := validateDSSAssignments(db.MustBegin().Tx, dsInfo, serverInfo, false)
+
+ if sysErr != nil {
+ t.Errorf("expected no system error, but got sysErr: %v", sysErr)
+ }
+ if userErr == nil {
+ t.Errorf("expected error while trying to assign EDGE server to a topology, but got nothing")
+ }
+ if sc != http.StatusBadRequest {
+ t.Errorf("expected status code to be 400, but got %d instead", sc)
+ }
+
+ // Try to assign ORG servers without required capabilities to a topology based DS (with required capabilities)
+ for i, _ := range serverInfo {
+ serverInfo[i].Type = "ORG"
+ }
+ mock.ExpectBegin()
+
+ rows := sqlmock.NewRows([]string{"array_agg", "array_agg"})
+ rows.AddRow([]byte("{20,21}"), []byte("{cg1,cg2}"))
+ mock.ExpectQuery("SELECT ARRAY(c.id)*").WithArgs("topology1").WillReturnRows(rows)
+
+ rows = sqlmock.NewRows([]string{"required_capabilities"})
+ rows.AddRow("{reqCap1}")
+ mock.ExpectQuery("SELECT required_capabilities*").WithArgs(1).WillReturnRows(rows)
+
+ rows = sqlmock.NewRows([]string{"host_name", "capabilities"})
+ rows.AddRow("blah", "{reqCap2, reqCap3}")
+ mock.ExpectQuery("SELECT s.host_name*").WithArgs(pq.StringArray{}).WillReturnRows(rows)
+ userErr, sysErr, sc = validateDSSAssignments(db.MustBegin().Tx, dsInfo, serverInfo, false)
+
+ if sysErr != nil {
+ t.Errorf("expected no system error, but got sysErr: %v", sysErr)
+ }
+ if userErr == nil {
+ t.Errorf("expected error while trying to assign server without a required capability, but got nothing")
+ }
+ if sc != http.StatusBadRequest {
+ t.Errorf("expected status code to be 400, but got %d instead", sc)
+ }
+
+ // Try to assign ORG servers with required capabilities to a topology based DS (with required capabilities)
+ mock.ExpectBegin()
+ rows = sqlmock.NewRows([]string{"array_agg", "array_agg"})
+ rows.AddRow([]byte("{20,21}"), []byte("{cg1,cg2}"))
+ mock.ExpectQuery("SELECT ARRAY(c.id)*").WithArgs("topology1").WillReturnRows(rows)
+
+ rows = sqlmock.NewRows([]string{"required_capabilities"})
+ rows.AddRow("{reqCap1}")
+ mock.ExpectQuery("SELECT required_capabilities*").WithArgs(1).WillReturnRows(rows)
+
+ rows = sqlmock.NewRows([]string{"host_name", "capabilities"})
+ rows.AddRow("blah", "{reqCap1, reqCap2, reqCap3}")
+ mock.ExpectQuery("SELECT s.host_name*").WithArgs(pq.StringArray{}).WillReturnRows(rows)
+ userErr, sysErr, sc = validateDSSAssignments(db.MustBegin().Tx, dsInfo, serverInfo, false)
+
+ if userErr != nil || sysErr != nil {
+ t.Errorf("expected no errors, but got userErr: %v, sysErr: %v", userErr, sysErr)
+ }
+ if sc != http.StatusOK {
+ t.Errorf("expected status code to be 200, but got %d instead", sc)
+ }
+
+ // Try to assign EDGE servers without required capabilities to a DS (with required capabilities)
+ dsInfo.Topology = nil
+ for i, _ := range serverInfo {
+ serverInfo[i].Type = "EDGE"
+ }
+
+ mock.ExpectBegin()
+ rows = sqlmock.NewRows([]string{"required_capabilities"})
+ rows.AddRow("{reqCap1}")
+ mock.ExpectQuery("SELECT required_capabilities*").WithArgs(1).WillReturnRows(rows)
+
+ rows = sqlmock.NewRows([]string{"host_name", "capabilities"})
+ rows.AddRow("blah", "{reqCap2, reqCap3}")
+ mock.ExpectQuery("SELECT s.host_name*").WithArgs(pq.StringArray{"blah", "blah2"}).WillReturnRows(rows)
+ userErr, sysErr, sc = validateDSSAssignments(db.MustBegin().Tx, dsInfo, serverInfo, false)
+
+ if sysErr != nil {
+ t.Errorf("expected no system error, but got sysErr: %v", sysErr)
+ }
+ if userErr == nil {
+ t.Errorf("expected error while trying to assign server without a required capability, but got nothing")
+ }
+ if sc != http.StatusBadRequest {
+ t.Errorf("expected status code to be 400, but got %d instead", sc)
+ }
+
+ // Try to assign EDGE servers with required capabilities to a DS (with required capabilities)
+ mock.ExpectBegin()
+ rows = sqlmock.NewRows([]string{"required_capabilities"})
+ rows.AddRow("{reqCap1}")
+ mock.ExpectQuery("SELECT required_capabilities*").WithArgs(1).WillReturnRows(rows)
+
+ rows = sqlmock.NewRows([]string{"host_name", "capabilities"})
+ rows.AddRow("blah", "{reqCap1, reqCap2, reqCap3}")
+ mock.ExpectQuery("SELECT s.host_name*").WithArgs(pq.StringArray{"blah", "blah2"}).WillReturnRows(rows)
+ userErr, sysErr, sc = validateDSSAssignments(db.MustBegin().Tx, dsInfo, serverInfo, false)
+
+ if userErr != nil || sysErr != nil {
+ t.Errorf("expected no errors, but got userErr: %v, sysErr: %v", userErr, sysErr)
+ }
+ if sc != http.StatusOK {
+ t.Errorf("expected status code to be 200, but got %d instead", sc)
+ }
+}
+
+func TestHasAvailableEdgesCurrentlyAssigned(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()
+ mock.ExpectBegin()
+
+ rows := sqlmock.NewRows([]string{"name"})
+ rows.AddRow("edge1")
+
+ mock.ExpectQuery("SELECT t.name AS name*").WithArgs(1).WillReturnRows(rows)
+ assigned, err := hasAvailableEdgesCurrentlyAssigned(db.MustBegin().Tx, 1)
+ if err != nil {
+ t.Fatalf("expected no error, but got %v", err)
+ }
+ if !assigned {
+ t.Errorf("expected 'hasAvailableEdgesCurrentlyAssigned' to return true, but got false")
+ }
+}
+
+func TestReadDSS(t *testing.T) {
+ //func (dss *TODeliveryServiceServer) readDSS(h http.Header, tx *sqlx.Tx, user *auth.CurrentUser, params map[string]string, intParams map[string]int, dsIDs []int64, serverIDs []int64, useIMS bool) (*tc.DeliveryServiceServerResponse, error, *time.Time)
+ dss := getTestDeliveryServiceServer()
+
+ 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()
+
+ rows := sqlmock.NewRows([]string{"id"})
+ rows.AddRow(10)
+ rows.AddRow(20)
+
+ mock.ExpectBegin()
+ mock.ExpectQuery("WITH RECURSIVE*").WithArgs(10).WillReturnRows(rows)
+
+ rows = sqlmock.NewRows([]string{"server", "deliveryservice", "last_updated"})
+ rows.AddRow(1, 2, time.Now())
+
+ mock.ExpectQuery("SELECT*").WithArgs(pq.Int64Array{10, 20}).WillReturnRows(rows)
+ response, err, _ := dss.readDSS(nil, db.MustBegin(), &auth.CurrentUser{PrivLevel: 30, TenantID: 10}, nil, nil, nil, nil, false)
+ if err != nil {
+ t.Fatalf("expected no error, but got: %v", err)
+ }
+ if response == nil {
+ t.Fatalf("expected a valid response, but got nothing")
+ }
+ if len(response.Response) != 1 {
+ t.Fatalf("expected response to have 1 deliveryserviceServer, but got %d", len(response.Response))
+ }
+ if response.Response[0].Server == nil || response.Response[0].DeliveryService == nil {
+ t.Fatalf("expected valid values for server and deliveryservice, but got nil instead. server: %v, deliveryservice: %v", response.Response[0].Server, response.Response[0].DeliveryService)
+ }
+ if *response.Response[0].Server != 1 || *response.Response[0].DeliveryService != 2 {
+ t.Errorf("expected server to be 1 and deliveryservice to be 2, but got server: %d, deliveryservice: %d instead", *response.Response[0].Server, *response.Response[0].DeliveryService)
+ }
+}
+
+func TestValidate(t *testing.T) {
+ dss := getTestDeliveryServiceServer()
+ err := dss.Validate(nil)
+ if err == nil {
+ t.Errorf("expected error about deliveryservice and server not being present, but got nothing")
+ }
+ dss.Server = util.Ptr(1)
+ dss.DeliveryService = util.Ptr(2)
+ err = dss.Validate(nil)
+ if err != nil {
+ t.Errorf("expected no error, but got %v", err)
+ }
+}
+
+func TestSetKeys(t *testing.T) {
+ dss := getTestDeliveryServiceServer()
+ keys := make(map[string]interface{})
+ keys["server"] = 1
+ keys["deliveryservice"] = 2
+ dss.SetKeys(keys)
+ if dss.DeliveryService == nil || dss.Server == nil {
+ t.Fatalf("expected both server and deliveryservice to be not nil")
+ }
+ if *dss.DeliveryService != 2 {
+ t.Errorf("expected deliveryservice key to be 2, but got %d", *dss.DeliveryService)
+ }
+ if *dss.Server != 1 {
+ t.Errorf("expected server key to be 1, but got %d", *dss.Server)
+ }
+}
+
+func TestGetAuditName(t *testing.T) {
+ dss := getTestDeliveryServiceServer()
+
+ auditName := dss.GetAuditName()
+ if auditName != "unknown" {
+ t.Errorf("expected audit name to be 'unknown', but got %s", auditName)
+ }
+
+ dss.DeliveryServiceServer.Server = util.Ptr(1)
+ dss.DeliveryServiceServer.DeliveryService = util.Ptr(2)
+ auditName = dss.GetAuditName()
+ if auditName != "2-1" {
+ t.Errorf("expected audit name to be '2-1', but got %s", auditName)
+ }
+}
+
+func TestGetKeys(t *testing.T) {
+ dss := getTestDeliveryServiceServer()
+ dss.Server = util.Ptr(1)
+ dss.DeliveryService = util.Ptr(2)
+ keys, exists := dss.GetKeys()
+ if keys == nil {
+ t.Fatalf("expected function to return a valid map of keys, but got nothing")
+ }
+ if !exists {
+ t.Fatalf("expected function to return a true boolean for exists, got false")
+ }
+ if serverID, ok := keys["server"]; !ok {
+ t.Fatalf("expected returned keys to have 'server' key, but key wasn't present")
+ } else if serverID.(int) != 1 {
+ t.Errorf("expected serverID to be 1, but got %d", serverID.(int))
+ }
+
+ if dsID, ok := keys["deliveryservice"]; !ok {
+ t.Fatalf("expected returned keys to have 'deliveryservice' key, but key wasn't present")
+ } else if dsID.(int) != 2 {
+ t.Errorf("expected dsID to be 2, but got %d", dsID.(int))
+ }
+
+ // check with nil values for server and deliveryservice
+ dss.DeliveryServiceServer.Server = nil
+ dss.DeliveryServiceServer.DeliveryService = nil
+ keys, exists = dss.GetKeys()
+ if keys == nil {
+ t.Fatalf("expected function to return a valid map of keys, but got nothing")
+ }
+ if exists {
+ t.Fatalf("expected function to return a false boolean for exists, got true")
+ }
+ if dsID, ok := keys["deliveryservice"]; !ok {
+ t.Fatalf("expected returned keys to have 'deliveryservice' key, but key wasn't present")
+ } else if dsID.(int) != 0 {
+ t.Errorf("expected dsID to be 0, but got %d", dsID.(int))
+ }
+ if _, ok := keys["server"]; ok {
+ t.Errorf("'server' key was not expected to be present")
+ }
+ dss.DeliveryServiceServer.DeliveryService = util.Ptr(2)
+ keys, exists = dss.GetKeys()
+ if keys == nil {
+ t.Fatalf("expected function to return a valid map of keys, but got nothing")
+ }
+ if exists {
+ t.Fatalf("expected function to return a false boolean for exists, got true")
+ }
+ if serverID, ok := keys["server"]; !ok {
+ t.Fatalf("expected returned keys to have 'server' key, but key wasn't present")
+ } else if serverID.(int) != 0 {
+ t.Errorf("expected serverID to be 0, but got %d", serverID.(int))
+ }
+}
+
+func TestValidateDSS(t *testing.T) {
expected := `server and delivery service CDNs do not match`
cdnID := 1
ds := DSInfo{