...

Source file src/github.com/docker/cli/opts/opts_test.go

Documentation: github.com/docker/cli/opts

     1  package opts
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"testing"
     7  
     8  	"gotest.tools/v3/assert"
     9  	is "gotest.tools/v3/assert/cmp"
    10  )
    11  
    12  func TestValidateIPAddress(t *testing.T) {
    13  	tests := []struct {
    14  		doc         string
    15  		input       string
    16  		expectedOut string
    17  		expectedErr string
    18  	}{
    19  		{
    20  			doc:         "IPv4 loopback",
    21  			input:       `127.0.0.1`,
    22  			expectedOut: `127.0.0.1`,
    23  		},
    24  		{
    25  			doc:         "IPv4 loopback with whitespace",
    26  			input:       ` 127.0.0.1 `,
    27  			expectedOut: `127.0.0.1`,
    28  		},
    29  		{
    30  			doc:         "IPv6 loopback long form",
    31  			input:       `0:0:0:0:0:0:0:1`,
    32  			expectedOut: `::1`,
    33  		},
    34  		{
    35  			doc:         "IPv6 loopback",
    36  			input:       `::1`,
    37  			expectedOut: `::1`,
    38  		},
    39  		{
    40  			doc:         "IPv6 loopback with whitespace",
    41  			input:       ` ::1 `,
    42  			expectedOut: `::1`,
    43  		},
    44  		{
    45  			doc:         "IPv6 lowercase",
    46  			input:       `2001:db8::68`,
    47  			expectedOut: `2001:db8::68`,
    48  		},
    49  		{
    50  			doc:         "IPv6 uppercase",
    51  			input:       `2001:DB8::68`,
    52  			expectedOut: `2001:db8::68`,
    53  		},
    54  		{
    55  			doc:         "IPv6 with brackets",
    56  			input:       `[::1]`,
    57  			expectedErr: `IP address is not correctly formatted: [::1]`,
    58  		},
    59  		{
    60  			doc:         "IPv4 partial",
    61  			input:       `127`,
    62  			expectedErr: `IP address is not correctly formatted: 127`,
    63  		},
    64  		{
    65  			doc:         "random invalid string",
    66  			input:       `random invalid string`,
    67  			expectedErr: `IP address is not correctly formatted: random invalid string`,
    68  		},
    69  	}
    70  
    71  	for _, tc := range tests {
    72  		tc := tc
    73  		t.Run(tc.input, func(t *testing.T) {
    74  			actualOut, actualErr := ValidateIPAddress(tc.input)
    75  			assert.Check(t, is.Equal(tc.expectedOut, actualOut))
    76  			if tc.expectedErr == "" {
    77  				assert.Check(t, actualErr)
    78  			} else {
    79  				assert.Check(t, is.Error(actualErr, tc.expectedErr))
    80  			}
    81  		})
    82  	}
    83  }
    84  
    85  func TestMapOpts(t *testing.T) {
    86  	tmpMap := make(map[string]string)
    87  	o := NewMapOpts(tmpMap, sampleValidator)
    88  	err := o.Set("valid-option=1")
    89  	if err != nil {
    90  		t.Error(err)
    91  	}
    92  	if o.String() != "map[valid-option:1]" {
    93  		t.Errorf("%s != [map[valid-option:1]", o.String())
    94  	}
    95  
    96  	err = o.Set("valid-option2=2")
    97  	if err != nil {
    98  		t.Error(err)
    99  	}
   100  	if len(tmpMap) != 2 {
   101  		t.Errorf("map length %d != 2", len(tmpMap))
   102  	}
   103  
   104  	if tmpMap["valid-option"] != "1" {
   105  		t.Errorf("valid-option = %s != 1", tmpMap["valid-option"])
   106  	}
   107  	if tmpMap["valid-option2"] != "2" {
   108  		t.Errorf("valid-option2 = %s != 2", tmpMap["valid-option2"])
   109  	}
   110  
   111  	if o.Set("dummy-val=3") == nil {
   112  		t.Error("validator is not being called")
   113  	}
   114  }
   115  
   116  func TestListOptsWithoutValidator(t *testing.T) {
   117  	o := NewListOpts(nil)
   118  	err := o.Set("foo")
   119  	if err != nil {
   120  		t.Error(err)
   121  	}
   122  	if o.String() != "[foo]" {
   123  		t.Errorf("%s != [foo]", o.String())
   124  	}
   125  	err = o.Set("bar")
   126  	if err != nil {
   127  		t.Error(err)
   128  	}
   129  	if o.Len() != 2 {
   130  		t.Errorf("%d != 2", o.Len())
   131  	}
   132  	err = o.Set("bar")
   133  	if err != nil {
   134  		t.Error(err)
   135  	}
   136  	if o.Len() != 3 {
   137  		t.Errorf("%d != 3", o.Len())
   138  	}
   139  	if !o.Get("bar") {
   140  		t.Error("o.Get(\"bar\") == false")
   141  	}
   142  	if o.Get("baz") {
   143  		t.Error("o.Get(\"baz\") == true")
   144  	}
   145  	o.Delete("foo")
   146  	if o.String() != "[bar bar]" {
   147  		t.Errorf("%s != [bar bar]", o.String())
   148  	}
   149  	listOpts := o.GetAll()
   150  	if len(listOpts) != 2 || listOpts[0] != "bar" || listOpts[1] != "bar" {
   151  		t.Errorf("Expected [[bar bar]], got [%v]", listOpts)
   152  	}
   153  	mapListOpts := o.GetMap()
   154  	if len(mapListOpts) != 1 {
   155  		t.Errorf("Expected [map[bar:{}]], got [%v]", mapListOpts)
   156  	}
   157  }
   158  
   159  func TestListOptsWithValidator(t *testing.T) {
   160  	o := NewListOpts(sampleValidator)
   161  	err := o.Set("foo")
   162  	if err == nil {
   163  		t.Error(err)
   164  	}
   165  	if o.String() != "" {
   166  		t.Errorf(`%s != ""`, o.String())
   167  	}
   168  	err = o.Set("foo=bar")
   169  	if err == nil {
   170  		t.Error(err)
   171  	}
   172  	if o.String() != "" {
   173  		t.Errorf(`%s != ""`, o.String())
   174  	}
   175  	err = o.Set("valid-option2=2")
   176  	if err != nil {
   177  		t.Error(err)
   178  	}
   179  	if o.Len() != 1 {
   180  		t.Errorf("%d != 1", o.Len())
   181  	}
   182  	if !o.Get("valid-option2=2") {
   183  		t.Error(`o.Get("valid-option2=2") == false`)
   184  	}
   185  	if o.Get("baz") {
   186  		t.Error(`o.Get("baz") == true`)
   187  	}
   188  	o.Delete("valid-option2=2")
   189  	if o.String() != "" {
   190  		t.Errorf(`%s != ""`, o.String())
   191  	}
   192  }
   193  
   194  //nolint:lll
   195  func TestValidateDNSSearch(t *testing.T) {
   196  	valid := []string{
   197  		`.`,
   198  		`a`,
   199  		`a.`,
   200  		`1.foo`,
   201  		`17.foo`,
   202  		`foo.bar`,
   203  		`foo.bar.baz`,
   204  		`foo.bar.`,
   205  		`foo.bar.baz`,
   206  		`foo1.bar2`,
   207  		`foo1.bar2.baz`,
   208  		`1foo.2bar.`,
   209  		`1foo.2bar.baz`,
   210  		`foo-1.bar-2`,
   211  		`foo-1.bar-2.baz`,
   212  		`foo-1.bar-2.`,
   213  		`foo-1.bar-2.baz`,
   214  		`1-foo.2-bar`,
   215  		`1-foo.2-bar.baz`,
   216  		`1-foo.2-bar.`,
   217  		`1-foo.2-bar.baz`,
   218  	}
   219  
   220  	invalid := []string{
   221  		``,
   222  		` `,
   223  		`  `,
   224  		`17`,
   225  		`17.`,
   226  		`.17`,
   227  		`17-.`,
   228  		`17-.foo`,
   229  		`.foo`,
   230  		`foo-.bar`,
   231  		`-foo.bar`,
   232  		`foo.bar-`,
   233  		`foo.bar-.baz`,
   234  		`foo.-bar`,
   235  		`foo.-bar.baz`,
   236  		`foo.bar.baz.this.should.fail.on.long.name.because.it.is.longer.thanisshouldbethis.should.fail.on.long.name.because.it.is.longer.thanisshouldbethis.should.fail.on.long.name.because.it.is.longer.thanisshouldbethis.should.fail.on.long.name.because.it.is.longer.thanisshouldbe`,
   237  	}
   238  
   239  	for _, domain := range valid {
   240  		if ret, err := ValidateDNSSearch(domain); err != nil || ret == "" {
   241  			t.Fatalf("ValidateDNSSearch(`"+domain+"`) got %s %s", ret, err)
   242  		}
   243  	}
   244  
   245  	for _, domain := range invalid {
   246  		if ret, err := ValidateDNSSearch(domain); err == nil || ret != "" {
   247  			t.Fatalf("ValidateDNSSearch(`"+domain+"`) got %s %s", ret, err)
   248  		}
   249  	}
   250  }
   251  
   252  func TestValidateLabel(t *testing.T) {
   253  	tests := []struct {
   254  		name        string
   255  		value       string
   256  		expectedErr string
   257  	}{
   258  		{
   259  			name:        "empty",
   260  			expectedErr: `invalid label '': empty name`,
   261  		},
   262  		{
   263  			name:        "whitespace only ",
   264  			value:       " ",
   265  			expectedErr: `invalid label ' ': empty name`,
   266  		},
   267  		{
   268  			name:        "whitespace around equal-sign",
   269  			value:       " = ",
   270  			expectedErr: `invalid label ' = ': empty name`,
   271  		},
   272  		{
   273  			name:  "leading whitespace",
   274  			value: "    label=value",
   275  		},
   276  		{
   277  			name:        "whitespaces in key without value",
   278  			value:       "this is a label without value",
   279  			expectedErr: `label 'this is a label without value' contains whitespaces`,
   280  		},
   281  		{
   282  			name:        "whitespaces in key",
   283  			value:       "this is a label=value",
   284  			expectedErr: `label 'this is a label' contains whitespaces`,
   285  		},
   286  		{
   287  			name:  "whitespaces in value",
   288  			value: "label=a value that has whitespace",
   289  		},
   290  		{
   291  			name:  "trailing whitespace in value",
   292  			value: "label=value      ",
   293  		},
   294  		{
   295  			name:  "leading whitespace in value",
   296  			value: "label=    value",
   297  		},
   298  		{
   299  			name:  "no value",
   300  			value: "label",
   301  		},
   302  		{
   303  			name:        "no key",
   304  			value:       "=label",
   305  			expectedErr: `invalid label '=label': empty name`,
   306  		},
   307  		{
   308  			name:  "empty value",
   309  			value: "label=",
   310  		},
   311  		{
   312  			name:  "key value",
   313  			value: "key1=value1",
   314  		},
   315  		{
   316  			name:  "double equal-signs",
   317  			value: "key1=value1=value2",
   318  		},
   319  		{
   320  			name:  "multiple equal-signs",
   321  			value: "key1=value1=value2=value",
   322  		},
   323  		{
   324  			name:  "double quotes in key and value",
   325  			value: `key"with"quotes={"hello"}`,
   326  		},
   327  		{
   328  			name:  "double quotes around key and value",
   329  			value: `"quoted-label"="quoted value"`,
   330  		},
   331  		{
   332  			name:  "single quotes in key and value",
   333  			value: `key'with'quotes=hello'with'quotes`,
   334  		},
   335  		{
   336  			name:  "single quotes around key and value",
   337  			value: `'quoted-label'='quoted value''`,
   338  		},
   339  	}
   340  
   341  	for _, tc := range tests {
   342  		tc := tc
   343  		t.Run(tc.name, func(t *testing.T) {
   344  			val, err := ValidateLabel(tc.value)
   345  			if tc.expectedErr != "" {
   346  				assert.Error(t, err, tc.expectedErr)
   347  				return
   348  			}
   349  			assert.NilError(t, err)
   350  			assert.Equal(t, val, tc.value)
   351  		})
   352  	}
   353  }
   354  
   355  func sampleValidator(val string) (string, error) {
   356  	allowedKeys := map[string]string{"valid-option": "1", "valid-option2": "2"}
   357  	k, _, _ := strings.Cut(val, "=")
   358  	if allowedKeys[k] != "" {
   359  		return val, nil
   360  	}
   361  	return "", fmt.Errorf("invalid key %s", k)
   362  }
   363  
   364  func TestNamedListOpts(t *testing.T) {
   365  	var v []string
   366  	o := NewNamedListOptsRef("foo-name", &v, nil)
   367  
   368  	o.Set("foo")
   369  	if o.String() != "[foo]" {
   370  		t.Errorf("%s != [foo]", o.String())
   371  	}
   372  	if o.Name() != "foo-name" {
   373  		t.Errorf("%s != foo-name", o.Name())
   374  	}
   375  	if len(v) != 1 {
   376  		t.Errorf("expected foo to be in the values, got %v", v)
   377  	}
   378  }
   379  
   380  func TestNamedMapOpts(t *testing.T) {
   381  	tmpMap := make(map[string]string)
   382  	o := NewNamedMapOpts("max-name", tmpMap, nil)
   383  
   384  	o.Set("max-size=1")
   385  	if o.String() != "map[max-size:1]" {
   386  		t.Errorf("%s != [map[max-size:1]", o.String())
   387  	}
   388  	if o.Name() != "max-name" {
   389  		t.Errorf("%s != max-name", o.Name())
   390  	}
   391  	if _, exist := tmpMap["max-size"]; !exist {
   392  		t.Errorf("expected map-size to be in the values, got %v", tmpMap)
   393  	}
   394  }
   395  
   396  func TestValidateMACAddress(t *testing.T) {
   397  	if _, err := ValidateMACAddress(`92:d0:c6:0a:29:33`); err != nil {
   398  		t.Fatalf("ValidateMACAddress(`92:d0:c6:0a:29:33`) got %s", err)
   399  	}
   400  
   401  	if _, err := ValidateMACAddress(`92:d0:c6:0a:33`); err == nil {
   402  		t.Fatalf("ValidateMACAddress(`92:d0:c6:0a:33`) succeeded; expected failure on invalid MAC")
   403  	}
   404  
   405  	if _, err := ValidateMACAddress(`random invalid string`); err == nil {
   406  		t.Fatalf("ValidateMACAddress(`random invalid string`) succeeded; expected failure on invalid MAC")
   407  	}
   408  }
   409  
   410  func TestValidateLink(t *testing.T) {
   411  	valid := []string{
   412  		"name",
   413  		"dcdfbe62ecd0:alias",
   414  		"7a67485460b7642516a4ad82ecefe7f57d0c4916f530561b71a50a3f9c4e33da",
   415  		"angry_torvalds:linus",
   416  	}
   417  	invalid := map[string]string{
   418  		"":               "empty string specified for links",
   419  		"too:much:of:it": "bad format for links: too:much:of:it",
   420  	}
   421  
   422  	for _, link := range valid {
   423  		if _, err := ValidateLink(link); err != nil {
   424  			t.Fatalf("ValidateLink(`%q`) should succeed: error %q", link, err)
   425  		}
   426  	}
   427  
   428  	for link, expectedError := range invalid {
   429  		if _, err := ValidateLink(link); err == nil {
   430  			t.Fatalf("ValidateLink(`%q`) should have failed validation", link)
   431  		} else if !strings.Contains(err.Error(), expectedError) {
   432  			t.Fatalf("ValidateLink(`%q`) error should contain %q", link, expectedError)
   433  		}
   434  	}
   435  }
   436  
   437  func TestParseLink(t *testing.T) {
   438  	name, alias, err := ParseLink("name:alias")
   439  	if err != nil {
   440  		t.Fatalf("Expected not to error out on a valid name:alias format but got: %v", err)
   441  	}
   442  	if name != "name" {
   443  		t.Fatalf("Link name should have been name, got %s instead", name)
   444  	}
   445  	if alias != "alias" {
   446  		t.Fatalf("Link alias should have been alias, got %s instead", alias)
   447  	}
   448  	// short format definition
   449  	name, alias, err = ParseLink("name")
   450  	if err != nil {
   451  		t.Fatalf("Expected not to error out on a valid name only format but got: %v", err)
   452  	}
   453  	if name != "name" {
   454  		t.Fatalf("Link name should have been name, got %s instead", name)
   455  	}
   456  	if alias != "name" {
   457  		t.Fatalf("Link alias should have been name, got %s instead", alias)
   458  	}
   459  	// empty string link definition is not allowed
   460  	if _, _, err := ParseLink(""); err == nil || !strings.Contains(err.Error(), "empty string specified for links") {
   461  		t.Fatalf("Expected error 'empty string specified for links' but got: %v", err)
   462  	}
   463  	// more than two colons are not allowed
   464  	if _, _, err := ParseLink("link:alias:wrong"); err == nil || !strings.Contains(err.Error(), "bad format for links: link:alias:wrong") {
   465  		t.Fatalf("Expected error 'bad format for links: link:alias:wrong' but got: %v", err)
   466  	}
   467  }
   468  

View as plain text