github.com/mutagen-io/mutagen@v0.18.0-rc1/pkg/url/parse_test.go (about)

     1  package url
     2  
     3  import (
     4  	"runtime"
     5  	"testing"
     6  
     7  	"github.com/mutagen-io/mutagen/pkg/filesystem"
     8  )
     9  
    10  type parseTestCase struct {
    11  	raw      string
    12  	kind     Kind
    13  	first    bool
    14  	fail     bool
    15  	expected *URL
    16  }
    17  
    18  func (c *parseTestCase) run(t *testing.T) {
    19  	// Mark this as a helper function to remove it from error traces.
    20  	t.Helper()
    21  
    22  	// Attempt to parse.
    23  	url, err := Parse(c.raw, c.kind, c.first)
    24  	if err != nil {
    25  		if !c.fail {
    26  			t.Fatal("parsing failed when it should have succeeded:", err)
    27  		}
    28  		return
    29  	} else if c.fail {
    30  		t.Fatal("parsing should have failed but did not")
    31  	}
    32  
    33  	// Verify kind.
    34  	if url.Kind != c.expected.Kind {
    35  		t.Error("kind mismatch:", url.Kind, "!=", c.expected.Kind)
    36  	}
    37  
    38  	// Verify protocol.
    39  	if url.Protocol != c.expected.Protocol {
    40  		t.Error("protocol mismatch:", url.Protocol, "!=", c.expected.Protocol)
    41  	}
    42  
    43  	// Verify username.
    44  	if url.User != c.expected.User {
    45  		t.Error("username mismatch:", url.User, "!=", c.expected.User)
    46  	}
    47  
    48  	// Verify hostname.
    49  	if url.Host != c.expected.Host {
    50  		t.Error("hostname mismatch:", url.Host, "!=", c.expected.Host)
    51  	}
    52  
    53  	// Verify port.
    54  	if url.Port != c.expected.Port {
    55  		t.Error("port mismatch:", url.Port, "!=", c.expected.Port)
    56  	}
    57  
    58  	// Verify path.
    59  	if url.Path != c.expected.Path {
    60  		t.Error("path mismatch:", url.Path, "!=", c.expected.Path)
    61  	}
    62  
    63  	// Verify environment variables.
    64  	if len(url.Environment) != len(c.expected.Environment) {
    65  		t.Error("environment length mismatch:", len(url.Environment), "!=", len(c.expected.Environment))
    66  	} else {
    67  		for ek, ev := range c.expected.Environment {
    68  			if v, ok := url.Environment[ek]; !ok {
    69  				t.Error("expected environment variable", ek, "not in URL environment")
    70  			} else if v != ev {
    71  				t.Error("environment variable", ek, "value does not match expected:", v, "!=", ev)
    72  			}
    73  		}
    74  	}
    75  }
    76  
    77  func TestParseEmptyInvalid(t *testing.T) {
    78  	test := parseTestCase{
    79  		raw:  "",
    80  		fail: true,
    81  	}
    82  	test.run(t)
    83  }
    84  
    85  func TestParseLocalPathRelative(t *testing.T) {
    86  	// Compute the normalized form of a relative path.
    87  	path := "relative/path"
    88  	normalized, err := filesystem.Normalize(path)
    89  	if err != nil {
    90  		t.Fatal("unable to normalize relative path:", err)
    91  	}
    92  
    93  	// Create and run the test case.
    94  	test := parseTestCase{
    95  		raw: path,
    96  		expected: &URL{
    97  			Protocol: Protocol_Local,
    98  			User:     "",
    99  			Host:     "",
   100  			Port:     0,
   101  			Path:     normalized,
   102  		},
   103  	}
   104  	test.run(t)
   105  }
   106  
   107  func TestParseLocalPathAbsolute(t *testing.T) {
   108  	// Compute the normalized form of an absolute path.
   109  	path := "/this/is/a:path"
   110  	normalized, err := filesystem.Normalize(path)
   111  	if err != nil {
   112  		t.Fatal("unable to normalize absolute path:", err)
   113  	}
   114  
   115  	// Create and run the test case.
   116  	test := parseTestCase{
   117  		raw: path,
   118  		expected: &URL{
   119  			Protocol: Protocol_Local,
   120  			User:     "",
   121  			Host:     "",
   122  			Port:     0,
   123  			Path:     normalized,
   124  		},
   125  	}
   126  	test.run(t)
   127  }
   128  
   129  func TestParseForwardingLocalTCP(t *testing.T) {
   130  	test := parseTestCase{
   131  		raw:  "tcp:localhost:5050",
   132  		kind: Kind_Forwarding,
   133  		expected: &URL{
   134  			Kind:     Kind_Forwarding,
   135  			Protocol: Protocol_Local,
   136  			User:     "",
   137  			Host:     "",
   138  			Port:     0,
   139  			Path:     "tcp:localhost:5050",
   140  		},
   141  	}
   142  	test.run(t)
   143  }
   144  
   145  func TestParseForwardingLocalUnixRelativeSocket(t *testing.T) {
   146  	// Compute the normalized form of a relative socket path.
   147  	path := "relative/path/to/socket.sock"
   148  	normalized, err := filesystem.Normalize(path)
   149  	if err != nil {
   150  		t.Fatal("unable to normalize relative socket path:", err)
   151  	}
   152  
   153  	// Create and run the test case.
   154  	test := parseTestCase{
   155  		raw:  "unix:" + path,
   156  		kind: Kind_Forwarding,
   157  		expected: &URL{
   158  			Kind:     Kind_Forwarding,
   159  			Protocol: Protocol_Local,
   160  			User:     "",
   161  			Host:     "",
   162  			Port:     0,
   163  			Path:     "unix:" + normalized,
   164  		},
   165  	}
   166  	test.run(t)
   167  }
   168  
   169  func TestParseForwardingLocalUnixAbsoluteSocket(t *testing.T) {
   170  	// Compute the normalized form of a absolute socket path.
   171  	path := "/path/to/socket.sock"
   172  	normalized, err := filesystem.Normalize(path)
   173  	if err != nil {
   174  		t.Fatal("unable to normalize absolute socket path:", err)
   175  	}
   176  
   177  	// Create and run the test case.
   178  	test := parseTestCase{
   179  		raw:  "unix:" + path,
   180  		kind: Kind_Forwarding,
   181  		expected: &URL{
   182  			Kind:     Kind_Forwarding,
   183  			Protocol: Protocol_Local,
   184  			User:     "",
   185  			Host:     "",
   186  			Port:     0,
   187  			Path:     "unix:" + normalized,
   188  		},
   189  	}
   190  	test.run(t)
   191  }
   192  
   193  func TestParseLocalPathWithAtSymbol(t *testing.T) {
   194  	// Compute the normalized form of the path.
   195  	path := "/some@path"
   196  	normalized, err := filesystem.Normalize(path)
   197  	if err != nil {
   198  		t.Fatal("unable to normalize path:", err)
   199  	}
   200  
   201  	// Create and run the test case.
   202  	test := parseTestCase{
   203  		raw: path,
   204  		expected: &URL{
   205  			Protocol: Protocol_Local,
   206  			User:     "",
   207  			Host:     "",
   208  			Port:     0,
   209  			Path:     normalized,
   210  		},
   211  	}
   212  	test.run(t)
   213  }
   214  
   215  func TestParsePOSIXSCPSSHWindowsLocal(t *testing.T) {
   216  	// Compute the expected URL.
   217  	raw := "C:/local/path"
   218  	var expected *URL
   219  	if runtime.GOOS == "windows" {
   220  		if normalized, err := filesystem.Normalize(raw); err != nil {
   221  			t.Fatal("unable to normalize path:", err)
   222  		} else {
   223  			expected = &URL{
   224  				Path: normalized,
   225  			}
   226  		}
   227  	} else {
   228  		expected = &URL{
   229  			Protocol: Protocol_SSH,
   230  			Host:     "C",
   231  			Path:     "/local/path",
   232  		}
   233  	}
   234  
   235  	// Create and run the test case.
   236  	test := &parseTestCase{
   237  		raw:      raw,
   238  		expected: expected,
   239  	}
   240  	test.run(t)
   241  }
   242  
   243  func TestParseSCPSSHEmptyHostnameInvalid(t *testing.T) {
   244  	test := parseTestCase{
   245  		raw:  ":path",
   246  		fail: true,
   247  	}
   248  	test.run(t)
   249  }
   250  
   251  func TestParseSCPSSHEmptyHostnameAndPathInvalid(t *testing.T) {
   252  	test := parseTestCase{
   253  		raw:  ":",
   254  		fail: true,
   255  	}
   256  	test.run(t)
   257  }
   258  
   259  func TestParseSCPSSHUsernameEmptyHostnameInvalid(t *testing.T) {
   260  	test := parseTestCase{
   261  		raw:  "user@:path",
   262  		fail: true,
   263  	}
   264  	test.run(t)
   265  }
   266  
   267  func TestParseSCPSSHUsernameEmptyPathInvalid(t *testing.T) {
   268  	test := parseTestCase{
   269  		raw:  "user@host:",
   270  		fail: true,
   271  	}
   272  	test.run(t)
   273  }
   274  
   275  func TestParseSCPSSHEmptyUsernameInvalid(t *testing.T) {
   276  	test := parseTestCase{
   277  		raw:  "@host:path",
   278  		fail: true,
   279  	}
   280  	test.run(t)
   281  }
   282  
   283  func TestParseSCPSSHUsernamePortEmptyPathInvalid(t *testing.T) {
   284  	test := parseTestCase{
   285  		raw:  "user@host:5332:",
   286  		fail: true,
   287  	}
   288  	test.run(t)
   289  }
   290  
   291  func TestParseSCPSSHHostnameEmptyPathInvalid(t *testing.T) {
   292  	test := parseTestCase{
   293  		raw:  "host:",
   294  		fail: true,
   295  	}
   296  	test.run(t)
   297  }
   298  
   299  func TestParseSCPSSHUsernameHostnamePathEmptyPortInvalid(t *testing.T) {
   300  	test := parseTestCase{
   301  		raw:  "user@host::path",
   302  		fail: true,
   303  	}
   304  	test.run(t)
   305  }
   306  
   307  func TestParseSCPSSHHostnamePath(t *testing.T) {
   308  	test := parseTestCase{
   309  		raw: "host:path",
   310  		expected: &URL{
   311  			Protocol: Protocol_SSH,
   312  			User:     "",
   313  			Host:     "host",
   314  			Port:     0,
   315  			Path:     "path",
   316  		},
   317  	}
   318  	test.run(t)
   319  }
   320  
   321  func TestParseForwardingSCPSSHHostnameTCPEndpoint(t *testing.T) {
   322  	test := parseTestCase{
   323  		raw:  "host:tcp4:localhost:5050",
   324  		kind: Kind_Forwarding,
   325  		expected: &URL{
   326  			Kind:     Kind_Forwarding,
   327  			Protocol: Protocol_SSH,
   328  			User:     "",
   329  			Host:     "host",
   330  			Port:     0,
   331  			Path:     "tcp4:localhost:5050",
   332  		},
   333  	}
   334  	test.run(t)
   335  }
   336  
   337  func TestParseForwardingSCPSSHHostnameUnixDomainSocketEndpoint(t *testing.T) {
   338  	test := parseTestCase{
   339  		raw:  "host:unix:~/socket.sock",
   340  		kind: Kind_Forwarding,
   341  		expected: &URL{
   342  			Kind:     Kind_Forwarding,
   343  			Protocol: Protocol_SSH,
   344  			User:     "",
   345  			Host:     "host",
   346  			Port:     0,
   347  			Path:     "unix:~/socket.sock",
   348  		},
   349  	}
   350  	test.run(t)
   351  }
   352  
   353  func TestParseForwardingSCPSSHHostnameNamedPipeEndpoint(t *testing.T) {
   354  	test := parseTestCase{
   355  		raw:  `host:npipe:\\.\pipe\pipename`,
   356  		kind: Kind_Forwarding,
   357  		expected: &URL{
   358  			Kind:     Kind_Forwarding,
   359  			Protocol: Protocol_SSH,
   360  			User:     "",
   361  			Host:     "host",
   362  			Port:     0,
   363  			Path:     `npipe:\\.\pipe\pipename`,
   364  		},
   365  	}
   366  	test.run(t)
   367  }
   368  
   369  func TestParseSCPSSHUsernameHostnamePath(t *testing.T) {
   370  	test := parseTestCase{
   371  		raw: "user@host:path",
   372  		expected: &URL{
   373  			Protocol: Protocol_SSH,
   374  			User:     "user",
   375  			Host:     "host",
   376  			Port:     0,
   377  			Path:     "path",
   378  		},
   379  	}
   380  	test.run(t)
   381  }
   382  
   383  func TestParseSCPSSHUsernameHostnamePathWithColonInMiddle(t *testing.T) {
   384  	test := parseTestCase{
   385  		raw: "user@host:pa:th",
   386  		expected: &URL{
   387  			Protocol: Protocol_SSH,
   388  			User:     "user",
   389  			Host:     "host",
   390  			Port:     0,
   391  			Path:     "pa:th",
   392  		},
   393  	}
   394  	test.run(t)
   395  }
   396  
   397  func TestParseSCPSSHUsernameHostnamePathWithColonAtEnd(t *testing.T) {
   398  	test := parseTestCase{
   399  		raw: "user@host:path:",
   400  		expected: &URL{
   401  			Protocol: Protocol_SSH,
   402  			User:     "user",
   403  			Host:     "host",
   404  			Port:     0,
   405  			Path:     "path:",
   406  		},
   407  	}
   408  	test.run(t)
   409  }
   410  
   411  func TestParseSCPSSHUsernameHostnameWithAtPath(t *testing.T) {
   412  	test := parseTestCase{
   413  		raw: "user@ho@st:path",
   414  		expected: &URL{
   415  			Protocol: Protocol_SSH,
   416  			User:     "user",
   417  			Host:     "ho@st",
   418  			Port:     0,
   419  			Path:     "path",
   420  		},
   421  	}
   422  	test.run(t)
   423  }
   424  
   425  func TestParseSCPSSHUsernameHostnamePathWithAt(t *testing.T) {
   426  	test := parseTestCase{
   427  		raw: "user@host:pa@th",
   428  		expected: &URL{
   429  			Protocol: Protocol_SSH,
   430  			User:     "user",
   431  			Host:     "host",
   432  			Port:     0,
   433  			Path:     "pa@th",
   434  		},
   435  	}
   436  	test.run(t)
   437  }
   438  
   439  func TestParseSCPSSHUsernameHostnamePortPath(t *testing.T) {
   440  	test := parseTestCase{
   441  		raw: "user@host:65535:path",
   442  		expected: &URL{
   443  			Protocol: Protocol_SSH,
   444  			User:     "user",
   445  			Host:     "host",
   446  			Port:     65535,
   447  			Path:     "path",
   448  		},
   449  	}
   450  	test.run(t)
   451  }
   452  
   453  func TestParseSCPSSHUsernameHostnameZeroPortPath(t *testing.T) {
   454  	test := parseTestCase{
   455  		raw: "user@host:0:path",
   456  		expected: &URL{
   457  			Protocol: Protocol_SSH,
   458  			User:     "user",
   459  			Host:     "host",
   460  			Port:     0,
   461  			Path:     "path",
   462  		},
   463  	}
   464  	test.run(t)
   465  }
   466  
   467  func TestParseSCPSSHUsernameHostnameDoubleZeroPortPath(t *testing.T) {
   468  	test := parseTestCase{
   469  		raw: "user@host:00:path",
   470  		expected: &URL{
   471  			Protocol: Protocol_SSH,
   472  			User:     "user",
   473  			Host:     "host",
   474  			Port:     0,
   475  			Path:     "path",
   476  		},
   477  	}
   478  	test.run(t)
   479  }
   480  
   481  func TestParseSCPSSHUsernameHostnameOutOfBoundsPortInvalid(t *testing.T) {
   482  	test := parseTestCase{
   483  		raw:  "user@host:65536:path",
   484  		fail: true,
   485  	}
   486  	test.run(t)
   487  }
   488  
   489  func TestParseSCPSSHUsernameHostnameHexNumericPath(t *testing.T) {
   490  	test := parseTestCase{
   491  		raw: "user@host:aaa:path",
   492  		expected: &URL{
   493  			Protocol: Protocol_SSH,
   494  			User:     "user",
   495  			Host:     "host",
   496  			Port:     0,
   497  			Path:     "aaa:path",
   498  		},
   499  	}
   500  	test.run(t)
   501  }
   502  
   503  func TestParseSCPSSHUnicodeUsernameHostnamePath(t *testing.T) {
   504  	test := parseTestCase{
   505  		raw: "üsér@høst:пат",
   506  		expected: &URL{
   507  			Protocol: Protocol_SSH,
   508  			User:     "üsér",
   509  			Host:     "høst",
   510  			Port:     0,
   511  			Path:     "пат",
   512  		},
   513  	}
   514  	test.run(t)
   515  }
   516  
   517  func TestParseSCPSSHUnicodeUsernameHostnamePortPath(t *testing.T) {
   518  	test := parseTestCase{
   519  		raw: "üsér@høst:23:пат",
   520  		expected: &URL{
   521  			Protocol: Protocol_SSH,
   522  			User:     "üsér",
   523  			Host:     "høst",
   524  			Port:     23,
   525  			Path:     "пат",
   526  		},
   527  	}
   528  	test.run(t)
   529  }
   530  
   531  func TestParseForwardingDockerWithSourceSpecificVariables(t *testing.T) {
   532  	test := parseTestCase{
   533  		raw:   "docker://cøntainer:unix:/some/socket.sock",
   534  		kind:  Kind_Forwarding,
   535  		first: true,
   536  		expected: &URL{
   537  			Kind:     Kind_Forwarding,
   538  			Protocol: Protocol_Docker,
   539  			Host:     "cøntainer",
   540  			Path:     "unix:/some/socket.sock",
   541  			Environment: map[string]string{
   542  				"DOCKER_HOST":       defaultDockerHost,
   543  				"DOCKER_TLS_VERIFY": defaultDockerTLSVerify,
   544  				"DOCKER_CONTEXT":    sourceSpecificDockerContext,
   545  			},
   546  		},
   547  	}
   548  	test.run(t)
   549  }
   550  
   551  func TestParseForwardingDockerWithDestinationSpecificVariables(t *testing.T) {
   552  	test := parseTestCase{
   553  		raw:  "docker://cøntainer:tcp6:[::1]:5543",
   554  		kind: Kind_Forwarding,
   555  		expected: &URL{
   556  			Kind:     Kind_Forwarding,
   557  			Protocol: Protocol_Docker,
   558  			Host:     "cøntainer",
   559  			Path:     "tcp6:[::1]:5543",
   560  			Environment: map[string]string{
   561  				"DOCKER_HOST":       defaultDockerHost,
   562  				"DOCKER_TLS_VERIFY": destinationSpecificDockerTLSVerify,
   563  			},
   564  		},
   565  	}
   566  	test.run(t)
   567  }
   568  
   569  func TestParseDockerWithBetaSpecificVariables(t *testing.T) {
   570  	test := parseTestCase{
   571  		raw:  "docker://cøntainer/пат/to/the file",
   572  		fail: false,
   573  		expected: &URL{
   574  			Protocol: Protocol_Docker,
   575  			Host:     "cøntainer",
   576  			Path:     "/пат/to/the file",
   577  			Environment: map[string]string{
   578  				"DOCKER_HOST":       defaultDockerHost,
   579  				"DOCKER_TLS_VERIFY": betaSpecificDockerTLSVerify,
   580  			},
   581  		},
   582  	}
   583  	test.run(t)
   584  }
   585  
   586  func TestParseDockerWithWindowsPathAndAlphaSpecificVariables(t *testing.T) {
   587  	test := parseTestCase{
   588  		raw:   `docker://cøntainer/C:\пат/to\the file`,
   589  		first: true,
   590  		fail:  false,
   591  		expected: &URL{
   592  			Protocol: Protocol_Docker,
   593  			Host:     "cøntainer",
   594  			Path:     `C:\пат/to\the file`,
   595  			Environment: map[string]string{
   596  				"DOCKER_HOST":       alphaSpecificDockerHost,
   597  				"DOCKER_TLS_VERIFY": defaultDockerTLSVerify,
   598  			},
   599  		},
   600  	}
   601  	test.run(t)
   602  }
   603  
   604  func TestParseDockerWithUsernameHomeRelativePathAndAlphaSpecificVariables(t *testing.T) {
   605  	test := parseTestCase{
   606  		raw:   "docker://üsér@cøntainer/~/пат/to/the file",
   607  		first: true,
   608  		fail:  false,
   609  		expected: &URL{
   610  			Protocol: Protocol_Docker,
   611  			User:     "üsér",
   612  			Host:     "cøntainer",
   613  			Path:     "~/пат/to/the file",
   614  			Environment: map[string]string{
   615  				"DOCKER_HOST":       alphaSpecificDockerHost,
   616  				"DOCKER_TLS_VERIFY": defaultDockerTLSVerify,
   617  			},
   618  		},
   619  	}
   620  	test.run(t)
   621  }
   622  
   623  func TestParseDockerWithUsernameUserRelativePathAndAlphaSpecificVariables(t *testing.T) {
   624  	test := parseTestCase{
   625  		raw:   "docker://üsér@cøntainer/~otheruser/пат/to/the file",
   626  		first: true,
   627  		fail:  false,
   628  		expected: &URL{
   629  			Protocol: Protocol_Docker,
   630  			User:     "üsér",
   631  			Host:     "cøntainer",
   632  			Path:     "~otheruser/пат/to/the file",
   633  			Environment: map[string]string{
   634  				"DOCKER_HOST":       alphaSpecificDockerHost,
   635  				"DOCKER_TLS_VERIFY": defaultDockerTLSVerify,
   636  			},
   637  		},
   638  	}
   639  	test.run(t)
   640  }