github.com/openshift/installer@v1.4.17/pkg/validate/validate_test.go (about)

     1  package validate
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"strings"
     7  	"testing"
     8  
     9  	"github.com/stretchr/testify/assert"
    10  )
    11  
    12  func TestClusterName(t *testing.T) {
    13  	maxSizeName := strings.Repeat("123456789.", 5) + "1234"
    14  
    15  	cases := []struct {
    16  		name        string
    17  		clusterName string
    18  		valid       bool
    19  	}{
    20  		{"empty", "", false},
    21  		{"only whitespace", " ", false},
    22  		{"single lowercase", "a", true},
    23  		{"single uppercase", "A", false},
    24  		{"contains whitespace", "abc D", false},
    25  		{"single number", "1", true},
    26  		{"single dot", ".", false},
    27  		{"ends with dot", "a.", false},
    28  		{"starts with dot", ".a", false},
    29  		{"multiple labels", "a.a", true},
    30  		{"starts with dash", "-a", false},
    31  		{"ends with dash", "a-", false},
    32  		{"label starts with dash", "a.-a", false},
    33  		{"label ends with dash", "a-.a", false},
    34  		{"invalid percent", "a%a", false},
    35  		{"only non-ascii", "日本語", false},
    36  		{"contains non-ascii", "a日本語a", false},
    37  		{"max size", maxSizeName, true},
    38  		{"too long", maxSizeName + "a", false},
    39  	}
    40  	for _, tc := range cases {
    41  		t.Run(tc.name, func(t *testing.T) {
    42  			err := ClusterName(tc.clusterName)
    43  			if tc.valid {
    44  				assert.NoError(t, err)
    45  			} else {
    46  				assert.Error(t, err)
    47  			}
    48  		})
    49  	}
    50  }
    51  
    52  func TestOnPremClusterName(t *testing.T) {
    53  	cases := []struct {
    54  		name        string
    55  		clusterName string
    56  		valid       bool
    57  	}{
    58  		{"single lowercase", "a", true},
    59  		{"has a dot", "a.a", false},
    60  		{"valid name", "abcde", true},
    61  	}
    62  	for _, tc := range cases {
    63  		t.Run(tc.name, func(t *testing.T) {
    64  			err := OnPremClusterName(tc.clusterName)
    65  			if tc.valid {
    66  				assert.NoError(t, err)
    67  			} else {
    68  				assert.Error(t, err)
    69  			}
    70  		})
    71  	}
    72  }
    73  
    74  func TestClusterName1035(t *testing.T) {
    75  	maxSizeName := "a" + strings.Repeat("123456789.", 5) + "123"
    76  
    77  	cases := []struct {
    78  		name        string
    79  		clusterName string
    80  		valid       bool
    81  	}{
    82  		{"empty", "", false},
    83  		{"only whitespace", " ", false},
    84  		{"single lowercase", "a", true},
    85  		{"single uppercase", "A", false},
    86  		{"contains whitespace", "abc D", false},
    87  		{"single number", "1", false},
    88  		{"single dot", ".", false},
    89  		{"ends with dot", "a.", false},
    90  		{"starts with dot", ".a", false},
    91  		{"multiple labels", "a.a", true},
    92  		{"starts with dash", "-a", false},
    93  		{"ends with dash", "a-", false},
    94  		{"label starts with dash", "a.-a", false},
    95  		{"label ends with dash", "a-.a", false},
    96  		{"invalid percent", "a%a", false},
    97  		{"only non-ascii", "日本語", false},
    98  		{"contains non-ascii", "a日本語a", false},
    99  		{"max size", maxSizeName, true},
   100  		{"too long", maxSizeName + "a", false},
   101  		{"URLs", "https://hello.openshift.org", false},
   102  	}
   103  	for _, tc := range cases {
   104  		t.Run(tc.name, func(t *testing.T) {
   105  			err := ClusterName1035(tc.clusterName)
   106  			if tc.valid {
   107  				assert.NoError(t, err)
   108  			} else {
   109  				assert.Error(t, err)
   110  			}
   111  		})
   112  	}
   113  }
   114  
   115  func TestVCenter(t *testing.T) {
   116  	cases := []struct {
   117  		name        string
   118  		clusterName string
   119  		valid       bool
   120  	}{
   121  		{"empty", "", false},
   122  		{"only whitespace", " ", false},
   123  		{"single lowercase", "a", true},
   124  		{"single uppercase", "A", false},
   125  		{"contains whitespace", "abc D", false},
   126  		{"single number", "1", true},
   127  		{"single dot", ".", false},
   128  		{"ends with dot", "a.", false},
   129  		{"starts with dot", ".a", false},
   130  		{"multiple labels", "a.a", true},
   131  		{"starts with dash", "-a", false},
   132  		{"ends with dash", "a-", false},
   133  		{"label starts with dash", "a.-a", false},
   134  		{"label ends with dash", "a-.a", false},
   135  		{"invalid percent", "a%a", false},
   136  		{"only non-ascii", "日本語", false},
   137  		{"contains non-ascii", "a日本語a", false},
   138  		{"URLs", "https://hello.openshift.org", false},
   139  		{"IP", "192.168.1.1", true},
   140  	}
   141  	for _, tc := range cases {
   142  		t.Run(tc.name, func(t *testing.T) {
   143  			err := Host(tc.clusterName)
   144  			if tc.valid {
   145  				assert.NoError(t, err)
   146  			} else {
   147  				assert.Error(t, err)
   148  			}
   149  		})
   150  	}
   151  }
   152  
   153  func TestSubnetCIDR(t *testing.T) {
   154  	cases := []struct {
   155  		cidr   string
   156  		expErr string
   157  	}{
   158  		{"0.0.0.0/32", "address must be specified"},
   159  		{"1.2.3.4/0", "invalid network address. got 1.2.3.4/0, expecting 0.0.0.0/0"},
   160  		{"1.2.3.4/1", "invalid network address. got 1.2.3.4/1, expecting 0.0.0.0/1"},
   161  		{"1.2.3.4/31", ""},
   162  		{"1.2.3.4/32", ""},
   163  		{"0:0:0:0:0:1:102:304/116", "invalid network address. got ::1:102:304/116, expecting ::1:102:0/116"},
   164  		{"0:0:0:0:0:ffff:102:304/116", "invalid network address. got 1.2.3.4/20, expecting 1.2.0.0/20"},
   165  		{"255.255.255.255/1", "invalid network address. got 255.255.255.255/1, expecting 128.0.0.0/1"},
   166  		{"255.255.255.255/32", ""},
   167  	}
   168  	for _, tc := range cases {
   169  		t.Run(tc.cidr, func(t *testing.T) {
   170  			ip, cidr, err := net.ParseCIDR(tc.cidr)
   171  			if err != nil {
   172  				t.Fatalf("could not parse cidr: %v", err)
   173  			}
   174  			err = SubnetCIDR(&net.IPNet{IP: ip, Mask: cidr.Mask})
   175  			if tc.expErr != "" {
   176  				assert.EqualError(t, err, tc.expErr)
   177  			} else {
   178  				assert.NoError(t, err)
   179  			}
   180  		})
   181  	}
   182  }
   183  
   184  func TestDomainName_AcceptingTrailingDot(t *testing.T) {
   185  	cases := []struct {
   186  		domain string
   187  		valid  bool
   188  	}{
   189  		{"", false},
   190  		{" ", false},
   191  		{"a", true},
   192  		{".", false},
   193  		{"日本語", false},
   194  		{"日本語.com", false},
   195  		{"abc.日本語.com", false},
   196  		{"a日本語a.com", false},
   197  		{"abc", true},
   198  		{"ABC", false},
   199  		{"ABC123", false},
   200  		{"ABC123.COM123", false},
   201  		{"1", true},
   202  		{"0.0", true},
   203  		{"1.2.3.4", true},
   204  		{"1.2.3.4.", true},
   205  		{"abc.", true},
   206  		{"abc.com", true},
   207  		{"abc.com.", true},
   208  		{"a.b.c.d.e.f", true},
   209  		{".abc", false},
   210  		{".abc.com", false},
   211  		{".abc.com", false},
   212  	}
   213  	for _, tc := range cases {
   214  		t.Run(tc.domain, func(t *testing.T) {
   215  			err := DomainName(tc.domain, true)
   216  			if tc.valid {
   217  				assert.NoError(t, err)
   218  			} else {
   219  				assert.Error(t, err)
   220  			}
   221  		})
   222  	}
   223  }
   224  
   225  func TestDomainName_RejectingTrailingDot(t *testing.T) {
   226  	cases := []struct {
   227  		domain string
   228  		valid  bool
   229  	}{
   230  		{"", false},
   231  		{" ", false},
   232  		{"a", true},
   233  		{".", false},
   234  		{"日本語", false},
   235  		{"日本語.com", false},
   236  		{"abc.日本語.com", false},
   237  		{"a日本語a.com", false},
   238  		{"abc", true},
   239  		{"ABC", false},
   240  		{"ABC123", false},
   241  		{"ABC123.COM123", false},
   242  		{"1", true},
   243  		{"0.0", true},
   244  		{"1.2.3.4", true},
   245  		{"1.2.3.4.", false},
   246  		{"abc.", false},
   247  		{"abc.com", true},
   248  		{"abc.com.", false},
   249  		{"a.b.c.d.e.f", true},
   250  		{".abc", false},
   251  		{".abc.com", false},
   252  		{".abc.com", false},
   253  	}
   254  	for _, tc := range cases {
   255  		t.Run(tc.domain, func(t *testing.T) {
   256  			err := DomainName(tc.domain, false)
   257  			if tc.valid {
   258  				assert.NoError(t, err)
   259  			} else {
   260  				assert.Error(t, err)
   261  			}
   262  		})
   263  	}
   264  }
   265  
   266  func TestNoProxyDomainName(t *testing.T) {
   267  	cases := []struct {
   268  		domain string
   269  		valid  bool
   270  	}{
   271  		{"", false},
   272  		{" ", false},
   273  		{"a", true},
   274  		{".", false},
   275  		{"日本語", false},
   276  		{"日本語.com", false},
   277  		{"abc.日本語.com", false},
   278  		{"a日本語a.com", false},
   279  		{"abc", true},
   280  		{"ABC", false},
   281  		{"ABC123", false},
   282  		{"ABC123.COM123", false},
   283  		{"1", true},
   284  		{"0.0", true},
   285  		{"1.2.3.4", true},
   286  		{"1.2.3.4.", true},
   287  		{"abc.", true},
   288  		{"abc.com", true},
   289  		{"abc.com.", true},
   290  		{"a.b.c.d.e.f", true},
   291  		{".abc", true},
   292  		{".abc.com", true},
   293  	}
   294  	for _, tc := range cases {
   295  		t.Run(tc.domain, func(t *testing.T) {
   296  			err := NoProxyDomainName(tc.domain)
   297  			if tc.valid {
   298  				assert.NoError(t, err)
   299  			} else {
   300  				assert.Error(t, err)
   301  			}
   302  		})
   303  	}
   304  }
   305  
   306  func TestDoCIDRsOverlap(t *testing.T) {
   307  	cases := []struct {
   308  		a       string
   309  		b       string
   310  		overlap bool
   311  	}{
   312  		{
   313  			a:       "192.168.0.0/30",
   314  			b:       "192.168.0.3/30",
   315  			overlap: true,
   316  		},
   317  		{
   318  			a:       "192.168.0.0/30",
   319  			b:       "192.168.0.4/30",
   320  			overlap: false,
   321  		},
   322  		{
   323  			a:       "192.168.0.0/29",
   324  			b:       "192.168.0.4/30",
   325  			overlap: true,
   326  		},
   327  		{
   328  			a:       "0.0.0.0/0",
   329  			b:       "192.168.0.0/24",
   330  			overlap: true,
   331  		},
   332  	}
   333  	for _, tc := range cases {
   334  		t.Run(fmt.Sprintf("%s %s", tc.a, tc.b), func(t *testing.T) {
   335  			_, a, err := net.ParseCIDR(tc.a)
   336  			if err != nil {
   337  				t.Fatalf("could not parse cidr %q: %v", tc.a, err)
   338  			}
   339  			_, b, err := net.ParseCIDR(tc.b)
   340  			if err != nil {
   341  				t.Fatalf("could not parse cidr %q: %v", tc.b, err)
   342  			}
   343  			actual := DoCIDRsOverlap(a, b)
   344  			assert.Equal(t, tc.overlap, actual)
   345  		})
   346  	}
   347  }
   348  
   349  func TestImagePullSecret(t *testing.T) {
   350  	cases := []struct {
   351  		name   string
   352  		secret string
   353  		valid  bool
   354  	}{
   355  		{
   356  			name:   "single entry with auth",
   357  			secret: `{"auths":{"example.com":{"auth":"authorization value"}}}`,
   358  			valid:  true,
   359  		},
   360  		{
   361  			name:   "single entry with credsStore",
   362  			secret: `{"auths":{"example.com":{"credsStore":"creds store value"}}}`,
   363  			valid:  true,
   364  		},
   365  		{
   366  			name:   "empty",
   367  			secret: `{}`,
   368  			valid:  false,
   369  		},
   370  		{
   371  			name:   "no auths",
   372  			secret: `{"not-auths":{"example.com":{"auth":"authorization value"}}}`,
   373  			valid:  false,
   374  		},
   375  		{
   376  			name:   "no auth or credsStore",
   377  			secret: `{"auths":{"example.com":{"unrequired-field":"value"}}}`,
   378  			valid:  false,
   379  		},
   380  		{
   381  			name:   "additional fields",
   382  			secret: `{"auths":{"example.com":{"auth":"authorization value","other-field":"other field value"}}}`,
   383  			valid:  true,
   384  		},
   385  		{
   386  			name:   "no entries",
   387  			secret: `{"auths":{}}`,
   388  			valid:  false,
   389  		},
   390  		{
   391  			name:   "multiple valid entries",
   392  			secret: `{"auths":{"example.com":{"auth":"authorization value"},"other-example.com":{"auth":"other auth value"}}}`,
   393  			valid:  true,
   394  		},
   395  		{
   396  			name:   "mix of valid and invalid entries",
   397  			secret: `{"auths":{"example.com":{"auth":"authorization value"},"other-example.com":{"unrequired-field":"value"}}}`,
   398  			valid:  false,
   399  		},
   400  	}
   401  	for _, tc := range cases {
   402  		t.Run(tc.name, func(t *testing.T) {
   403  			err := ImagePullSecret(tc.secret)
   404  			if tc.valid {
   405  				assert.NoError(t, err)
   406  			} else {
   407  				assert.Error(t, err)
   408  			}
   409  		})
   410  	}
   411  }
   412  
   413  const invalidFormatCertificate = `-----INVALID FORMAT-----
   414  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
   445  rLWYdt40z0pjcR3FDVzh
   446  -----INVALID FORMAT-----
   447  `
   448  const validCACertificate = `-----BEGIN CERTIFICATE-----
   449  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
   480  rLWYdt40z0pjcR3FDVzh
   481  -----END CERTIFICATE-----
   482  `
   483  
   484  const validCertificate = `-----BEGIN CERTIFICATE-----
   485  MIIF0TCCA7mgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwdzELMAkGA1UEBhMCVVMx
   486  FzAVBgNVBAgMDk5vcnRoIENhcm9saW5hMRQwEgYDVQQKDAtSZWQgSGF0IEluYzEf
   487  MB0GA1UECwwWT3BlblNoaWZ0IEluc3RhbGwgVGVzdDEYMBYGA1UEAwwPSW50ZXJt
   488  ZWRpYXRlIENBMB4XDTE5MDcyMjIwMTMwMloXDTIwMDczMTIwMTMwMlowgY4xCzAJ
   489  BgNVBAYTAlVTMRcwFQYDVQQIDA5Ob3J0aCBDYXJvbGluYTEQMA4GA1UEBwwHUmFs
   490  ZWlnaDEUMBIGA1UECgwLUmVkIEhhdCBJbmMxHzAdBgNVBAsMFk9wZW5TaGlmdCBJ
   491  bnN0YWxsIFRlc3QxHTAbBgNVBAMMFHJlZ2lzdHJ5LmV4YW1wbGUuY29tMIIBIjAN
   492  BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyCT3n3zYL7PkLtnzBU9WUyZBz1Q+
   493  SXUP739DjT+xmRunE1ViD2wfkVIhTHowlw6B7+23tSRQngEu5i4+lglzqouYY5jE
   494  sqWUXaPMa5FeeDstI6LIUxqk9/2yWRBrrdJlVWor57F310aTzkYtkmkCJTDy3k9R
   495  Le8jma8fnchaVpttbHgN/F+CiS+OV8u9PtALGuJ4DfHy2hM4pxhiKMxFpYOaxBuq
   496  41Y0ts8CXyEiVWwZB7+fFrAjog8uJuhAdya1rAWvSDo+GQr2CDY2/PJcAVHO1n9F
   497  h1LkjqIOd4OOqOy9gIYDc5bvZvWGuzeCN8icrdH8KM53witq5yhZHRL4EQIDAQAB
   498  o4IBTTCCAUkwCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBkAwMwYJYIZIAYb4
   499  QgENBCYWJE9wZW5TU0wgR2VuZXJhdGVkIFNlcnZlciBDZXJ0aWZpY2F0ZTAdBgNV
   500  HQ4EFgQUxzJ/lMs83RIoGheipNbag+SZr4wwga8GA1UdIwSBpzCBpIAUPUqJPYDZ
   501  eUXbBlR0xXA/F+DYYaihgYekgYQwgYExCzAJBgNVBAYTAlVTMRcwFQYDVQQIDA5O
   502  b3J0aCBDYXJvbGluYTEQMA4GA1UEBwwHUmFsZWlnaDEUMBIGA1UECgwLUmVkIEhh
   503  dCBJbmMxHzAdBgNVBAsMFk9wZW5TaGlmdCBJbnN0YWxsIFRlc3QxEDAOBgNVBAMM
   504  B1Jvb3QgQ0GCAhAAMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcD
   505  ATANBgkqhkiG9w0BAQsFAAOCAgEALXbZyIAXZPBBe+uWBscUFpmXvfQR9CQRbnYi
   506  X4453dLKZqEsWNVcUQihrfaNyHCrkG7dSgT64JWWJcyXLXe9TfVR9FLGjzt4p0P2
   507  V9T+PFjp3JN+Elh6XDeNisZ7fHzYs2yYnugZELdWkLOcUwkvUHhSQ5aSWYFrngn7
   508  J3mT3GS3WSpLUvVQDn3RBDbS0ossnF1tq9n6Nhs4Xvhdso6gEZU9SeztbnSK9N/k
   509  zWLV5PjgwpevJ17jzpxm7ZIAlcp31i4SIircJtGwgUS3cJZXPPWMdK72qnLQjFMF
   510  BNEc11EBilMK8tn/K4Dn06BBJMRtOCkq0KhopeZX0HmtQE29z6hy9fcTBklCwLXQ
   511  NMSOKemXsOiGTwghosa0xw0H2e9R8z9KTX5xBGgHbHbWu7e/oDVY9+XTnfQ0ZqFi
   512  aBa/U/WWLMQQDNvQQGsllxBHC+pOpDD8YhycPmbpsFfhNo58U9VQ6mqtX3o7j5nP
   513  imNTY4B5RmZUILe+C0XhON6VL5RCa+s6YngIUcfeylTSB8BTeVBxIAInubKzrgZM
   514  4ThJWLbaiTkRqaT/viDfxsmgzJsrDm3ZWzYXwF/a5o6NHK4lqCYf/1nvbjgE5PAm
   515  69R88P32rKeiRJ8AoC4N/5YR++NkB11gsW9ooU2nV90owi6eMhQ6+qGLTrq0mTtv
   516  CNA1OOo=
   517  -----END CERTIFICATE-----
   518  `
   519  const invalidStringWithCertificate = `-----BEGIN CERTIFICATE-----
   520  MIIF0TCCA7mgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwdzELMAkGA1UEBhMCVVMx
   521  FzAVBgNVBAgMDk5vcnRoIENhcm9saW5hMRQwEgYDVQQKDAtSZWQgSGF0IEluYzEf
   522  MB0GA1UECwwWT3BlblNoaWZ0IEluc3RhbGwgVGVzdDEYMBYGA1UEAwwPSW50ZXJt
   523  ZWRpYXRlIENBMB4XDTE5MDcyMjIwMTMwMloXDTIwMDczMTIwMTMwMlowgY4xCzAJ
   524  BgNVBAYTAlVTMRcwFQYDVQQIDA5Ob3J0aCBDYXJvbGluYTEQMA4GA1UEBwwHUmFs
   525  ZWlnaDEUMBIGA1UECgwLUmVkIEhhdCBJbmMxHzAdBgNVBAsMFk9wZW5TaGlmdCBJ
   526  bnN0YWxsIFRlc3QxHTAbBgNVBAMMFHJlZ2lzdHJ5LmV4YW1wbGUuY29tMIIBIjAN
   527  BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyCT3n3zYL7PkLtnzBU9WUyZBz1Q+
   528  SXUP739DjT+xmRunE1ViD2wfkVIhTHowlw6B7+23tSRQngEu5i4+lglzqouYY5jE
   529  sqWUXaPMa5FeeDstI6LIUxqk9/2yWRBrrdJlVWor57F310aTzkYtkmkCJTDy3k9R
   530  Le8jma8fnchaVpttbHgN/F+CiS+OV8u9PtALGuJ4DfHy2hM4pxhiKMxFpYOaxBuq
   531  41Y0ts8CXyEiVWwZB7+fFrAjog8uJuhAdya1rAWvSDo+GQr2CDY2/PJcAVHO1n9F
   532  h1LkjqIOd4OOqOy9gIYDc5bvZvWGuzeCN8icrdH8KM53witq5yhZHRL4EQIDAQAB
   533  o4IBTTCCAUkwCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBkAwMwYJYIZIAYb4
   534  QgENBCYWJE9wZW5TU0wgR2VuZXJhdGVkIFNlcnZlciBDZXJ0aWZpY2F0ZTAdBgNV
   535  HQ4EFgQUxzJ/lMs83RIoGheipNbag+SZr4wwga8GA1UdIwSBpzCBpIAUPUqJPYDZ
   536  eUXbBlR0xXA/F+DYYaihgYekgYQwgYExCzAJBgNVBAYTAlVTMRcwFQYDVQQIDA5O
   537  b3J0aCBDYXJvbGluYTEQMA4GA1UEBwwHUmFsZWlnaDEUMBIGA1UECgwLUmVkIEhh
   538  dCBJbmMxHzAdBgNVBAsMFk9wZW5TaGlmdCBJbnN0YWxsIFRlc3QxEDAOBgNVBAMM
   539  B1Jvb3QgQ0GCAhAAMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcD
   540  ATANBgkqhkiG9w0BAQsFAAOCAgEALXbZyIAXZPBBe+uWBscUFpmXvfQR9CQRbnYi
   541  X4453dLKZqEsWNVcUQihrfaNyHCrkG7dSgT64JWWJcyXLXe9TfVR9FLGjzt4p0P2
   542  V9T+PFjp3JN+Elh6XDeNisZ7fHzYs2yYnugZELdWkLOcUwkvUHhSQ5aSWYFrngn7
   543  J3mT3GS3WSpLUvVQDn3RBDbS0ossnF1tq9n6Nhs4Xvhdso6gEZU9SeztbnSK9N/k
   544  zWLV5PjgwpevJ17jzpxm7ZIAlcp31i4SIircJtGwgUS3cJZXPPWMdK72qnLQjFMF
   545  BNEc11EBilMK8tn/K4Dn06BBJMRtOCkq0KhopeZX0HmtQE29z6hy9fcTBklCwLXQ
   546  NMSOKemXsOiGTwghosa0xw0H2e9R8z9KTX5xBGgHbHbWu7e/oDVY9+XTnfQ0ZqFi
   547  aBa/U/WWLMQQDNvQQGsllxBHC+pOpDD8YhycPmbpsFfhNo58U9VQ6mqtX3o7j5nP
   548  imNTY4B5RmZUILe+C0XhON6VL5RCa+s6YngIUcfeylTSB8BTeVBxIAInubKzrgZM
   549  4ThJWLbaiTkRqaT/viDfxsmgzJsrDm3ZWzYXwF/a5o6NHK4lqCYf/1nvbjgE5PAm
   550  69R88P32rKeiRJ8AoC4N/5YR++NkB11gsW9ooU2nV90owi6eMhQ6+qGLTrq0mTtv
   551  CNA1OOo=
   552  -----END CERTIFICATE-----
   553  Invalid data here
   554  `
   555  
   556  func TestAdditionalTrustBundle(t *testing.T) {
   557  	cases := []struct {
   558  		name        string
   559  		certificate string
   560  		valid       bool
   561  	}{
   562  		{
   563  			name:        "valid ca certificate",
   564  			certificate: validCACertificate,
   565  			valid:       true,
   566  		},
   567  		{
   568  			name:        "valid certificate",
   569  			certificate: validCertificate,
   570  			valid:       true,
   571  		},
   572  		{
   573  			name:        "invalid format",
   574  			certificate: invalidFormatCertificate,
   575  			valid:       false,
   576  		},
   577  		{
   578  			name:        "invalid certificate",
   579  			certificate: invalidFormatCertificate,
   580  			valid:       false,
   581  		},
   582  		{
   583  			name:        "valid certificate with extra invalid string",
   584  			certificate: invalidStringWithCertificate,
   585  			valid:       false,
   586  		},
   587  	}
   588  	for _, tc := range cases {
   589  		t.Run(tc.name, func(t *testing.T) {
   590  			err := CABundle(tc.certificate)
   591  			if tc.valid {
   592  				assert.NoError(t, err)
   593  			} else {
   594  				assert.Error(t, err)
   595  			}
   596  		})
   597  	}
   598  }
   599  
   600  func TestSSHPublicKey(t *testing.T) {
   601  	cases := []struct {
   602  		name  string
   603  		key   string
   604  		valid bool
   605  	}{
   606  		{
   607  			name:  "valid",
   608  			key:   "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom/BWDSUGPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3Pbv7kOdJ/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK/7XAt3FaoJoAsncM1Q9x5+3V0Ww68/eIFmb1zuUFljQJKprrX88XypNDvjYNby6vw/Pb0rwert/EnmZ+AW4OZPnTPI89ZPmVMLuayrD2cE86Z/il8b+gw3r3+1nKatmIkjn2so1d01QraTlMqVSsbxNrRFi9wrf+M7Q==",
   609  			valid: true,
   610  		},
   611  		{
   612  			name:  "valid with email",
   613  			key:   "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom/BWDSUGPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3Pbv7kOdJ/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK/7XAt3FaoJoAsncM1Q9x5+3V0Ww68/eIFmb1zuUFljQJKprrX88XypNDvjYNby6vw/Pb0rwert/EnmZ+AW4OZPnTPI89ZPmVMLuayrD2cE86Z/il8b+gw3r3+1nKatmIkjn2so1d01QraTlMqVSsbxNrRFi9wrf+M7Q== name@example.com",
   614  			valid: true,
   615  		},
   616  		{
   617  			name:  "invalid format",
   618  			key:   "bad-format AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom/BWDSUGPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3Pbv7kOdJ/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK/7XAt3FaoJoAsncM1Q9x5+3V0Ww68/eIFmb1zuUFljQJKprrX88XypNDvjYNby6vw/Pb0rwert/EnmZ+AW4OZPnTPI89ZPmVMLuayrD2cE86Z/il8b+gw3r3+1nKatmIkjn2so1d01QraTlMqVSsbxNrRFi9wrf+M7Q==",
   619  			valid: true,
   620  		},
   621  		{
   622  			name:  "invalid key",
   623  			key:   "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDxL",
   624  			valid: false,
   625  		},
   626  	}
   627  	for _, tc := range cases {
   628  		t.Run(tc.name, func(t *testing.T) {
   629  			err := SSHPublicKey(tc.key)
   630  			if tc.valid {
   631  				assert.NoError(t, err)
   632  			} else {
   633  				assert.Error(t, err)
   634  			}
   635  		})
   636  	}
   637  }
   638  
   639  func TestURI(t *testing.T) {
   640  	cases := []struct {
   641  		name  string
   642  		uri   string
   643  		valid bool
   644  	}{
   645  		{
   646  			name:  "valid",
   647  			uri:   "https://example.com",
   648  			valid: true,
   649  		},
   650  		{
   651  			name:  "missing scheme",
   652  			uri:   "example.com",
   653  			valid: false,
   654  		},
   655  	}
   656  	for _, tc := range cases {
   657  		t.Run(tc.name, func(t *testing.T) {
   658  			err := URI(tc.uri)
   659  			if tc.valid {
   660  				assert.NoError(t, err)
   661  			} else {
   662  				assert.Error(t, err)
   663  			}
   664  		})
   665  	}
   666  }
   667  
   668  func TestMAC(t *testing.T) {
   669  	cases := []struct {
   670  		name     string
   671  		addr     string
   672  		expected string
   673  	}{
   674  		{
   675  			name: "valid_mac",
   676  			addr: "7A:CE:E3:29:35:6F",
   677  		},
   678  		{
   679  			name:     "invalid_multicast",
   680  			addr:     "7D:CE:E3:29:35:6F",
   681  			expected: "expected unicast mac address",
   682  		},
   683  		{
   684  			name:     "invalid_infiniband",
   685  			addr:     "00-00-00-00-fe-80-00-00-00-00-00-00-02-00-5e-10-00-00-00-01",
   686  			expected: "invalid MAC address",
   687  		},
   688  		{
   689  			name:     "invalid_mac",
   690  			addr:     "this is a bad mac",
   691  			expected: "invalid MAC address",
   692  		},
   693  	}
   694  	for _, tc := range cases {
   695  		t.Run(tc.name, func(t *testing.T) {
   696  			err := MAC(tc.addr)
   697  			if tc.expected == "" {
   698  				assert.NoError(t, err)
   699  			} else {
   700  				assert.Regexp(t, tc.expected, err)
   701  			}
   702  		})
   703  	}
   704  }