cmd/vet: add subtestnames analyzer; fix all existing violations

Add a new vet analyzer that checks t.Run subtest names don't contain
characters requiring quoting when re-running via "go test -run". This
enforces the style guide rule: don't use spaces or punctuation in
subtest names.

The analyzer flags:
- Direct t.Run calls with string literal names containing spaces,
  regex metacharacters, quotes, or other problematic characters
- Table-driven t.Run(tt.name, ...) calls where tt ranges over a
  slice/map literal with bad name field values

Also fix all 978 existing violations across 81 test files, replacing
spaces with hyphens and shortening long sentence-like names to concise
hyphenated forms.

Updates #19242

Change-Id: Ib0ad96a111bd8e764582d1d4902fe2599454ab65
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
main
Brad Fitzpatrick 2 weeks ago committed by Brad Fitzpatrick
parent 0f02c20c5e
commit 5ef3713c9f
  1. 4
      .github/workflows/vet.yml
  2. 24
      client/freedesktop/freedesktop_test.go
  3. 4
      client/tailscale/tailscale_test.go
  4. 30
      client/web/web_test.go
  5. 75
      clientupdate/clientupdate_test.go
  6. 34
      clientupdate/distsign/distsign_test.go
  7. 18
      cmd/containerboot/kube_test.go
  8. 36
      cmd/containerboot/settings.go
  9. 12
      cmd/derper/derper_test.go
  10. 4
      cmd/gitops-pusher/gitops-pusher_test.go
  11. 30
      cmd/k8s-nameserver/main_test.go
  12. 12
      cmd/k8s-operator/nameserver_test.go
  13. 18
      cmd/k8s-operator/operator_test.go
  14. 24
      cmd/k8s-operator/sts_test.go
  15. 2
      cmd/k8s-operator/tsrecorder_specs_test.go
  16. 8
      cmd/tailscale/cli/cli_test.go
  17. 2
      cmd/tailscale/cli/configure-kube_test.go
  18. 6
      cmd/tailscale/cli/configure-synology-cert_test.go
  19. 4
      cmd/tailscale/cli/exitnode_test.go
  20. 89
      cmd/tailscale/cli/serve_v2_test.go
  21. 4
      cmd/tailscaled/tailscaled_test.go
  22. 123
      cmd/tsidp/tsidp_test.go
  23. 227
      cmd/vet/subtestnames/analyzer.go
  24. 15
      cmd/vet/subtestnames/analyzer_test.go
  25. 112
      cmd/vet/subtestnames/testdata/src/example/example_test.go
  26. 3
      cmd/vet/vet.go
  27. 8
      control/controlclient/map_test.go
  28. 4
      derp/derp_test.go
  29. 56
      derp/xdp/xdp_linux_test.go
  30. 22
      drive/driveimpl/dirfs/dirfs_test.go
  31. 10
      drive/driveimpl/drive_test.go
  32. 16
      feature/identityfederation/identityfederation_test.go
  33. 18
      feature/oauthkey/oauthkey_test.go
  34. 38
      feature/relayserver/relayserver_test.go
  35. 2
      flake.nix
  36. 2
      go.mod.sri
  37. 16
      health/health_test.go
  38. 2
      ipn/ipnlocal/c2n_test.go
  39. 12
      ipn/ipnlocal/cert_test.go
  40. 222
      ipn/ipnlocal/local_test.go
  41. 14
      ipn/ipnlocal/network-lock_test.go
  42. 44
      ipn/ipnlocal/serve_test.go
  43. 6
      ipn/ipnlocal/serve_unix_test.go
  44. 10
      ipn/prefs_test.go
  45. 4
      ipn/serve_test.go
  46. 6
      k8s-operator/reconciler/proxygrouppolicy/proxygrouppolicy_test.go
  47. 18
      k8s-operator/reconciler/tailnet/tailnet_test.go
  48. 10
      log/sockstatlog/logger_test.go
  49. 44
      net/batching/conn_linux_test.go
  50. 6
      net/dns/manager_linux_test.go
  51. 8
      net/dns/resolver/forwarder_test.go
  52. 2
      net/netcheck/netcheck_test.go
  53. 4
      net/netmon/netmon_linux_test.go
  54. 4
      net/speedtest/speedtest_test.go
  55. 8
      net/stun/stun_test.go
  56. 14
      net/tshttpproxy/tshttpproxy_synology_test.go
  57. 10
      net/tshttpproxy/tshttpproxy_test.go
  58. 14
      net/udprelay/endpoint/endpoint_test.go
  59. 6
      net/udprelay/server_test.go
  60. 70
      safeweb/http_test.go
  61. 6
      sessionrecording/connect_test.go
  62. 2
      shell.nix
  63. 8
      ssh/tailssh/accept_env_test.go
  64. 6
      syncs/shardedint_test.go
  65. 34
      tailcfg/proto_port_range_test.go
  66. 4
      tailcfg/tailcfg_test.go
  67. 14
      tka/tailchonk_test.go
  68. 17
      tsconsensus/authorization_test.go
  69. 34
      tsnet/tsnet_test.go
  70. 130
      tstest/clock_test.go
  71. 4
      tstest/integration/vms/vms_test.go
  72. 26
      tstest/resource_test.go
  73. 44
      tsweb/tsweb_test.go
  74. 12
      types/dnstype/dnstype_test.go
  75. 46
      util/cmpver/version_test.go
  76. 8
      util/deephash/deephash_test.go
  77. 46
      util/eventbus/eventbustest/eventbustest_test.go
  78. 12
      util/linuxfw/nftables_runner_test.go
  79. 12
      util/syspolicy/setting/policy_scope_test.go
  80. 58
      util/syspolicy/syspolicy_test.go
  81. 34
      wgengine/magicsock/endpoint_test.go
  82. 6
      wgengine/magicsock/magicsock_linux_test.go
  83. 106
      wgengine/magicsock/magicsock_test.go
  84. 16
      wgengine/magicsock/relaymanager_test.go
  85. 32
      wgengine/router/osrouter/router_linux_test.go
  86. 6
      wgengine/watchdog_test.go
  87. 14
      wgengine/wgcfg/device_test.go

@ -36,8 +36,10 @@ jobs:
- name: Run 'go vet' - name: Run 'go vet'
working-directory: src working-directory: src
# Use listpkgs --ignore-3p to skip tempfork/ packages, which
# intentionally match upstream and may not follow our style rules.
# Must use ./... instead of tailscale.com/... because the latter will # Must use ./... instead of tailscale.com/... because the latter will
# include the v2 go client (tailscale.com/client/tailscale/v2) if it's # include the v2 go client (tailscale.com/client/tailscale/v2) if it's
# a dependency in our go.mod file. Possibly a go vet bug, but avoid # a dependency in our go.mod file. Possibly a go vet bug, but avoid
# cross-repo vetting for now so we can safely add the dependency. # cross-repo vetting for now so we can safely add the dependency.
run: ./tool/go vet -vettool=/tmp/vettool ./... run: ./tool/go vet -vettool=/tmp/vettool $(./tool/go run ./tool/listpkgs --ignore-3p ./...)

@ -13,12 +13,12 @@ func TestEscape(t *testing.T) {
name, input, want string name, input, want string
}{ }{
{ {
name: "no illegal chars", name: "no-illegal-chars",
input: "/home/user", input: "/home/user",
want: "/home/user", want: "/home/user",
}, },
{ {
name: "empty string", name: "empty-string",
input: "", input: "",
want: "\"\"", want: "\"\"",
}, },
@ -38,12 +38,12 @@ func TestEscape(t *testing.T) {
want: "\"\n\"", want: "\"\n\"",
}, },
{ {
name: "double quote", name: "double-quote",
input: "\"", input: "\"",
want: "\"\\\"\"", want: "\"\\\"\"",
}, },
{ {
name: "single quote", name: "single-quote",
input: "'", input: "'",
want: "\"'\"", want: "\"'\"",
}, },
@ -53,12 +53,12 @@ func TestEscape(t *testing.T) {
want: "\"\\\\\"", want: "\"\\\\\"",
}, },
{ {
name: "greater than", name: "greater-than",
input: ">", input: ">",
want: "\">\"", want: "\">\"",
}, },
{ {
name: "less than", name: "less-than",
input: "<", input: "<",
want: "\"<\"", want: "\"<\"",
}, },
@ -93,7 +93,7 @@ func TestEscape(t *testing.T) {
want: "\"*\"", want: "\"*\"",
}, },
{ {
name: "question mark", name: "question-mark",
input: "?", input: "?",
want: "\"?\"", want: "\"?\"",
}, },
@ -103,12 +103,12 @@ func TestEscape(t *testing.T) {
want: "\"#\"", want: "\"#\"",
}, },
{ {
name: "open paren", name: "open-paren",
input: "(", input: "(",
want: "\"(\"", want: "\"(\"",
}, },
{ {
name: "close paren", name: "close-paren",
input: ")", input: ")",
want: "\")\"", want: "\")\"",
}, },
@ -118,17 +118,17 @@ func TestEscape(t *testing.T) {
want: "\"\\`\"", want: "\"\\`\"",
}, },
{ {
name: "char without escape", name: "char-without-escape",
input: "/home/user\t", input: "/home/user\t",
want: "\"/home/user\t\"", want: "\"/home/user\t\"",
}, },
{ {
name: "char with escape", name: "char-with-escape",
input: "/home/user\\", input: "/home/user\\",
want: "\"/home/user\\\\\"", want: "\"/home/user\\\\\"",
}, },
{ {
name: "all illegal chars", name: "all-illegal-chars",
input: "/home/user" + needsEscape, input: "/home/user" + needsEscape,
want: "\"/home/user \t\n\\\"'\\\\><~|&;\\$*?#()\\`\"", want: "\"/home/user \t\n\\\"'\\\\><~|&;\\$*?#()\\`\"",
}, },

@ -31,7 +31,7 @@ func TestClientBuildURL(t *testing.T) {
want: `http://127.0.0.1:1234/api/v2/tailnet/example%20dot%20com%3Ffoo=bar`, want: `http://127.0.0.1:1234/api/v2/tailnet/example%20dot%20com%3Ffoo=bar`,
}, },
{ {
desc: "url.Values", desc: "url-Values",
elements: []any{"tailnet", "example.com", "acl", url.Values{"details": {"1"}}}, elements: []any{"tailnet", "example.com", "acl", url.Values{"details": {"1"}}},
want: `http://127.0.0.1:1234/api/v2/tailnet/example.com/acl?details=1`, want: `http://127.0.0.1:1234/api/v2/tailnet/example.com/acl?details=1`,
}, },
@ -71,7 +71,7 @@ func TestClientBuildTailnetURL(t *testing.T) {
want: `http://127.0.0.1:1234/api/v2/tailnet/example.com/foo%20bar%3Fbaz=qux`, want: `http://127.0.0.1:1234/api/v2/tailnet/example.com/foo%20bar%3Fbaz=qux`,
}, },
{ {
desc: "url.Values", desc: "url-Values",
elements: []any{"acl", url.Values{"details": {"1"}}}, elements: []any{"acl", url.Values{"details": {"1"}}},
want: `http://127.0.0.1:1234/api/v2/tailnet/example.com/acl?details=1`, want: `http://127.0.0.1:1234/api/v2/tailnet/example.com/acl?details=1`,
}, },

@ -41,37 +41,37 @@ func TestQnapAuthnURL(t *testing.T) {
want string want string
}{ }{
{ {
name: "localhost http", name: "localhost-http",
in: "http://localhost:8088/", in: "http://localhost:8088/",
want: "http://localhost:8088/cgi-bin/authLogin.cgi?qtoken=token", want: "http://localhost:8088/cgi-bin/authLogin.cgi?qtoken=token",
}, },
{ {
name: "localhost https", name: "localhost-https",
in: "https://localhost:5000/", in: "https://localhost:5000/",
want: "https://localhost:5000/cgi-bin/authLogin.cgi?qtoken=token", want: "https://localhost:5000/cgi-bin/authLogin.cgi?qtoken=token",
}, },
{ {
name: "IP http", name: "IP-http",
in: "http://10.1.20.4:80/", in: "http://10.1.20.4:80/",
want: "http://10.1.20.4:80/cgi-bin/authLogin.cgi?qtoken=token", want: "http://10.1.20.4:80/cgi-bin/authLogin.cgi?qtoken=token",
}, },
{ {
name: "IP6 https", name: "IP6-https",
in: "https://[ff7d:0:1:2::1]/", in: "https://[ff7d:0:1:2::1]/",
want: "https://[ff7d:0:1:2::1]/cgi-bin/authLogin.cgi?qtoken=token", want: "https://[ff7d:0:1:2::1]/cgi-bin/authLogin.cgi?qtoken=token",
}, },
{ {
name: "hostname https", name: "hostname-https",
in: "https://qnap.example.com/", in: "https://qnap.example.com/",
want: "https://qnap.example.com/cgi-bin/authLogin.cgi?qtoken=token", want: "https://qnap.example.com/cgi-bin/authLogin.cgi?qtoken=token",
}, },
{ {
name: "invalid URL", name: "invalid-URL",
in: "This is not a URL, it is a really really really really really really really really really really really really long string to exercise the URL truncation code in the error path.", in: "This is not a URL, it is a really really really really really really really really really really really really long string to exercise the URL truncation code in the error path.",
want: "http://localhost/cgi-bin/authLogin.cgi?qtoken=token", want: "http://localhost/cgi-bin/authLogin.cgi?qtoken=token",
}, },
{ {
name: "err != nil", name: "err-not-nil",
in: "http://192.168.0.%31/", in: "http://192.168.0.%31/",
want: "http://localhost/cgi-bin/authLogin.cgi?qtoken=token", want: "http://localhost/cgi-bin/authLogin.cgi?qtoken=token",
}, },
@ -1516,47 +1516,47 @@ func TestCSRFProtect(t *testing.T) {
wantError bool wantError bool
}{ }{
{ {
name: "GET requests with no header are allowed", name: "GET-no-header-allowed", // GET requests with no header are allowed
method: "GET", method: "GET",
}, },
{ {
name: "POST requests with same-origin are allowed", name: "POST-same-origin-allowed",
method: "POST", method: "POST",
secFetchSite: "same-origin", secFetchSite: "same-origin",
}, },
{ {
name: "POST requests with cross-site are not allowed", name: "POST-cross-site-rejected",
method: "POST", method: "POST",
secFetchSite: "cross-site", secFetchSite: "cross-site",
wantError: true, wantError: true,
}, },
{ {
name: "POST requests with unknown sec-fetch-site values are not allowed", name: "POST-unknown-sec-fetch-site-rejected",
method: "POST", method: "POST",
secFetchSite: "new-unknown-value", secFetchSite: "new-unknown-value",
wantError: true, wantError: true,
}, },
{ {
name: "POST requests with none are not allowed", name: "POST-sec-fetch-none-rejected",
method: "POST", method: "POST",
secFetchSite: "none", secFetchSite: "none",
wantError: true, wantError: true,
}, },
{ {
name: "POST requests with no sec-fetch-site header but matching host and origin are allowed", name: "POST-no-sec-fetch-site-matching-host-origin", // no sec-fetch-site header but matching host and origin are allowed
method: "POST", method: "POST",
host: "example.com", host: "example.com",
origin: "https://example.com", origin: "https://example.com",
}, },
{ {
name: "POST requests with no sec-fetch-site and non-matching host and origin are not allowed", name: "POST-no-sec-fetch-site-mismatched-host-origin-rejected",
method: "POST", method: "POST",
host: "example.com", host: "example.com",
origin: "https://example.net", origin: "https://example.net",
wantError: true, wantError: true,
}, },
{ {
name: "POST requests with no sec-fetch-site and and origin that matches the override are allowed", name: "POST-no-sec-fetch-site-origin-override-allowed",
method: "POST", method: "POST",
originOverride: "example.net", originOverride: "example.net",
host: "internal.example.foo", // Host can be changed by reverse proxies host: "internal.example.foo", // Host can be changed by reverse proxies

@ -148,27 +148,27 @@ func TestUpdateYUMRepoTrack(t *testing.T) {
wantErr bool wantErr bool
}{ }{
{ {
desc: "same track", desc: "same-track",
before: YUMRepos[StableTrack], before: YUMRepos[StableTrack],
track: StableTrack, track: StableTrack,
after: YUMRepos[StableTrack], after: YUMRepos[StableTrack],
}, },
{ {
desc: "change track", desc: "change-track",
before: YUMRepos[StableTrack], before: YUMRepos[StableTrack],
track: UnstableTrack, track: UnstableTrack,
after: YUMRepos[UnstableTrack], after: YUMRepos[UnstableTrack],
rewrote: true, rewrote: true,
}, },
{ {
desc: "change track RC", desc: "change-track-RC",
before: YUMRepos[StableTrack], before: YUMRepos[StableTrack],
track: ReleaseCandidateTrack, track: ReleaseCandidateTrack,
after: YUMRepos[ReleaseCandidateTrack], after: YUMRepos[ReleaseCandidateTrack],
rewrote: true, rewrote: true,
}, },
{ {
desc: "non-tailscale repo file", desc: "non-tailscale-repo-file",
before: YUMRepos["FakeRepo"], before: YUMRepos["FakeRepo"],
track: StableTrack, track: StableTrack,
wantErr: true, wantErr: true,
@ -215,7 +215,7 @@ func TestParseAlpinePackageVersion(t *testing.T) {
wantErr bool wantErr bool
}{ }{
{ {
desc: "valid version", desc: "valid-version",
out: ` out: `
tailscale-1.44.2-r0 description: tailscale-1.44.2-r0 description:
The easiest, most secure way to use WireGuard and 2FA The easiest, most secure way to use WireGuard and 2FA
@ -229,7 +229,7 @@ tailscale-1.44.2-r0 installed size:
want: "1.44.2", want: "1.44.2",
}, },
{ {
desc: "wrong package output", desc: "wrong-package-output",
out: ` out: `
busybox-1.36.1-r0 description: busybox-1.36.1-r0 description:
Size optimized toolbox of many common UNIX utilities Size optimized toolbox of many common UNIX utilities
@ -243,7 +243,7 @@ busybox-1.36.1-r0 installed size:
wantErr: true, wantErr: true,
}, },
{ {
desc: "missing version", desc: "missing-version",
out: ` out: `
tailscale description: tailscale description:
The easiest, most secure way to use WireGuard and 2FA The easiest, most secure way to use WireGuard and 2FA
@ -257,12 +257,12 @@ tailscale installed size:
wantErr: true, wantErr: true,
}, },
{ {
desc: "empty output", desc: "empty-output",
out: "", out: "",
wantErr: true, wantErr: true,
}, },
{ {
desc: "multiple versions", desc: "multiple-versions",
out: ` out: `
tailscale-1.54.1-r0 description: tailscale-1.54.1-r0 description:
The easiest, most secure way to use WireGuard and 2FA The easiest, most secure way to use WireGuard and 2FA
@ -322,14 +322,14 @@ func TestCheckOutdatedAlpineRepo(t *testing.T) {
track string track string
}{ }{
{ {
name: "Up to date", name: "up-to-date",
fileContent: "https://dl-cdn.alpinelinux.org/alpine/v3.20/main", fileContent: "https://dl-cdn.alpinelinux.org/alpine/v3.20/main",
latestHTTPVersion: "1.95.3", latestHTTPVersion: "1.95.3",
latestApkVersion: "1.95.3", latestApkVersion: "1.95.3",
track: "unstable", track: "unstable",
}, },
{ {
name: "Behind unstable", name: "behind-unstable",
fileContent: "https://dl-cdn.alpinelinux.org/alpine/v3.20/main", fileContent: "https://dl-cdn.alpinelinux.org/alpine/v3.20/main",
latestHTTPVersion: "1.95.4", latestHTTPVersion: "1.95.4",
latestApkVersion: "1.95.3", latestApkVersion: "1.95.3",
@ -339,7 +339,7 @@ func TestCheckOutdatedAlpineRepo(t *testing.T) {
track: "unstable", track: "unstable",
}, },
{ {
name: "Behind stable", name: "behind-stable",
fileContent: "https://dl-cdn.alpinelinux.org/alpine/v2.40/main", fileContent: "https://dl-cdn.alpinelinux.org/alpine/v2.40/main",
latestHTTPVersion: "1.94.3", latestHTTPVersion: "1.94.3",
latestApkVersion: "1.92.1", latestApkVersion: "1.92.1",
@ -349,7 +349,7 @@ func TestCheckOutdatedAlpineRepo(t *testing.T) {
track: "stable", track: "stable",
}, },
{ {
name: "Nothing in dist file", name: "nothing-in-dist-file",
fileContent: "", fileContent: "",
latestHTTPVersion: "1.94.3", latestHTTPVersion: "1.94.3",
latestApkVersion: "1.92.1", latestApkVersion: "1.92.1",
@ -505,14 +505,14 @@ unique=synology_88f6281_213air
want: "88f6281", want: "88f6281",
}, },
{ {
desc: "missing unique", desc: "missing-unique",
content: ` content: `
company_title="Synology" company_title="Synology"
`, `,
wantErr: true, wantErr: true,
}, },
{ {
desc: "empty unique", desc: "empty-unique",
content: ` content: `
company_title="Synology" company_title="Synology"
unique= unique=
@ -520,7 +520,7 @@ unique=
wantErr: true, wantErr: true,
}, },
{ {
desc: "empty unique double-quoted", desc: "empty-unique-double-quoted",
content: ` content: `
company_title="Synology" company_title="Synology"
unique="" unique=""
@ -528,7 +528,7 @@ unique=""
wantErr: true, wantErr: true,
}, },
{ {
desc: "empty unique single-quoted", desc: "empty-unique-single-quoted",
content: ` content: `
company_title="Synology" company_title="Synology"
unique='' unique=''
@ -536,7 +536,7 @@ unique=''
wantErr: true, wantErr: true,
}, },
{ {
desc: "malformed unique", desc: "malformed-unique",
content: ` content: `
company_title="Synology" company_title="Synology"
unique="synology_88f6281" unique="synology_88f6281"
@ -544,12 +544,12 @@ unique="synology_88f6281"
wantErr: true, wantErr: true,
}, },
{ {
desc: "empty file", desc: "empty-file",
content: ``, content: ``,
wantErr: true, wantErr: true,
}, },
{ {
desc: "empty lines and comments", desc: "empty-lines-and-comments",
content: ` content: `
# In a file named synoinfo? Shocking! # In a file named synoinfo? Shocking!
@ -613,7 +613,7 @@ func TestUnpackLinuxTarball(t *testing.T) {
}, },
}, },
{ {
desc: "don't touch unrelated files", desc: "skip-unrelated-files", // don't touch unrelated files
before: map[string]string{ before: map[string]string{
"tailscale": "v1", "tailscale": "v1",
"tailscaled": "v1", "tailscaled": "v1",
@ -645,7 +645,7 @@ func TestUnpackLinuxTarball(t *testing.T) {
}, },
}, },
{ {
desc: "ignore extra tarball files", desc: "ignore-extra-tarball-files",
before: map[string]string{ before: map[string]string{
"tailscale": "v1", "tailscale": "v1",
"tailscaled": "v1", "tailscaled": "v1",
@ -661,7 +661,7 @@ func TestUnpackLinuxTarball(t *testing.T) {
}, },
}, },
{ {
desc: "tarball missing tailscaled", desc: "tarball-missing-tailscaled",
before: map[string]string{ before: map[string]string{
"tailscale": "v1", "tailscale": "v1",
"tailscaled": "v1", "tailscaled": "v1",
@ -677,7 +677,7 @@ func TestUnpackLinuxTarball(t *testing.T) {
wantErr: true, wantErr: true,
}, },
{ {
desc: "duplicate tailscale binary", desc: "duplicate-tailscale-binary",
before: map[string]string{ before: map[string]string{
"tailscale": "v1", "tailscale": "v1",
"tailscaled": "v1", "tailscaled": "v1",
@ -696,7 +696,7 @@ func TestUnpackLinuxTarball(t *testing.T) {
wantErr: true, wantErr: true,
}, },
{ {
desc: "empty archive", desc: "empty-archive",
before: map[string]string{ before: map[string]string{
"tailscale": "v1", "tailscale": "v1",
"tailscaled": "v1", "tailscaled": "v1",
@ -952,17 +952,18 @@ func TestCleanupOldDownloads(t *testing.T) {
func TestParseUnraidPluginVersion(t *testing.T) { func TestParseUnraidPluginVersion(t *testing.T) {
tests := []struct { tests := []struct {
name string
plgPath string plgPath string
wantVer string wantVer string
wantErr string wantErr string
}{ }{
{plgPath: "testdata/tailscale-1.52.0.plg", wantVer: "1.52.0"}, {name: "v1_52_0", plgPath: "testdata/tailscale-1.52.0.plg", wantVer: "1.52.0"},
{plgPath: "testdata/tailscale-1.54.0.plg", wantVer: "1.54.0"}, {name: "v1_54_0", plgPath: "testdata/tailscale-1.54.0.plg", wantVer: "1.54.0"},
{plgPath: "testdata/tailscale-nover.plg", wantErr: "version not found in plg file"}, {name: "nover", plgPath: "testdata/tailscale-nover.plg", wantErr: "version not found in plg file"},
{plgPath: "testdata/tailscale-nover-path-mentioned.plg", wantErr: "version not found in plg file"}, {name: "nover-path-mentioned", plgPath: "testdata/tailscale-nover-path-mentioned.plg", wantErr: "version not found in plg file"},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.plgPath, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
got, err := parseUnraidPluginVersion(tt.plgPath) got, err := parseUnraidPluginVersion(tt.plgPath)
if got != tt.wantVer { if got != tt.wantVer {
t.Errorf("got version: %q, want %q", got, tt.wantVer) t.Errorf("got version: %q, want %q", got, tt.wantVer)
@ -992,7 +993,7 @@ func TestConfirm(t *testing.T) {
want bool want bool
}{ }{
{ {
desc: "on latest stable", desc: "on-latest-stable",
fromTrack: StableTrack, fromTrack: StableTrack,
toTrack: StableTrack, toTrack: StableTrack,
fromVer: "1.66.0", fromVer: "1.66.0",
@ -1000,7 +1001,7 @@ func TestConfirm(t *testing.T) {
want: false, want: false,
}, },
{ {
desc: "stable upgrade", desc: "stable-upgrade",
fromTrack: StableTrack, fromTrack: StableTrack,
toTrack: StableTrack, toTrack: StableTrack,
fromVer: "1.66.0", fromVer: "1.66.0",
@ -1008,7 +1009,7 @@ func TestConfirm(t *testing.T) {
want: true, want: true,
}, },
{ {
desc: "unstable upgrade", desc: "unstable-upgrade",
fromTrack: UnstableTrack, fromTrack: UnstableTrack,
toTrack: UnstableTrack, toTrack: UnstableTrack,
fromVer: "1.67.1", fromVer: "1.67.1",
@ -1016,7 +1017,7 @@ func TestConfirm(t *testing.T) {
want: true, want: true,
}, },
{ {
desc: "from stable to unstable", desc: "from-stable-to-unstable",
fromTrack: StableTrack, fromTrack: StableTrack,
toTrack: UnstableTrack, toTrack: UnstableTrack,
fromVer: "1.66.0", fromVer: "1.66.0",
@ -1024,7 +1025,7 @@ func TestConfirm(t *testing.T) {
want: true, want: true,
}, },
{ {
desc: "from unstable to stable", desc: "from-unstable-to-stable",
fromTrack: UnstableTrack, fromTrack: UnstableTrack,
toTrack: StableTrack, toTrack: StableTrack,
fromVer: "1.67.1", fromVer: "1.67.1",
@ -1032,7 +1033,7 @@ func TestConfirm(t *testing.T) {
want: true, want: true,
}, },
{ {
desc: "confirm callback rejects", desc: "confirm-callback-rejects",
fromTrack: StableTrack, fromTrack: StableTrack,
toTrack: StableTrack, toTrack: StableTrack,
fromVer: "1.66.0", fromVer: "1.66.0",
@ -1043,7 +1044,7 @@ func TestConfirm(t *testing.T) {
want: false, want: false,
}, },
{ {
desc: "confirm callback allows", desc: "confirm-callback-allows",
fromTrack: StableTrack, fromTrack: StableTrack,
toTrack: StableTrack, toTrack: StableTrack,
fromVer: "1.66.0", fromVer: "1.66.0",

@ -30,7 +30,7 @@ func TestDownload(t *testing.T) {
wantErr bool wantErr bool
}{ }{
{ {
desc: "missing file", desc: "missing-file",
before: func(*testing.T) {}, before: func(*testing.T) {},
src: "hello", src: "hello",
wantErr: true, wantErr: true,
@ -44,7 +44,7 @@ func TestDownload(t *testing.T) {
want: []byte("world"), want: []byte("world"),
}, },
{ {
desc: "no signature", desc: "no-signature",
before: func(*testing.T) { before: func(*testing.T) {
srv.add("hello", []byte("world")) srv.add("hello", []byte("world"))
}, },
@ -52,7 +52,7 @@ func TestDownload(t *testing.T) {
wantErr: true, wantErr: true,
}, },
{ {
desc: "bad signature", desc: "bad-signature",
before: func(*testing.T) { before: func(*testing.T) {
srv.add("hello", []byte("world")) srv.add("hello", []byte("world"))
srv.add("hello.sig", []byte("potato")) srv.add("hello.sig", []byte("potato"))
@ -61,7 +61,7 @@ func TestDownload(t *testing.T) {
wantErr: true, wantErr: true,
}, },
{ {
desc: "signed with untrusted key", desc: "signed-untrusted-key",
before: func(t *testing.T) { before: func(t *testing.T) {
srv.add("hello", []byte("world")) srv.add("hello", []byte("world"))
srv.add("hello.sig", newSigningKeyPair(t).sign([]byte("world"))) srv.add("hello.sig", newSigningKeyPair(t).sign([]byte("world")))
@ -70,7 +70,7 @@ func TestDownload(t *testing.T) {
wantErr: true, wantErr: true,
}, },
{ {
desc: "signed with root key", desc: "signed-with-root-key",
before: func(t *testing.T) { before: func(t *testing.T) {
srv.add("hello", []byte("world")) srv.add("hello", []byte("world"))
srv.add("hello.sig", ed25519.Sign(srv.roots[0].k, []byte("world"))) srv.add("hello.sig", ed25519.Sign(srv.roots[0].k, []byte("world")))
@ -79,7 +79,7 @@ func TestDownload(t *testing.T) {
wantErr: true, wantErr: true,
}, },
{ {
desc: "bad signing key signature", desc: "bad-signing-key-signature",
before: func(t *testing.T) { before: func(t *testing.T) {
srv.add("distsign.pub.sig", []byte("potato")) srv.add("distsign.pub.sig", []byte("potato"))
srv.addSigned("hello", []byte("world")) srv.addSigned("hello", []byte("world"))
@ -130,7 +130,7 @@ func TestValidateLocalBinary(t *testing.T) {
wantErr bool wantErr bool
}{ }{
{ {
desc: "missing file", desc: "missing-file",
before: func(*testing.T) {}, before: func(*testing.T) {},
src: "hello", src: "hello",
wantErr: true, wantErr: true,
@ -143,7 +143,7 @@ func TestValidateLocalBinary(t *testing.T) {
src: "hello", src: "hello",
}, },
{ {
desc: "contents changed", desc: "contents-changed",
before: func(*testing.T) { before: func(*testing.T) {
srv.addSigned("hello", []byte("new world")) srv.addSigned("hello", []byte("new world"))
}, },
@ -151,7 +151,7 @@ func TestValidateLocalBinary(t *testing.T) {
wantErr: true, wantErr: true,
}, },
{ {
desc: "no signature", desc: "no-signature",
before: func(*testing.T) { before: func(*testing.T) {
srv.add("hello", []byte("world")) srv.add("hello", []byte("world"))
}, },
@ -159,7 +159,7 @@ func TestValidateLocalBinary(t *testing.T) {
wantErr: true, wantErr: true,
}, },
{ {
desc: "bad signature", desc: "bad-signature",
before: func(*testing.T) { before: func(*testing.T) {
srv.add("hello", []byte("world")) srv.add("hello", []byte("world"))
srv.add("hello.sig", []byte("potato")) srv.add("hello.sig", []byte("potato"))
@ -168,7 +168,7 @@ func TestValidateLocalBinary(t *testing.T) {
wantErr: true, wantErr: true,
}, },
{ {
desc: "signed with untrusted key", desc: "signed-untrusted-key",
before: func(t *testing.T) { before: func(t *testing.T) {
srv.add("hello", []byte("world")) srv.add("hello", []byte("world"))
srv.add("hello.sig", newSigningKeyPair(t).sign([]byte("world"))) srv.add("hello.sig", newSigningKeyPair(t).sign([]byte("world")))
@ -177,7 +177,7 @@ func TestValidateLocalBinary(t *testing.T) {
wantErr: true, wantErr: true,
}, },
{ {
desc: "signed with root key", desc: "signed-with-root-key",
before: func(t *testing.T) { before: func(t *testing.T) {
srv.add("hello", []byte("world")) srv.add("hello", []byte("world"))
srv.add("hello.sig", ed25519.Sign(srv.roots[0].k, []byte("world"))) srv.add("hello.sig", ed25519.Sign(srv.roots[0].k, []byte("world")))
@ -186,7 +186,7 @@ func TestValidateLocalBinary(t *testing.T) {
wantErr: true, wantErr: true,
}, },
{ {
desc: "bad signing key signature", desc: "bad-signing-key-signature",
before: func(t *testing.T) { before: func(t *testing.T) {
srv.add("distsign.pub.sig", []byte("potato")) srv.add("distsign.pub.sig", []byte("potato"))
srv.addSigned("hello", []byte("world")) srv.addSigned("hello", []byte("world"))
@ -341,7 +341,7 @@ func TestParseRootKey(t *testing.T) {
wantErr: true, wantErr: true,
}, },
{ {
desc: "invalid PEM tag", desc: "invalid-PEM-tag",
generate: func() ([]byte, []byte, error) { generate: func() ([]byte, []byte, error) {
priv, pub, err := GenerateRootKey() priv, pub, err := GenerateRootKey()
priv = bytes.Replace(priv, []byte("ROOT "), nil, -1) priv = bytes.Replace(priv, []byte("ROOT "), nil, -1)
@ -350,7 +350,7 @@ func TestParseRootKey(t *testing.T) {
wantErr: true, wantErr: true,
}, },
{ {
desc: "not PEM", desc: "not-PEM",
generate: func() ([]byte, []byte, error) { return []byte("s3cr3t"), nil, nil }, generate: func() ([]byte, []byte, error) { return []byte("s3cr3t"), nil, nil },
wantErr: true, wantErr: true,
}, },
@ -399,7 +399,7 @@ func TestParseSigningKey(t *testing.T) {
wantErr: true, wantErr: true,
}, },
{ {
desc: "invalid PEM tag", desc: "invalid-PEM-tag",
generate: func() ([]byte, []byte, error) { generate: func() ([]byte, []byte, error) {
priv, pub, err := GenerateSigningKey() priv, pub, err := GenerateSigningKey()
priv = bytes.Replace(priv, []byte("SIGNING "), nil, -1) priv = bytes.Replace(priv, []byte("SIGNING "), nil, -1)
@ -408,7 +408,7 @@ func TestParseSigningKey(t *testing.T) {
wantErr: true, wantErr: true,
}, },
{ {
desc: "not PEM", desc: "not-PEM",
generate: func() ([]byte, []byte, error) { return []byte("s3cr3t"), nil, nil }, generate: func() ([]byte, []byte, error) { return []byte("s3cr3t"), nil, nil },
wantErr: true, wantErr: true,
}, },

@ -31,7 +31,7 @@ func TestSetupKube(t *testing.T) {
kc *kubeClient kc *kubeClient
}{ }{
{ {
name: "TS_AUTHKEY set, state Secret exists", name: "authkey-set-secret-exists",
cfg: &settings{ cfg: &settings{
AuthKey: "foo", AuthKey: "foo",
KubeSecret: "foo", KubeSecret: "foo",
@ -50,7 +50,7 @@ func TestSetupKube(t *testing.T) {
}, },
}, },
{ {
name: "TS_AUTHKEY set, state Secret does not exist, we have permissions to create it", name: "authkey-set-secret-missing-can-create",
cfg: &settings{ cfg: &settings{
AuthKey: "foo", AuthKey: "foo",
KubeSecret: "foo", KubeSecret: "foo",
@ -69,7 +69,7 @@ func TestSetupKube(t *testing.T) {
}, },
}, },
{ {
name: "TS_AUTHKEY set, state Secret does not exist, we do not have permissions to create it", name: "authkey-set-secret-missing-cannot-create",
cfg: &settings{ cfg: &settings{
AuthKey: "foo", AuthKey: "foo",
KubeSecret: "foo", KubeSecret: "foo",
@ -89,7 +89,7 @@ func TestSetupKube(t *testing.T) {
wantErr: true, wantErr: true,
}, },
{ {
name: "TS_AUTHKEY set, we encounter a non-404 error when trying to retrieve the state Secret", name: "authkey-set-get-secret-non-404-error",
cfg: &settings{ cfg: &settings{
AuthKey: "foo", AuthKey: "foo",
KubeSecret: "foo", KubeSecret: "foo",
@ -109,7 +109,7 @@ func TestSetupKube(t *testing.T) {
wantErr: true, wantErr: true,
}, },
{ {
name: "TS_AUTHKEY set, we encounter a non-404 error when trying to check Secret permissions", name: "authkey-set-check-perms-error",
cfg: &settings{ cfg: &settings{
AuthKey: "foo", AuthKey: "foo",
KubeSecret: "foo", KubeSecret: "foo",
@ -127,7 +127,7 @@ func TestSetupKube(t *testing.T) {
}, },
{ {
// Interactive login using URL in Pod logs // Interactive login using URL in Pod logs
name: "TS_AUTHKEY not set, state Secret does not exist, we have permissions to create it", name: "no-authkey-secret-missing-can-create",
cfg: &settings{ cfg: &settings{
KubeSecret: "foo", KubeSecret: "foo",
}, },
@ -145,7 +145,7 @@ func TestSetupKube(t *testing.T) {
}, },
{ {
// Interactive login using URL in Pod logs // Interactive login using URL in Pod logs
name: "TS_AUTHKEY not set, state Secret exists, but does not contain auth key", name: "no-authkey-secret-exists-no-key",
cfg: &settings{ cfg: &settings{
KubeSecret: "foo", KubeSecret: "foo",
}, },
@ -162,7 +162,7 @@ func TestSetupKube(t *testing.T) {
}}, }},
}, },
{ {
name: "TS_AUTHKEY not set, state Secret contains auth key, we do not have RBAC to patch it", name: "no-authkey-secret-has-key-cannot-patch",
cfg: &settings{ cfg: &settings{
KubeSecret: "foo", KubeSecret: "foo",
}, },
@ -180,7 +180,7 @@ func TestSetupKube(t *testing.T) {
wantErr: true, wantErr: true,
}, },
{ {
name: "TS_AUTHKEY not set, state Secret contains auth key, we have RBAC to patch it", name: "no-authkey-secret-has-key-can-patch",
cfg: &settings{ cfg: &settings{
KubeSecret: "foo", KubeSecret: "foo",
}, },

@ -89,24 +89,24 @@ type settings struct {
func configFromEnv() (*settings, error) { func configFromEnv() (*settings, error) {
cfg := &settings{ cfg := &settings{
AuthKey: defaultEnvs([]string{"TS_AUTHKEY", "TS_AUTH_KEY"}, ""), AuthKey: defaultEnvs([]string{"TS_AUTHKEY", "TS_AUTH_KEY"}, ""),
ClientID: defaultEnv("TS_CLIENT_ID", ""), ClientID: defaultEnv("TS_CLIENT_ID", ""),
ClientSecret: defaultEnv("TS_CLIENT_SECRET", ""), ClientSecret: defaultEnv("TS_CLIENT_SECRET", ""),
IDToken: defaultEnv("TS_ID_TOKEN", ""), IDToken: defaultEnv("TS_ID_TOKEN", ""),
Audience: defaultEnv("TS_AUDIENCE", ""), Audience: defaultEnv("TS_AUDIENCE", ""),
Hostname: defaultEnv("TS_HOSTNAME", ""), Hostname: defaultEnv("TS_HOSTNAME", ""),
Routes: defaultEnvStringPointer("TS_ROUTES"), Routes: defaultEnvStringPointer("TS_ROUTES"),
ServeConfigPath: defaultEnv("TS_SERVE_CONFIG", ""), ServeConfigPath: defaultEnv("TS_SERVE_CONFIG", ""),
ProxyTargetIP: defaultEnv("TS_DEST_IP", ""), ProxyTargetIP: defaultEnv("TS_DEST_IP", ""),
ProxyTargetDNSName: defaultEnv("TS_EXPERIMENTAL_DEST_DNS_NAME", ""), ProxyTargetDNSName: defaultEnv("TS_EXPERIMENTAL_DEST_DNS_NAME", ""),
TailnetTargetIP: defaultEnv("TS_TAILNET_TARGET_IP", ""), TailnetTargetIP: defaultEnv("TS_TAILNET_TARGET_IP", ""),
TailnetTargetFQDN: defaultEnv("TS_TAILNET_TARGET_FQDN", ""), TailnetTargetFQDN: defaultEnv("TS_TAILNET_TARGET_FQDN", ""),
DaemonExtraArgs: defaultEnv("TS_TAILSCALED_EXTRA_ARGS", ""), DaemonExtraArgs: defaultEnv("TS_TAILSCALED_EXTRA_ARGS", ""),
ExtraArgs: defaultEnv("TS_EXTRA_ARGS", ""), ExtraArgs: defaultEnv("TS_EXTRA_ARGS", ""),
InKubernetes: os.Getenv("KUBERNETES_SERVICE_HOST") != "", InKubernetes: os.Getenv("KUBERNETES_SERVICE_HOST") != "",
UserspaceMode: defaultBool("TS_USERSPACE", true), UserspaceMode: defaultBool("TS_USERSPACE", true),
StateDir: defaultEnv("TS_STATE_DIR", ""), StateDir: defaultEnv("TS_STATE_DIR", ""),
AcceptDNS: defaultEnvBoolPointer("TS_ACCEPT_DNS"), AcceptDNS: defaultEnvBoolPointer("TS_ACCEPT_DNS"),
KubeSecret: func() string { KubeSecret: func() string {
if os.Getenv("KUBERNETES_SERVICE_HOST") != "" { if os.Getenv("KUBERNETES_SERVICE_HOST") != "" {
return defaultEnv("TS_KUBE_SECRET", "tailscale") return defaultEnv("TS_KUBE_SECRET", "tailscale")

@ -46,30 +46,30 @@ func TestNoContent(t *testing.T) {
want string want string
}{ }{
{ {
name: "no challenge", name: "no-challenge",
}, },
{ {
name: "valid challenge", name: "valid-challenge",
input: "input", input: "input",
want: "response input", want: "response input",
}, },
{ {
name: "valid challenge hostname", name: "valid-challenge-hostname",
input: "ts_derp99b.tailscale.com", input: "ts_derp99b.tailscale.com",
want: "response ts_derp99b.tailscale.com", want: "response ts_derp99b.tailscale.com",
}, },
{ {
name: "invalid challenge", name: "invalid-challenge",
input: "foo\x00bar", input: "foo\x00bar",
want: "", want: "",
}, },
{ {
name: "whitespace invalid challenge", name: "whitespace-invalid-challenge",
input: "foo bar", input: "foo bar",
want: "", want: "",
}, },
{ {
name: "long challenge", name: "long-challenge",
input: strings.Repeat("x", 65), input: strings.Repeat("x", 65),
want: "", want: "",
}, },

@ -30,7 +30,7 @@ func TestEmbeddedTypeUnmarshal(t *testing.T) {
}, },
} }
t.Run("unmarshal gitops type from acl type", func(t *testing.T) { t.Run("unmarshal-gitops-from-acl", func(t *testing.T) {
b, _ := json.Marshal(aclTestErr) b, _ := json.Marshal(aclTestErr)
var e ACLGitopsTestError var e ACLGitopsTestError
err := json.Unmarshal(b, &e) err := json.Unmarshal(b, &e)
@ -41,7 +41,7 @@ func TestEmbeddedTypeUnmarshal(t *testing.T) {
t.Fatalf("user heading for 'ACLError' not found in gitops error: %v", e.Error()) t.Fatalf("user heading for 'ACLError' not found in gitops error: %v", e.Error())
} }
}) })
t.Run("unmarshal acl type from gitops type", func(t *testing.T) { t.Run("unmarshal-acl-from-gitops", func(t *testing.T) {
b, _ := json.Marshal(gitopsErr) b, _ := json.Marshal(gitopsErr)
var e tailscale.ACLTestError var e tailscale.ACLTestError
err := json.Unmarshal(b, &e) err := json.Unmarshal(b, &e)

@ -24,7 +24,7 @@ func TestNameserver(t *testing.T) {
wantResp *dns.Msg wantResp *dns.Msg
}{ }{
{ {
name: "A record query, record exists", name: "A-record-exists",
ip4: map[dnsname.FQDN][]net.IP{dnsname.FQDN("foo.bar.com."): {{1, 2, 3, 4}}}, ip4: map[dnsname.FQDN][]net.IP{dnsname.FQDN("foo.bar.com."): {{1, 2, 3, 4}}},
query: &dns.Msg{ query: &dns.Msg{
Question: []dns.Question{{Name: "foo.bar.com", Qtype: dns.TypeA}}, Question: []dns.Question{{Name: "foo.bar.com", Qtype: dns.TypeA}},
@ -46,7 +46,7 @@ func TestNameserver(t *testing.T) {
}}, }},
}, },
{ {
name: "A record query, record does not exist", name: "A-record-not-exists",
ip4: map[dnsname.FQDN][]net.IP{dnsname.FQDN("foo.bar.com."): {{1, 2, 3, 4}}}, ip4: map[dnsname.FQDN][]net.IP{dnsname.FQDN("foo.bar.com."): {{1, 2, 3, 4}}},
query: &dns.Msg{ query: &dns.Msg{
Question: []dns.Question{{Name: "baz.bar.com", Qtype: dns.TypeA}}, Question: []dns.Question{{Name: "baz.bar.com", Qtype: dns.TypeA}},
@ -64,7 +64,7 @@ func TestNameserver(t *testing.T) {
}}, }},
}, },
{ {
name: "A record query, but the name is not a valid FQDN", name: "A-record-invalid-FQDN",
ip4: map[dnsname.FQDN][]net.IP{dnsname.FQDN("foo.bar.com."): {{1, 2, 3, 4}}}, ip4: map[dnsname.FQDN][]net.IP{dnsname.FQDN("foo.bar.com."): {{1, 2, 3, 4}}},
query: &dns.Msg{ query: &dns.Msg{
Question: []dns.Question{{Name: "foo..bar.com", Qtype: dns.TypeA}}, Question: []dns.Question{{Name: "foo..bar.com", Qtype: dns.TypeA}},
@ -80,7 +80,7 @@ func TestNameserver(t *testing.T) {
}}, }},
}, },
{ {
name: "AAAA record query, A record exists", name: "AAAA-query-A-record-exists",
ip4: map[dnsname.FQDN][]net.IP{dnsname.FQDN("foo.bar.com."): {{1, 2, 3, 4}}}, ip4: map[dnsname.FQDN][]net.IP{dnsname.FQDN("foo.bar.com."): {{1, 2, 3, 4}}},
query: &dns.Msg{ query: &dns.Msg{
Question: []dns.Question{{Name: "foo.bar.com", Qtype: dns.TypeAAAA}}, Question: []dns.Question{{Name: "foo.bar.com", Qtype: dns.TypeAAAA}},
@ -97,7 +97,7 @@ func TestNameserver(t *testing.T) {
}}, }},
}, },
{ {
name: "AAAA record query, A record does not exist", name: "AAAA-query-A-record-not-exists",
ip4: map[dnsname.FQDN][]net.IP{dnsname.FQDN("foo.bar.com."): {{1, 2, 3, 4}}}, ip4: map[dnsname.FQDN][]net.IP{dnsname.FQDN("foo.bar.com."): {{1, 2, 3, 4}}},
query: &dns.Msg{ query: &dns.Msg{
Question: []dns.Question{{Name: "baz.bar.com", Qtype: dns.TypeAAAA}}, Question: []dns.Question{{Name: "baz.bar.com", Qtype: dns.TypeAAAA}},
@ -114,7 +114,7 @@ func TestNameserver(t *testing.T) {
}}, }},
}, },
{ {
name: "AAAA record query with IPv6 record", name: "AAAA-query-ipv6-record",
ip6: map[dnsname.FQDN][]net.IP{dnsname.FQDN("foo.bar.com."): {net.ParseIP("2001:db8::1")}}, ip6: map[dnsname.FQDN][]net.IP{dnsname.FQDN("foo.bar.com."): {net.ParseIP("2001:db8::1")}},
query: &dns.Msg{ query: &dns.Msg{
Question: []dns.Question{{Name: "foo.bar.com", Qtype: dns.TypeAAAA}}, Question: []dns.Question{{Name: "foo.bar.com", Qtype: dns.TypeAAAA}},
@ -136,7 +136,7 @@ func TestNameserver(t *testing.T) {
}}, }},
}, },
{ {
name: "Dual-stack: both A and AAAA records exist", name: "dual-stack-A-and-AAAA",
ip4: map[dnsname.FQDN][]net.IP{dnsname.FQDN("dual.bar.com."): {{10, 0, 0, 1}}}, ip4: map[dnsname.FQDN][]net.IP{dnsname.FQDN("dual.bar.com."): {{10, 0, 0, 1}}},
ip6: map[dnsname.FQDN][]net.IP{dnsname.FQDN("dual.bar.com."): {net.ParseIP("2001:db8::1")}}, ip6: map[dnsname.FQDN][]net.IP{dnsname.FQDN("dual.bar.com."): {net.ParseIP("2001:db8::1")}},
query: &dns.Msg{ query: &dns.Msg{
@ -157,7 +157,7 @@ func TestNameserver(t *testing.T) {
}}, }},
}, },
{ {
name: "CNAME record query", name: "CNAME-query",
ip4: map[dnsname.FQDN][]net.IP{dnsname.FQDN("foo.bar.com."): {{1, 2, 3, 4}}}, ip4: map[dnsname.FQDN][]net.IP{dnsname.FQDN("foo.bar.com."): {{1, 2, 3, 4}}},
query: &dns.Msg{ query: &dns.Msg{
Question: []dns.Question{{Name: "foo.bar.com", Qtype: dns.TypeCNAME}}, Question: []dns.Question{{Name: "foo.bar.com", Qtype: dns.TypeCNAME}},
@ -200,20 +200,20 @@ func TestResetRecords(t *testing.T) {
wantsErr bool wantsErr bool
}{ }{
{ {
name: "previously empty nameserver.ip4 gets set", name: "previously-empty-nameserver-ip4-gets-set",
config: []byte(`{"version": "v1alpha1", "ip4": {"foo.bar.com": ["1.2.3.4"]}}`), config: []byte(`{"version": "v1alpha1", "ip4": {"foo.bar.com": ["1.2.3.4"]}}`),
wantsIp4: map[dnsname.FQDN][]net.IP{"foo.bar.com.": {{1, 2, 3, 4}}}, wantsIp4: map[dnsname.FQDN][]net.IP{"foo.bar.com.": {{1, 2, 3, 4}}},
wantsIp6: make(map[dnsname.FQDN][]net.IP), wantsIp6: make(map[dnsname.FQDN][]net.IP),
}, },
{ {
name: "nameserver.ip4 gets reset", name: "nameserver-ip4-gets-reset",
hasIp4: map[dnsname.FQDN][]net.IP{"baz.bar.com.": {{1, 1, 3, 3}}}, hasIp4: map[dnsname.FQDN][]net.IP{"baz.bar.com.": {{1, 1, 3, 3}}},
config: []byte(`{"version": "v1alpha1", "ip4": {"foo.bar.com": ["1.2.3.4"]}}`), config: []byte(`{"version": "v1alpha1", "ip4": {"foo.bar.com": ["1.2.3.4"]}}`),
wantsIp4: map[dnsname.FQDN][]net.IP{"foo.bar.com.": {{1, 2, 3, 4}}}, wantsIp4: map[dnsname.FQDN][]net.IP{"foo.bar.com.": {{1, 2, 3, 4}}},
wantsIp6: make(map[dnsname.FQDN][]net.IP), wantsIp6: make(map[dnsname.FQDN][]net.IP),
}, },
{ {
name: "configuration with incompatible version", name: "configuration-with-incompatible-version",
hasIp4: map[dnsname.FQDN][]net.IP{"baz.bar.com.": {{1, 1, 3, 3}}}, hasIp4: map[dnsname.FQDN][]net.IP{"baz.bar.com.": {{1, 1, 3, 3}}},
config: []byte(`{"version": "v1beta1", "ip4": {"foo.bar.com": ["1.2.3.4"]}}`), config: []byte(`{"version": "v1beta1", "ip4": {"foo.bar.com": ["1.2.3.4"]}}`),
wantsIp4: map[dnsname.FQDN][]net.IP{"baz.bar.com.": {{1, 1, 3, 3}}}, wantsIp4: map[dnsname.FQDN][]net.IP{"baz.bar.com.": {{1, 1, 3, 3}}},
@ -221,26 +221,26 @@ func TestResetRecords(t *testing.T) {
wantsErr: true, wantsErr: true,
}, },
{ {
name: "nameserver.ip4 gets reset to empty config when no configuration is provided", name: "nameserver-ip4-gets-reset-to-empty-config-when-no-configuration-is-provided",
hasIp4: map[dnsname.FQDN][]net.IP{"baz.bar.com.": {{1, 1, 3, 3}}}, hasIp4: map[dnsname.FQDN][]net.IP{"baz.bar.com.": {{1, 1, 3, 3}}},
wantsIp4: make(map[dnsname.FQDN][]net.IP), wantsIp4: make(map[dnsname.FQDN][]net.IP),
wantsIp6: make(map[dnsname.FQDN][]net.IP), wantsIp6: make(map[dnsname.FQDN][]net.IP),
}, },
{ {
name: "nameserver.ip4 gets reset to empty config when the provided configuration is empty", name: "nameserver-ip4-gets-reset-to-empty-config-when-the-provided-configuration-is-empty",
hasIp4: map[dnsname.FQDN][]net.IP{"baz.bar.com.": {{1, 1, 3, 3}}}, hasIp4: map[dnsname.FQDN][]net.IP{"baz.bar.com.": {{1, 1, 3, 3}}},
config: []byte(`{"version": "v1alpha1", "ip4": {}}`), config: []byte(`{"version": "v1alpha1", "ip4": {}}`),
wantsIp4: make(map[dnsname.FQDN][]net.IP), wantsIp4: make(map[dnsname.FQDN][]net.IP),
wantsIp6: make(map[dnsname.FQDN][]net.IP), wantsIp6: make(map[dnsname.FQDN][]net.IP),
}, },
{ {
name: "nameserver.ip6 gets set", name: "nameserver-ip6-gets-set",
config: []byte(`{"version": "v1alpha1", "ip6": {"foo.bar.com": ["2001:db8::1"]}}`), config: []byte(`{"version": "v1alpha1", "ip6": {"foo.bar.com": ["2001:db8::1"]}}`),
wantsIp4: make(map[dnsname.FQDN][]net.IP), wantsIp4: make(map[dnsname.FQDN][]net.IP),
wantsIp6: map[dnsname.FQDN][]net.IP{"foo.bar.com.": {net.ParseIP("2001:db8::1")}}, wantsIp6: map[dnsname.FQDN][]net.IP{"foo.bar.com.": {net.ParseIP("2001:db8::1")}},
}, },
{ {
name: "dual-stack configuration", name: "dual-stack-configuration",
config: []byte(`{"version": "v1alpha1", "ip4": {"dual.bar.com": ["10.0.0.1"]}, "ip6": {"dual.bar.com": ["2001:db8::1"]}}`), config: []byte(`{"version": "v1alpha1", "ip4": {"dual.bar.com": ["10.0.0.1"]}, "ip6": {"dual.bar.com": ["2001:db8::1"]}}`),
wantsIp4: map[dnsname.FQDN][]net.IP{"dual.bar.com.": {{10, 0, 0, 1}}}, wantsIp4: map[dnsname.FQDN][]net.IP{"dual.bar.com.": {{10, 0, 0, 1}}},
wantsIp6: map[dnsname.FQDN][]net.IP{"dual.bar.com.": {net.ParseIP("2001:db8::1")}}, wantsIp6: map[dnsname.FQDN][]net.IP{"dual.bar.com.": {net.ParseIP("2001:db8::1")}},

@ -80,7 +80,7 @@ func TestNameserverReconciler(t *testing.T) {
nameserverLabels := nameserverResourceLabels(dnsConfig.Name, tsNamespace) nameserverLabels := nameserverResourceLabels(dnsConfig.Name, tsNamespace)
wantsDeploy := &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "nameserver", Namespace: tsNamespace}, TypeMeta: metav1.TypeMeta{Kind: "Deployment", APIVersion: appsv1.SchemeGroupVersion.Identifier()}} wantsDeploy := &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "nameserver", Namespace: tsNamespace}, TypeMeta: metav1.TypeMeta{Kind: "Deployment", APIVersion: appsv1.SchemeGroupVersion.Identifier()}}
t.Run("deployment has expected fields", func(t *testing.T) { t.Run("deployment-expected-fields", func(t *testing.T) {
if err = yaml.Unmarshal(deployYaml, wantsDeploy); err != nil { if err = yaml.Unmarshal(deployYaml, wantsDeploy); err != nil {
t.Fatalf("unmarshalling yaml: %v", err) t.Fatalf("unmarshalling yaml: %v", err)
} }
@ -102,7 +102,7 @@ func TestNameserverReconciler(t *testing.T) {
}) })
wantsSvc := &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "nameserver", Namespace: tsNamespace}, TypeMeta: metav1.TypeMeta{Kind: "Service", APIVersion: corev1.SchemeGroupVersion.Identifier()}} wantsSvc := &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "nameserver", Namespace: tsNamespace}, TypeMeta: metav1.TypeMeta{Kind: "Service", APIVersion: corev1.SchemeGroupVersion.Identifier()}}
t.Run("service has expected fields", func(t *testing.T) { t.Run("service-expected-fields", func(t *testing.T) {
if err = yaml.Unmarshal(svcYaml, wantsSvc); err != nil { if err = yaml.Unmarshal(svcYaml, wantsSvc); err != nil {
t.Fatalf("unmarshalling yaml: %v", err) t.Fatalf("unmarshalling yaml: %v", err)
} }
@ -113,7 +113,7 @@ func TestNameserverReconciler(t *testing.T) {
expectEqual(t, fc, wantsSvc) expectEqual(t, fc, wantsSvc)
}) })
t.Run("dns config status is set", func(t *testing.T) { t.Run("dns-config-status-set", func(t *testing.T) {
// Verify that DNSConfig advertizes the nameserver's Service IP address, // Verify that DNSConfig advertizes the nameserver's Service IP address,
// has the ready status condition and tailscale finalizer. // has the ready status condition and tailscale finalizer.
mustUpdate(t, fc, "tailscale", "nameserver", func(svc *corev1.Service) { mustUpdate(t, fc, "tailscale", "nameserver", func(svc *corev1.Service) {
@ -136,7 +136,7 @@ func TestNameserverReconciler(t *testing.T) {
expectEqual(t, fc, dnsConfig) expectEqual(t, fc, dnsConfig)
}) })
t.Run("nameserver image can be updated", func(t *testing.T) { t.Run("nameserver-image-updated", func(t *testing.T) {
// Verify that nameserver image gets updated to match DNSConfig spec. // Verify that nameserver image gets updated to match DNSConfig spec.
mustUpdate(t, fc, "", "test", func(dnsCfg *tsapi.DNSConfig) { mustUpdate(t, fc, "", "test", func(dnsCfg *tsapi.DNSConfig) {
dnsCfg.Spec.Nameserver.Image.Tag = "v0.0.2" dnsCfg.Spec.Nameserver.Image.Tag = "v0.0.2"
@ -146,7 +146,7 @@ func TestNameserverReconciler(t *testing.T) {
expectEqual(t, fc, wantsDeploy) expectEqual(t, fc, wantsDeploy)
}) })
t.Run("reconciler does not overwrite custom configuration", func(t *testing.T) { t.Run("reconciler-preserves-custom-config", func(t *testing.T) {
// Verify that when another actor sets ConfigMap data, it does not get // Verify that when another actor sets ConfigMap data, it does not get
// overwritten by nameserver reconciler. // overwritten by nameserver reconciler.
dnsRecords := &operatorutils.Records{Version: "v1alpha1", IP4: map[string][]string{"foo.ts.net": {"1.2.3.4"}}} dnsRecords := &operatorutils.Records{Version: "v1alpha1", IP4: map[string][]string{"foo.ts.net": {"1.2.3.4"}}}
@ -175,7 +175,7 @@ func TestNameserverReconciler(t *testing.T) {
expectEqual(t, fc, wantCm) expectEqual(t, fc, wantCm)
}) })
t.Run("uses default nameserver image", func(t *testing.T) { t.Run("uses-default-nameserver-image", func(t *testing.T) {
// Verify that if dnsconfig.spec.nameserver.image.{repo,tag} are unset, // Verify that if dnsconfig.spec.nameserver.image.{repo,tag} are unset,
// the nameserver image defaults to tailscale/k8s-nameserver:unstable. // the nameserver image defaults to tailscale/k8s-nameserver:unstable.
mustUpdate(t, fc, "", "test", func(dnsCfg *tsapi.DNSConfig) { mustUpdate(t, fc, "", "test", func(dnsCfg *tsapi.DNSConfig) {

@ -1498,24 +1498,28 @@ func TestProxyFirewallMode(t *testing.T) {
func Test_isMagicDNSName(t *testing.T) { func Test_isMagicDNSName(t *testing.T) {
tests := []struct { tests := []struct {
name string
in string in string
want bool want bool
}{ }{
{ {
name: "foo-tail4567-ts-net",
in: "foo.tail4567.ts.net", in: "foo.tail4567.ts.net",
want: true, want: true,
}, },
{ {
name: "foo-tail4567-ts-net-trailing-dot",
in: "foo.tail4567.ts.net.", in: "foo.tail4567.ts.net.",
want: true, want: true,
}, },
{ {
name: "foo-tail4567",
in: "foo.tail4567", in: "foo.tail4567",
want: false, want: false,
}, },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.in, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
if got := isMagicDNSName(tt.in); got != tt.want { if got := isMagicDNSName(tt.in); got != tt.want {
t.Errorf("isMagicDNSName(%q) = %v, want %v", tt.in, got, tt.want) t.Errorf("isMagicDNSName(%q) = %v, want %v", tt.in, got, tt.want)
} }
@ -1756,7 +1760,7 @@ func Test_clusterDomainFromResolverConf(t *testing.T) {
want string want string
}{ }{
{ {
name: "success- custom domain", name: "success-custom-domain",
conf: &resolvconffile.Config{ conf: &resolvconffile.Config{
SearchDomains: []dnsname.FQDN{toFQDN(t, "foo.svc.department.org.io"), toFQDN(t, "svc.department.org.io"), toFQDN(t, "department.org.io")}, SearchDomains: []dnsname.FQDN{toFQDN(t, "foo.svc.department.org.io"), toFQDN(t, "svc.department.org.io"), toFQDN(t, "department.org.io")},
}, },
@ -1764,7 +1768,7 @@ func Test_clusterDomainFromResolverConf(t *testing.T) {
want: "department.org.io", want: "department.org.io",
}, },
{ {
name: "success- default domain", name: "success-default-domain",
conf: &resolvconffile.Config{ conf: &resolvconffile.Config{
SearchDomains: []dnsname.FQDN{toFQDN(t, "foo.svc.cluster.local."), toFQDN(t, "svc.cluster.local."), toFQDN(t, "cluster.local.")}, SearchDomains: []dnsname.FQDN{toFQDN(t, "foo.svc.cluster.local."), toFQDN(t, "svc.cluster.local."), toFQDN(t, "cluster.local.")},
}, },
@ -1772,7 +1776,7 @@ func Test_clusterDomainFromResolverConf(t *testing.T) {
want: "cluster.local", want: "cluster.local",
}, },
{ {
name: "only two search domains found", name: "only-two-search-domains",
conf: &resolvconffile.Config{ conf: &resolvconffile.Config{
SearchDomains: []dnsname.FQDN{toFQDN(t, "svc.department.org.io"), toFQDN(t, "department.org.io")}, SearchDomains: []dnsname.FQDN{toFQDN(t, "svc.department.org.io"), toFQDN(t, "department.org.io")},
}, },
@ -1780,7 +1784,7 @@ func Test_clusterDomainFromResolverConf(t *testing.T) {
want: "cluster.local", want: "cluster.local",
}, },
{ {
name: "first search domain does not match the expected structure", name: "first-search-domain-mismatch",
conf: &resolvconffile.Config{ conf: &resolvconffile.Config{
SearchDomains: []dnsname.FQDN{toFQDN(t, "foo.bar.department.org.io"), toFQDN(t, "svc.department.org.io"), toFQDN(t, "some.other.fqdn")}, SearchDomains: []dnsname.FQDN{toFQDN(t, "foo.bar.department.org.io"), toFQDN(t, "svc.department.org.io"), toFQDN(t, "some.other.fqdn")},
}, },
@ -1788,7 +1792,7 @@ func Test_clusterDomainFromResolverConf(t *testing.T) {
want: "cluster.local", want: "cluster.local",
}, },
{ {
name: "second search domain does not match the expected structure", name: "second-search-domain-mismatch",
conf: &resolvconffile.Config{ conf: &resolvconffile.Config{
SearchDomains: []dnsname.FQDN{toFQDN(t, "foo.svc.department.org.io"), toFQDN(t, "foo.department.org.io"), toFQDN(t, "some.other.fqdn")}, SearchDomains: []dnsname.FQDN{toFQDN(t, "foo.svc.department.org.io"), toFQDN(t, "foo.department.org.io"), toFQDN(t, "some.other.fqdn")},
}, },
@ -1796,7 +1800,7 @@ func Test_clusterDomainFromResolverConf(t *testing.T) {
want: "cluster.local", want: "cluster.local",
}, },
{ {
name: "third search domain does not match the expected structure", name: "third-search-domain-mismatch",
conf: &resolvconffile.Config{ conf: &resolvconffile.Config{
SearchDomains: []dnsname.FQDN{toFQDN(t, "foo.svc.department.org.io"), toFQDN(t, "svc.department.org.io"), toFQDN(t, "some.other.fqdn")}, SearchDomains: []dnsname.FQDN{toFQDN(t, "foo.svc.department.org.io"), toFQDN(t, "svc.department.org.io"), toFQDN(t, "some.other.fqdn")},
}, },

@ -324,76 +324,76 @@ func Test_mergeStatefulSetLabelsOrAnnots(t *testing.T) {
want map[string]string want map[string]string
}{ }{
{ {
name: "no custom labels specified and none present in current labels, return current labels", name: "no-custom-labels-none-present",
current: map[string]string{kubetypes.LabelManaged: "true", LabelParentName: "foo", LabelParentType: "svc", LabelParentNamespace: "foo"}, current: map[string]string{kubetypes.LabelManaged: "true", LabelParentName: "foo", LabelParentType: "svc", LabelParentNamespace: "foo"},
want: map[string]string{kubetypes.LabelManaged: "true", LabelParentName: "foo", LabelParentType: "svc", LabelParentNamespace: "foo"}, want: map[string]string{kubetypes.LabelManaged: "true", LabelParentName: "foo", LabelParentType: "svc", LabelParentNamespace: "foo"},
managed: tailscaleManagedLabels, managed: tailscaleManagedLabels,
}, },
{ {
name: "no custom labels specified, but some present in current labels, return tailscale managed labels only from the current labels", name: "no-custom-labels-some-present",
current: map[string]string{"foo": "bar", "something.io/foo": "bar", kubetypes.LabelManaged: "true", LabelParentName: "foo", LabelParentType: "svc", LabelParentNamespace: "foo"}, current: map[string]string{"foo": "bar", "something.io/foo": "bar", kubetypes.LabelManaged: "true", LabelParentName: "foo", LabelParentType: "svc", LabelParentNamespace: "foo"},
want: map[string]string{kubetypes.LabelManaged: "true", LabelParentName: "foo", LabelParentType: "svc", LabelParentNamespace: "foo"}, want: map[string]string{kubetypes.LabelManaged: "true", LabelParentName: "foo", LabelParentType: "svc", LabelParentNamespace: "foo"},
managed: tailscaleManagedLabels, managed: tailscaleManagedLabels,
}, },
{ {
name: "custom labels specified, current labels only contain tailscale managed labels, return a union of both", name: "custom-labels-with-managed-only",
current: map[string]string{kubetypes.LabelManaged: "true", LabelParentName: "foo", LabelParentType: "svc", LabelParentNamespace: "foo"}, current: map[string]string{kubetypes.LabelManaged: "true", LabelParentName: "foo", LabelParentType: "svc", LabelParentNamespace: "foo"},
custom: map[string]string{"foo": "bar", "something.io/foo": "bar"}, custom: map[string]string{"foo": "bar", "something.io/foo": "bar"},
want: map[string]string{"foo": "bar", "something.io/foo": "bar", kubetypes.LabelManaged: "true", LabelParentName: "foo", LabelParentType: "svc", LabelParentNamespace: "foo"}, want: map[string]string{"foo": "bar", "something.io/foo": "bar", kubetypes.LabelManaged: "true", LabelParentName: "foo", LabelParentType: "svc", LabelParentNamespace: "foo"},
managed: tailscaleManagedLabels, managed: tailscaleManagedLabels,
}, },
{ {
name: "custom labels specified, current labels contain tailscale managed labels and custom labels, some of which re not present in the new custom labels, return a union of managed labels and the desired custom labels", name: "custom-labels-stale-removed",
current: map[string]string{"foo": "bar", "bar": "baz", "app": "1234", kubetypes.LabelManaged: "true", LabelParentName: "foo", LabelParentType: "svc", LabelParentNamespace: "foo"}, current: map[string]string{"foo": "bar", "bar": "baz", "app": "1234", kubetypes.LabelManaged: "true", LabelParentName: "foo", LabelParentType: "svc", LabelParentNamespace: "foo"},
custom: map[string]string{"foo": "bar", "something.io/foo": "bar"}, custom: map[string]string{"foo": "bar", "something.io/foo": "bar"},
want: map[string]string{"foo": "bar", "something.io/foo": "bar", "app": "1234", kubetypes.LabelManaged: "true", LabelParentName: "foo", LabelParentType: "svc", LabelParentNamespace: "foo"}, want: map[string]string{"foo": "bar", "something.io/foo": "bar", "app": "1234", kubetypes.LabelManaged: "true", LabelParentName: "foo", LabelParentType: "svc", LabelParentNamespace: "foo"},
managed: tailscaleManagedLabels, managed: tailscaleManagedLabels,
}, },
{ {
name: "no current labels present, return custom labels only", name: "no-current-labels-return-custom",
custom: map[string]string{"foo": "bar", "something.io/foo": "bar"}, custom: map[string]string{"foo": "bar", "something.io/foo": "bar"},
want: map[string]string{"foo": "bar", "something.io/foo": "bar"}, want: map[string]string{"foo": "bar", "something.io/foo": "bar"},
managed: tailscaleManagedLabels, managed: tailscaleManagedLabels,
}, },
{ {
name: "no current labels present, no custom labels specified, return empty map", name: "no-current-no-custom-return-empty",
want: map[string]string{}, want: map[string]string{},
managed: tailscaleManagedLabels, managed: tailscaleManagedLabels,
}, },
{ {
name: "no custom annots specified and none present in current annots, return current annots", name: "no-custom-annots-none-present",
current: map[string]string{podAnnotationLastSetClusterIP: "1.2.3.4"}, current: map[string]string{podAnnotationLastSetClusterIP: "1.2.3.4"},
want: map[string]string{podAnnotationLastSetClusterIP: "1.2.3.4"}, want: map[string]string{podAnnotationLastSetClusterIP: "1.2.3.4"},
managed: tailscaleManagedAnnotations, managed: tailscaleManagedAnnotations,
}, },
{ {
name: "no custom annots specified, but some present in current annots, return tailscale managed annots only from the current annots", name: "no-custom-annots-some-present",
current: map[string]string{"foo": "bar", "something.io/foo": "bar", podAnnotationLastSetClusterIP: "1.2.3.4"}, current: map[string]string{"foo": "bar", "something.io/foo": "bar", podAnnotationLastSetClusterIP: "1.2.3.4"},
want: map[string]string{podAnnotationLastSetClusterIP: "1.2.3.4"}, want: map[string]string{podAnnotationLastSetClusterIP: "1.2.3.4"},
managed: tailscaleManagedAnnotations, managed: tailscaleManagedAnnotations,
}, },
{ {
name: "custom annots specified, current annots only contain tailscale managed annots, return a union of both", name: "custom-annots-with-managed-only",
current: map[string]string{podAnnotationLastSetClusterIP: "1.2.3.4"}, current: map[string]string{podAnnotationLastSetClusterIP: "1.2.3.4"},
custom: map[string]string{"foo": "bar", "something.io/foo": "bar"}, custom: map[string]string{"foo": "bar", "something.io/foo": "bar"},
want: map[string]string{"foo": "bar", "something.io/foo": "bar", podAnnotationLastSetClusterIP: "1.2.3.4"}, want: map[string]string{"foo": "bar", "something.io/foo": "bar", podAnnotationLastSetClusterIP: "1.2.3.4"},
managed: tailscaleManagedAnnotations, managed: tailscaleManagedAnnotations,
}, },
{ {
name: "custom annots specified, current annots contain tailscale managed annots and custom annots, some of which are not present in the new custom annots, return a union of managed annots and the desired custom annots", name: "custom-annots-stale-removed",
current: map[string]string{"foo": "bar", "something.io/foo": "bar", podAnnotationLastSetClusterIP: "1.2.3.4"}, current: map[string]string{"foo": "bar", "something.io/foo": "bar", podAnnotationLastSetClusterIP: "1.2.3.4"},
custom: map[string]string{"something.io/foo": "bar"}, custom: map[string]string{"something.io/foo": "bar"},
want: map[string]string{"something.io/foo": "bar", podAnnotationLastSetClusterIP: "1.2.3.4"}, want: map[string]string{"something.io/foo": "bar", podAnnotationLastSetClusterIP: "1.2.3.4"},
managed: tailscaleManagedAnnotations, managed: tailscaleManagedAnnotations,
}, },
{ {
name: "no current annots present, return custom annots only", name: "no-current-annots-return-custom",
custom: map[string]string{"foo": "bar", "something.io/foo": "bar"}, custom: map[string]string{"foo": "bar", "something.io/foo": "bar"},
want: map[string]string{"foo": "bar", "something.io/foo": "bar"}, want: map[string]string{"foo": "bar", "something.io/foo": "bar"},
managed: tailscaleManagedAnnotations, managed: tailscaleManagedAnnotations,
}, },
{ {
name: "no current labels present, no custom labels specified, return empty map", name: "no-current-annots-no-custom-return-empty",
want: map[string]string{}, want: map[string]string{},
managed: tailscaleManagedAnnotations, managed: tailscaleManagedAnnotations,
}, },

@ -17,7 +17,7 @@ import (
) )
func TestRecorderSpecs(t *testing.T) { func TestRecorderSpecs(t *testing.T) {
t.Run("ensure spec fields are passed through correctly", func(t *testing.T) { t.Run("spec-fields-passthrough", func(t *testing.T) {
tsr := &tsapi.Recorder{ tsr := &tsapi.Recorder{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: "test", Name: "test",

@ -1533,13 +1533,13 @@ func TestParseNLArgs(t *testing.T) {
parseDisablements: true, parseDisablements: true,
}, },
{ {
name: "key no votes", name: "key-no-votes",
input: []string{"nlpub:" + strings.Repeat("00", 32)}, input: []string{"nlpub:" + strings.Repeat("00", 32)},
parseKeys: true, parseKeys: true,
wantKeys: []tka.Key{{Kind: tka.Key25519, Votes: 1, Public: bytes.Repeat([]byte{0}, 32)}}, wantKeys: []tka.Key{{Kind: tka.Key25519, Votes: 1, Public: bytes.Repeat([]byte{0}, 32)}},
}, },
{ {
name: "key with votes", name: "key-with-votes",
input: []string{"nlpub:" + strings.Repeat("01", 32) + "?5"}, input: []string{"nlpub:" + strings.Repeat("01", 32) + "?5"},
parseKeys: true, parseKeys: true,
wantKeys: []tka.Key{{Kind: tka.Key25519, Votes: 5, Public: bytes.Repeat([]byte{1}, 32)}}, wantKeys: []tka.Key{{Kind: tka.Key25519, Votes: 5, Public: bytes.Repeat([]byte{1}, 32)}},
@ -1551,13 +1551,13 @@ func TestParseNLArgs(t *testing.T) {
wantDisablements: [][]byte{bytes.Repeat([]byte{2}, 32), bytes.Repeat([]byte{3}, 32)}, wantDisablements: [][]byte{bytes.Repeat([]byte{2}, 32), bytes.Repeat([]byte{3}, 32)},
}, },
{ {
name: "disablements not allowed", name: "disablements-not-allowed",
input: []string{"disablement:" + strings.Repeat("02", 32)}, input: []string{"disablement:" + strings.Repeat("02", 32)},
parseKeys: true, parseKeys: true,
wantErr: fmt.Errorf("parsing key 1: key hex string doesn't have expected type prefix tlpub:"), wantErr: fmt.Errorf("parsing key 1: key hex string doesn't have expected type prefix tlpub:"),
}, },
{ {
name: "keys not allowed", name: "keys-not-allowed",
input: []string{"nlpub:" + strings.Repeat("02", 32)}, input: []string{"nlpub:" + strings.Repeat("02", 32)},
parseDisablements: true, parseDisablements: true,
wantErr: fmt.Errorf("parsing argument 1: expected value with \"disablement:\" or \"disablement-secret:\" prefix, got %q", "nlpub:0202020202020202020202020202020202020202020202020202020202020202"), wantErr: fmt.Errorf("parsing argument 1: expected value with \"disablement:\" or \"disablement-secret:\" prefix, got %q", "nlpub:0202020202020202020202020202020202020202020202020202020202020202"),

@ -76,7 +76,7 @@ users:
token: unused`, token: unused`,
}, },
{ {
name: "all configs, clusters, users have been deleted", name: "all-configs-clusters-users-deleted",
in: `apiVersion: v1 in: `apiVersion: v1
clusters: null clusters: null
contexts: null contexts: null

@ -30,7 +30,7 @@ func Test_listCerts(t *testing.T) {
wantErr bool wantErr bool
}{ }{
{ {
name: "normal response", name: "normal-response",
caller: fakeAPICaller{ caller: fakeAPICaller{
Data: json.RawMessage(`{ Data: json.RawMessage(`{
"certificates" : [ "certificates" : [
@ -117,12 +117,12 @@ func Test_listCerts(t *testing.T) {
}, },
}, },
{ {
name: "call error", name: "call-error",
caller: fakeAPICaller{nil, fmt.Errorf("caller failed")}, caller: fakeAPICaller{nil, fmt.Errorf("caller failed")},
wantErr: true, wantErr: true,
}, },
{ {
name: "payload decode error", name: "payload-decode-error",
caller: fakeAPICaller{json.RawMessage("This isn't JSON!"), nil}, caller: fakeAPICaller{json.RawMessage("This isn't JSON!"), nil},
wantErr: true, wantErr: true,
}, },

@ -14,7 +14,7 @@ import (
) )
func TestFilterFormatAndSortExitNodes(t *testing.T) { func TestFilterFormatAndSortExitNodes(t *testing.T) {
t.Run("without filter", func(t *testing.T) { t.Run("without-filter", func(t *testing.T) {
ps := []*ipnstate.PeerStatus{ ps := []*ipnstate.PeerStatus{
{ {
HostName: "everest-1", HostName: "everest-1",
@ -139,7 +139,7 @@ func TestFilterFormatAndSortExitNodes(t *testing.T) {
} }
}) })
t.Run("with country filter", func(t *testing.T) { t.Run("with-country-filter", func(t *testing.T) {
ps := []*ipnstate.PeerStatus{ ps := []*ipnstate.PeerStatus{
{ {
HostName: "baker-1", HostName: "baker-1",

@ -1056,49 +1056,49 @@ func TestSrcTypeFromFlags(t *testing.T) {
expectedErr bool expectedErr bool
}{ }{
{ {
name: "only http set", name: "only-http-set",
env: &serveEnv{http: 80}, env: &serveEnv{http: 80},
expectedType: serveTypeHTTP, expectedType: serveTypeHTTP,
expectedPort: 80, expectedPort: 80,
expectedErr: false, expectedErr: false,
}, },
{ {
name: "only https set", name: "only-https-set",
env: &serveEnv{https: 10000}, env: &serveEnv{https: 10000},
expectedType: serveTypeHTTPS, expectedType: serveTypeHTTPS,
expectedPort: 10000, expectedPort: 10000,
expectedErr: false, expectedErr: false,
}, },
{ {
name: "only tcp set", name: "only-tcp-set",
env: &serveEnv{tcp: 8000}, env: &serveEnv{tcp: 8000},
expectedType: serveTypeTCP, expectedType: serveTypeTCP,
expectedPort: 8000, expectedPort: 8000,
expectedErr: false, expectedErr: false,
}, },
{ {
name: "only tls-terminated-tcp set", name: "only-tls-terminated-tcp-set",
env: &serveEnv{tlsTerminatedTCP: 8080}, env: &serveEnv{tlsTerminatedTCP: 8080},
expectedType: serveTypeTLSTerminatedTCP, expectedType: serveTypeTLSTerminatedTCP,
expectedPort: 8080, expectedPort: 8080,
expectedErr: false, expectedErr: false,
}, },
{ {
name: "defaults to https, port 443", name: "defaults-to-https-443",
env: &serveEnv{}, env: &serveEnv{},
expectedType: serveTypeHTTPS, expectedType: serveTypeHTTPS,
expectedPort: 443, expectedPort: 443,
expectedErr: false, expectedErr: false,
}, },
{ {
name: "defaults to https, port 443 for service", name: "defaults-to-https-443-for-service",
env: &serveEnv{service: "svc:foo"}, env: &serveEnv{service: "svc:foo"},
expectedType: serveTypeHTTPS, expectedType: serveTypeHTTPS,
expectedPort: 443, expectedPort: 443,
expectedErr: false, expectedErr: false,
}, },
{ {
name: "multiple types set", name: "multiple-types-set",
env: &serveEnv{http: 80, https: 443}, env: &serveEnv{http: 80, https: 443},
expectedPort: 0, expectedPort: 0,
expectedErr: true, expectedErr: true,
@ -1235,19 +1235,20 @@ func TestAcceptSetAppCapsFlag(t *testing.T) {
func TestCleanURLPath(t *testing.T) { func TestCleanURLPath(t *testing.T) {
tests := []struct { tests := []struct {
name string
input string input string
expected string expected string
wantErr bool wantErr bool
}{ }{
{input: "", expected: "/"}, {name: "empty", input: "", expected: "/"},
{input: "/", expected: "/"}, {name: "slash", input: "/", expected: "/"},
{input: "/foo", expected: "/foo"}, {name: "foo", input: "/foo", expected: "/foo"},
{input: "/foo/", expected: "/foo/"}, {name: "foo-trailing-slash", input: "/foo/", expected: "/foo/"},
{input: "/../bar", wantErr: true}, {name: "dotdot-bar", input: "/../bar", wantErr: true},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
actual, err := cleanURLPath(tt.input) actual, err := cleanURLPath(tt.input)
if tt.wantErr == true && err == nil { if tt.wantErr == true && err == nil {
@ -1275,18 +1276,18 @@ func TestAddServiceToPrefs(t *testing.T) {
expected []string expected []string
}{ }{
{ {
name: "add service to empty prefs", name: "add-service-to-empty-prefs",
svcName: "svc:foo", svcName: "svc:foo",
expected: []string{"svc:foo"}, expected: []string{"svc:foo"},
}, },
{ {
name: "add service to existing prefs", name: "add-service-to-existing-prefs",
svcName: "svc:bar", svcName: "svc:bar",
startServices: []string{"svc:foo"}, startServices: []string{"svc:foo"},
expected: []string{"svc:foo", "svc:bar"}, expected: []string{"svc:foo", "svc:bar"},
}, },
{ {
name: "add existing service to prefs", name: "add-existing-service-to-prefs",
svcName: "svc:foo", svcName: "svc:foo",
startServices: []string{"svc:foo"}, startServices: []string{"svc:foo"},
expected: []string{"svc:foo"}, expected: []string{"svc:foo"},
@ -1323,18 +1324,18 @@ func TestRemoveServiceFromPrefs(t *testing.T) {
expected []string expected []string
}{ }{
{ {
name: "remove service from empty prefs", name: "remove-service-from-empty-prefs",
svcName: "svc:foo", svcName: "svc:foo",
expected: []string{}, expected: []string{},
}, },
{ {
name: "remove existing service from prefs", name: "remove-existing-service-from-prefs",
svcName: "svc:foo", svcName: "svc:foo",
startServices: []string{"svc:foo"}, startServices: []string{"svc:foo"},
expected: []string{}, expected: []string{},
}, },
{ {
name: "remove service not in prefs", name: "remove-service-not-in-prefs",
svcName: "svc:bar", svcName: "svc:bar",
startServices: []string{"svc:foo"}, startServices: []string{"svc:foo"},
expected: []string{"svc:foo"}, expected: []string{"svc:foo"},
@ -1446,7 +1447,7 @@ func TestMessageForPort(t *testing.T) {
}, "\n"), }, "\n"),
}, },
{ {
name: "serve service http", name: "serve-service-http",
subcmd: serve, subcmd: serve,
serveConfig: &ipn.ServeConfig{ serveConfig: &ipn.ServeConfig{
Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{ Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{
@ -1490,7 +1491,7 @@ func TestMessageForPort(t *testing.T) {
}, "\n"), }, "\n"),
}, },
{ {
name: "serve service no capmap", name: "serve-service-no-capmap",
subcmd: serve, subcmd: serve,
serveConfig: &ipn.ServeConfig{ serveConfig: &ipn.ServeConfig{
Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{ Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{
@ -1534,7 +1535,7 @@ func TestMessageForPort(t *testing.T) {
}, "\n"), }, "\n"),
}, },
{ {
name: "serve service https non-default port", name: "serve-service-https-non-default-port",
subcmd: serve, subcmd: serve,
serveConfig: &ipn.ServeConfig{ serveConfig: &ipn.ServeConfig{
Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{ Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{
@ -1576,7 +1577,7 @@ func TestMessageForPort(t *testing.T) {
}, "\n"), }, "\n"),
}, },
{ {
name: "serve service TCPForward", name: "serve-service-TCPForward",
subcmd: serve, subcmd: serve,
serveConfig: &ipn.ServeConfig{ serveConfig: &ipn.ServeConfig{
Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{ Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{
@ -1613,7 +1614,7 @@ func TestMessageForPort(t *testing.T) {
}, "\n"), }, "\n"),
}, },
{ {
name: "serve service Tun", name: "serve-service-Tun",
subcmd: serve, subcmd: serve,
serveConfig: &ipn.ServeConfig{ serveConfig: &ipn.ServeConfig{
Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{ Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{
@ -1790,7 +1791,7 @@ func TestSetServe(t *testing.T) {
expectErr bool expectErr bool
}{ }{
{ {
name: "add new handler", name: "add-new-handler",
desc: "add a new http handler to empty config", desc: "add a new http handler to empty config",
cfg: &ipn.ServeConfig{}, cfg: &ipn.ServeConfig{},
dnsName: "foo.test.ts.net", dnsName: "foo.test.ts.net",
@ -1810,7 +1811,7 @@ func TestSetServe(t *testing.T) {
}, },
}, },
{ {
name: "update http handler", name: "update-http-handler",
desc: "update an existing http handler on the same port to same type", desc: "update an existing http handler on the same port to same type",
cfg: &ipn.ServeConfig{ cfg: &ipn.ServeConfig{
TCP: map[uint16]*ipn.TCPPortHandler{80: {HTTP: true}}, TCP: map[uint16]*ipn.TCPPortHandler{80: {HTTP: true}},
@ -1839,7 +1840,7 @@ func TestSetServe(t *testing.T) {
}, },
}, },
{ {
name: "update TCP handler", name: "update-TCP-handler",
desc: "update an existing TCP handler on the same port to a http handler", desc: "update an existing TCP handler on the same port to a http handler",
cfg: &ipn.ServeConfig{ cfg: &ipn.ServeConfig{
TCP: map[uint16]*ipn.TCPPortHandler{80: {TCPForward: "http://localhost:3000"}}, TCP: map[uint16]*ipn.TCPPortHandler{80: {TCPForward: "http://localhost:3000"}},
@ -1852,7 +1853,7 @@ func TestSetServe(t *testing.T) {
expectErr: true, expectErr: true,
}, },
{ {
name: "add new service handler", name: "add-new-service-handler",
desc: "add a new service TCP handler to empty config", desc: "add a new service TCP handler to empty config",
cfg: &ipn.ServeConfig{}, cfg: &ipn.ServeConfig{},
@ -1869,7 +1870,7 @@ func TestSetServe(t *testing.T) {
}, },
}, },
{ {
name: "update service handler", name: "update-service-handler",
desc: "update an existing service TCP handler on the same port to same type", desc: "update an existing service TCP handler on the same port to same type",
cfg: &ipn.ServeConfig{ cfg: &ipn.ServeConfig{
Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{ Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{
@ -1891,7 +1892,7 @@ func TestSetServe(t *testing.T) {
}, },
}, },
{ {
name: "update service handler", name: "update-service-handler",
desc: "update an existing service TCP handler on the same port to a http handler", desc: "update an existing service TCP handler on the same port to a http handler",
cfg: &ipn.ServeConfig{ cfg: &ipn.ServeConfig{
Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{ Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{
@ -1908,7 +1909,7 @@ func TestSetServe(t *testing.T) {
expectErr: true, expectErr: true,
}, },
{ {
name: "add new service handler", name: "add-new-service-handler",
desc: "add a new service HTTP handler to empty config", desc: "add a new service HTTP handler to empty config",
cfg: &ipn.ServeConfig{}, cfg: &ipn.ServeConfig{},
dnsName: "svc:bar", dnsName: "svc:bar",
@ -1932,7 +1933,7 @@ func TestSetServe(t *testing.T) {
}, },
}, },
{ {
name: "update existing service handler", name: "update-existing-service-handler",
desc: "update an existing service HTTP handler", desc: "update an existing service HTTP handler",
cfg: &ipn.ServeConfig{ cfg: &ipn.ServeConfig{
Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{ Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{
@ -1969,7 +1970,7 @@ func TestSetServe(t *testing.T) {
}, },
}, },
{ {
name: "add new service handler", name: "add-new-service-handler",
desc: "add a new service HTTP handler to existing service config", desc: "add a new service HTTP handler to existing service config",
cfg: &ipn.ServeConfig{ cfg: &ipn.ServeConfig{
Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{ Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{
@ -2014,7 +2015,7 @@ func TestSetServe(t *testing.T) {
}, },
}, },
{ {
name: "add new service mount", name: "add-new-service-mount",
desc: "add a new service mount to existing service config", desc: "add a new service mount to existing service config",
cfg: &ipn.ServeConfig{ cfg: &ipn.ServeConfig{
Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{ Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{
@ -2054,7 +2055,7 @@ func TestSetServe(t *testing.T) {
}, },
}, },
{ {
name: "add new service handler", name: "add-new-service-handler",
desc: "add a new service handler in tun mode to empty config", desc: "add a new service handler in tun mode to empty config",
cfg: &ipn.ServeConfig{}, cfg: &ipn.ServeConfig{},
dnsName: "svc:bar", dnsName: "svc:bar",
@ -2103,7 +2104,7 @@ func TestUnsetServe(t *testing.T) {
expectErr bool expectErr bool
}{ }{
{ {
name: "unset http handler", name: "unset-http-handler",
desc: "remove an existing http handler", desc: "remove an existing http handler",
cfg: &ipn.ServeConfig{ cfg: &ipn.ServeConfig{
TCP: map[uint16]*ipn.TCPPortHandler{ TCP: map[uint16]*ipn.TCPPortHandler{
@ -2128,7 +2129,7 @@ func TestUnsetServe(t *testing.T) {
expectErr: false, expectErr: false,
}, },
{ {
name: "unset service handler", name: "unset-service-handler",
desc: "remove an existing service TCP handler", desc: "remove an existing service TCP handler",
cfg: &ipn.ServeConfig{ cfg: &ipn.ServeConfig{
Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{ Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{
@ -2157,7 +2158,7 @@ func TestUnsetServe(t *testing.T) {
expectErr: false, expectErr: false,
}, },
{ {
name: "unset service handler tun", name: "unset-service-handler-tun",
desc: "remove an existing service handler in tun mode", desc: "remove an existing service handler in tun mode",
cfg: &ipn.ServeConfig{ cfg: &ipn.ServeConfig{
Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{ Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{
@ -2175,7 +2176,7 @@ func TestUnsetServe(t *testing.T) {
expectErr: false, expectErr: false,
}, },
{ {
name: "unset service handler tcp", name: "unset-service-handler-tcp",
desc: "remove an existing service TCP handler", desc: "remove an existing service TCP handler",
cfg: &ipn.ServeConfig{ cfg: &ipn.ServeConfig{
Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{ Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{
@ -2196,7 +2197,7 @@ func TestUnsetServe(t *testing.T) {
expectErr: false, expectErr: false,
}, },
{ {
name: "unset http handler not found", name: "unset-http-handler-not-found",
desc: "try to remove a non-existing http handler", desc: "try to remove a non-existing http handler",
cfg: &ipn.ServeConfig{ cfg: &ipn.ServeConfig{
TCP: map[uint16]*ipn.TCPPortHandler{ TCP: map[uint16]*ipn.TCPPortHandler{
@ -2221,7 +2222,7 @@ func TestUnsetServe(t *testing.T) {
expectErr: true, expectErr: true,
}, },
{ {
name: "unset service handler not found", name: "unset-service-handler-not-found",
desc: "try to remove a non-existing service TCP handler", desc: "try to remove a non-existing service TCP handler",
cfg: &ipn.ServeConfig{ cfg: &ipn.ServeConfig{
@ -2253,7 +2254,7 @@ func TestUnsetServe(t *testing.T) {
expectErr: true, expectErr: true,
}, },
{ {
name: "unset service doesn't exist", name: "unset-service-doesnt-exist",
desc: "try to remove a non-existing service's handler", desc: "try to remove a non-existing service's handler",
cfg: &ipn.ServeConfig{ cfg: &ipn.ServeConfig{
Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{ Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{
@ -2273,7 +2274,7 @@ func TestUnsetServe(t *testing.T) {
expectErr: true, expectErr: true,
}, },
{ {
name: "unset tcp while port is in use", name: "unset-tcp-while-port-in-use",
desc: "try to remove a TCP handler while the port is used for web", desc: "try to remove a TCP handler while the port is used for web",
cfg: &ipn.ServeConfig{ cfg: &ipn.ServeConfig{
TCP: map[uint16]*ipn.TCPPortHandler{ TCP: map[uint16]*ipn.TCPPortHandler{
@ -2297,7 +2298,7 @@ func TestUnsetServe(t *testing.T) {
expectErr: true, expectErr: true,
}, },
{ {
name: "unset service tcp while port is in use", name: "unset-service-tcp-while-port-in-use",
desc: "try to remove a service TCP handler while the port is used for web", desc: "try to remove a service TCP handler while the port is used for web",
cfg: &ipn.ServeConfig{ cfg: &ipn.ServeConfig{
Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{ Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{

@ -58,7 +58,7 @@ func TestStateStoreError(t *testing.T) {
args.statedir = t.TempDir() args.statedir = t.TempDir()
args.tunname = "userspace-networking" args.tunname = "userspace-networking"
t.Run("new state", func(t *testing.T) { t.Run("new-state", func(t *testing.T) {
sys := tsd.NewSystem() sys := tsd.NewSystem()
sys.NetMon.Set(must.Get(netmon.New(sys.Bus.Get(), t.Logf))) sys.NetMon.Set(must.Get(netmon.New(sys.Bus.Get(), t.Logf)))
lb, err := getLocalBackend(t.Context(), t.Logf, logID.Public(), sys) lb, err := getLocalBackend(t.Context(), t.Logf, logID.Public(), sys)
@ -70,7 +70,7 @@ func TestStateStoreError(t *testing.T) {
t.Errorf("StateStoreHealth is unhealthy on fresh LocalBackend:\n%s", strings.Join(lb.HealthTracker().Strings(), "\n")) t.Errorf("StateStoreHealth is unhealthy on fresh LocalBackend:\n%s", strings.Join(lb.HealthTracker().Strings(), "\n"))
} }
}) })
t.Run("corrupt state", func(t *testing.T) { t.Run("corrupt-state", func(t *testing.T) {
sys := tsd.NewSystem() sys := tsd.NewSystem()
sys.NetMon.Set(must.Get(netmon.New(sys.Bus.Get(), t.Logf))) sys.NetMon.Set(must.Get(netmon.New(sys.Bus.Get(), t.Logf)))
// Populate the state file with something that will fail to parse to // Populate the state file with something that will fail to parse to

@ -137,14 +137,14 @@ func TestFlattenExtraClaims(t *testing.T) {
expected map[string]any expected map[string]any
}{ }{
{ {
name: "empty extra claims", name: "empty-extra-claims",
input: []capRule{ input: []capRule{
{ExtraClaims: map[string]any{}}, {ExtraClaims: map[string]any{}},
}, },
expected: map[string]any{}, expected: map[string]any{},
}, },
{ {
name: "string and number values", name: "string-and-number-values",
input: []capRule{ input: []capRule{
{ {
ExtraClaims: map[string]any{ ExtraClaims: map[string]any{
@ -159,7 +159,7 @@ func TestFlattenExtraClaims(t *testing.T) {
}, },
}, },
{ {
name: "slice of strings and ints", name: "slice-of-strings-and-ints",
input: []capRule{ input: []capRule{
{ {
ExtraClaims: map[string]any{ ExtraClaims: map[string]any{
@ -172,7 +172,8 @@ func TestFlattenExtraClaims(t *testing.T) {
}, },
}, },
{ {
name: "duplicate values deduplicated (slice input)", // duplicate values deduplicated via slice input
name: "dedup-slice-input",
input: []capRule{ input: []capRule{
{ {
ExtraClaims: map[string]any{ ExtraClaims: map[string]any{
@ -190,7 +191,8 @@ func TestFlattenExtraClaims(t *testing.T) {
}, },
}, },
{ {
name: "ignore unsupported map type, keep valid scalar", // ignore unsupported map type, keep valid scalar
name: "ignore-unsupported-map-keep-scalar",
input: []capRule{ input: []capRule{
{ {
ExtraClaims: map[string]any{ ExtraClaims: map[string]any{
@ -204,7 +206,7 @@ func TestFlattenExtraClaims(t *testing.T) {
}, },
}, },
{ {
name: "scalar first, slice second", name: "scalar-first-slice-second",
input: []capRule{ input: []capRule{
{ExtraClaims: map[string]any{"foo": "bar"}}, {ExtraClaims: map[string]any{"foo": "bar"}},
{ExtraClaims: map[string]any{"foo": []any{"baz"}}}, {ExtraClaims: map[string]any{"foo": []any{"baz"}}},
@ -214,7 +216,7 @@ func TestFlattenExtraClaims(t *testing.T) {
}, },
}, },
{ {
name: "conflicting scalar and unsupported map", name: "conflicting-scalar-and-unsupported-map",
input: []capRule{ input: []capRule{
{ExtraClaims: map[string]any{"foo": "bar"}}, {ExtraClaims: map[string]any{"foo": "bar"}},
{ExtraClaims: map[string]any{"foo": map[string]any{"bad": "entry"}}}, {ExtraClaims: map[string]any{"foo": map[string]any{"bad": "entry"}}},
@ -224,7 +226,7 @@ func TestFlattenExtraClaims(t *testing.T) {
}, },
}, },
{ {
name: "multiple slices with overlap", name: "multiple-slices-with-overlap",
input: []capRule{ input: []capRule{
{ExtraClaims: map[string]any{"roles": []any{"admin", "user"}}}, {ExtraClaims: map[string]any{"roles": []any{"admin", "user"}}},
{ExtraClaims: map[string]any{"roles": []any{"admin", "guest"}}}, {ExtraClaims: map[string]any{"roles": []any{"admin", "guest"}}},
@ -234,7 +236,7 @@ func TestFlattenExtraClaims(t *testing.T) {
}, },
}, },
{ {
name: "slice with unsupported values", name: "slice-with-unsupported-values",
input: []capRule{ input: []capRule{
{ExtraClaims: map[string]any{ {ExtraClaims: map[string]any{
"mixed": []any{"ok", 42, map[string]string{"oops": "fail"}}, "mixed": []any{"ok", 42, map[string]string{"oops": "fail"}},
@ -245,7 +247,7 @@ func TestFlattenExtraClaims(t *testing.T) {
}, },
}, },
{ {
name: "duplicate scalar value", name: "duplicate-scalar-value",
input: []capRule{ input: []capRule{
{ExtraClaims: map[string]any{"env": "prod"}}, {ExtraClaims: map[string]any{"env": "prod"}},
{ExtraClaims: map[string]any{"env": "prod"}}, {ExtraClaims: map[string]any{"env": "prod"}},
@ -279,7 +281,7 @@ func TestExtraClaims(t *testing.T) {
expectError bool expectError bool
}{ }{
{ {
name: "extra claim", name: "extra-claim",
claim: tailscaleClaims{ claim: tailscaleClaims{
Claims: jwt.Claims{}, Claims: jwt.Claims{},
Nonce: "foobar", Nonce: "foobar",
@ -312,7 +314,7 @@ func TestExtraClaims(t *testing.T) {
}, },
}, },
{ {
name: "duplicate claim distinct values", name: "duplicate-claim-distinct-values",
claim: tailscaleClaims{ claim: tailscaleClaims{
Claims: jwt.Claims{}, Claims: jwt.Claims{},
Nonce: "foobar", Nonce: "foobar",
@ -350,7 +352,7 @@ func TestExtraClaims(t *testing.T) {
}, },
}, },
{ {
name: "multiple extra claims", name: "multiple-extra-claims",
claim: tailscaleClaims{ claim: tailscaleClaims{
Claims: jwt.Claims{}, Claims: jwt.Claims{},
Nonce: "foobar", Nonce: "foobar",
@ -389,7 +391,7 @@ func TestExtraClaims(t *testing.T) {
}, },
}, },
{ {
name: "overwrite claim", name: "overwrite-claim",
claim: tailscaleClaims{ claim: tailscaleClaims{
Claims: jwt.Claims{}, Claims: jwt.Claims{},
Nonce: "foobar", Nonce: "foobar",
@ -422,7 +424,7 @@ func TestExtraClaims(t *testing.T) {
expectError: true, expectError: true,
}, },
{ {
name: "empty extra claims", name: "empty-extra-claims",
claim: tailscaleClaims{ claim: tailscaleClaims{
Claims: jwt.Claims{}, Claims: jwt.Claims{},
Nonce: "foobar", Nonce: "foobar",
@ -496,21 +498,21 @@ func TestServeToken(t *testing.T) {
expected map[string]any expected map[string]any
}{ }{
{ {
name: "GET not allowed", name: "GET-not-allowed",
method: "GET", method: "GET",
grantType: "authorization_code", grantType: "authorization_code",
strictMode: false, strictMode: false,
expectError: true, expectError: true,
}, },
{ {
name: "unsupported grant type", name: "unsupported-grant-type",
method: "POST", method: "POST",
grantType: "pkcs", grantType: "pkcs",
strictMode: false, strictMode: false,
expectError: true, expectError: true,
}, },
{ {
name: "invalid code", name: "invalid-code",
method: "POST", method: "POST",
grantType: "authorization_code", grantType: "authorization_code",
code: "invalid-code", code: "invalid-code",
@ -518,7 +520,7 @@ func TestServeToken(t *testing.T) {
expectError: true, expectError: true,
}, },
{ {
name: "omit code from form", name: "omit-code-from-form",
method: "POST", method: "POST",
grantType: "authorization_code", grantType: "authorization_code",
omitCode: true, omitCode: true,
@ -526,7 +528,7 @@ func TestServeToken(t *testing.T) {
expectError: true, expectError: true,
}, },
{ {
name: "invalid redirect uri", name: "invalid-redirect-uri",
method: "POST", method: "POST",
grantType: "authorization_code", grantType: "authorization_code",
code: "valid-code", code: "valid-code",
@ -536,7 +538,7 @@ func TestServeToken(t *testing.T) {
expectError: true, expectError: true,
}, },
{ {
name: "invalid remoteAddr", name: "invalid-remoteAddr",
method: "POST", method: "POST",
grantType: "authorization_code", grantType: "authorization_code",
redirectURI: "https://rp.example.com/callback", redirectURI: "https://rp.example.com/callback",
@ -546,7 +548,7 @@ func TestServeToken(t *testing.T) {
expectError: true, expectError: true,
}, },
{ {
name: "extra claim included (non-strict)", name: "extra-claim-included-non-strict",
method: "POST", method: "POST",
grantType: "authorization_code", grantType: "authorization_code",
redirectURI: "https://rp.example.com/callback", redirectURI: "https://rp.example.com/callback",
@ -568,7 +570,8 @@ func TestServeToken(t *testing.T) {
}, },
}, },
{ {
name: "attempt to overwrite protected claim (non-strict)", // attempt to overwrite protected claim in non-strict mode
name: "overwrite-protected-claim-non-strict",
method: "POST", method: "POST",
grantType: "authorization_code", grantType: "authorization_code",
redirectURI: "https://rp.example.com/callback", redirectURI: "https://rp.example.com/callback",
@ -708,7 +711,7 @@ func TestExtraUserInfo(t *testing.T) {
expectError bool expectError bool
}{ }{
{ {
name: "extra claim", name: "extra-claim",
tokenValidTill: time.Now().Add(1 * time.Minute), tokenValidTill: time.Now().Add(1 * time.Minute),
caps: tailcfg.PeerCapMap{ caps: tailcfg.PeerCapMap{
tailcfg.PeerCapabilityTsIDP: { tailcfg.PeerCapabilityTsIDP: {
@ -725,7 +728,7 @@ func TestExtraUserInfo(t *testing.T) {
}, },
}, },
{ {
name: "duplicate claim distinct values", name: "duplicate-claim-distinct-values",
tokenValidTill: time.Now().Add(1 * time.Minute), tokenValidTill: time.Now().Add(1 * time.Minute),
caps: tailcfg.PeerCapMap{ caps: tailcfg.PeerCapMap{
tailcfg.PeerCapabilityTsIDP: { tailcfg.PeerCapabilityTsIDP: {
@ -742,7 +745,7 @@ func TestExtraUserInfo(t *testing.T) {
}, },
}, },
{ {
name: "multiple extra claims", name: "multiple-extra-claims",
tokenValidTill: time.Now().Add(1 * time.Minute), tokenValidTill: time.Now().Add(1 * time.Minute),
caps: tailcfg.PeerCapMap{ caps: tailcfg.PeerCapMap{
tailcfg.PeerCapabilityTsIDP: { tailcfg.PeerCapabilityTsIDP: {
@ -761,13 +764,13 @@ func TestExtraUserInfo(t *testing.T) {
}, },
}, },
{ {
name: "empty extra claims", name: "empty-extra-claims",
caps: tailcfg.PeerCapMap{}, caps: tailcfg.PeerCapMap{},
tokenValidTill: time.Now().Add(1 * time.Minute), tokenValidTill: time.Now().Add(1 * time.Minute),
expected: map[string]any{}, expected: map[string]any{},
}, },
{ {
name: "attempt to overwrite protected claim", name: "overwrite-protected-claim",
tokenValidTill: time.Now().Add(1 * time.Minute), tokenValidTill: time.Now().Add(1 * time.Minute),
caps: tailcfg.PeerCapMap{ caps: tailcfg.PeerCapMap{
tailcfg.PeerCapabilityTsIDP: { tailcfg.PeerCapabilityTsIDP: {
@ -783,7 +786,7 @@ func TestExtraUserInfo(t *testing.T) {
expectError: true, expectError: true,
}, },
{ {
name: "extra claim omitted", name: "extra-claim-omitted",
tokenValidTill: time.Now().Add(1 * time.Minute), tokenValidTill: time.Now().Add(1 * time.Minute),
caps: tailcfg.PeerCapMap{ caps: tailcfg.PeerCapMap{
tailcfg.PeerCapabilityTsIDP: { tailcfg.PeerCapabilityTsIDP: {
@ -798,7 +801,7 @@ func TestExtraUserInfo(t *testing.T) {
expected: map[string]any{}, expected: map[string]any{},
}, },
{ {
name: "expired token", name: "expired-token",
caps: tailcfg.PeerCapMap{}, caps: tailcfg.PeerCapMap{},
tokenValidTill: time.Now().Add(-1 * time.Minute), tokenValidTill: time.Now().Add(-1 * time.Minute),
expected: map[string]any{}, expected: map[string]any{},
@ -1131,19 +1134,22 @@ func TestGetAllowInsecureRegistration(t *testing.T) {
expectAllowInsecureRegistration bool expectAllowInsecureRegistration bool
}{ }{
{ {
name: "flag explicitly set to false - insecure registration disabled (strict mode)", // flag explicitly set to false - insecure registration disabled (strict mode)
name: "flag-false-insecure-disabled",
flagSet: true, flagSet: true,
flagValue: false, flagValue: false,
expectAllowInsecureRegistration: false, expectAllowInsecureRegistration: false,
}, },
{ {
name: "flag explicitly set to true - insecure registration enabled", // flag explicitly set to true - insecure registration enabled
name: "flag-true-insecure-enabled",
flagSet: true, flagSet: true,
flagValue: true, flagValue: true,
expectAllowInsecureRegistration: true, expectAllowInsecureRegistration: true,
}, },
{ {
name: "flag unset - insecure registration enabled (default for backward compatibility)", // flag unset - insecure registration enabled (default for backward compatibility)
name: "flag-unset-insecure-enabled-default",
flagSet: false, flagSet: false,
flagValue: false, // not used when unset flagValue: false, // not used when unset
expectAllowInsecureRegistration: true, expectAllowInsecureRegistration: true,
@ -1192,7 +1198,7 @@ func TestMigrateOAuthClients(t *testing.T) {
expectOldRenamed bool expectOldRenamed bool
}{ }{
{ {
name: "migrate from old file to new file", name: "migrate-old-to-new",
setupOldFile: true, setupOldFile: true,
oldFileContent: map[string]*funnelClient{ oldFileContent: map[string]*funnelClient{
"old-client": { "old-client": {
@ -1206,7 +1212,7 @@ func TestMigrateOAuthClients(t *testing.T) {
expectOldRenamed: true, expectOldRenamed: true,
}, },
{ {
name: "new file already exists - no migration", name: "new-file-exists-no-migration",
setupNewFile: true, setupNewFile: true,
newFileContent: map[string]*funnelClient{ newFileContent: map[string]*funnelClient{
"existing-client": { "existing-client": {
@ -1220,12 +1226,12 @@ func TestMigrateOAuthClients(t *testing.T) {
expectOldRenamed: false, expectOldRenamed: false,
}, },
{ {
name: "neither file exists - create empty new file", name: "neither-exists-create-empty",
expectNewFileExists: true, expectNewFileExists: true,
expectOldRenamed: false, expectOldRenamed: false,
}, },
{ {
name: "both files exist - prefer new file", name: "both-exist-prefer-new",
setupOldFile: true, setupOldFile: true,
setupNewFile: true, setupNewFile: true,
oldFileContent: map[string]*funnelClient{ oldFileContent: map[string]*funnelClient{
@ -1373,19 +1379,19 @@ func TestGetConfigFilePath(t *testing.T) {
expectError bool expectError bool
}{ }{
{ {
name: "file exists in current directory - use current directory", name: "file-in-cwd-use-cwd",
fileName: "test-config.json", fileName: "test-config.json",
createInCwd: true, createInCwd: true,
expectInCwd: true, expectInCwd: true,
}, },
{ {
name: "file does not exist - use root path", name: "file-missing-use-root",
fileName: "test-config.json", fileName: "test-config.json",
createInCwd: false, createInCwd: false,
expectInCwd: false, expectInCwd: false,
}, },
{ {
name: "file exists in both - prefer current directory", name: "file-in-both-prefer-cwd",
fileName: "test-config.json", fileName: "test-config.json",
createInCwd: true, createInCwd: true,
createInRoot: true, createInRoot: true,
@ -1472,7 +1478,7 @@ func TestAuthorizeStrictMode(t *testing.T) {
}{ }{
// Security boundary test: funnel rejection // Security boundary test: funnel rejection
{ {
name: "funnel requests are always rejected for security", name: "funnel-rejected",
strictMode: true, strictMode: true,
clientID: "test-client", clientID: "test-client",
redirectURI: "https://rp.example.com/callback", redirectURI: "https://rp.example.com/callback",
@ -1487,7 +1493,7 @@ func TestAuthorizeStrictMode(t *testing.T) {
// Strict mode parameter validation tests (non-funnel) // Strict mode parameter validation tests (non-funnel)
{ {
name: "strict mode - missing client_id", name: "strict-missing-client_id",
strictMode: true, strictMode: true,
clientID: "", clientID: "",
redirectURI: "https://rp.example.com/callback", redirectURI: "https://rp.example.com/callback",
@ -1496,7 +1502,7 @@ func TestAuthorizeStrictMode(t *testing.T) {
expectCode: http.StatusBadRequest, expectCode: http.StatusBadRequest,
}, },
{ {
name: "strict mode - missing redirect_uri", name: "strict-missing-redirect_uri",
strictMode: true, strictMode: true,
clientID: "test-client", clientID: "test-client",
redirectURI: "", redirectURI: "",
@ -1507,7 +1513,7 @@ func TestAuthorizeStrictMode(t *testing.T) {
// Strict mode client validation tests (non-funnel) // Strict mode client validation tests (non-funnel)
{ {
name: "strict mode - invalid client_id", name: "strict-invalid-client_id",
strictMode: true, strictMode: true,
clientID: "invalid-client", clientID: "invalid-client",
redirectURI: "https://rp.example.com/callback", redirectURI: "https://rp.example.com/callback",
@ -1517,7 +1523,7 @@ func TestAuthorizeStrictMode(t *testing.T) {
expectCode: http.StatusBadRequest, expectCode: http.StatusBadRequest,
}, },
{ {
name: "strict mode - redirect_uri mismatch", name: "strict-redirect_uri-mismatch",
strictMode: true, strictMode: true,
clientID: "test-client", clientID: "test-client",
redirectURI: "https://wrong.example.com/callback", redirectURI: "https://wrong.example.com/callback",
@ -1666,7 +1672,7 @@ func TestServeTokenWithClientValidation(t *testing.T) {
expectIDToken bool expectIDToken bool
}{ }{
{ {
name: "strict mode - valid token exchange with form credentials", name: "strict-token-exchange-form-creds",
strictMode: true, strictMode: true,
method: "POST", method: "POST",
grantType: "authorization_code", grantType: "authorization_code",
@ -1680,7 +1686,7 @@ func TestServeTokenWithClientValidation(t *testing.T) {
expectIDToken: true, expectIDToken: true,
}, },
{ {
name: "strict mode - valid token exchange with basic auth", name: "strict-token-exchange-basic-auth",
strictMode: true, strictMode: true,
method: "POST", method: "POST",
grantType: "authorization_code", grantType: "authorization_code",
@ -1695,7 +1701,7 @@ func TestServeTokenWithClientValidation(t *testing.T) {
expectIDToken: true, expectIDToken: true,
}, },
{ {
name: "strict mode - missing client credentials", name: "strict-missing-client-creds",
strictMode: true, strictMode: true,
method: "POST", method: "POST",
grantType: "authorization_code", grantType: "authorization_code",
@ -1708,7 +1714,7 @@ func TestServeTokenWithClientValidation(t *testing.T) {
expectCode: http.StatusUnauthorized, expectCode: http.StatusUnauthorized,
}, },
{ {
name: "strict mode - client_id mismatch", name: "strict-client_id-mismatch",
strictMode: true, strictMode: true,
method: "POST", method: "POST",
grantType: "authorization_code", grantType: "authorization_code",
@ -1722,7 +1728,7 @@ func TestServeTokenWithClientValidation(t *testing.T) {
expectCode: http.StatusBadRequest, expectCode: http.StatusBadRequest,
}, },
{ {
name: "strict mode - invalid client secret", name: "strict-invalid-client-secret",
strictMode: true, strictMode: true,
method: "POST", method: "POST",
grantType: "authorization_code", grantType: "authorization_code",
@ -1737,7 +1743,7 @@ func TestServeTokenWithClientValidation(t *testing.T) {
expectCode: http.StatusUnauthorized, expectCode: http.StatusUnauthorized,
}, },
{ {
name: "strict mode - redirect_uri mismatch", name: "strict-redirect_uri-mismatch",
strictMode: true, strictMode: true,
method: "POST", method: "POST",
grantType: "authorization_code", grantType: "authorization_code",
@ -1752,7 +1758,7 @@ func TestServeTokenWithClientValidation(t *testing.T) {
expectCode: http.StatusBadRequest, expectCode: http.StatusBadRequest,
}, },
{ {
name: "non-strict mode - no client validation required", name: "non-strict-no-client-validation",
strictMode: false, strictMode: false,
method: "POST", method: "POST",
grantType: "authorization_code", grantType: "authorization_code",
@ -1913,7 +1919,7 @@ func TestServeUserInfoWithClientValidation(t *testing.T) {
expectUserInfo bool expectUserInfo bool
}{ }{
{ {
name: "strict mode - valid token with existing client", name: "strict-valid-token-existing-client",
strictMode: true, strictMode: true,
setupToken: true, setupToken: true,
setupClient: true, setupClient: true,
@ -1923,7 +1929,8 @@ func TestServeUserInfoWithClientValidation(t *testing.T) {
expectUserInfo: true, expectUserInfo: true,
}, },
{ {
name: "strict mode - valid token but client no longer exists", // valid token but client no longer exists
name: "strict-token-client-deleted",
strictMode: true, strictMode: true,
setupToken: true, setupToken: true,
setupClient: false, setupClient: false,
@ -1934,7 +1941,7 @@ func TestServeUserInfoWithClientValidation(t *testing.T) {
expectCode: http.StatusUnauthorized, expectCode: http.StatusUnauthorized,
}, },
{ {
name: "strict mode - expired token", name: "strict-expired-token",
strictMode: true, strictMode: true,
setupToken: true, setupToken: true,
setupClient: true, setupClient: true,
@ -1945,7 +1952,7 @@ func TestServeUserInfoWithClientValidation(t *testing.T) {
expectCode: http.StatusBadRequest, expectCode: http.StatusBadRequest,
}, },
{ {
name: "strict mode - invalid token", name: "strict-invalid-token",
strictMode: true, strictMode: true,
setupToken: false, setupToken: false,
token: "invalid-token", token: "invalid-token",
@ -1953,7 +1960,7 @@ func TestServeUserInfoWithClientValidation(t *testing.T) {
expectCode: http.StatusBadRequest, expectCode: http.StatusBadRequest,
}, },
{ {
name: "strict mode - token without client association", name: "strict-token-no-client-assoc",
strictMode: true, strictMode: true,
setupToken: true, setupToken: true,
setupClient: false, setupClient: false,
@ -1964,7 +1971,7 @@ func TestServeUserInfoWithClientValidation(t *testing.T) {
expectCode: http.StatusBadRequest, expectCode: http.StatusBadRequest,
}, },
{ {
name: "non-strict mode - no client validation required", name: "non-strict-no-client-validation",
strictMode: false, strictMode: false,
setupToken: true, setupToken: true,
setupClient: false, setupClient: false,

@ -0,0 +1,227 @@
// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
// Package subtestnames checks that t.Run subtest names don't contain characters
// that require quoting or escaping when re-running via "go test -run".
//
// Go's testing package rewrites subtest names: spaces become underscores,
// non-printable characters get escaped, and regex metacharacters require
// escaping in -run patterns. This makes it hard to re-run specific subtests
// or search for them in code.
//
// This analyzer flags:
// - Direct t.Run calls with string literal names containing bad characters
// - t.Run calls using tt.name (or similar) where tt ranges over a slice/map
// of test cases with string literal names containing bad characters
package subtestnames
import (
"go/ast"
"go/token"
"strconv"
"strings"
"unicode"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/inspect"
"golang.org/x/tools/go/ast/inspector"
)
// Analyzer checks that t.Run subtest names are clean for use with "go test -run".
var Analyzer = &analysis.Analyzer{
Name: "subtestnames",
Doc: "check that t.Run subtest names don't require quoting when re-running via go test -run",
Requires: []*analysis.Analyzer{inspect.Analyzer},
Run: run,
}
// badChars are characters that are problematic in subtest names.
// Spaces are rewritten to underscores by testing.rewrite, and regex
// metacharacters require escaping in -run patterns.
const badChars = " \t\n\r^$.*+?()[]{}|\\'\"#"
// hasBadChar reports whether s contains any character that would be
// problematic in a subtest name.
func hasBadChar(s string) bool {
return strings.ContainsAny(s, badChars) || strings.ContainsFunc(s, func(r rune) bool {
return !unicode.IsPrint(r)
})
}
// hasBadDash reports whether s starts or ends with a dash, which is
// problematic in subtest names because "go test -run" may interpret a
// leading dash as a flag, and trailing dashes are confusing.
func hasBadDash(s string) bool {
return strings.HasPrefix(s, "-") || strings.HasSuffix(s, "-")
}
func run(pass *analysis.Pass) (any, error) {
insp := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
// Build a stack of enclosing nodes so we can find the RangeStmt
// enclosing a given t.Run call.
nodeFilter := []ast.Node{
(*ast.RangeStmt)(nil),
(*ast.CallExpr)(nil),
}
var rangeStack []*ast.RangeStmt
insp.Nodes(nodeFilter, func(n ast.Node, push bool) bool {
switch n := n.(type) {
case *ast.RangeStmt:
if push {
rangeStack = append(rangeStack, n)
} else {
rangeStack = rangeStack[:len(rangeStack)-1]
}
return true
case *ast.CallExpr:
if !push {
return true
}
checkCallExpr(pass, n, rangeStack)
return true
}
return true
})
return nil, nil
}
func checkCallExpr(pass *analysis.Pass, call *ast.CallExpr, rangeStack []*ast.RangeStmt) {
// Check if this is a t.Run(...) or b.Run(...) call.
sel, ok := call.Fun.(*ast.SelectorExpr)
if !ok || sel.Sel.Name != "Run" || len(call.Args) < 2 {
return
}
// Verify the receiver is *testing.T, *testing.B, or *testing.F.
if !isTestingTBF(pass, sel) {
return
}
nameArg := call.Args[0]
// Case 1: Direct string literal, e.g. t.Run("foo bar", ...)
if lit, ok := nameArg.(*ast.BasicLit); ok && lit.Kind == token.STRING {
val, err := strconv.Unquote(lit.Value)
if err != nil {
return
}
if hasBadChar(val) {
pass.Reportf(lit.Pos(), "subtest name %s contains characters that require quoting in go test -run patterns", lit.Value)
} else if hasBadDash(val) {
pass.Reportf(lit.Pos(), "subtest name %s starts or ends with '-' which is problematic in go test -run patterns", lit.Value)
}
return
}
// Case 2: Selector expression like tt.name, tc.name, etc.
// where tt is a range variable over a slice/map of test cases.
selExpr, ok := nameArg.(*ast.SelectorExpr)
if !ok {
return
}
ident, ok := selExpr.X.(*ast.Ident)
if !ok {
return
}
// Find the enclosing range statement where ident is the value variable.
for i := len(rangeStack) - 1; i >= 0; i-- {
rs := rangeStack[i]
valIdent, ok := rs.Value.(*ast.Ident)
if !ok || valIdent.Obj != ident.Obj {
continue
}
// Found the range statement. Check the source being iterated.
checkRangeSource(pass, rs.X, selExpr.Sel)
return
}
}
// isTestingTBF checks whether sel looks like a method call on *testing.T, *testing.B, or *testing.F.
func isTestingTBF(pass *analysis.Pass, sel *ast.SelectorExpr) bool {
typ := pass.TypesInfo.TypeOf(sel.X)
if typ != nil {
s := typ.String()
return s == "*testing.T" || s == "*testing.B" || s == "*testing.F"
}
return false
}
// checkRangeSource examines the expression being ranged over and checks
// composite literal elements for bad subtest name fields.
func checkRangeSource(pass *analysis.Pass, rangeExpr ast.Expr, fieldName *ast.Ident) {
switch x := rangeExpr.(type) {
case *ast.Ident:
if x.Obj == nil {
return
}
switch decl := x.Obj.Decl.(type) {
case *ast.AssignStmt:
// e.g. tests := []struct{...}{...}
for _, rhs := range decl.Rhs {
checkCompositeLit(pass, rhs, fieldName)
}
case *ast.ValueSpec:
// e.g. var tests = []struct{...}{...}
for _, val := range decl.Values {
checkCompositeLit(pass, val, fieldName)
}
}
case *ast.CompositeLit:
checkCompositeLit(pass, x, fieldName)
}
}
// checkCompositeLit checks a composite literal (slice/map) for elements
// that have a field with a bad subtest name.
func checkCompositeLit(pass *analysis.Pass, expr ast.Expr, fieldName *ast.Ident) {
comp, ok := expr.(*ast.CompositeLit)
if !ok {
return
}
for _, elt := range comp.Elts {
// For map literals, check the value.
if kv, ok := elt.(*ast.KeyValueExpr); ok {
elt = kv.Value
}
checkStructLitField(pass, elt, fieldName)
}
}
// checkStructLitField checks a struct literal for a field with the given name
// that contains a bad subtest name string.
func checkStructLitField(pass *analysis.Pass, expr ast.Expr, fieldName *ast.Ident) {
comp, ok := expr.(*ast.CompositeLit)
if !ok {
return
}
for _, elt := range comp.Elts {
kv, ok := elt.(*ast.KeyValueExpr)
if !ok {
continue
}
key, ok := kv.Key.(*ast.Ident)
if !ok || key.Name != fieldName.Name {
continue
}
lit, ok := kv.Value.(*ast.BasicLit)
if !ok || lit.Kind != token.STRING {
continue
}
val, err := strconv.Unquote(lit.Value)
if err != nil {
continue
}
if hasBadChar(val) {
pass.Reportf(lit.Pos(), "subtest name %s contains characters that require quoting in go test -run patterns", lit.Value)
} else if hasBadDash(val) {
pass.Reportf(lit.Pos(), "subtest name %s starts or ends with '-' which is problematic in go test -run patterns", lit.Value)
}
}
}

@ -0,0 +1,15 @@
// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
package subtestnames
import (
"testing"
"golang.org/x/tools/go/analysis/analysistest"
)
func TestAnalyzer(t *testing.T) {
testdata := analysistest.TestData()
analysistest.Run(t, testdata, Analyzer, "example")
}

@ -0,0 +1,112 @@
// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
package example
import "testing"
func TestDirect(t *testing.T) {
// Bad: spaces
t.Run("that everything's cool", func(t *testing.T) {}) // want `subtest name "that everything's cool" contains characters that require quoting`
// Bad: apostrophe
t.Run("it's working", func(t *testing.T) {}) // want `subtest name "it's working" contains characters that require quoting`
// Bad: regex metacharacters
t.Run("test(foo)", func(t *testing.T) {}) // want `subtest name "test\(foo\)" contains characters that require quoting`
t.Run("test[0]", func(t *testing.T) {}) // want `subtest name "test\[0\]" contains characters that require quoting`
t.Run("a|b", func(t *testing.T) {}) // want `subtest name "a\|b" contains characters that require quoting`
t.Run("a*b", func(t *testing.T) {}) // want `subtest name "a\*b" contains characters that require quoting`
t.Run("a+b", func(t *testing.T) {}) // want `subtest name "a\+b" contains characters that require quoting`
t.Run("a.b", func(t *testing.T) {}) // want `subtest name "a\.b" contains characters that require quoting`
t.Run("^start", func(t *testing.T) {}) // want `subtest name "\^start" contains characters that require quoting`
t.Run("end$", func(t *testing.T) {}) // want `subtest name "end\$" contains characters that require quoting`
t.Run("a{2}", func(t *testing.T) {}) // want `subtest name "a\{2\}" contains characters that require quoting`
t.Run("a?b", func(t *testing.T) {}) // want `subtest name "a\?b" contains characters that require quoting`
t.Run("a\\b", func(t *testing.T) {}) // want `subtest name "a\\\\b" contains characters that require quoting`
// Bad: double quotes
t.Run("say \"hello\"", func(t *testing.T) {}) // want `subtest name "say \\"hello\\"" contains characters that require quoting`
// Bad: hash
t.Run("comment#1", func(t *testing.T) {}) // want `subtest name "comment#1" contains characters that require quoting`
// Bad: leading/trailing dash
t.Run("-leading-dash", func(t *testing.T) {}) // want `subtest name "-leading-dash" starts or ends with '-' which is problematic`
t.Run("trailing-dash-", func(t *testing.T) {}) // want `subtest name "trailing-dash-" starts or ends with '-' which is problematic`
t.Run("-both-", func(t *testing.T) {}) // want `subtest name "-both-" starts or ends with '-' which is problematic`
// Good: clean names
t.Run("zero-passes", func(t *testing.T) {})
t.Run("simple_test", func(t *testing.T) {})
t.Run("CamelCase", func(t *testing.T) {})
t.Run("with-dashes", func(t *testing.T) {})
t.Run("123", func(t *testing.T) {})
t.Run("comma,separated", func(t *testing.T) {})
t.Run("colon:value", func(t *testing.T) {})
t.Run("slash/path", func(t *testing.T) {})
t.Run("equals=sign", func(t *testing.T) {})
}
func TestTableDriven(t *testing.T) {
tests := []struct {
name string
val int
}{
{name: "bad space name", val: 1}, // want `subtest name "bad space name" contains characters that require quoting`
{name: "good-name", val: 2},
{name: "also(bad)", val: 3}, // want `subtest name "also\(bad\)" contains characters that require quoting`
{name: "it's-bad", val: 4}, // want `subtest name "it's-bad" contains characters that require quoting`
{name: "clean-name", val: 5},
{name: "-leading-dash", val: 6}, // want `subtest name "-leading-dash" starts or ends with '-' which is problematic`
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {})
}
}
func TestTableDrivenVar(t *testing.T) {
var tests = []struct {
name string
val int
}{
{name: "has spaces", val: 1}, // want `subtest name "has spaces" contains characters that require quoting`
{name: "ok-name", val: 2},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {})
}
}
func TestTableDrivenMap(t *testing.T) {
tests := map[string]struct {
name string
val int
}{
"key1": {name: "bad name here", val: 1}, // want `subtest name "bad name here" contains characters that require quoting`
"key2": {name: "ok-name", val: 2},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {})
}
}
func TestNotTesting(t *testing.T) {
// Not a t.Run call, should not trigger.
s := struct{ Run func(string, func()) }{}
s.Run("bad name here", func() {})
}
func TestDynamicName(t *testing.T) {
// Dynamic name, not a string literal — should not trigger.
name := getName()
t.Run(name, func(t *testing.T) {})
}
func getName() string { return "foo" }
func BenchmarkDirect(b *testing.B) {
// Also check b.Run.
b.Run("bad name here", func(b *testing.B) {}) // want `subtest name "bad name here" contains characters that require quoting`
b.Run("good-name", func(b *testing.B) {})
}

@ -9,6 +9,7 @@ import (
"golang.org/x/tools/go/analysis/unitchecker" "golang.org/x/tools/go/analysis/unitchecker"
"tailscale.com/cmd/vet/jsontags" "tailscale.com/cmd/vet/jsontags"
"tailscale.com/cmd/vet/subtestnames"
) )
//go:embed jsontags_allowlist //go:embed jsontags_allowlist
@ -20,5 +21,5 @@ func init() {
} }
func main() { func main() {
unitchecker.Main(jsontags.Analyzer) unitchecker.Main(jsontags.Analyzer, subtestnames.Analyzer)
} }

@ -801,7 +801,7 @@ func TestUpdateDiscoForNodeCallbackWithFullNetmap(t *testing.T) {
expectNewDisco bool expectNewDisco bool
}{ }{
{ {
name: "disco key wired through when newer lastSeen", name: "disco-key-newer-lastSeen",
initialOnline: false, initialOnline: false,
initialLastSeen: oldTime, initialLastSeen: oldTime,
updateOnline: false, updateOnline: false,
@ -809,7 +809,7 @@ func TestUpdateDiscoForNodeCallbackWithFullNetmap(t *testing.T) {
expectNewDisco: true, expectNewDisco: true,
}, },
{ {
name: "disco key NOT wired through when older lastSeen", name: "disco-key-older-lastSeen",
initialOnline: false, initialOnline: false,
initialLastSeen: now, initialLastSeen: now,
updateOnline: false, updateOnline: false,
@ -817,7 +817,7 @@ func TestUpdateDiscoForNodeCallbackWithFullNetmap(t *testing.T) {
expectNewDisco: false, expectNewDisco: false,
}, },
{ {
name: "disco key wired through when newer lastSeen, going offline", name: "disco-key-newer-lastSeen-going-offline",
initialOnline: true, initialOnline: true,
initialLastSeen: oldTime, initialLastSeen: oldTime,
updateOnline: false, updateOnline: false,
@ -825,7 +825,7 @@ func TestUpdateDiscoForNodeCallbackWithFullNetmap(t *testing.T) {
expectNewDisco: true, expectNewDisco: true,
}, },
{ {
name: "online flip with newer lastSeen", name: "online-flip-newer-lastSeen",
initialOnline: false, initialOnline: false,
initialLastSeen: oldTime, initialLastSeen: oldTime,
updateOnline: true, updateOnline: true,

@ -616,13 +616,13 @@ func TestSendFreeze(t *testing.T) {
} }
} }
t.Run("initial send", func(t *testing.T) { t.Run("initial-send", func(t *testing.T) {
drain(t, "bob") drain(t, "bob")
drain(t, "cathy") drain(t, "cathy")
isEmpty(t, "alice") isEmpty(t, "alice")
}) })
t.Run("block cathy", func(t *testing.T) { t.Run("block-cathy", func(t *testing.T) {
// Block cathy. Now the cathyConn buffer will fill up quickly, // Block cathy. Now the cathyConn buffer will fill up quickly,
// and the derp server will back up. // and the derp server will back up.
cathyConn.SetReadBlock(true) cathyConn.SetReadBlock(true)

@ -447,7 +447,7 @@ func TestXDP(t *testing.T) {
wantMetrics map[bpfCountersKey]uint64 wantMetrics map[bpfCountersKey]uint64
}{ }{
{ {
name: "ipv4 STUN Binding Request Drop STUN", name: "ipv4-STUN-Binding-Request-Drop-STUN",
dropSTUN: true, dropSTUN: true,
packetIn: ipv4STUNBindingReqTX, packetIn: ipv4STUNBindingReqTX,
wantCode: xdpActionDrop, wantCode: xdpActionDrop,
@ -466,7 +466,7 @@ func TestXDP(t *testing.T) {
}, },
}, },
{ {
name: "ipv6 STUN Binding Request Drop STUN", name: "ipv6-STUN-Binding-Request-Drop-STUN",
dropSTUN: true, dropSTUN: true,
packetIn: ipv6STUNBindingReqTX, packetIn: ipv6STUNBindingReqTX,
wantCode: xdpActionDrop, wantCode: xdpActionDrop,
@ -485,7 +485,7 @@ func TestXDP(t *testing.T) {
}, },
}, },
{ {
name: "ipv4 STUN Binding Request TX", name: "ipv4-STUN-Binding-Request-TX",
packetIn: ipv4STUNBindingReqTX, packetIn: ipv4STUNBindingReqTX,
wantCode: xdpActionTX, wantCode: xdpActionTX,
wantPacketOut: getIPv4STUNBindingResp(), wantPacketOut: getIPv4STUNBindingResp(),
@ -503,7 +503,7 @@ func TestXDP(t *testing.T) {
}, },
}, },
{ {
name: "ipv6 STUN Binding Request TX", name: "ipv6-STUN-Binding-Request-TX",
packetIn: ipv6STUNBindingReqTX, packetIn: ipv6STUNBindingReqTX,
wantCode: xdpActionTX, wantCode: xdpActionTX,
wantPacketOut: getIPv6STUNBindingResp(), wantPacketOut: getIPv6STUNBindingResp(),
@ -521,7 +521,7 @@ func TestXDP(t *testing.T) {
}, },
}, },
{ {
name: "ipv4 STUN Binding Request invalid ip csum PASS", name: "ipv4-STUN-Binding-Request-invalid-ip-csum-PASS",
packetIn: ipv4STUNBindingReqIPCsumPass, packetIn: ipv4STUNBindingReqIPCsumPass,
wantCode: xdpActionPass, wantCode: xdpActionPass,
wantPacketOut: ipv4STUNBindingReqIPCsumPass, wantPacketOut: ipv4STUNBindingReqIPCsumPass,
@ -539,7 +539,7 @@ func TestXDP(t *testing.T) {
}, },
}, },
{ {
name: "ipv4 STUN Binding Request ihl PASS", name: "ipv4-STUN-Binding-Request-ihl-PASS",
packetIn: ipv4STUNBindingReqIHLPass, packetIn: ipv4STUNBindingReqIHLPass,
wantCode: xdpActionPass, wantCode: xdpActionPass,
wantPacketOut: ipv4STUNBindingReqIHLPass, wantPacketOut: ipv4STUNBindingReqIHLPass,
@ -557,7 +557,7 @@ func TestXDP(t *testing.T) {
}, },
}, },
{ {
name: "ipv4 STUN Binding Request ip version PASS", name: "ipv4-STUN-Binding-Request-ip-version-PASS",
packetIn: ipv4STUNBindingReqIPVerPass, packetIn: ipv4STUNBindingReqIPVerPass,
wantCode: xdpActionPass, wantCode: xdpActionPass,
wantPacketOut: ipv4STUNBindingReqIPVerPass, wantPacketOut: ipv4STUNBindingReqIPVerPass,
@ -575,7 +575,7 @@ func TestXDP(t *testing.T) {
}, },
}, },
{ {
name: "ipv4 STUN Binding Request ip proto PASS", name: "ipv4-STUN-Binding-Request-ip-proto-PASS",
packetIn: ipv4STUNBindingReqIPProtoPass, packetIn: ipv4STUNBindingReqIPProtoPass,
wantCode: xdpActionPass, wantCode: xdpActionPass,
wantPacketOut: ipv4STUNBindingReqIPProtoPass, wantPacketOut: ipv4STUNBindingReqIPProtoPass,
@ -593,7 +593,7 @@ func TestXDP(t *testing.T) {
}, },
}, },
{ {
name: "ipv4 STUN Binding Request frag offset PASS", name: "ipv4-STUN-Binding-Request-frag-offset-PASS",
packetIn: ipv4STUNBindingReqFragOffsetPass, packetIn: ipv4STUNBindingReqFragOffsetPass,
wantCode: xdpActionPass, wantCode: xdpActionPass,
wantPacketOut: ipv4STUNBindingReqFragOffsetPass, wantPacketOut: ipv4STUNBindingReqFragOffsetPass,
@ -611,7 +611,7 @@ func TestXDP(t *testing.T) {
}, },
}, },
{ {
name: "ipv4 STUN Binding Request flags mf PASS", name: "ipv4-STUN-Binding-Request-flags-mf-PASS",
packetIn: ipv4STUNBindingReqFlagsMFPass, packetIn: ipv4STUNBindingReqFlagsMFPass,
wantCode: xdpActionPass, wantCode: xdpActionPass,
wantPacketOut: ipv4STUNBindingReqFlagsMFPass, wantPacketOut: ipv4STUNBindingReqFlagsMFPass,
@ -629,7 +629,7 @@ func TestXDP(t *testing.T) {
}, },
}, },
{ {
name: "ipv4 STUN Binding Request tot len PASS", name: "ipv4-STUN-Binding-Request-tot-len-PASS",
packetIn: ipv4STUNBindingReqTotLenPass, packetIn: ipv4STUNBindingReqTotLenPass,
wantCode: xdpActionPass, wantCode: xdpActionPass,
wantPacketOut: ipv4STUNBindingReqTotLenPass, wantPacketOut: ipv4STUNBindingReqTotLenPass,
@ -647,7 +647,7 @@ func TestXDP(t *testing.T) {
}, },
}, },
{ {
name: "ipv6 STUN Binding Request ip version PASS", name: "ipv6-STUN-Binding-Request-ip-version-PASS",
packetIn: ipv6STUNBindingReqIPVerPass, packetIn: ipv6STUNBindingReqIPVerPass,
wantCode: xdpActionPass, wantCode: xdpActionPass,
wantPacketOut: ipv6STUNBindingReqIPVerPass, wantPacketOut: ipv6STUNBindingReqIPVerPass,
@ -665,7 +665,7 @@ func TestXDP(t *testing.T) {
}, },
}, },
{ {
name: "ipv6 STUN Binding Request next hdr PASS", name: "ipv6-STUN-Binding-Request-next-hdr-PASS",
packetIn: ipv6STUNBindingReqNextHdrPass, packetIn: ipv6STUNBindingReqNextHdrPass,
wantCode: xdpActionPass, wantCode: xdpActionPass,
wantPacketOut: ipv6STUNBindingReqNextHdrPass, wantPacketOut: ipv6STUNBindingReqNextHdrPass,
@ -683,7 +683,7 @@ func TestXDP(t *testing.T) {
}, },
}, },
{ {
name: "ipv6 STUN Binding Request payload len PASS", name: "ipv6-STUN-Binding-Request-payload-len-PASS",
packetIn: ipv6STUNBindingReqPayloadLenPass, packetIn: ipv6STUNBindingReqPayloadLenPass,
wantCode: xdpActionPass, wantCode: xdpActionPass,
wantPacketOut: ipv6STUNBindingReqPayloadLenPass, wantPacketOut: ipv6STUNBindingReqPayloadLenPass,
@ -701,7 +701,7 @@ func TestXDP(t *testing.T) {
}, },
}, },
{ {
name: "ipv4 STUN Binding Request UDP csum PASS", name: "ipv4-STUN-Binding-Request-UDP-csum-PASS",
packetIn: ipv4STUNBindingReqUDPCsumPass, packetIn: ipv4STUNBindingReqUDPCsumPass,
wantCode: xdpActionPass, wantCode: xdpActionPass,
wantPacketOut: ipv4STUNBindingReqUDPCsumPass, wantPacketOut: ipv4STUNBindingReqUDPCsumPass,
@ -719,7 +719,7 @@ func TestXDP(t *testing.T) {
}, },
}, },
{ {
name: "ipv6 STUN Binding Request UDP csum PASS", name: "ipv6-STUN-Binding-Request-UDP-csum-PASS",
packetIn: ipv6STUNBindingReqUDPCsumPass, packetIn: ipv6STUNBindingReqUDPCsumPass,
wantCode: xdpActionPass, wantCode: xdpActionPass,
wantPacketOut: ipv6STUNBindingReqUDPCsumPass, wantPacketOut: ipv6STUNBindingReqUDPCsumPass,
@ -737,7 +737,7 @@ func TestXDP(t *testing.T) {
}, },
}, },
{ {
name: "ipv4 STUN Binding Request STUN type PASS", name: "ipv4-STUN-Binding-Request-STUN-type-PASS",
packetIn: ipv4STUNBindingReqSTUNTypePass, packetIn: ipv4STUNBindingReqSTUNTypePass,
wantCode: xdpActionPass, wantCode: xdpActionPass,
wantPacketOut: ipv4STUNBindingReqSTUNTypePass, wantPacketOut: ipv4STUNBindingReqSTUNTypePass,
@ -755,7 +755,7 @@ func TestXDP(t *testing.T) {
}, },
}, },
{ {
name: "ipv6 STUN Binding Request STUN type PASS", name: "ipv6-STUN-Binding-Request-STUN-type-PASS",
packetIn: ipv6STUNBindingReqSTUNTypePass, packetIn: ipv6STUNBindingReqSTUNTypePass,
wantCode: xdpActionPass, wantCode: xdpActionPass,
wantPacketOut: ipv6STUNBindingReqSTUNTypePass, wantPacketOut: ipv6STUNBindingReqSTUNTypePass,
@ -773,7 +773,7 @@ func TestXDP(t *testing.T) {
}, },
}, },
{ {
name: "ipv4 STUN Binding Request STUN magic PASS", name: "ipv4-STUN-Binding-Request-STUN-magic-PASS",
packetIn: ipv4STUNBindingReqSTUNMagicPass, packetIn: ipv4STUNBindingReqSTUNMagicPass,
wantCode: xdpActionPass, wantCode: xdpActionPass,
wantPacketOut: ipv4STUNBindingReqSTUNMagicPass, wantPacketOut: ipv4STUNBindingReqSTUNMagicPass,
@ -791,7 +791,7 @@ func TestXDP(t *testing.T) {
}, },
}, },
{ {
name: "ipv6 STUN Binding Request STUN magic PASS", name: "ipv6-STUN-Binding-Request-STUN-magic-PASS",
packetIn: ipv6STUNBindingReqSTUNMagicPass, packetIn: ipv6STUNBindingReqSTUNMagicPass,
wantCode: xdpActionPass, wantCode: xdpActionPass,
wantPacketOut: ipv6STUNBindingReqSTUNMagicPass, wantPacketOut: ipv6STUNBindingReqSTUNMagicPass,
@ -809,7 +809,7 @@ func TestXDP(t *testing.T) {
}, },
}, },
{ {
name: "ipv4 STUN Binding Request STUN attrs len PASS", name: "ipv4-STUN-Binding-Request-STUN-attrs-len-PASS",
packetIn: ipv4STUNBindingReqSTUNAttrsLenPass, packetIn: ipv4STUNBindingReqSTUNAttrsLenPass,
wantCode: xdpActionPass, wantCode: xdpActionPass,
wantPacketOut: ipv4STUNBindingReqSTUNAttrsLenPass, wantPacketOut: ipv4STUNBindingReqSTUNAttrsLenPass,
@ -827,7 +827,7 @@ func TestXDP(t *testing.T) {
}, },
}, },
{ {
name: "ipv6 STUN Binding Request STUN attrs len PASS", name: "ipv6-STUN-Binding-Request-STUN-attrs-len-PASS",
packetIn: ipv6STUNBindingReqSTUNAttrsLenPass, packetIn: ipv6STUNBindingReqSTUNAttrsLenPass,
wantCode: xdpActionPass, wantCode: xdpActionPass,
wantPacketOut: ipv6STUNBindingReqSTUNAttrsLenPass, wantPacketOut: ipv6STUNBindingReqSTUNAttrsLenPass,
@ -845,7 +845,7 @@ func TestXDP(t *testing.T) {
}, },
}, },
{ {
name: "ipv4 STUN Binding Request STUN SW val PASS", name: "ipv4-STUN-Binding-Request-STUN-SW-val-PASS",
packetIn: ipv4STUNBindingReqSTUNSWValPass, packetIn: ipv4STUNBindingReqSTUNSWValPass,
wantCode: xdpActionPass, wantCode: xdpActionPass,
wantPacketOut: ipv4STUNBindingReqSTUNSWValPass, wantPacketOut: ipv4STUNBindingReqSTUNSWValPass,
@ -863,7 +863,7 @@ func TestXDP(t *testing.T) {
}, },
}, },
{ {
name: "ipv6 STUN Binding Request STUN SW val PASS", name: "ipv6-STUN-Binding-Request-STUN-SW-val-PASS",
packetIn: ipv6STUNBindingReqSTUNSWValPass, packetIn: ipv6STUNBindingReqSTUNSWValPass,
wantCode: xdpActionPass, wantCode: xdpActionPass,
wantPacketOut: ipv6STUNBindingReqSTUNSWValPass, wantPacketOut: ipv6STUNBindingReqSTUNSWValPass,
@ -881,7 +881,7 @@ func TestXDP(t *testing.T) {
}, },
}, },
{ {
name: "ipv4 STUN Binding Request STUN first attr PASS", name: "ipv4-STUN-Binding-Request-STUN-first-attr-PASS",
packetIn: ipv4STUNBindingReqSTUNFirstAttrPass, packetIn: ipv4STUNBindingReqSTUNFirstAttrPass,
wantCode: xdpActionPass, wantCode: xdpActionPass,
wantPacketOut: ipv4STUNBindingReqSTUNFirstAttrPass, wantPacketOut: ipv4STUNBindingReqSTUNFirstAttrPass,
@ -899,7 +899,7 @@ func TestXDP(t *testing.T) {
}, },
}, },
{ {
name: "ipv6 STUN Binding Request STUN first attr PASS", name: "ipv6-STUN-Binding-Request-STUN-first-attr-PASS",
packetIn: ipv6STUNBindingReqSTUNFirstAttrPass, packetIn: ipv6STUNBindingReqSTUNFirstAttrPass,
wantCode: xdpActionPass, wantCode: xdpActionPass,
wantPacketOut: ipv6STUNBindingReqSTUNFirstAttrPass, wantPacketOut: ipv6STUNBindingReqSTUNFirstAttrPass,
@ -917,7 +917,7 @@ func TestXDP(t *testing.T) {
}, },
}, },
{ {
name: "ipv4 UDP zero csum TX", name: "ipv4-UDP-zero-csum-TX",
packetIn: ipv4STUNBindingReqUDPZeroCsumTx, packetIn: ipv4STUNBindingReqUDPZeroCsumTx,
wantCode: xdpActionTX, wantCode: xdpActionTX,
wantPacketOut: getIPv4STUNBindingResp(), wantPacketOut: getIPv4STUNBindingResp(),
@ -935,7 +935,7 @@ func TestXDP(t *testing.T) {
}, },
}, },
{ {
name: "ipv6 UDP zero csum PASS", name: "ipv6-UDP-zero-csum-PASS",
packetIn: ipv6STUNBindingReqUDPZeroCsumPass, packetIn: ipv6STUNBindingReqUDPZeroCsumPass,
wantCode: xdpActionPass, wantCode: xdpActionPass,
wantPacketOut: ipv6STUNBindingReqUDPZeroCsumPass, wantPacketOut: ipv6STUNBindingReqUDPZeroCsumPass,

@ -29,7 +29,7 @@ func TestStat(t *testing.T) {
err error err error
}{ }{
{ {
label: "root folder", label: "root-folder",
name: "", name: "",
expected: &shared.StaticFileInfo{ expected: &shared.StaticFileInfo{
Named: "", Named: "",
@ -40,7 +40,7 @@ func TestStat(t *testing.T) {
}, },
}, },
{ {
label: "static root folder", label: "static-root-folder",
name: "/domain", name: "/domain",
expected: &shared.StaticFileInfo{ expected: &shared.StaticFileInfo{
Named: "domain", Named: "domain",
@ -73,7 +73,7 @@ func TestStat(t *testing.T) {
}, },
}, },
{ {
label: "non-existent remote", label: "non-existent-remote",
name: "remote3", name: "remote3",
err: os.ErrNotExist, err: os.ErrNotExist,
}, },
@ -108,7 +108,7 @@ func TestListDir(t *testing.T) {
err error err error
}{ }{
{ {
label: "root folder", label: "root-folder",
name: "", name: "",
expected: []fs.FileInfo{ expected: []fs.FileInfo{
&shared.StaticFileInfo{ &shared.StaticFileInfo{
@ -121,7 +121,7 @@ func TestListDir(t *testing.T) {
}, },
}, },
{ {
label: "static root folder", label: "static-root-folder",
name: "/domain", name: "/domain",
expected: []fs.FileInfo{ expected: []fs.FileInfo{
&shared.StaticFileInfo{ &shared.StaticFileInfo{
@ -189,19 +189,19 @@ func TestMkdir(t *testing.T) {
err error err error
}{ }{
{ {
label: "attempt to create root folder", label: "create-root-folder",
name: "/", name: "/",
}, },
{ {
label: "attempt to create static root folder", label: "create-static-root-folder",
name: "/domain", name: "/domain",
}, },
{ {
label: "attempt to create remote", label: "create-remote",
name: "/domain/remote1", name: "/domain/remote1",
}, },
{ {
label: "attempt to create non-existent remote", label: "create-non-existent-remote",
name: "/domain/remote3", name: "/domain/remote3",
err: os.ErrPermission, err: os.ErrPermission,
}, },
@ -231,7 +231,7 @@ func TestRemoveAll(t *testing.T) {
err error err error
}{ }{
{ {
label: "attempt to remove root folder", label: "remove-root-folder",
name: "/", name: "/",
err: os.ErrPermission, err: os.ErrPermission,
}, },
@ -258,7 +258,7 @@ func TestRename(t *testing.T) {
err error err error
}{ }{
{ {
label: "attempt to move root folder", label: "move-root-folder",
oldName: "/", oldName: "/",
newName: "/domain/remote2/copy.txt", newName: "/domain/remote2/copy.txt",
err: os.ErrPermission, err: os.ErrPermission,

@ -156,27 +156,27 @@ func TestMissingPaths(t *testing.T) {
wantStatus int wantStatus int
}{ }{
{ {
name: "empty path", name: "empty-path",
path: "", path: "",
wantStatus: http.StatusForbidden, wantStatus: http.StatusForbidden,
}, },
{ {
name: "single slash", name: "single-slash",
path: "/", path: "/",
wantStatus: http.StatusForbidden, wantStatus: http.StatusForbidden,
}, },
{ {
name: "only token", name: "only-token",
path: "/" + secretToken, path: "/" + secretToken,
wantStatus: http.StatusBadRequest, wantStatus: http.StatusBadRequest,
}, },
{ {
name: "token with trailing slash", name: "token-trailing-slash",
path: "/" + secretToken + "/", path: "/" + secretToken + "/",
wantStatus: http.StatusBadRequest, wantStatus: http.StatusBadRequest,
}, },
{ {
name: "token and invalid share", name: "token-invalid-share",
path: "/" + secretToken + "/nonexistentshare", path: "/" + secretToken + "/nonexistentshare",
wantStatus: http.StatusNotFound, wantStatus: http.StatusNotFound,
}, },

@ -31,7 +31,7 @@ func TestResolveAuthKey(t *testing.T) {
wantErr: "", wantErr: "",
}, },
{ {
name: "missing client id short-circuits without error", name: "missing-client-id-noop",
clientID: "", clientID: "",
idToken: "token", idToken: "token",
audience: "api://tailscale-wif", audience: "api://tailscale-wif",
@ -40,7 +40,7 @@ func TestResolveAuthKey(t *testing.T) {
wantErr: "", wantErr: "",
}, },
{ {
name: "missing id token and audience", name: "missing-id-token-and-audience",
clientID: "client-123", clientID: "client-123",
idToken: "", idToken: "",
audience: "", audience: "",
@ -48,7 +48,7 @@ func TestResolveAuthKey(t *testing.T) {
wantErr: "federated identity requires either an ID token or an audience", wantErr: "federated identity requires either an ID token or an audience",
}, },
{ {
name: "missing tags", name: "missing-tags",
clientID: "client-123", clientID: "client-123",
idToken: "token", idToken: "token",
audience: "api://tailscale-wif", audience: "api://tailscale-wif",
@ -56,7 +56,7 @@ func TestResolveAuthKey(t *testing.T) {
wantErr: "federated identity authkeys require --advertise-tags", wantErr: "federated identity authkeys require --advertise-tags",
}, },
{ {
name: "invalid client id attributes", name: "invalid-client-id-attrs",
clientID: "client-123?invalid=value", clientID: "client-123?invalid=value",
idToken: "token", idToken: "token",
audience: "api://tailscale-wif", audience: "api://tailscale-wif",
@ -99,7 +99,7 @@ func TestParseOptionalAttributes(t *testing.T) {
wantErr string wantErr string
}{ }{
{ {
name: "default values", name: "default-values",
clientID: "client-123", clientID: "client-123",
wantClientID: "client-123", wantClientID: "client-123",
wantEphemeral: true, wantEphemeral: true,
@ -107,7 +107,7 @@ func TestParseOptionalAttributes(t *testing.T) {
wantErr: "", wantErr: "",
}, },
{ {
name: "custom values", name: "custom-values",
clientID: "client-123?ephemeral=false&preauthorized=true", clientID: "client-123?ephemeral=false&preauthorized=true",
wantClientID: "client-123", wantClientID: "client-123",
wantEphemeral: false, wantEphemeral: false,
@ -115,7 +115,7 @@ func TestParseOptionalAttributes(t *testing.T) {
wantErr: "", wantErr: "",
}, },
{ {
name: "unknown attribute", name: "unknown-attribute",
clientID: "client-123?unknown=value", clientID: "client-123?unknown=value",
wantClientID: "", wantClientID: "",
wantEphemeral: false, wantEphemeral: false,
@ -123,7 +123,7 @@ func TestParseOptionalAttributes(t *testing.T) {
wantErr: `unknown optional config attribute "unknown"`, wantErr: `unknown optional config attribute "unknown"`,
}, },
{ {
name: "invalid value", name: "invalid-value",
clientID: "client-123?ephemeral=invalid", clientID: "client-123?ephemeral=invalid",
wantClientID: "", wantClientID: "",
wantEphemeral: false, wantEphemeral: false,

@ -20,42 +20,42 @@ func TestResolveAuthKey(t *testing.T) {
wantErr bool wantErr bool
}{ }{
{ {
name: "keys without client secret prefix pass through unchanged", name: "non-client-secret-passthrough",
clientID: "tskey-auth-regular", clientID: "tskey-auth-regular",
tags: []string{"tag:test"}, tags: []string{"tag:test"},
wantAuthKey: "tskey-auth-regular", wantAuthKey: "tskey-auth-regular",
wantErr: false, wantErr: false,
}, },
{ {
name: "client secret without advertised tags", name: "client-secret-no-tags",
clientID: "tskey-client-abc", clientID: "tskey-client-abc",
tags: nil, tags: nil,
wantAuthKey: "", wantAuthKey: "",
wantErr: true, wantErr: true,
}, },
{ {
name: "client secret with default attributes", name: "client-secret-default-attrs",
clientID: "tskey-client-abc", clientID: "tskey-client-abc",
tags: []string{"tag:test"}, tags: []string{"tag:test"},
wantAuthKey: "tskey-auth-xyz", wantAuthKey: "tskey-auth-xyz",
wantErr: false, wantErr: false,
}, },
{ {
name: "client secret with custom attributes", name: "client-secret-custom-attrs",
clientID: "tskey-client-abc?ephemeral=false&preauthorized=true", clientID: "tskey-client-abc?ephemeral=false&preauthorized=true",
tags: []string{"tag:test"}, tags: []string{"tag:test"},
wantAuthKey: "tskey-auth-xyz", wantAuthKey: "tskey-auth-xyz",
wantErr: false, wantErr: false,
}, },
{ {
name: "client secret with unknown attribute", name: "client-secret-unknown-attr",
clientID: "tskey-client-abc?unknown=value", clientID: "tskey-client-abc?unknown=value",
tags: []string{"tag:test"}, tags: []string{"tag:test"},
wantAuthKey: "", wantAuthKey: "",
wantErr: true, wantErr: true,
}, },
{ {
name: "oauth client secret with invalid attribute value", name: "client-secret-invalid-attr-value",
clientID: "tskey-client-abc?ephemeral=invalid", clientID: "tskey-client-abc?ephemeral=invalid",
tags: []string{"tag:test"}, tags: []string{"tag:test"},
wantAuthKey: "", wantAuthKey: "",
@ -111,7 +111,7 @@ func TestResolveAuthKeyAttributes(t *testing.T) {
wantBaseURL string wantBaseURL string
}{ }{
{ {
name: "default values", name: "default-values",
clientSecret: "tskey-client-abc", clientSecret: "tskey-client-abc",
wantEphemeral: true, wantEphemeral: true,
wantPreauth: false, wantPreauth: false,
@ -132,14 +132,14 @@ func TestResolveAuthKeyAttributes(t *testing.T) {
wantBaseURL: "https://api.tailscale.com", wantBaseURL: "https://api.tailscale.com",
}, },
{ {
name: "baseURL=https://api.example.com", name: "baseURL-custom",
clientSecret: "tskey-client-abc?baseURL=https://api.example.com", clientSecret: "tskey-client-abc?baseURL=https://api.example.com",
wantEphemeral: true, wantEphemeral: true,
wantPreauth: false, wantPreauth: false,
wantBaseURL: "https://api.example.com", wantBaseURL: "https://api.example.com",
}, },
{ {
name: "all custom values", name: "all-custom-values",
clientSecret: "tskey-client-abc?ephemeral=false&preauthorized=true&baseURL=https://api.example.com", clientSecret: "tskey-client-abc?ephemeral=false&preauthorized=true&baseURL=https://api.example.com",
wantEphemeral: false, wantEphemeral: false,
wantPreauth: true, wantPreauth: true,

@ -48,7 +48,7 @@ func Test_extension_profileStateChanged(t *testing.T) {
wantEndpoints []netip.AddrPort wantEndpoints []netip.AddrPort
}{ }{
{ {
name: "no changes non-nil port previously running", name: "no-changes-non-nil-port-running",
fields: fields{ fields: fields{
port: new(uint16(1)), port: new(uint16(1)),
rs: mockRelayServerNotZeroVal(), rs: mockRelayServerNotZeroVal(),
@ -62,7 +62,7 @@ func Test_extension_profileStateChanged(t *testing.T) {
wantRelayServerFieldMutated: false, wantRelayServerFieldMutated: false,
}, },
{ {
name: "set addr ports unchanged port previously running", name: "set-addr-ports-unchanged-running",
fields: fields{ fields: fields{
port: new(uint16(1)), port: new(uint16(1)),
rs: mockRelayServerNotZeroVal(), rs: mockRelayServerNotZeroVal(),
@ -77,7 +77,7 @@ func Test_extension_profileStateChanged(t *testing.T) {
wantEndpoints: prefsWithPortOneRelayEndpoints.RelayServerStaticEndpoints, wantEndpoints: prefsWithPortOneRelayEndpoints.RelayServerStaticEndpoints,
}, },
{ {
name: "set addr ports not previously running", name: "set-addr-ports-not-running",
fields: fields{ fields: fields{
port: nil, port: nil,
rs: nil, rs: nil,
@ -92,7 +92,7 @@ func Test_extension_profileStateChanged(t *testing.T) {
wantEndpoints: prefsWithPortOneRelayEndpoints.RelayServerStaticEndpoints, wantEndpoints: prefsWithPortOneRelayEndpoints.RelayServerStaticEndpoints,
}, },
{ {
name: "clear addr ports unchanged port previously running", name: "clear-addr-ports-unchanged-running",
fields: fields{ fields: fields{
port: new(uint16(1)), port: new(uint16(1)),
staticEndpoints: views.SliceOf(prefsWithPortOneRelayEndpoints.RelayServerStaticEndpoints), staticEndpoints: views.SliceOf(prefsWithPortOneRelayEndpoints.RelayServerStaticEndpoints),
@ -108,7 +108,7 @@ func Test_extension_profileStateChanged(t *testing.T) {
wantEndpoints: nil, wantEndpoints: nil,
}, },
{ {
name: "prefs port nil", name: "prefs-port-nil",
fields: fields{ fields: fields{
port: new(uint16(1)), port: new(uint16(1)),
}, },
@ -121,7 +121,7 @@ func Test_extension_profileStateChanged(t *testing.T) {
wantRelayServerFieldMutated: false, wantRelayServerFieldMutated: false,
}, },
{ {
name: "prefs port nil previously running", name: "prefs-port-nil-running",
fields: fields{ fields: fields{
port: new(uint16(1)), port: new(uint16(1)),
rs: mockRelayServerNotZeroVal(), rs: mockRelayServerNotZeroVal(),
@ -135,7 +135,7 @@ func Test_extension_profileStateChanged(t *testing.T) {
wantRelayServerFieldMutated: true, wantRelayServerFieldMutated: true,
}, },
{ {
name: "prefs port changed", name: "prefs-port-changed",
fields: fields{ fields: fields{
port: new(uint16(2)), port: new(uint16(2)),
}, },
@ -148,7 +148,7 @@ func Test_extension_profileStateChanged(t *testing.T) {
wantRelayServerFieldMutated: true, wantRelayServerFieldMutated: true,
}, },
{ {
name: "prefs port changed previously running", name: "prefs-port-changed-running",
fields: fields{ fields: fields{
port: new(uint16(2)), port: new(uint16(2)),
rs: mockRelayServerNotZeroVal(), rs: mockRelayServerNotZeroVal(),
@ -162,7 +162,7 @@ func Test_extension_profileStateChanged(t *testing.T) {
wantRelayServerFieldMutated: true, wantRelayServerFieldMutated: true,
}, },
{ {
name: "sameNode false", name: "sameNode-false",
fields: fields{ fields: fields{
port: new(uint16(1)), port: new(uint16(1)),
}, },
@ -175,7 +175,7 @@ func Test_extension_profileStateChanged(t *testing.T) {
wantRelayServerFieldMutated: true, wantRelayServerFieldMutated: true,
}, },
{ {
name: "sameNode false previously running", name: "sameNode-false-running",
fields: fields{ fields: fields{
port: new(uint16(1)), port: new(uint16(1)),
rs: mockRelayServerNotZeroVal(), rs: mockRelayServerNotZeroVal(),
@ -189,7 +189,7 @@ func Test_extension_profileStateChanged(t *testing.T) {
wantRelayServerFieldMutated: true, wantRelayServerFieldMutated: true,
}, },
{ {
name: "prefs port non-nil extension port nil", name: "prefs-port-non-nil-ext-nil",
fields: fields{ fields: fields{
port: nil, port: nil,
}, },
@ -277,7 +277,7 @@ func Test_extension_handleRelayServerLifetimeLocked(t *testing.T) {
wantRelayServerFieldMutated bool wantRelayServerFieldMutated bool
}{ }{
{ {
name: "want running", name: "want-running",
shutdown: false, shutdown: false,
port: new(uint16(1)), port: new(uint16(1)),
hasNodeAttrDisableRelayServer: false, hasNodeAttrDisableRelayServer: false,
@ -285,7 +285,7 @@ func Test_extension_handleRelayServerLifetimeLocked(t *testing.T) {
wantRelayServerFieldMutated: true, wantRelayServerFieldMutated: true,
}, },
{ {
name: "want running previously running", name: "want-running-previously-running",
shutdown: false, shutdown: false,
port: new(uint16(1)), port: new(uint16(1)),
rs: mockRelayServerNotZeroVal(), rs: mockRelayServerNotZeroVal(),
@ -294,7 +294,7 @@ func Test_extension_handleRelayServerLifetimeLocked(t *testing.T) {
wantRelayServerFieldMutated: false, wantRelayServerFieldMutated: false,
}, },
{ {
name: "shutdown true", name: "shutdown-true",
shutdown: true, shutdown: true,
port: new(uint16(1)), port: new(uint16(1)),
hasNodeAttrDisableRelayServer: false, hasNodeAttrDisableRelayServer: false,
@ -302,7 +302,7 @@ func Test_extension_handleRelayServerLifetimeLocked(t *testing.T) {
wantRelayServerFieldMutated: false, wantRelayServerFieldMutated: false,
}, },
{ {
name: "shutdown true previously running", name: "shutdown-true-previously-running",
shutdown: true, shutdown: true,
port: new(uint16(1)), port: new(uint16(1)),
rs: mockRelayServerNotZeroVal(), rs: mockRelayServerNotZeroVal(),
@ -311,7 +311,7 @@ func Test_extension_handleRelayServerLifetimeLocked(t *testing.T) {
wantRelayServerFieldMutated: true, wantRelayServerFieldMutated: true,
}, },
{ {
name: "port nil", name: "port-nil",
shutdown: false, shutdown: false,
port: nil, port: nil,
hasNodeAttrDisableRelayServer: false, hasNodeAttrDisableRelayServer: false,
@ -319,7 +319,7 @@ func Test_extension_handleRelayServerLifetimeLocked(t *testing.T) {
wantRelayServerFieldMutated: false, wantRelayServerFieldMutated: false,
}, },
{ {
name: "port nil previously running", name: "port-nil-previously-running",
shutdown: false, shutdown: false,
port: nil, port: nil,
rs: mockRelayServerNotZeroVal(), rs: mockRelayServerNotZeroVal(),
@ -328,7 +328,7 @@ func Test_extension_handleRelayServerLifetimeLocked(t *testing.T) {
wantRelayServerFieldMutated: true, wantRelayServerFieldMutated: true,
}, },
{ {
name: "hasNodeAttrDisableRelayServer true", name: "hasNodeAttrDisableRelayServer-true",
shutdown: false, shutdown: false,
port: nil, port: nil,
hasNodeAttrDisableRelayServer: true, hasNodeAttrDisableRelayServer: true,
@ -336,7 +336,7 @@ func Test_extension_handleRelayServerLifetimeLocked(t *testing.T) {
wantRelayServerFieldMutated: false, wantRelayServerFieldMutated: false,
}, },
{ {
name: "hasNodeAttrDisableRelayServer true previously running", name: "hasNodeAttrDisableRelayServer-true-running",
shutdown: false, shutdown: false,
port: nil, port: nil,
rs: mockRelayServerNotZeroVal(), rs: mockRelayServerNotZeroVal(),

@ -163,4 +163,4 @@
}); });
}; };
} }
# nix-direnv cache busting line: sha256-39axT5Q0+fNTcMgZCMLMNfJEJN46wMaaKDgfI+Uj+Ps= # nix-direnv cache busting line: sha256-VsVMvTEblVx/HNbuCVxC9UgKpriRwixswUSKVGLMf3Q=

@ -1 +1 @@
sha256-39axT5Q0+fNTcMgZCMLMNfJEJN46wMaaKDgfI+Uj+Ps= sha256-VsVMvTEblVx/HNbuCVxC9UgKpriRwixswUSKVGLMf3Q=

@ -388,7 +388,7 @@ func TestShowUpdateWarnable(t *testing.T) {
wantShow bool wantShow bool
}{ }{
{ {
desc: "nil ClientVersion", desc: "nil-ClientVersion",
check: true, check: true,
cv: nil, cv: nil,
wantWarnable: nil, wantWarnable: nil,
@ -402,35 +402,35 @@ func TestShowUpdateWarnable(t *testing.T) {
wantShow: false, wantShow: false,
}, },
{ {
desc: "no LatestVersion", desc: "no-LatestVersion",
check: true, check: true,
cv: &tailcfg.ClientVersion{RunningLatest: false, LatestVersion: ""}, cv: &tailcfg.ClientVersion{RunningLatest: false, LatestVersion: ""},
wantWarnable: nil, wantWarnable: nil,
wantShow: false, wantShow: false,
}, },
{ {
desc: "show regular update", desc: "show-regular-update",
check: true, check: true,
cv: &tailcfg.ClientVersion{RunningLatest: false, LatestVersion: "1.2.3"}, cv: &tailcfg.ClientVersion{RunningLatest: false, LatestVersion: "1.2.3"},
wantWarnable: updateAvailableWarnable, wantWarnable: updateAvailableWarnable,
wantShow: true, wantShow: true,
}, },
{ {
desc: "show security update", desc: "show-security-update",
check: true, check: true,
cv: &tailcfg.ClientVersion{RunningLatest: false, LatestVersion: "1.2.3", UrgentSecurityUpdate: true}, cv: &tailcfg.ClientVersion{RunningLatest: false, LatestVersion: "1.2.3", UrgentSecurityUpdate: true},
wantWarnable: securityUpdateAvailableWarnable, wantWarnable: securityUpdateAvailableWarnable,
wantShow: true, wantShow: true,
}, },
{ {
desc: "update check disabled", desc: "update-check-disabled",
check: false, check: false,
cv: &tailcfg.ClientVersion{RunningLatest: false, LatestVersion: "1.2.3"}, cv: &tailcfg.ClientVersion{RunningLatest: false, LatestVersion: "1.2.3"},
wantWarnable: nil, wantWarnable: nil,
wantShow: false, wantShow: false,
}, },
{ {
desc: "hide update with auto-updates", desc: "hide-update-with-auto-updates",
check: true, check: true,
apply: opt.NewBool(true), apply: opt.NewBool(true),
cv: &tailcfg.ClientVersion{RunningLatest: false, LatestVersion: "1.2.3"}, cv: &tailcfg.ClientVersion{RunningLatest: false, LatestVersion: "1.2.3"},
@ -438,7 +438,7 @@ func TestShowUpdateWarnable(t *testing.T) {
wantShow: false, wantShow: false,
}, },
{ {
desc: "show security update with auto-updates", desc: "show-security-update-with-auto-updates",
check: true, check: true,
apply: opt.NewBool(true), apply: opt.NewBool(true),
cv: &tailcfg.ClientVersion{RunningLatest: false, LatestVersion: "1.2.3", UrgentSecurityUpdate: true}, cv: &tailcfg.ClientVersion{RunningLatest: false, LatestVersion: "1.2.3", UrgentSecurityUpdate: true},
@ -622,7 +622,7 @@ func TestControlHealth(t *testing.T) {
} }
}) })
t.Run("Strings()", func(t *testing.T) { t.Run("Strings", func(t *testing.T) {
wantStrs := []string{ wantStrs := []string{
"Control health message: Extra help.", "Control health message: Extra help.",
"Control health message: Extra help. Learn more: http://www.example.com", "Control health message: Extra help. Learn more: http://www.example.com",

@ -63,7 +63,7 @@ func TestHandleC2NTLSCertStatus(t *testing.T) {
want *tailcfg.C2NTLSCertInfo want *tailcfg.C2NTLSCertInfo
}{ }{
{ {
name: "no domain", name: "no-domain",
wantStatus: 400, wantStatus: 400,
wantError: "no 'domain'\n", wantError: "no 'domain'\n",
}, },

@ -39,25 +39,29 @@ func TestCertRequest(t *testing.T) {
} }
tests := []struct { tests := []struct {
name string
domain string domain string
wantSANs []string wantSANs []string
}{ }{
{ {
name: "example-com",
domain: "example.com", domain: "example.com",
wantSANs: []string{"example.com"}, wantSANs: []string{"example.com"},
}, },
{ {
name: "wildcard-example-com",
domain: "*.example.com", domain: "*.example.com",
wantSANs: []string{"*.example.com", "example.com"}, wantSANs: []string{"*.example.com", "example.com"},
}, },
{ {
name: "wildcard-foo-bar-com",
domain: "*.foo.bar.com", domain: "*.foo.bar.com",
wantSANs: []string{"*.foo.bar.com", "foo.bar.com"}, wantSANs: []string{"*.foo.bar.com", "foo.bar.com"},
}, },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.domain, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
csrDER, err := certRequest(key, tt.domain, nil) csrDER, err := certRequest(key, tt.domain, nil)
if err != nil { if err != nil {
t.Fatalf("certRequest: %v", err) t.Fatalf("certRequest: %v", err)
@ -365,19 +369,19 @@ func TestShouldStartDomainRenewal(t *testing.T) {
wantErr string wantErr string
}{ }{
{ {
name: "should renew", name: "should-renew",
notBefore: now.AddDate(0, 0, -89), notBefore: now.AddDate(0, 0, -89),
lifetime: 90 * 24 * time.Hour, lifetime: 90 * 24 * time.Hour,
want: true, want: true,
}, },
{ {
name: "short-lived renewal", name: "short-lived-renewal",
notBefore: now.AddDate(0, 0, -7), notBefore: now.AddDate(0, 0, -7),
lifetime: 10 * 24 * time.Hour, lifetime: 10 * 24 * time.Hour,
want: true, want: true,
}, },
{ {
name: "no renew", name: "no-renew",
notBefore: now.AddDate(0, 0, -59), // 59 days ago == not 2/3rds of the way through 90 days yet notBefore: now.AddDate(0, 0, -59), // 59 days ago == not 2/3rds of the way through 90 days yet
lifetime: 90 * 24 * time.Hour, lifetime: 90 * 24 * time.Hour,
want: false, want: false,

@ -3035,20 +3035,20 @@ func TestSetExitNodeIDPolicy(t *testing.T) {
lastSuggestedExitNode tailcfg.StableNodeID lastSuggestedExitNode tailcfg.StableNodeID
}{ }{
{ {
name: "ExitNodeID key is set", name: "exitNodeID-set",
exitNodeIDKey: true, exitNodeIDKey: true,
exitNodeID: "123", exitNodeID: "123",
exitNodeIDWant: "123", exitNodeIDWant: "123",
prefsChanged: true, prefsChanged: true,
}, },
{ {
name: "ExitNodeID key not set", name: "exitNodeID-not-set",
exitNodeIDKey: true, exitNodeIDKey: true,
exitNodeIDWant: "", exitNodeIDWant: "",
prefsChanged: false, prefsChanged: false,
}, },
{ {
name: "ExitNodeID key set, ExitNodeIP preference set", name: "exitNodeID-set-exitNodeIP-pref-set",
exitNodeIDKey: true, exitNodeIDKey: true,
exitNodeID: "123", exitNodeID: "123",
prefs: &ipn.Prefs{ExitNodeIP: netip.MustParseAddr("127.0.0.1")}, prefs: &ipn.Prefs{ExitNodeIP: netip.MustParseAddr("127.0.0.1")},
@ -3056,7 +3056,7 @@ func TestSetExitNodeIDPolicy(t *testing.T) {
prefsChanged: true, prefsChanged: true,
}, },
{ {
name: "ExitNodeID key not set, ExitNodeIP key set", name: "exitNodeID-not-set-exitNodeIP-set",
exitNodeIPKey: true, exitNodeIPKey: true,
exitNodeIP: "127.0.0.1", exitNodeIP: "127.0.0.1",
prefs: &ipn.Prefs{ExitNodeIP: netip.MustParseAddr("127.0.0.1")}, prefs: &ipn.Prefs{ExitNodeIP: netip.MustParseAddr("127.0.0.1")},
@ -3064,7 +3064,7 @@ func TestSetExitNodeIDPolicy(t *testing.T) {
prefsChanged: false, prefsChanged: false,
}, },
{ {
name: "ExitNodeIP key set, existing ExitNodeIP pref", name: "exitNodeIP-set-existing-pref",
exitNodeIPKey: true, exitNodeIPKey: true,
exitNodeIP: "127.0.0.1", exitNodeIP: "127.0.0.1",
prefs: &ipn.Prefs{ExitNodeIP: netip.MustParseAddr("127.0.0.1")}, prefs: &ipn.Prefs{ExitNodeIP: netip.MustParseAddr("127.0.0.1")},
@ -3072,7 +3072,7 @@ func TestSetExitNodeIDPolicy(t *testing.T) {
prefsChanged: false, prefsChanged: false,
}, },
{ {
name: "existing preferences match policy", name: "existing-prefs-match-policy",
exitNodeIDKey: true, exitNodeIDKey: true,
exitNodeID: "123", exitNodeID: "123",
prefs: &ipn.Prefs{ExitNodeID: tailcfg.StableNodeID("123")}, prefs: &ipn.Prefs{ExitNodeID: tailcfg.StableNodeID("123")},
@ -3080,7 +3080,8 @@ func TestSetExitNodeIDPolicy(t *testing.T) {
prefsChanged: false, prefsChanged: false,
}, },
{ {
name: "ExitNodeIP set if net map does not have corresponding node", // ExitNodeIP is set when net map does not have a corresponding node.
name: "exitNodeIP-set-no-matching-node",
exitNodeIPKey: true, exitNodeIPKey: true,
prefs: &ipn.Prefs{ExitNodeIP: netip.MustParseAddr("127.0.0.1")}, prefs: &ipn.Prefs{ExitNodeIP: netip.MustParseAddr("127.0.0.1")},
exitNodeIP: "127.0.0.1", exitNodeIP: "127.0.0.1",
@ -3116,7 +3117,8 @@ func TestSetExitNodeIDPolicy(t *testing.T) {
}, },
}, },
{ {
name: "ExitNodeIP cleared if net map has corresponding node - policy matches prefs", // ExitNodeIP cleared when net map has corresponding node and policy matches prefs.
name: "exitNodeIP-cleared-matching-node-policy-matches",
prefs: &ipn.Prefs{ExitNodeIP: netip.MustParseAddr("127.0.0.1")}, prefs: &ipn.Prefs{ExitNodeIP: netip.MustParseAddr("127.0.0.1")},
exitNodeIPKey: true, exitNodeIPKey: true,
exitNodeIP: "127.0.0.1", exitNodeIP: "127.0.0.1",
@ -3156,7 +3158,8 @@ func TestSetExitNodeIDPolicy(t *testing.T) {
}, },
}, },
{ {
name: "ExitNodeIP cleared if net map has corresponding node - no policy set", // ExitNodeIP cleared when net map has corresponding node and no policy is set.
name: "exitNodeIP-cleared-matching-node-no-policy",
prefs: &ipn.Prefs{ExitNodeIP: netip.MustParseAddr("127.0.0.1")}, prefs: &ipn.Prefs{ExitNodeIP: netip.MustParseAddr("127.0.0.1")},
exitNodeIPWant: "", exitNodeIPWant: "",
exitNodeIDWant: "123", exitNodeIDWant: "123",
@ -3194,7 +3197,8 @@ func TestSetExitNodeIDPolicy(t *testing.T) {
}, },
}, },
{ {
name: "ExitNodeIP cleared if net map has corresponding node - different exit node IP in policy", // ExitNodeIP cleared when net map has corresponding node but policy has different exit node IP.
name: "exitNodeIP-cleared-matching-node-different-policy-IP",
exitNodeIPKey: true, exitNodeIPKey: true,
prefs: &ipn.Prefs{ExitNodeIP: netip.MustParseAddr("127.0.0.1")}, prefs: &ipn.Prefs{ExitNodeIP: netip.MustParseAddr("127.0.0.1")},
exitNodeIP: "100.64.5.6", exitNodeIP: "100.64.5.6",
@ -3234,7 +3238,7 @@ func TestSetExitNodeIDPolicy(t *testing.T) {
}, },
}, },
{ {
name: "ExitNodeID key is set to auto:any and last suggested exit node is populated", name: "exitNodeID-auto-any-last-suggested-populated",
exitNodeIDKey: true, exitNodeIDKey: true,
exitNodeID: "auto:any", exitNodeID: "auto:any",
lastSuggestedExitNode: "123", lastSuggestedExitNode: "123",
@ -3243,7 +3247,7 @@ func TestSetExitNodeIDPolicy(t *testing.T) {
prefsChanged: true, prefsChanged: true,
}, },
{ {
name: "ExitNodeID key is set to auto:any and last suggested exit node is not populated", name: "exitNodeID-auto-any-last-suggested-not-populated",
exitNodeIDKey: true, exitNodeIDKey: true,
exitNodeID: "auto:any", exitNodeID: "auto:any",
exitNodeIDWant: "auto:any", exitNodeIDWant: "auto:any",
@ -3251,7 +3255,7 @@ func TestSetExitNodeIDPolicy(t *testing.T) {
prefsChanged: true, prefsChanged: true,
}, },
{ {
name: "ExitNodeID key is set to auto:foo and last suggested exit node is populated", name: "exitNodeID-auto-foo-last-suggested-populated",
exitNodeIDKey: true, exitNodeIDKey: true,
exitNodeID: "auto:foo", exitNodeID: "auto:foo",
lastSuggestedExitNode: "123", lastSuggestedExitNode: "123",
@ -3260,7 +3264,7 @@ func TestSetExitNodeIDPolicy(t *testing.T) {
prefsChanged: true, prefsChanged: true,
}, },
{ {
name: "ExitNodeID key is set to auto:foo and last suggested exit node is not populated", name: "exitNodeID-auto-foo-last-suggested-not-populated",
exitNodeIDKey: true, exitNodeIDKey: true,
exitNodeID: "auto:foo", exitNodeID: "auto:foo",
exitNodeIDWant: "auto:any", // should be "auto:any" for compatibility with existing clients exitNodeIDWant: "auto:any", // should be "auto:any" for compatibility with existing clients
@ -3645,10 +3649,10 @@ func TestApplySysPolicy(t *testing.T) {
stringPolicies map[pkey.Key]string stringPolicies map[pkey.Key]string
}{ }{
{ {
name: "empty prefs without policies", name: "empty-prefs-no-policies",
}, },
{ {
name: "prefs set without policies", name: "prefs-set-no-policies",
prefs: ipn.Prefs{ prefs: ipn.Prefs{
ControlURL: "1", ControlURL: "1",
ShieldsUp: true, ShieldsUp: true,
@ -3667,7 +3671,7 @@ func TestApplySysPolicy(t *testing.T) {
}, },
}, },
{ {
name: "empty prefs with policies", name: "empty-prefs-with-policies",
wantPrefs: ipn.Prefs{ wantPrefs: ipn.Prefs{
ControlURL: "1", ControlURL: "1",
ShieldsUp: true, ShieldsUp: true,
@ -3687,7 +3691,7 @@ func TestApplySysPolicy(t *testing.T) {
}, },
}, },
{ {
name: "prefs set with matching policies", name: "prefs-set-matching-policies",
prefs: ipn.Prefs{ prefs: ipn.Prefs{
ControlURL: "1", ControlURL: "1",
ShieldsUp: true, ShieldsUp: true,
@ -3708,7 +3712,7 @@ func TestApplySysPolicy(t *testing.T) {
}, },
}, },
{ {
name: "prefs set with conflicting policies", name: "prefs-set-conflicting-policies",
prefs: ipn.Prefs{ prefs: ipn.Prefs{
ControlURL: "1", ControlURL: "1",
ShieldsUp: true, ShieldsUp: true,
@ -3736,7 +3740,7 @@ func TestApplySysPolicy(t *testing.T) {
}, },
}, },
{ {
name: "prefs set with neutral policies", name: "prefs-set-neutral-policies",
prefs: ipn.Prefs{ prefs: ipn.Prefs{
ControlURL: "1", ControlURL: "1",
ShieldsUp: true, ShieldsUp: true,
@ -3772,7 +3776,7 @@ func TestApplySysPolicy(t *testing.T) {
}, },
}, },
{ {
name: "enable AutoUpdate apply does not unset check", name: "enable-apply-keeps-check",
prefs: ipn.Prefs{ prefs: ipn.Prefs{
AutoUpdate: ipn.AutoUpdatePrefs{ AutoUpdate: ipn.AutoUpdatePrefs{
Check: true, Check: true,
@ -3791,7 +3795,7 @@ func TestApplySysPolicy(t *testing.T) {
}, },
}, },
{ {
name: "disable AutoUpdate apply does not unset check", name: "disable-apply-keeps-check",
prefs: ipn.Prefs{ prefs: ipn.Prefs{
AutoUpdate: ipn.AutoUpdatePrefs{ AutoUpdate: ipn.AutoUpdatePrefs{
Check: true, Check: true,
@ -3810,7 +3814,7 @@ func TestApplySysPolicy(t *testing.T) {
}, },
}, },
{ {
name: "enable AutoUpdate check does not unset apply", name: "enable-check-keeps-apply",
prefs: ipn.Prefs{ prefs: ipn.Prefs{
AutoUpdate: ipn.AutoUpdatePrefs{ AutoUpdate: ipn.AutoUpdatePrefs{
Check: false, Check: false,
@ -3829,7 +3833,7 @@ func TestApplySysPolicy(t *testing.T) {
}, },
}, },
{ {
name: "disable AutoUpdate check does not unset apply", name: "disable-check-keeps-apply",
prefs: ipn.Prefs{ prefs: ipn.Prefs{
AutoUpdate: ipn.AutoUpdatePrefs{ AutoUpdate: ipn.AutoUpdatePrefs{
Check: true, Check: true,
@ -3879,7 +3883,7 @@ func TestApplySysPolicy(t *testing.T) {
} }
}) })
t.Run("status update", func(t *testing.T) { t.Run("status-update", func(t *testing.T) {
// Profile manager fills in blank ControlURL but it's not set // Profile manager fills in blank ControlURL but it's not set
// in most test cases to avoid cluttering them, so adjust for // in most test cases to avoid cluttering them, so adjust for
// that. // that.
@ -3919,75 +3923,75 @@ func TestPreferencePolicyInfo(t *testing.T) {
policyError error policyError error
}{ }{
{ {
name: "force enable modify", name: "force-enable-modify",
initialValue: false, initialValue: false,
wantValue: true, wantValue: true,
wantChange: true, wantChange: true,
policyValue: "always", policyValue: "always",
}, },
{ {
name: "force enable unchanged", name: "force-enable-unchanged",
initialValue: true, initialValue: true,
wantValue: true, wantValue: true,
policyValue: "always", policyValue: "always",
}, },
{ {
name: "force disable modify", name: "force-disable-modify",
initialValue: true, initialValue: true,
wantValue: false, wantValue: false,
wantChange: true, wantChange: true,
policyValue: "never", policyValue: "never",
}, },
{ {
name: "force disable unchanged", name: "force-disable-unchanged",
initialValue: false, initialValue: false,
wantValue: false, wantValue: false,
policyValue: "never", policyValue: "never",
}, },
{ {
name: "unforced enabled", name: "unforced-enabled",
initialValue: true, initialValue: true,
wantValue: true, wantValue: true,
policyValue: "user-decides", policyValue: "user-decides",
}, },
{ {
name: "unforced disabled", name: "unforced-disabled",
initialValue: false, initialValue: false,
wantValue: false, wantValue: false,
policyValue: "user-decides", policyValue: "user-decides",
}, },
{ {
name: "blank enabled", name: "blank-enabled",
initialValue: true, initialValue: true,
wantValue: true, wantValue: true,
policyValue: "", policyValue: "",
}, },
{ {
name: "blank disabled", name: "blank-disabled",
initialValue: false, initialValue: false,
wantValue: false, wantValue: false,
policyValue: "", policyValue: "",
}, },
{ {
name: "unset enabled", name: "unset-enabled",
initialValue: true, initialValue: true,
wantValue: true, wantValue: true,
policyError: syspolicy.ErrNoSuchKey, policyError: syspolicy.ErrNoSuchKey,
}, },
{ {
name: "unset disabled", name: "unset-disabled",
initialValue: false, initialValue: false,
wantValue: false, wantValue: false,
policyError: syspolicy.ErrNoSuchKey, policyError: syspolicy.ErrNoSuchKey,
}, },
{ {
name: "error enabled", name: "error-enabled",
initialValue: true, initialValue: true,
wantValue: true, wantValue: true,
policyError: errors.New("test error"), policyError: errors.New("test error"),
}, },
{ {
name: "error disabled", name: "error-disabled",
initialValue: false, initialValue: false,
wantValue: false, wantValue: false,
policyError: errors.New("test error"), policyError: errors.New("test error"),
@ -4113,53 +4117,62 @@ func TestOnTailnetDefaultAutoUpdate(t *testing.T) {
func TestTCPHandlerForDst(t *testing.T) { func TestTCPHandlerForDst(t *testing.T) {
b := newTestBackend(t) b := newTestBackend(t)
tests := []struct { tests := []struct {
name string
desc string desc string
dst string dst string
intercept bool intercept bool
}{ }{
{ {
name: "100_100_100_100-port80",
desc: "intercept port 80 (Web UI) on quad100 IPv4", desc: "intercept port 80 (Web UI) on quad100 IPv4",
dst: "100.100.100.100:80", dst: "100.100.100.100:80",
intercept: true, intercept: true,
}, },
{ {
name: "fd7a-115c-a1e0--53-port80",
desc: "intercept port 80 (Web UI) on quad100 IPv6", desc: "intercept port 80 (Web UI) on quad100 IPv6",
dst: "[fd7a:115c:a1e0::53]:80", dst: "[fd7a:115c:a1e0::53]:80",
intercept: true, intercept: true,
}, },
{ {
name: "100_100_103_100-port80",
desc: "don't intercept port 80 on local ip", desc: "don't intercept port 80 on local ip",
dst: "100.100.103.100:80", dst: "100.100.103.100:80",
intercept: false, intercept: false,
}, },
{ {
name: "fd7a-115c-a1e0--53-port8080",
desc: "intercept port 8080 (Taildrive) on quad100 IPv4", desc: "intercept port 8080 (Taildrive) on quad100 IPv4",
dst: "[fd7a:115c:a1e0::53]:8080", dst: "[fd7a:115c:a1e0::53]:8080",
intercept: true, intercept: true,
}, },
{ {
name: "100_100_103_100-port8080",
desc: "don't intercept port 8080 on local ip", desc: "don't intercept port 8080 on local ip",
dst: "100.100.103.100:8080", dst: "100.100.103.100:8080",
intercept: false, intercept: false,
}, },
{ {
name: "100_100_100_100-port9080",
desc: "don't intercept port 9080 on quad100 IPv4", desc: "don't intercept port 9080 on quad100 IPv4",
dst: "100.100.100.100:9080", dst: "100.100.100.100:9080",
intercept: false, intercept: false,
}, },
{ {
name: "fd7a-115c-a1e0--53-port9080",
desc: "don't intercept port 9080 on quad100 IPv6", desc: "don't intercept port 9080 on quad100 IPv6",
dst: "[fd7a:115c:a1e0::53]:9080", dst: "[fd7a:115c:a1e0::53]:9080",
intercept: false, intercept: false,
}, },
{ {
name: "100_100_103_100-port9080",
desc: "don't intercept port 9080 on local ip", desc: "don't intercept port 9080 on local ip",
dst: "100.100.103.100:9080", dst: "100.100.103.100:9080",
intercept: false, intercept: false,
}, },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.dst, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
t.Log(tt.desc) t.Log(tt.desc)
src := netip.MustParseAddrPort("100.100.102.100:51234") src := netip.MustParseAddrPort("100.100.102.100:51234")
h, _ := b.TCPHandlerForDst(src, netip.MustParseAddrPort(tt.dst)) h, _ := b.TCPHandlerForDst(src, netip.MustParseAddrPort(tt.dst))
@ -4258,122 +4271,146 @@ func TestTCPHandlerForDstWithVIPService(t *testing.T) {
} }
tests := []struct { tests := []struct {
name string
desc string desc string
dst string dst string
intercept bool intercept bool
}{ }{
{ {
name: "100_100_100_100-port80",
desc: "intercept port 80 (Web UI) on quad100 IPv4", desc: "intercept port 80 (Web UI) on quad100 IPv4",
dst: "100.100.100.100:80", dst: "100.100.100.100:80",
intercept: true, intercept: true,
}, },
{ {
name: "fd7a-115c-a1e0--53-port80",
desc: "intercept port 80 (Web UI) on quad100 IPv6", desc: "intercept port 80 (Web UI) on quad100 IPv6",
dst: "[fd7a:115c:a1e0::53]:80", dst: "[fd7a:115c:a1e0::53]:80",
intercept: true, intercept: true,
}, },
{ {
name: "100_100_103_100-port80",
desc: "don't intercept port 80 on local ip", desc: "don't intercept port 80 on local ip",
dst: "100.100.103.100:80", dst: "100.100.103.100:80",
intercept: false, intercept: false,
}, },
{ {
name: "100_100_100_100-port8080",
desc: "intercept port 8080 (Taildrive) on quad100 IPv4", desc: "intercept port 8080 (Taildrive) on quad100 IPv4",
dst: "100.100.100.100:8080", dst: "100.100.100.100:8080",
intercept: true, intercept: true,
}, },
{ {
name: "fd7a-115c-a1e0--53-port8080",
desc: "intercept port 8080 (Taildrive) on quad100 IPv6", desc: "intercept port 8080 (Taildrive) on quad100 IPv6",
dst: "[fd7a:115c:a1e0::53]:8080", dst: "[fd7a:115c:a1e0::53]:8080",
intercept: true, intercept: true,
}, },
{ {
name: "100_100_103_100-port8080",
desc: "don't intercept port 8080 on local ip", desc: "don't intercept port 8080 on local ip",
dst: "100.100.103.100:8080", dst: "100.100.103.100:8080",
intercept: false, intercept: false,
}, },
{ {
name: "100_100_100_100-port9080",
desc: "don't intercept port 9080 on quad100 IPv4", desc: "don't intercept port 9080 on quad100 IPv4",
dst: "100.100.100.100:9080", dst: "100.100.100.100:9080",
intercept: false, intercept: false,
}, },
{ {
name: "fd7a-115c-a1e0--53-port9080",
desc: "don't intercept port 9080 on quad100 IPv6", desc: "don't intercept port 9080 on quad100 IPv6",
dst: "[fd7a:115c:a1e0::53]:9080", dst: "[fd7a:115c:a1e0::53]:9080",
intercept: false, intercept: false,
}, },
{ {
name: "100_100_103_100-port9080",
desc: "don't intercept port 9080 on local ip", desc: "don't intercept port 9080 on local ip",
dst: "100.100.103.100:9080", dst: "100.100.103.100:9080",
intercept: false, intercept: false,
}, },
// VIP service destinations // VIP service destinations
{ {
name: "100_101_101_101-port882",
desc: "intercept port 882 (HTTP) on service foo IPv4", desc: "intercept port 882 (HTTP) on service foo IPv4",
dst: "100.101.101.101:882", dst: "100.101.101.101:882",
intercept: true, intercept: true,
}, },
{ {
name: "fd7a-115c-a1e0-ab12-4843-cd96-6565-6565-port882",
desc: "intercept port 882 (HTTP) on service foo IPv6", desc: "intercept port 882 (HTTP) on service foo IPv6",
dst: "[fd7a:115c:a1e0:ab12:4843:cd96:6565:6565]:882", dst: "[fd7a:115c:a1e0:ab12:4843:cd96:6565:6565]:882",
intercept: true, intercept: true,
}, },
{ {
name: "100_101_101_101-port883",
desc: "intercept port 883 (HTTPS) on service foo IPv4", desc: "intercept port 883 (HTTPS) on service foo IPv4",
dst: "100.101.101.101:883", dst: "100.101.101.101:883",
intercept: true, intercept: true,
}, },
{ {
name: "fd7a-115c-a1e0-ab12-4843-cd96-6565-6565-port883",
desc: "intercept port 883 (HTTPS) on service foo IPv6", desc: "intercept port 883 (HTTPS) on service foo IPv6",
dst: "[fd7a:115c:a1e0:ab12:4843:cd96:6565:6565]:883", dst: "[fd7a:115c:a1e0:ab12:4843:cd96:6565:6565]:883",
intercept: true, intercept: true,
}, },
{ {
name: "100_99_99_99-port990",
desc: "intercept port 990 (TCPForward) on service bar IPv4", desc: "intercept port 990 (TCPForward) on service bar IPv4",
dst: "100.99.99.99:990", dst: "100.99.99.99:990",
intercept: true, intercept: true,
}, },
{ {
name: "fd7a-115c-a1e0-ab12-4843-cd96-626b-628b-port990",
desc: "intercept port 990 (TCPForward) on service bar IPv6", desc: "intercept port 990 (TCPForward) on service bar IPv6",
dst: "[fd7a:115c:a1e0:ab12:4843:cd96:626b:628b]:990", dst: "[fd7a:115c:a1e0:ab12:4843:cd96:626b:628b]:990",
intercept: true, intercept: true,
}, },
{ {
name: "100_99_99_99-port990-terminateTLS",
desc: "intercept port 991 (TCPForward with TerminateTLS) on service bar IPv4", desc: "intercept port 991 (TCPForward with TerminateTLS) on service bar IPv4",
dst: "100.99.99.99:990", dst: "100.99.99.99:990",
intercept: true, intercept: true,
}, },
{ {
name: "fd7a-115c-a1e0-ab12-4843-cd96-626b-628b-port990-terminateTLS",
desc: "intercept port 991 (TCPForward with TerminateTLS) on service bar IPv6", desc: "intercept port 991 (TCPForward with TerminateTLS) on service bar IPv6",
dst: "[fd7a:115c:a1e0:ab12:4843:cd96:626b:628b]:990", dst: "[fd7a:115c:a1e0:ab12:4843:cd96:626b:628b]:990",
intercept: true, intercept: true,
}, },
{ {
name: "100_101_101_101-port4444",
desc: "don't intercept port 4444 on service foo IPv4", desc: "don't intercept port 4444 on service foo IPv4",
dst: "100.101.101.101:4444", dst: "100.101.101.101:4444",
intercept: false, intercept: false,
}, },
{ {
name: "fd7a-115c-a1e0-ab12-4843-cd96-6565-6565-port4444",
desc: "don't intercept port 4444 on service foo IPv6", desc: "don't intercept port 4444 on service foo IPv6",
dst: "[fd7a:115c:a1e0:ab12:4843:cd96:6565:6565]:4444", dst: "[fd7a:115c:a1e0:ab12:4843:cd96:6565:6565]:4444",
intercept: false, intercept: false,
}, },
{ {
name: "100_22_22_22-port883",
desc: "don't intercept port 600 on unknown service IPv4", desc: "don't intercept port 600 on unknown service IPv4",
dst: "100.22.22.22:883", dst: "100.22.22.22:883",
intercept: false, intercept: false,
}, },
{ {
name: "fd7a-115c-a1e0-ab12-4843-cd96-626b-628b-port883",
desc: "don't intercept port 600 on unknown service IPv6", desc: "don't intercept port 600 on unknown service IPv6",
dst: "[fd7a:115c:a1e0:ab12:4843:cd96:626b:628b]:883", dst: "[fd7a:115c:a1e0:ab12:4843:cd96:626b:628b]:883",
intercept: false, intercept: false,
}, },
{ {
name: "100_133_133_133-port600",
desc: "don't intercept port 600 (HTTPS) on service baz IPv4", desc: "don't intercept port 600 (HTTPS) on service baz IPv4",
dst: "100.133.133.133:600", dst: "100.133.133.133:600",
intercept: false, intercept: false,
}, },
{ {
name: "fd7a-115c-a1e0-ab12-4843-cd96-8585-8585-port600",
desc: "don't intercept port 600 (HTTPS) on service baz IPv6", desc: "don't intercept port 600 (HTTPS) on service baz IPv6",
dst: "[fd7a:115c:a1e0:ab12:4843:cd96:8585:8585]:600", dst: "[fd7a:115c:a1e0:ab12:4843:cd96:8585:8585]:600",
intercept: false, intercept: false,
@ -4381,7 +4418,7 @@ func TestTCPHandlerForDstWithVIPService(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.dst, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
t.Log(tt.desc) t.Log(tt.desc)
src := netip.MustParseAddrPort("100.100.102.100:51234") src := netip.MustParseAddrPort("100.100.102.100:51234")
h, _ := b.TCPHandlerForDst(src, netip.MustParseAddrPort(tt.dst)) h, _ := b.TCPHandlerForDst(src, netip.MustParseAddrPort(tt.dst))
@ -4664,14 +4701,14 @@ func TestRoundTraffic(t *testing.T) {
bytes int64 bytes int64
want float64 want float64
}{ }{
{name: "under 5 bytes", bytes: 4, want: 4}, {name: "under-5B", bytes: 4, want: 4},
{name: "under 1000 bytes", bytes: 987, want: 990}, {name: "under-1000B", bytes: 987, want: 990},
{name: "under 10_000 bytes", bytes: 8875, want: 8900}, {name: "under-10000B", bytes: 8875, want: 8900},
{name: "under 100_000 bytes", bytes: 77777, want: 78000}, {name: "under-100000B", bytes: 77777, want: 78000},
{name: "under 1_000_000 bytes", bytes: 666523, want: 670000}, {name: "under-1000000B", bytes: 666523, want: 670000},
{name: "under 10_000_000 bytes", bytes: 22556677, want: 23000000}, {name: "under-10000000B", bytes: 22556677, want: 23000000},
{name: "under 1_000_000_000 bytes", bytes: 1234234234, want: 1200000000}, {name: "under-1000000000B", bytes: 1234234234, want: 1200000000},
{name: "under 1_000_000_000 bytes", bytes: 123423423499, want: 123400000000}, {name: "over-1000000000B", bytes: 123423423499, want: 123400000000},
} }
for _, tt := range tests { for _, tt := range tests {
@ -5033,7 +5070,7 @@ func TestSuggestExitNode(t *testing.T) {
wantError error wantError error
}{ }{
{ {
name: "2 exit nodes in same region", name: "2-exits-same-region",
lastReport: preferred1Report, lastReport: preferred1Report,
netMap: &netmap.NetworkMap{ netMap: &netmap.NetworkMap{
SelfNode: selfNode.View(), SelfNode: selfNode.View(),
@ -5051,7 +5088,7 @@ func TestSuggestExitNode(t *testing.T) {
wantID: "stable1", wantID: "stable1",
}, },
{ {
name: "2 exit nodes different regions unknown latency", name: "2-exits-different-regions-unknown-latency",
lastReport: noLatency1Report, lastReport: noLatency1Report,
netMap: defaultNetmap, netMap: defaultNetmap,
wantRegions: []int{1, 3}, // the only regions with peers wantRegions: []int{1, 3}, // the only regions with peers
@ -5060,7 +5097,7 @@ func TestSuggestExitNode(t *testing.T) {
wantID: "stable2", wantID: "stable2",
}, },
{ {
name: "2 derp based exit nodes, different regions, equal latency", name: "2-derp-exits-different-regions-equal-latency",
lastReport: &netcheck.Report{ lastReport: &netcheck.Report{
RegionLatency: map[int]time.Duration{ RegionLatency: map[int]time.Duration{
1: 10, 1: 10,
@ -5083,7 +5120,7 @@ func TestSuggestExitNode(t *testing.T) {
wantID: "stable1", wantID: "stable1",
}, },
{ {
name: "mullvad nodes, no derp based exit nodes", name: "mullvad-no-derp-exits",
lastReport: noLatency1Report, lastReport: noLatency1Report,
netMap: locationNetmap, netMap: locationNetmap,
wantID: "stable5", wantID: "stable5",
@ -5091,7 +5128,7 @@ func TestSuggestExitNode(t *testing.T) {
wantName: "Dallas", wantName: "Dallas",
}, },
{ {
name: "nearby mullvad nodes with different priorities", name: "nearby-mullvad-different-priorities",
lastReport: noLatency1Report, lastReport: noLatency1Report,
netMap: &netmap.NetworkMap{ netMap: &netmap.NetworkMap{
SelfNode: selfNode.View(), SelfNode: selfNode.View(),
@ -5107,7 +5144,7 @@ func TestSuggestExitNode(t *testing.T) {
wantName: "Fort Worth", wantName: "Fort Worth",
}, },
{ {
name: "nearby mullvad nodes with same priorities", name: "nearby-mullvad-same-priorities",
lastReport: noLatency1Report, lastReport: noLatency1Report,
netMap: &netmap.NetworkMap{ netMap: &netmap.NetworkMap{
SelfNode: selfNode.View(), SelfNode: selfNode.View(),
@ -5124,7 +5161,7 @@ func TestSuggestExitNode(t *testing.T) {
wantName: "Dallas", wantName: "Dallas",
}, },
{ {
name: "mullvad nodes, remaining node is not in preferred derp", name: "mullvad-remaining-not-in-preferred-derp",
lastReport: noLatency1Report, lastReport: noLatency1Report,
netMap: &netmap.NetworkMap{ netMap: &netmap.NetworkMap{
SelfNode: selfNode.View(), SelfNode: selfNode.View(),
@ -5140,7 +5177,7 @@ func TestSuggestExitNode(t *testing.T) {
wantName: "peer4", wantName: "peer4",
}, },
{ {
name: "no peers", name: "no-peers",
lastReport: noLatency1Report, lastReport: noLatency1Report,
netMap: &netmap.NetworkMap{ netMap: &netmap.NetworkMap{
SelfNode: selfNode.View(), SelfNode: selfNode.View(),
@ -5148,13 +5185,13 @@ func TestSuggestExitNode(t *testing.T) {
}, },
}, },
{ {
name: "nil report", name: "nil-report",
lastReport: nil, lastReport: nil,
netMap: largeNetmap, netMap: largeNetmap,
wantError: ErrNoPreferredDERP, wantError: ErrNoPreferredDERP,
}, },
{ {
name: "no preferred derp region", name: "no-preferred-derp-region",
lastReport: preferredNoneReport, lastReport: preferredNoneReport,
netMap: &netmap.NetworkMap{ netMap: &netmap.NetworkMap{
SelfNode: selfNode.View(), SelfNode: selfNode.View(),
@ -5163,13 +5200,13 @@ func TestSuggestExitNode(t *testing.T) {
wantError: ErrNoPreferredDERP, wantError: ErrNoPreferredDERP,
}, },
{ {
name: "nil netmap", name: "nil-netmap",
lastReport: noLatency1Report, lastReport: noLatency1Report,
netMap: nil, netMap: nil,
wantError: ErrNoPreferredDERP, wantError: ErrNoPreferredDERP,
}, },
{ {
name: "nil derpmap", name: "nil-derpmap",
lastReport: noLatency1Report, lastReport: noLatency1Report,
netMap: &netmap.NetworkMap{ netMap: &netmap.NetworkMap{
SelfNode: selfNode.View(), SelfNode: selfNode.View(),
@ -5181,7 +5218,7 @@ func TestSuggestExitNode(t *testing.T) {
wantError: ErrNoPreferredDERP, wantError: ErrNoPreferredDERP,
}, },
{ {
name: "missing suggestion capability", name: "missing-suggestion-capability",
lastReport: noLatency1Report, lastReport: noLatency1Report,
netMap: &netmap.NetworkMap{ netMap: &netmap.NetworkMap{
SelfNode: selfNode.View(), SelfNode: selfNode.View(),
@ -5193,7 +5230,7 @@ func TestSuggestExitNode(t *testing.T) {
}, },
}, },
{ {
name: "prefer last node", name: "prefer-last-node",
lastReport: preferred1Report, lastReport: preferred1Report,
netMap: &netmap.NetworkMap{ netMap: &netmap.NetworkMap{
SelfNode: selfNode.View(), SelfNode: selfNode.View(),
@ -5212,7 +5249,7 @@ func TestSuggestExitNode(t *testing.T) {
wantID: "stable2", wantID: "stable2",
}, },
{ {
name: "found better derp node", name: "found-better-derp-node",
lastSuggestion: "stable3", lastSuggestion: "stable3",
lastReport: preferred1Report, lastReport: preferred1Report,
netMap: defaultNetmap, netMap: defaultNetmap,
@ -5220,7 +5257,7 @@ func TestSuggestExitNode(t *testing.T) {
wantName: "peer2", wantName: "peer2",
}, },
{ {
name: "prefer last mullvad node", name: "prefer-last-mullvad-node",
lastSuggestion: "stable2", lastSuggestion: "stable2",
lastReport: preferred1Report, lastReport: preferred1Report,
netMap: &netmap.NetworkMap{ netMap: &netmap.NetworkMap{
@ -5238,7 +5275,7 @@ func TestSuggestExitNode(t *testing.T) {
wantLocation: dallas.View(), wantLocation: dallas.View(),
}, },
{ {
name: "prefer better mullvad node", name: "prefer-better-mullvad-node",
lastSuggestion: "stable2", lastSuggestion: "stable2",
lastReport: preferred1Report, lastReport: preferred1Report,
netMap: &netmap.NetworkMap{ netMap: &netmap.NetworkMap{
@ -5256,7 +5293,7 @@ func TestSuggestExitNode(t *testing.T) {
wantLocation: fortWorth.View(), wantLocation: fortWorth.View(),
}, },
{ {
name: "large netmap", name: "large-netmap",
lastReport: preferred1Report, lastReport: preferred1Report,
netMap: largeNetmap, netMap: largeNetmap,
wantNodes: []tailcfg.StableNodeID{"stable1", "stable2"}, wantNodes: []tailcfg.StableNodeID{"stable1", "stable2"},
@ -5264,13 +5301,13 @@ func TestSuggestExitNode(t *testing.T) {
wantName: "peer2", wantName: "peer2",
}, },
{ {
name: "no allowed suggestions", name: "no-allowed-suggestions",
lastReport: preferred1Report, lastReport: preferred1Report,
netMap: largeNetmap, netMap: largeNetmap,
allowPolicy: []tailcfg.StableNodeID{}, allowPolicy: []tailcfg.StableNodeID{},
}, },
{ {
name: "only derp suggestions", name: "only-derp-suggestions",
lastReport: preferred1Report, lastReport: preferred1Report,
netMap: largeNetmap, netMap: largeNetmap,
allowPolicy: []tailcfg.StableNodeID{"stable1", "stable2", "stable3"}, allowPolicy: []tailcfg.StableNodeID{"stable1", "stable2", "stable3"},
@ -5279,7 +5316,7 @@ func TestSuggestExitNode(t *testing.T) {
wantName: "peer2", wantName: "peer2",
}, },
{ {
name: "only mullvad suggestions", name: "only-mullvad-suggestions",
lastReport: preferred1Report, lastReport: preferred1Report,
netMap: largeNetmap, netMap: largeNetmap,
allowPolicy: []tailcfg.StableNodeID{"stable5", "stable6", "stable7"}, allowPolicy: []tailcfg.StableNodeID{"stable5", "stable6", "stable7"},
@ -5288,7 +5325,7 @@ func TestSuggestExitNode(t *testing.T) {
wantLocation: fortWorth.View(), wantLocation: fortWorth.View(),
}, },
{ {
name: "only worst derp", name: "only-worst-derp",
lastReport: preferred1Report, lastReport: preferred1Report,
netMap: largeNetmap, netMap: largeNetmap,
allowPolicy: []tailcfg.StableNodeID{"stable3"}, allowPolicy: []tailcfg.StableNodeID{"stable3"},
@ -5296,7 +5333,7 @@ func TestSuggestExitNode(t *testing.T) {
wantName: "peer3", wantName: "peer3",
}, },
{ {
name: "only worst mullvad", name: "only-worst-mullvad",
lastReport: preferred1Report, lastReport: preferred1Report,
netMap: largeNetmap, netMap: largeNetmap,
allowPolicy: []tailcfg.StableNodeID{"stable6"}, allowPolicy: []tailcfg.StableNodeID{"stable6"},
@ -5306,7 +5343,7 @@ func TestSuggestExitNode(t *testing.T) {
}, },
{ {
// Regression test for https://github.com/tailscale/tailscale/issues/17661 // Regression test for https://github.com/tailscale/tailscale/issues/17661
name: "exit nodes with no home DERP, randomly selected", name: "exits-no-home-DERP-random-selection",
lastReport: &netcheck.Report{ lastReport: &netcheck.Report{
RegionLatency: map[int]time.Duration{ RegionLatency: map[int]time.Duration{
1: 10, 1: 10,
@ -5388,7 +5425,7 @@ func TestSuggestExitNodePickWeighted(t *testing.T) {
wantIDs []tailcfg.StableNodeID wantIDs []tailcfg.StableNodeID
}{ }{
{ {
name: "different priorities", name: "different-priorities",
candidates: []tailcfg.NodeView{ candidates: []tailcfg.NodeView{
makePeer(2, withExitRoutes(), withLocation(location20.View())), makePeer(2, withExitRoutes(), withLocation(location20.View())),
makePeer(3, withExitRoutes(), withLocation(location10.View())), makePeer(3, withExitRoutes(), withLocation(location10.View())),
@ -5396,7 +5433,7 @@ func TestSuggestExitNodePickWeighted(t *testing.T) {
wantIDs: []tailcfg.StableNodeID{"stable2"}, wantIDs: []tailcfg.StableNodeID{"stable2"},
}, },
{ {
name: "same priorities", name: "same-priorities",
candidates: []tailcfg.NodeView{ candidates: []tailcfg.NodeView{
makePeer(2, withExitRoutes(), withLocation(location10.View())), makePeer(2, withExitRoutes(), withLocation(location10.View())),
makePeer(3, withExitRoutes(), withLocation(location10.View())), makePeer(3, withExitRoutes(), withLocation(location10.View())),
@ -5404,11 +5441,11 @@ func TestSuggestExitNodePickWeighted(t *testing.T) {
wantIDs: []tailcfg.StableNodeID{"stable2", "stable3"}, wantIDs: []tailcfg.StableNodeID{"stable2", "stable3"},
}, },
{ {
name: "<1 candidates", name: "lt1-candidates",
candidates: []tailcfg.NodeView{}, candidates: []tailcfg.NodeView{},
}, },
{ {
name: "1 candidate", name: "1-candidate",
candidates: []tailcfg.NodeView{ candidates: []tailcfg.NodeView{
makePeer(2, withExitRoutes(), withLocation(location20.View())), makePeer(2, withExitRoutes(), withLocation(location20.View())),
}, },
@ -5444,7 +5481,7 @@ func TestSuggestExitNodeLongLatDistance(t *testing.T) {
want float64 want float64
}{ }{
{ {
name: "zero values", name: "zero-values",
fromLat: 0, fromLat: 0,
fromLong: 0, fromLong: 0,
toLat: 0, toLat: 0,
@ -5452,7 +5489,7 @@ func TestSuggestExitNodeLongLatDistance(t *testing.T) {
want: 0, want: 0,
}, },
{ {
name: "valid values", name: "valid-values",
fromLat: 40.73061, fromLat: 40.73061,
fromLong: -73.935242, fromLong: -73.935242,
toLat: 37.3382082, toLat: 37.3382082,
@ -5460,7 +5497,8 @@ func TestSuggestExitNodeLongLatDistance(t *testing.T) {
want: 4117266.873301274, want: 4117266.873301274,
}, },
{ {
name: "valid values, locations in north and south of equator", // Locations in north and south of equator.
name: "valid-values-cross-equator",
fromLat: 40.73061, fromLat: 40.73061,
fromLong: -73.935242, fromLong: -73.935242,
toLat: -33.861481, toLat: -33.861481,
@ -5865,13 +5903,13 @@ func TestMinLatencyDERPregion(t *testing.T) {
wantRegion int wantRegion int
}{ }{
{ {
name: "regions, no latency values", name: "regions-no-latency",
regions: []int{1, 2, 3}, regions: []int{1, 2, 3},
wantRegion: 0, wantRegion: 0,
report: &netcheck.Report{}, report: &netcheck.Report{},
}, },
{ {
name: "regions, different latency values", name: "regions-different-latency",
regions: []int{1, 2, 3}, regions: []int{1, 2, 3},
wantRegion: 2, wantRegion: 2,
report: &netcheck.Report{ report: &netcheck.Report{
@ -5883,7 +5921,7 @@ func TestMinLatencyDERPregion(t *testing.T) {
}, },
}, },
{ {
name: "regions, same values", name: "regions-same-latency",
regions: []int{1, 2, 3}, regions: []int{1, 2, 3},
wantRegion: 1, wantRegion: 1,
report: &netcheck.Report{ report: &netcheck.Report{
@ -6030,7 +6068,7 @@ func TestFillAllowedSuggestions(t *testing.T) {
want: []tailcfg.StableNodeID{"one", "three", "four", "two"}, // order should not matter want: []tailcfg.StableNodeID{"one", "three", "four", "two"}, // order should not matter
}, },
{ {
name: "preserve case", name: "preserve-case",
allowPolicy: []string{"ABC", "def", "gHiJ"}, allowPolicy: []string{"ABC", "def", "gHiJ"},
want: []tailcfg.StableNodeID{"ABC", "def", "gHiJ"}, want: []tailcfg.StableNodeID{"ABC", "def", "gHiJ"},
}, },
@ -6184,61 +6222,61 @@ func TestNotificationTargetMatch(t *testing.T) {
wantMatch: false, wantMatch: false,
}, },
{ {
name: "FilterByUID+CID/Nil", name: "FilterByUID-CID/Nil",
target: notificationTarget{userID: "S-1-5-21-1-2-3-4"}, target: notificationTarget{userID: "S-1-5-21-1-2-3-4"},
actor: nil, actor: nil,
wantMatch: false, wantMatch: false,
}, },
{ {
name: "FilterByUID+CID/NoUID/NoCID", name: "FilterByUID-CID/NoUID/NoCID",
target: notificationTarget{userID: "S-1-5-21-1-2-3-4", clientID: ipnauth.ClientIDFrom("A")}, target: notificationTarget{userID: "S-1-5-21-1-2-3-4", clientID: ipnauth.ClientIDFrom("A")},
actor: &ipnauth.TestActor{}, actor: &ipnauth.TestActor{},
wantMatch: false, wantMatch: false,
}, },
{ {
name: "FilterByUID+CID/NoUID/SameCID", name: "FilterByUID-CID/NoUID/SameCID",
target: notificationTarget{userID: "S-1-5-21-1-2-3-4", clientID: ipnauth.ClientIDFrom("A")}, target: notificationTarget{userID: "S-1-5-21-1-2-3-4", clientID: ipnauth.ClientIDFrom("A")},
actor: &ipnauth.TestActor{CID: ipnauth.ClientIDFrom("A")}, actor: &ipnauth.TestActor{CID: ipnauth.ClientIDFrom("A")},
wantMatch: false, wantMatch: false,
}, },
{ {
name: "FilterByUID+CID/NoUID/DifferentCID", name: "FilterByUID-CID/NoUID/DifferentCID",
target: notificationTarget{userID: "S-1-5-21-1-2-3-4", clientID: ipnauth.ClientIDFrom("A")}, target: notificationTarget{userID: "S-1-5-21-1-2-3-4", clientID: ipnauth.ClientIDFrom("A")},
actor: &ipnauth.TestActor{CID: ipnauth.ClientIDFrom("B")}, actor: &ipnauth.TestActor{CID: ipnauth.ClientIDFrom("B")},
wantMatch: false, wantMatch: false,
}, },
{ {
name: "FilterByUID+CID/SameUID/NoCID", name: "FilterByUID-CID/SameUID/NoCID",
target: notificationTarget{userID: "S-1-5-21-1-2-3-4", clientID: ipnauth.ClientIDFrom("A")}, target: notificationTarget{userID: "S-1-5-21-1-2-3-4", clientID: ipnauth.ClientIDFrom("A")},
actor: &ipnauth.TestActor{UID: "S-1-5-21-1-2-3-4"}, actor: &ipnauth.TestActor{UID: "S-1-5-21-1-2-3-4"},
wantMatch: false, wantMatch: false,
}, },
{ {
name: "FilterByUID+CID/SameUID/SameCID", name: "FilterByUID-CID/SameUID/SameCID",
target: notificationTarget{userID: "S-1-5-21-1-2-3-4", clientID: ipnauth.ClientIDFrom("A")}, target: notificationTarget{userID: "S-1-5-21-1-2-3-4", clientID: ipnauth.ClientIDFrom("A")},
actor: &ipnauth.TestActor{UID: "S-1-5-21-1-2-3-4", CID: ipnauth.ClientIDFrom("A")}, actor: &ipnauth.TestActor{UID: "S-1-5-21-1-2-3-4", CID: ipnauth.ClientIDFrom("A")},
wantMatch: true, wantMatch: true,
}, },
{ {
name: "FilterByUID+CID/SameUID/DifferentCID", name: "FilterByUID-CID/SameUID/DifferentCID",
target: notificationTarget{userID: "S-1-5-21-1-2-3-4", clientID: ipnauth.ClientIDFrom("A")}, target: notificationTarget{userID: "S-1-5-21-1-2-3-4", clientID: ipnauth.ClientIDFrom("A")},
actor: &ipnauth.TestActor{UID: "S-1-5-21-1-2-3-4", CID: ipnauth.ClientIDFrom("B")}, actor: &ipnauth.TestActor{UID: "S-1-5-21-1-2-3-4", CID: ipnauth.ClientIDFrom("B")},
wantMatch: false, wantMatch: false,
}, },
{ {
name: "FilterByUID+CID/DifferentUID/NoCID", name: "FilterByUID-CID/DifferentUID/NoCID",
target: notificationTarget{userID: "S-1-5-21-1-2-3-4", clientID: ipnauth.ClientIDFrom("A")}, target: notificationTarget{userID: "S-1-5-21-1-2-3-4", clientID: ipnauth.ClientIDFrom("A")},
actor: &ipnauth.TestActor{UID: "S-1-5-21-5-6-7-8"}, actor: &ipnauth.TestActor{UID: "S-1-5-21-5-6-7-8"},
wantMatch: false, wantMatch: false,
}, },
{ {
name: "FilterByUID+CID/DifferentUID/SameCID", name: "FilterByUID-CID/DifferentUID/SameCID",
target: notificationTarget{userID: "S-1-5-21-1-2-3-4", clientID: ipnauth.ClientIDFrom("A")}, target: notificationTarget{userID: "S-1-5-21-1-2-3-4", clientID: ipnauth.ClientIDFrom("A")},
actor: &ipnauth.TestActor{UID: "S-1-5-21-5-6-7-8", CID: ipnauth.ClientIDFrom("A")}, actor: &ipnauth.TestActor{UID: "S-1-5-21-5-6-7-8", CID: ipnauth.ClientIDFrom("A")},
wantMatch: false, wantMatch: false,
}, },
{ {
name: "FilterByUID+CID/DifferentUID/DifferentCID", name: "FilterByUID-CID/DifferentUID/DifferentCID",
target: notificationTarget{userID: "S-1-5-21-1-2-3-4", clientID: ipnauth.ClientIDFrom("A")}, target: notificationTarget{userID: "S-1-5-21-1-2-3-4", clientID: ipnauth.ClientIDFrom("A")},
actor: &ipnauth.TestActor{UID: "S-1-5-21-5-6-7-8", CID: ipnauth.ClientIDFrom("B")}, actor: &ipnauth.TestActor{UID: "S-1-5-21-5-6-7-8", CID: ipnauth.ClientIDFrom("B")},
wantMatch: false, wantMatch: false,

@ -300,9 +300,9 @@ func TestTKASync(t *testing.T) {
} }
tcs := []tkaSyncScenario{ tcs := []tkaSyncScenario{
{name: "up to date"}, {name: "up-to-date"},
{ {
name: "control has an update", name: "control-has-an-update",
controlAUMs: func(t *testing.T, a *tka.Authority, storage tka.Chonk, signer tka.Signer) []tka.AUM { controlAUMs: func(t *testing.T, a *tka.Authority, storage tka.Chonk, signer tka.Signer) []tka.AUM {
b := a.NewUpdater(signer) b := a.NewUpdater(signer)
if err := b.RemoveKey(someKey.MustID()); err != nil { if err := b.RemoveKey(someKey.MustID()); err != nil {
@ -317,7 +317,7 @@ func TestTKASync(t *testing.T) {
}, },
{ {
// AKA 'control data loss' scenario // AKA 'control data loss' scenario
name: "node has an update", name: "node-has-an-update",
nodeAUMs: func(t *testing.T, a *tka.Authority, storage tka.Chonk, signer tka.Signer) []tka.AUM { nodeAUMs: func(t *testing.T, a *tka.Authority, storage tka.Chonk, signer tka.Signer) []tka.AUM {
b := a.NewUpdater(signer) b := a.NewUpdater(signer)
if err := b.RemoveKey(someKey.MustID()); err != nil { if err := b.RemoveKey(someKey.MustID()); err != nil {
@ -332,7 +332,7 @@ func TestTKASync(t *testing.T) {
}, },
{ {
// AKA 'control data loss + update in the meantime' scenario // AKA 'control data loss + update in the meantime' scenario
name: "node and control diverge", name: "node-and-control-diverge",
controlAUMs: func(t *testing.T, a *tka.Authority, storage tka.Chonk, signer tka.Signer) []tka.AUM { controlAUMs: func(t *testing.T, a *tka.Authority, storage tka.Chonk, signer tka.Signer) []tka.AUM {
b := a.NewUpdater(signer) b := a.NewUpdater(signer)
if err := b.SetKeyMeta(someKey.MustID(), map[string]string{"ye": "swiggity"}); err != nil { if err := b.SetKeyMeta(someKey.MustID(), map[string]string{"ye": "swiggity"}); err != nil {
@ -1020,7 +1020,7 @@ func TestTKAAffectedSigs(t *testing.T) {
wantErr string wantErr string
}{ }{
{ {
"no error", "no-error",
func() *tka.NodeKeySignature { func() *tka.NodeKeySignature {
sig, _ := signNodeKey(tailcfg.TKASignInfo{NodePublic: nodePriv.Public()}, nlPriv) sig, _ := signNodeKey(tailcfg.TKASignInfo{NodePublic: nodePriv.Public()}, nlPriv)
return sig return sig
@ -1028,7 +1028,7 @@ func TestTKAAffectedSigs(t *testing.T) {
"", "",
}, },
{ {
"signature for different keyID", "signature-for-different-keyID",
func() *tka.NodeKeySignature { func() *tka.NodeKeySignature {
sig, _ := signNodeKey(tailcfg.TKASignInfo{NodePublic: nodePriv.Public()}, untrustedKey) sig, _ := signNodeKey(tailcfg.TKASignInfo{NodePublic: nodePriv.Public()}, untrustedKey)
return sig return sig
@ -1036,7 +1036,7 @@ func TestTKAAffectedSigs(t *testing.T) {
fmt.Sprintf("got signature with keyID %X from request for %X", untrustedKey.KeyID(), nlPriv.KeyID()), fmt.Sprintf("got signature with keyID %X from request for %X", untrustedKey.KeyID(), nlPriv.KeyID()),
}, },
{ {
"invalid signature", "invalid-signature",
func() *tka.NodeKeySignature { func() *tka.NodeKeySignature {
sig, _ := signNodeKey(tailcfg.TKASignInfo{NodePublic: nodePriv.Public()}, nlPriv) sig, _ := signNodeKey(tailcfg.TKASignInfo{NodePublic: nodePriv.Public()}, nlPriv)
copy(sig.Signature, []byte{1, 2, 3, 4, 5, 6}) // overwrite with trash to invalid signature copy(sig.Signature, []byte{1, 2, 3, 4, 5, 6}) // overwrite with trash to invalid signature

@ -619,49 +619,49 @@ func TestServeHTTPProxyPath(t *testing.T) {
wantRequestPath string wantRequestPath string
}{ }{
{ {
name: "/foo -> /foo, with mount point and path /foo", name: "foo-to-foo-mount-foo",
mountPoint: "/foo", mountPoint: "/foo",
proxyPath: "/foo", proxyPath: "/foo",
requestPath: "/foo", requestPath: "/foo",
wantRequestPath: "/foo", wantRequestPath: "/foo",
}, },
{ {
name: "/foo/ -> /foo/, with mount point and path /foo", name: "foo-slash-to-foo-slash-mount-foo",
mountPoint: "/foo", mountPoint: "/foo",
proxyPath: "/foo", proxyPath: "/foo",
requestPath: "/foo/", requestPath: "/foo/",
wantRequestPath: "/foo/", wantRequestPath: "/foo/",
}, },
{ {
name: "/foo -> /foo/, with mount point and path /foo/", name: "foo-to-foo-slash-mount-foo-slash",
mountPoint: "/foo/", mountPoint: "/foo/",
proxyPath: "/foo/", proxyPath: "/foo/",
requestPath: "/foo", requestPath: "/foo",
wantRequestPath: "/foo/", wantRequestPath: "/foo/",
}, },
{ {
name: "/-> /, with mount point and path /", name: "root-to-root-mount-root",
mountPoint: "/", mountPoint: "/",
proxyPath: "/", proxyPath: "/",
requestPath: "/", requestPath: "/",
wantRequestPath: "/", wantRequestPath: "/",
}, },
{ {
name: "/foo -> /foo, with mount point and path /", name: "foo-to-foo-mount-root",
mountPoint: "/", mountPoint: "/",
proxyPath: "/", proxyPath: "/",
requestPath: "/foo", requestPath: "/foo",
wantRequestPath: "/foo", wantRequestPath: "/foo",
}, },
{ {
name: "/foo/bar -> /foo/bar, with mount point and path /foo", name: "foo-bar-to-foo-bar-mount-foo",
mountPoint: "/foo", mountPoint: "/foo",
proxyPath: "/foo", proxyPath: "/foo",
requestPath: "/foo/bar", requestPath: "/foo/bar",
wantRequestPath: "/foo/bar", wantRequestPath: "/foo/bar",
}, },
{ {
name: "/foo/bar/baz -> /foo/bar/baz, with mount point and path /foo", name: "foo-bar-baz-to-foo-bar-baz-mount-foo",
mountPoint: "/foo", mountPoint: "/foo",
proxyPath: "/foo", proxyPath: "/foo",
requestPath: "/foo/bar/baz", requestPath: "/foo/bar/baz",
@ -1457,7 +1457,7 @@ func TestValidateServeConfigUpdate(t *testing.T) {
wantError bool wantError bool
}{ }{
{ {
name: "empty existing config", name: "empty-existing-config",
description: "should be able to update with empty existing config", description: "should be able to update with empty existing config",
existing: &ipn.ServeConfig{}, existing: &ipn.ServeConfig{},
incoming: &ipn.ServeConfig{ incoming: &ipn.ServeConfig{
@ -1468,7 +1468,7 @@ func TestValidateServeConfigUpdate(t *testing.T) {
wantError: false, wantError: false,
}, },
{ {
name: "no existing config", name: "no-existing-config",
description: "should be able to update with no existing config", description: "should be able to update with no existing config",
existing: nil, existing: nil,
incoming: &ipn.ServeConfig{ incoming: &ipn.ServeConfig{
@ -1479,7 +1479,7 @@ func TestValidateServeConfigUpdate(t *testing.T) {
wantError: false, wantError: false,
}, },
{ {
name: "empty incoming config", name: "empty-incoming-config",
description: "wiping config should work", description: "wiping config should work",
existing: &ipn.ServeConfig{ existing: &ipn.ServeConfig{
TCP: map[uint16]*ipn.TCPPortHandler{ TCP: map[uint16]*ipn.TCPPortHandler{
@ -1490,7 +1490,7 @@ func TestValidateServeConfigUpdate(t *testing.T) {
wantError: false, wantError: false,
}, },
{ {
name: "no incoming config", name: "no-incoming-config",
description: "missing incoming config should not result in an error", description: "missing incoming config should not result in an error",
existing: &ipn.ServeConfig{ existing: &ipn.ServeConfig{
TCP: map[uint16]*ipn.TCPPortHandler{ TCP: map[uint16]*ipn.TCPPortHandler{
@ -1501,7 +1501,7 @@ func TestValidateServeConfigUpdate(t *testing.T) {
wantError: false, wantError: false,
}, },
{ {
name: "non-overlapping update", name: "non-overlapping-update",
description: "non-overlapping update should work", description: "non-overlapping update should work",
existing: &ipn.ServeConfig{ existing: &ipn.ServeConfig{
TCP: map[uint16]*ipn.TCPPortHandler{ TCP: map[uint16]*ipn.TCPPortHandler{
@ -1516,7 +1516,7 @@ func TestValidateServeConfigUpdate(t *testing.T) {
wantError: false, wantError: false,
}, },
{ {
name: "overwriting background port", name: "overwriting-background-port",
description: "should be able to overwrite a background port", description: "should be able to overwrite a background port",
existing: &ipn.ServeConfig{ existing: &ipn.ServeConfig{
TCP: map[uint16]*ipn.TCPPortHandler{ TCP: map[uint16]*ipn.TCPPortHandler{
@ -1535,7 +1535,7 @@ func TestValidateServeConfigUpdate(t *testing.T) {
wantError: false, wantError: false,
}, },
{ {
name: "broken existing config", name: "broken-existing-config",
description: "broken existing config should not prevent new config updates", description: "broken existing config should not prevent new config updates",
existing: &ipn.ServeConfig{ existing: &ipn.ServeConfig{
TCP: map[uint16]*ipn.TCPPortHandler{ TCP: map[uint16]*ipn.TCPPortHandler{
@ -1573,7 +1573,7 @@ func TestValidateServeConfigUpdate(t *testing.T) {
wantError: false, wantError: false,
}, },
{ {
name: "services same port as background", name: "services-same-port-as-background",
description: "services should be able to use the same port as background listeners", description: "services should be able to use the same port as background listeners",
existing: &ipn.ServeConfig{ existing: &ipn.ServeConfig{
TCP: map[uint16]*ipn.TCPPortHandler{ TCP: map[uint16]*ipn.TCPPortHandler{
@ -1592,7 +1592,7 @@ func TestValidateServeConfigUpdate(t *testing.T) {
wantError: false, wantError: false,
}, },
{ {
name: "services tun mode", name: "services-tun-mode",
description: "TUN mode should be mutually exclusive with TCP or web handlers for new Services", description: "TUN mode should be mutually exclusive with TCP or web handlers for new Services",
existing: &ipn.ServeConfig{}, existing: &ipn.ServeConfig{},
incoming: &ipn.ServeConfig{ incoming: &ipn.ServeConfig{
@ -1608,7 +1608,7 @@ func TestValidateServeConfigUpdate(t *testing.T) {
wantError: true, wantError: true,
}, },
{ {
name: "new foreground listener", name: "new-foreground-listener",
description: "new foreground listeners must be on open ports", description: "new foreground listeners must be on open ports",
existing: &ipn.ServeConfig{ existing: &ipn.ServeConfig{
TCP: map[uint16]*ipn.TCPPortHandler{ TCP: map[uint16]*ipn.TCPPortHandler{
@ -1627,7 +1627,7 @@ func TestValidateServeConfigUpdate(t *testing.T) {
wantError: true, wantError: true,
}, },
{ {
name: "new background listener", name: "new-background-listener",
description: "new background listers cannot overwrite foreground listeners", description: "new background listers cannot overwrite foreground listeners",
existing: &ipn.ServeConfig{ existing: &ipn.ServeConfig{
Foreground: map[string]*ipn.ServeConfig{ Foreground: map[string]*ipn.ServeConfig{
@ -1646,7 +1646,7 @@ func TestValidateServeConfigUpdate(t *testing.T) {
wantError: true, wantError: true,
}, },
{ {
name: "serve type overwrite", name: "serve-type-overwrite",
description: "incoming configuration cannot change the serve type in use by a port", description: "incoming configuration cannot change the serve type in use by a port",
existing: &ipn.ServeConfig{ existing: &ipn.ServeConfig{
TCP: map[uint16]*ipn.TCPPortHandler{ TCP: map[uint16]*ipn.TCPPortHandler{
@ -1665,7 +1665,7 @@ func TestValidateServeConfigUpdate(t *testing.T) {
wantError: true, wantError: true,
}, },
{ {
name: "serve type overwrite services", name: "serve-type-overwrite-services",
description: "incoming Services configuration cannot change the serve type in use by a port", description: "incoming Services configuration cannot change the serve type in use by a port",
existing: &ipn.ServeConfig{ existing: &ipn.ServeConfig{
Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{ Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{
@ -1692,7 +1692,7 @@ func TestValidateServeConfigUpdate(t *testing.T) {
wantError: true, wantError: true,
}, },
{ {
name: "tun mode with handlers", name: "tun-mode-with-handlers",
description: "Services cannot enable TUN mode if L4 or L7 handlers already exist", description: "Services cannot enable TUN mode if L4 or L7 handlers already exist",
existing: &ipn.ServeConfig{ existing: &ipn.ServeConfig{
Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{ Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{
@ -1720,7 +1720,7 @@ func TestValidateServeConfigUpdate(t *testing.T) {
wantError: true, wantError: true,
}, },
{ {
name: "handlers with tun mode", name: "handlers-with-tun-mode",
description: "Services cannot add L4 or L7 handlers if TUN mode is already enabled", description: "Services cannot add L4 or L7 handlers if TUN mode is already enabled",
existing: &ipn.ServeConfig{ existing: &ipn.ServeConfig{
Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{ Services: map[tailcfg.ServiceName]*ipn.ServiceConfig{

@ -23,26 +23,30 @@ import (
func TestExpandProxyArgUnix(t *testing.T) { func TestExpandProxyArgUnix(t *testing.T) {
tests := []struct { tests := []struct {
name string
input string input string
wantURL string wantURL string
wantInsecure bool wantInsecure bool
}{ }{
{ {
name: "unix-tmp-sock",
input: "unix:/tmp/test.sock", input: "unix:/tmp/test.sock",
wantURL: "unix:/tmp/test.sock", wantURL: "unix:/tmp/test.sock",
}, },
{ {
name: "unix-var-run-docker-sock",
input: "unix:/var/run/docker.sock", input: "unix:/var/run/docker.sock",
wantURL: "unix:/var/run/docker.sock", wantURL: "unix:/var/run/docker.sock",
}, },
{ {
name: "unix-relative-sock",
input: "unix:./relative.sock", input: "unix:./relative.sock",
wantURL: "unix:./relative.sock", wantURL: "unix:./relative.sock",
}, },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
gotURL, gotInsecure := expandProxyArg(tt.input) gotURL, gotInsecure := expandProxyArg(tt.input)
if gotURL != tt.wantURL { if gotURL != tt.wantURL {
t.Errorf("expandProxyArg(%q) url = %q, want %q", tt.input, gotURL, tt.wantURL) t.Errorf("expandProxyArg(%q) url = %q, want %q", tt.input, gotURL, tt.wantURL)

@ -458,7 +458,7 @@ func TestPrefsFromBytesPreservesOldValues(t *testing.T) {
want: Prefs{ControlURL: "https://foo", RouteAll: true}, want: Prefs{ControlURL: "https://foo", RouteAll: true},
}, },
{ {
name: "opt.Bool", // test that we don't normalize it early name: "opt-Bool", // test that we don't normalize it early
old: Prefs{Sync: "unset"}, old: Prefs{Sync: "unset"},
json: []byte(`{}`), json: []byte(`{}`),
want: Prefs{Sync: "unset"}, want: Prefs{Sync: "unset"},
@ -1236,13 +1236,13 @@ func TestParseAutoExitNodeString(t *testing.T) {
wantExpr ExitNodeExpression wantExpr ExitNodeExpression
}{ }{
{ {
name: "empty expr", name: "empty-expr",
exitNodeID: "", exitNodeID: "",
wantOk: false, wantOk: false,
wantExpr: "", wantExpr: "",
}, },
{ {
name: "no auto prefix", name: "no-auto-prefix",
exitNodeID: "foo", exitNodeID: "foo",
wantOk: false, wantOk: false,
wantExpr: "", wantExpr: "",
@ -1260,13 +1260,13 @@ func TestParseAutoExitNodeString(t *testing.T) {
wantExpr: "foo", wantExpr: "foo",
}, },
{ {
name: "auto prefix but empty suffix", name: "auto-prefix-empty-suffix",
exitNodeID: "auto:", exitNodeID: "auto:",
wantOk: false, wantOk: false,
wantExpr: "", wantExpr: "",
}, },
{ {
name: "auto prefix no colon", name: "auto-prefix-no-colon",
exitNodeID: "auto", exitNodeID: "auto",
wantOk: false, wantOk: false,
wantExpr: "", wantExpr: "",

@ -283,11 +283,11 @@ func TestExpandProxyTargetDev(t *testing.T) {
wantErr bool wantErr bool
}{ }{
{name: "port-only", input: "8080", expected: "http://127.0.0.1:8080"}, {name: "port-only", input: "8080", expected: "http://127.0.0.1:8080"},
{name: "hostname+port", input: "localhost:8080", expected: "http://localhost:8080"}, {name: "hostname-and-port", input: "localhost:8080", expected: "http://localhost:8080"},
{name: "no-change", input: "http://127.0.0.1:8080", expected: "http://127.0.0.1:8080"}, {name: "no-change", input: "http://127.0.0.1:8080", expected: "http://127.0.0.1:8080"},
{name: "include-path", input: "http://127.0.0.1:8080/foo", expected: "http://127.0.0.1:8080/foo"}, {name: "include-path", input: "http://127.0.0.1:8080/foo", expected: "http://127.0.0.1:8080/foo"},
{name: "https-scheme", input: "https://localhost:8080", expected: "https://localhost:8080"}, {name: "https-scheme", input: "https://localhost:8080", expected: "https://localhost:8080"},
{name: "https+insecure-scheme", input: "https+insecure://localhost:8080", expected: "https+insecure://localhost:8080"}, {name: "https-insecure-scheme", input: "https+insecure://localhost:8080", expected: "https+insecure://localhost:8080"},
{name: "change-default-scheme", input: "localhost:8080", defaultScheme: "https", expected: "https://localhost:8080"}, {name: "change-default-scheme", input: "localhost:8080", defaultScheme: "https", expected: "https://localhost:8080"},
{name: "change-supported-schemes", input: "localhost:8080", defaultScheme: "tcp", supportedSchemes: []string{"tcp"}, expected: "tcp://localhost:8080"}, {name: "change-supported-schemes", input: "localhost:8080", defaultScheme: "tcp", supportedSchemes: []string{"tcp"}, expected: "tcp://localhost:8080"},
{name: "remote-target", input: "https://example.com:8080", expected: "https://example.com:8080"}, {name: "remote-target", input: "https://example.com:8080", expected: "https://example.com:8080"},

@ -30,7 +30,7 @@ func TestReconciler_Reconcile(t *testing.T) {
ExpectsError bool ExpectsError bool
}{ }{
{ {
Name: "single policy, denies all", Name: "single-policy-denies-all",
ExpectedPolicyCount: 2, ExpectedPolicyCount: 2,
Request: reconcile.Request{ Request: reconcile.Request{
NamespacedName: types.NamespacedName{ NamespacedName: types.NamespacedName{
@ -53,7 +53,7 @@ func TestReconciler_Reconcile(t *testing.T) {
}, },
}, },
{ {
Name: "multiple policies merged", Name: "multiple-policies-merged",
ExpectedPolicyCount: 2, ExpectedPolicyCount: 2,
Request: reconcile.Request{ Request: reconcile.Request{
NamespacedName: types.NamespacedName{ NamespacedName: types.NamespacedName{
@ -89,7 +89,7 @@ func TestReconciler_Reconcile(t *testing.T) {
}, },
}, },
{ {
Name: "no policies, no child resources", Name: "no-policies-no-child-resources",
ExpectedPolicyCount: 0, ExpectedPolicyCount: 0,
Request: reconcile.Request{ Request: reconcile.Request{
NamespacedName: types.NamespacedName{ NamespacedName: types.NamespacedName{

@ -39,7 +39,7 @@ func TestReconciler_Reconcile(t *testing.T) {
ClientFunc func(*tsapi.Tailnet, *corev1.Secret) tailnet.TailscaleClient ClientFunc func(*tsapi.Tailnet, *corev1.Secret) tailnet.TailscaleClient
}{ }{
{ {
Name: "ignores unknown tailnet requests", Name: "ignores-unknown-tailnet-requests",
Request: reconcile.Request{ Request: reconcile.Request{
NamespacedName: types.NamespacedName{ NamespacedName: types.NamespacedName{
Name: "test", Name: "test",
@ -47,7 +47,7 @@ func TestReconciler_Reconcile(t *testing.T) {
}, },
}, },
{ {
Name: "invalid status for missing secret", Name: "invalid-status-missing-secret",
Request: reconcile.Request{ Request: reconcile.Request{
NamespacedName: types.NamespacedName{ NamespacedName: types.NamespacedName{
Name: "test", Name: "test",
@ -73,7 +73,7 @@ func TestReconciler_Reconcile(t *testing.T) {
}, },
}, },
{ {
Name: "invalid status for empty secret", Name: "invalid-status-empty-secret",
Request: reconcile.Request{ Request: reconcile.Request{
NamespacedName: types.NamespacedName{ NamespacedName: types.NamespacedName{
Name: "test", Name: "test",
@ -105,7 +105,7 @@ func TestReconciler_Reconcile(t *testing.T) {
}, },
}, },
{ {
Name: "invalid status for missing client id", Name: "invalid-status-missing-client-id",
Request: reconcile.Request{ Request: reconcile.Request{
NamespacedName: types.NamespacedName{ NamespacedName: types.NamespacedName{
Name: "test", Name: "test",
@ -140,7 +140,7 @@ func TestReconciler_Reconcile(t *testing.T) {
}, },
}, },
{ {
Name: "invalid status for missing client secret", Name: "invalid-status-missing-client-secret",
Request: reconcile.Request{ Request: reconcile.Request{
NamespacedName: types.NamespacedName{ NamespacedName: types.NamespacedName{
Name: "test", Name: "test",
@ -175,7 +175,7 @@ func TestReconciler_Reconcile(t *testing.T) {
}, },
}, },
{ {
Name: "invalid status for bad devices scope", Name: "invalid-status-bad-devices-scope",
Request: reconcile.Request{ Request: reconcile.Request{
NamespacedName: types.NamespacedName{ NamespacedName: types.NamespacedName{
Name: "test", Name: "test",
@ -214,7 +214,7 @@ func TestReconciler_Reconcile(t *testing.T) {
}, },
}, },
{ {
Name: "invalid status for bad services scope", Name: "invalid-status-bad-services-scope",
Request: reconcile.Request{ Request: reconcile.Request{
NamespacedName: types.NamespacedName{ NamespacedName: types.NamespacedName{
Name: "test", Name: "test",
@ -253,7 +253,7 @@ func TestReconciler_Reconcile(t *testing.T) {
}, },
}, },
{ {
Name: "invalid status for bad keys scope", Name: "invalid-status-bad-keys-scope",
Request: reconcile.Request{ Request: reconcile.Request{
NamespacedName: types.NamespacedName{ NamespacedName: types.NamespacedName{
Name: "test", Name: "test",
@ -292,7 +292,7 @@ func TestReconciler_Reconcile(t *testing.T) {
}, },
}, },
{ {
Name: "ready when valid and scopes are correct", Name: "ready-valid-scopes-correct",
Request: reconcile.Request{ Request: reconcile.Request{
NamespacedName: types.NamespacedName{ NamespacedName: types.NamespacedName{
Name: "default", Name: "default",

@ -39,19 +39,19 @@ func TestDelta(t *testing.T) {
wantStats map[sockstats.Label]deltaStat wantStats map[sockstats.Label]deltaStat
}{ }{
{ {
name: "nil a stat", name: "nil-a-stat",
a: nil, a: nil,
b: &sockstats.SockStats{}, b: &sockstats.SockStats{},
wantStats: nil, wantStats: nil,
}, },
{ {
name: "nil b stat", name: "nil-b-stat",
a: &sockstats.SockStats{}, a: &sockstats.SockStats{},
b: nil, b: nil,
wantStats: nil, wantStats: nil,
}, },
{ {
name: "no change", name: "no-change",
a: &sockstats.SockStats{ a: &sockstats.SockStats{
Stats: map[sockstats.Label]sockstats.SockStat{ Stats: map[sockstats.Label]sockstats.SockStat{
sockstats.LabelDERPHTTPClient: { sockstats.LabelDERPHTTPClient: {
@ -69,7 +69,7 @@ func TestDelta(t *testing.T) {
wantStats: nil, wantStats: nil,
}, },
{ {
name: "tx after empty stat", name: "tx-after-empty-stat",
a: &sockstats.SockStats{}, a: &sockstats.SockStats{},
b: &sockstats.SockStats{ b: &sockstats.SockStats{
Stats: map[sockstats.Label]sockstats.SockStat{ Stats: map[sockstats.Label]sockstats.SockStat{
@ -83,7 +83,7 @@ func TestDelta(t *testing.T) {
}, },
}, },
{ {
name: "rx after non-empty stat", name: "rx-after-non-empty-stat",
a: &sockstats.SockStats{ a: &sockstats.SockStats{
Stats: map[sockstats.Label]sockstats.SockStat{ Stats: map[sockstats.Label]sockstats.SockStat{
sockstats.LabelDERPHTTPClient: { sockstats.LabelDERPHTTPClient: {

@ -42,7 +42,7 @@ func Test_linuxBatchingConn_splitCoalescedMessages(t *testing.T) {
wantErr bool wantErr bool
}{ }{
{ {
name: "second last split last empty", name: "second-last-split-last-empty",
msgs: []ipv6.Message{ msgs: []ipv6.Message{
newMsg(0, 0), newMsg(0, 0),
newMsg(0, 0), newMsg(0, 0),
@ -55,7 +55,7 @@ func Test_linuxBatchingConn_splitCoalescedMessages(t *testing.T) {
wantErr: false, wantErr: false,
}, },
{ {
name: "second last no split last empty", name: "second-last-no-split-last-empty",
msgs: []ipv6.Message{ msgs: []ipv6.Message{
newMsg(0, 0), newMsg(0, 0),
newMsg(0, 0), newMsg(0, 0),
@ -68,7 +68,7 @@ func Test_linuxBatchingConn_splitCoalescedMessages(t *testing.T) {
wantErr: false, wantErr: false,
}, },
{ {
name: "second last no split last no split", name: "second-last-no-split-last-no-split",
msgs: []ipv6.Message{ msgs: []ipv6.Message{
newMsg(0, 0), newMsg(0, 0),
newMsg(0, 0), newMsg(0, 0),
@ -81,7 +81,7 @@ func Test_linuxBatchingConn_splitCoalescedMessages(t *testing.T) {
wantErr: false, wantErr: false,
}, },
{ {
name: "second last no split last split", name: "second-last-no-split-last-split",
msgs: []ipv6.Message{ msgs: []ipv6.Message{
newMsg(0, 0), newMsg(0, 0),
newMsg(0, 0), newMsg(0, 0),
@ -94,7 +94,7 @@ func Test_linuxBatchingConn_splitCoalescedMessages(t *testing.T) {
wantErr: false, wantErr: false,
}, },
{ {
name: "second last split last split", name: "second-last-split-last-split",
msgs: []ipv6.Message{ msgs: []ipv6.Message{
newMsg(0, 0), newMsg(0, 0),
newMsg(0, 0), newMsg(0, 0),
@ -107,7 +107,7 @@ func Test_linuxBatchingConn_splitCoalescedMessages(t *testing.T) {
wantErr: false, wantErr: false,
}, },
{ {
name: "second last no split last split overflow", name: "second-last-no-split-last-split-overflow",
msgs: []ipv6.Message{ msgs: []ipv6.Message{
newMsg(0, 0), newMsg(0, 0),
newMsg(0, 0), newMsg(0, 0),
@ -161,7 +161,7 @@ func Test_linuxBatchingConn_coalesceMessages(t *testing.T) {
wantGSO []int wantGSO []int
}{ }{
{ {
name: "one message no coalesce", name: "one-message-no-coalesce",
buffs: [][]byte{ buffs: [][]byte{
withGeneveSpace(1, 1), withGeneveSpace(1, 1),
}, },
@ -169,7 +169,7 @@ func Test_linuxBatchingConn_coalesceMessages(t *testing.T) {
wantGSO: []int{0}, wantGSO: []int{0},
}, },
{ {
name: "one message no coalesce vni.isSet", name: "one-message-no-coalesce-vni-isSet",
buffs: [][]byte{ buffs: [][]byte{
withGeneveSpace(1, 1), withGeneveSpace(1, 1),
}, },
@ -178,7 +178,7 @@ func Test_linuxBatchingConn_coalesceMessages(t *testing.T) {
wantGSO: []int{0}, wantGSO: []int{0},
}, },
{ {
name: "two messages equal len coalesce", name: "two-messages-equal-len-coalesce",
buffs: [][]byte{ buffs: [][]byte{
withGeneveSpace(1, 2), withGeneveSpace(1, 2),
withGeneveSpace(1, 1), withGeneveSpace(1, 1),
@ -187,7 +187,7 @@ func Test_linuxBatchingConn_coalesceMessages(t *testing.T) {
wantGSO: []int{1}, wantGSO: []int{1},
}, },
{ {
name: "two messages equal len coalesce vni.isSet", name: "two-messages-equal-len-coalesce-vni-isSet",
buffs: [][]byte{ buffs: [][]byte{
withGeneveSpace(1, 2+packet.GeneveFixedHeaderLength), withGeneveSpace(1, 2+packet.GeneveFixedHeaderLength),
withGeneveSpace(1, 1), withGeneveSpace(1, 1),
@ -197,7 +197,7 @@ func Test_linuxBatchingConn_coalesceMessages(t *testing.T) {
wantGSO: []int{1 + packet.GeneveFixedHeaderLength}, wantGSO: []int{1 + packet.GeneveFixedHeaderLength},
}, },
{ {
name: "two messages unequal len coalesce", name: "two-messages-unequal-len-coalesce",
buffs: [][]byte{ buffs: [][]byte{
withGeneveSpace(2, 3), withGeneveSpace(2, 3),
withGeneveSpace(1, 1), withGeneveSpace(1, 1),
@ -206,7 +206,7 @@ func Test_linuxBatchingConn_coalesceMessages(t *testing.T) {
wantGSO: []int{2}, wantGSO: []int{2},
}, },
{ {
name: "two messages unequal len coalesce vni.isSet", name: "two-messages-unequal-len-coalesce-vni-isSet",
buffs: [][]byte{ buffs: [][]byte{
withGeneveSpace(2, 3+packet.GeneveFixedHeaderLength), withGeneveSpace(2, 3+packet.GeneveFixedHeaderLength),
withGeneveSpace(1, 1), withGeneveSpace(1, 1),
@ -216,7 +216,7 @@ func Test_linuxBatchingConn_coalesceMessages(t *testing.T) {
wantGSO: []int{2 + packet.GeneveFixedHeaderLength}, wantGSO: []int{2 + packet.GeneveFixedHeaderLength},
}, },
{ {
name: "three messages second unequal len coalesce", name: "three-messages-second-unequal-len-coalesce",
buffs: [][]byte{ buffs: [][]byte{
withGeneveSpace(2, 3), withGeneveSpace(2, 3),
withGeneveSpace(1, 1), withGeneveSpace(1, 1),
@ -226,7 +226,7 @@ func Test_linuxBatchingConn_coalesceMessages(t *testing.T) {
wantGSO: []int{2, 0}, wantGSO: []int{2, 0},
}, },
{ {
name: "three messages second unequal len coalesce vni.isSet", name: "three-messages-second-unequal-len-coalesce-vni-isSet",
buffs: [][]byte{ buffs: [][]byte{
withGeneveSpace(2, 3+(2*packet.GeneveFixedHeaderLength)), withGeneveSpace(2, 3+(2*packet.GeneveFixedHeaderLength)),
withGeneveSpace(1, 1), withGeneveSpace(1, 1),
@ -237,7 +237,7 @@ func Test_linuxBatchingConn_coalesceMessages(t *testing.T) {
wantGSO: []int{2 + packet.GeneveFixedHeaderLength, 0}, wantGSO: []int{2 + packet.GeneveFixedHeaderLength, 0},
}, },
{ {
name: "three messages limited cap coalesce", name: "three-messages-limited-cap-coalesce",
buffs: [][]byte{ buffs: [][]byte{
withGeneveSpace(2, 4), withGeneveSpace(2, 4),
withGeneveSpace(2, 2), withGeneveSpace(2, 2),
@ -247,7 +247,7 @@ func Test_linuxBatchingConn_coalesceMessages(t *testing.T) {
wantGSO: []int{2}, wantGSO: []int{2},
}, },
{ {
name: "three messages limited cap coalesce vni.isSet", name: "three-messages-limited-cap-coalesce-vni-isSet",
buffs: [][]byte{ buffs: [][]byte{
withGeneveSpace(2, 4+packet.GeneveFixedHeaderLength), withGeneveSpace(2, 4+packet.GeneveFixedHeaderLength),
withGeneveSpace(2, 2), withGeneveSpace(2, 2),
@ -376,19 +376,19 @@ func Test_getRXQOverflowsFromControl(t *testing.T) {
wantErr: false, wantErr: false,
}, },
{ {
name: "rxq overflows", name: "rxq-overflows",
control: rxqOverflowsControl(1), control: rxqOverflowsControl(1),
want: 1, want: 1,
wantErr: false, wantErr: false,
}, },
{ {
name: "multiple cmsg rxq overflows at head", name: "multiple-cmsg-rxq-overflows-at-head",
control: append(rxqOverflowsControl(1), gsoControl(1)...), control: append(rxqOverflowsControl(1), gsoControl(1)...),
want: 1, want: 1,
wantErr: false, wantErr: false,
}, },
{ {
name: "multiple cmsg rxq overflows at tail", name: "multiple-cmsg-rxq-overflows-at-tail",
control: append(gsoControl(1), rxqOverflowsControl(1)...), control: append(gsoControl(1), rxqOverflowsControl(1)...),
want: 1, want: 1,
wantErr: false, wantErr: false,
@ -432,19 +432,19 @@ func Test_getGSOSizeFromControl(t *testing.T) {
wantErr: false, wantErr: false,
}, },
{ {
name: "rxq overflows", name: "rxq-overflows",
control: rxqOverflowsControl(1), control: rxqOverflowsControl(1),
want: 0, want: 0,
wantErr: false, wantErr: false,
}, },
{ {
name: "multiple cmsg gso at tail", name: "multiple-cmsg-gso-at-tail",
control: append(rxqOverflowsControl(1), gsoControl(1)...), control: append(rxqOverflowsControl(1), gsoControl(1)...),
want: 1, want: 1,
wantErr: false, wantErr: false,
}, },
{ {
name: "multiple cmsg gso at head", name: "multiple-cmsg-gso-at-head",
control: append(gsoControl(1), rxqOverflowsControl(1)...), control: append(gsoControl(1), rxqOverflowsControl(1)...),
want: 1, want: 1,
wantErr: false, wantErr: false,

@ -22,7 +22,7 @@ func TestLinuxDNSMode(t *testing.T) {
want string want string
}{ }{
{ {
name: "no_obvious_resolv.conf_owner", name: "no_obvious_resolvconf_owner",
env: env(resolvDotConf("nameserver 10.0.0.1")), env: env(resolvDotConf("nameserver 10.0.0.1")),
wantLog: "dns: [rc=unknown ret=direct]", wantLog: "dns: [rc=unknown ret=direct]",
want: "direct", want: "direct",
@ -153,7 +153,7 @@ func TestLinuxDNSMode(t *testing.T) {
// alleged that it was managed by systemd-resolved, but it // alleged that it was managed by systemd-resolved, but it
// was actually a completely static config file pointing // was actually a completely static config file pointing
// elsewhere. // elsewhere.
name: "allegedly_resolved_but_not_in_resolv.conf", name: "allegedly_resolved_but_not_in_resolvconf",
env: env(resolvDotConf("# Managed by systemd-resolved", "nameserver 10.0.0.1")), env: env(resolvDotConf("# Managed by systemd-resolved", "nameserver 10.0.0.1")),
wantLog: "dns: resolvedIsActuallyResolver error: resolv.conf doesn't point to systemd-resolved; points to [10.0.0.1]\n" + wantLog: "dns: resolvedIsActuallyResolver error: resolv.conf doesn't point to systemd-resolved; points to [10.0.0.1]\n" +
"dns: [rc=resolved resolved=not-in-use ret=direct]", "dns: [rc=resolved resolved=not-in-use ret=direct]",
@ -163,7 +163,7 @@ func TestLinuxDNSMode(t *testing.T) {
// We used to incorrectly decide that resolved wasn't in // We used to incorrectly decide that resolved wasn't in
// charge when handed this (admittedly weird and bugged) // charge when handed this (admittedly weird and bugged)
// resolv.conf. // resolv.conf.
name: "resolved_with_duplicates_in_resolv.conf", name: "resolved_with_duplicates_in_resolvconf",
env: env( env: env(
resolvDotConf( resolvDotConf(
"# Managed by systemd-resolved", "# Managed by systemd-resolved",

@ -1127,7 +1127,7 @@ func TestForwarderWithManyResolvers(t *testing.T) {
}, },
}, },
{ {
name: "ServFail+Success", name: "ServFail-and-Success",
responses: [][]byte{ // All upstream servers fail except for one. responses: [][]byte{ // All upstream servers fail except for one.
makeTestResponse(t, domain, dns.RCodeServerFailure), makeTestResponse(t, domain, dns.RCodeServerFailure),
makeTestResponse(t, domain, dns.RCodeServerFailure), makeTestResponse(t, domain, dns.RCodeServerFailure),
@ -1150,7 +1150,7 @@ func TestForwarderWithManyResolvers(t *testing.T) {
}, },
}, },
{ {
name: "NXDomain+Success", name: "NXDomain-and-Success",
responses: [][]byte{ // All upstream servers returned NXDOMAIN except for one. responses: [][]byte{ // All upstream servers returned NXDOMAIN except for one.
makeTestResponse(t, domain, dns.RCodeNameError), makeTestResponse(t, domain, dns.RCodeNameError),
makeTestResponse(t, domain, dns.RCodeNameError), makeTestResponse(t, domain, dns.RCodeNameError),
@ -1173,7 +1173,7 @@ func TestForwarderWithManyResolvers(t *testing.T) {
}, },
}, },
{ {
name: "Refused+Success", name: "Refused-and-Success",
responses: [][]byte{ // Some upstream servers refuse, but one succeeds. responses: [][]byte{ // Some upstream servers refuse, but one succeeds.
makeTestResponse(t, domain, dns.RCodeRefused), makeTestResponse(t, domain, dns.RCodeRefused),
makeTestResponse(t, domain, dns.RCodeRefused), makeTestResponse(t, domain, dns.RCodeRefused),
@ -1187,7 +1187,7 @@ func TestForwarderWithManyResolvers(t *testing.T) {
}, },
}, },
{ {
name: "Refused+ServFail", name: "Refused-and-ServFail",
responses: [][]byte{ // Some servers refuse, at least one fails. responses: [][]byte{ // Some servers refuse, at least one fails.
makeTestResponse(t, domain, dns.RCodeRefused), makeTestResponse(t, domain, dns.RCodeRefused),
makeTestResponse(t, domain, dns.RCodeServerFailure), makeTestResponse(t, domain, dns.RCodeServerFailure),

@ -998,7 +998,7 @@ func TestNodeAddrResolve(t *testing.T) {
} }
t.Logf("got IPv6 addr: %v", ap) t.Logf("got IPv6 addr: %v", ap)
}) })
t.Run("IPv6 Failure", func(t *testing.T) { t.Run("IPv6-Failure", func(t *testing.T) {
ap, ok := c.nodeAddrPort(ctx, dnV4Only, dn.STUNPort, probeIPv6) ap, ok := c.nodeAddrPort(ctx, dnV4Only, dn.STUNPort, probeIPv6)
if ok { if ok {
t.Fatalf("expected no addr but got: %v", ap) t.Fatalf("expected no addr but got: %v", ap)

@ -49,7 +49,7 @@ func TestIgnoreDuplicateNEWADDR(t *testing.T) {
return msg return msg
} }
t.Run("suppress duplicate NEWADDRs", func(t *testing.T) { t.Run("suppress-duplicate-NEWADDRs", func(t *testing.T) {
c := nlConn{ c := nlConn{
buffered: []netlink.Message{ buffered: []netlink.Message{
newAddrMsg(1, "192.168.0.5", unix.RTM_NEWADDR), newAddrMsg(1, "192.168.0.5", unix.RTM_NEWADDR),
@ -69,7 +69,7 @@ func TestIgnoreDuplicateNEWADDR(t *testing.T) {
} }
}) })
t.Run("do not suppress after DELADDR", func(t *testing.T) { t.Run("no-suppress-after-DELADDR", func(t *testing.T) {
c := nlConn{ c := nlConn{
buffered: []netlink.Message{ buffered: []netlink.Message{
newAddrMsg(1, "192.168.0.5", unix.RTM_NEWADDR), newAddrMsg(1, "192.168.0.5", unix.RTM_NEWADDR),

@ -47,7 +47,7 @@ func TestDownload(t *testing.T) {
// ensure that the test returns an appropriate number of Result structs // ensure that the test returns an appropriate number of Result structs
expectedLen := int(DefaultDuration.Seconds()) + 1 expectedLen := int(DefaultDuration.Seconds()) + 1
t.Run("download test", func(t *testing.T) { t.Run("download-test", func(t *testing.T) {
// conduct a download test // conduct a download test
results, err := RunClient(Download, DefaultDuration, serverIP) results, err := RunClient(Download, DefaultDuration, serverIP)
@ -65,7 +65,7 @@ func TestDownload(t *testing.T) {
} }
}) })
t.Run("upload test", func(t *testing.T) { t.Run("upload-test", func(t *testing.T) {
// conduct an upload test // conduct an upload test
results, err := RunClient(Upload, DefaultDuration, serverIP) results, err := RunClient(Upload, DefaultDuration, serverIP)

@ -60,7 +60,7 @@ var responseTests = []struct {
wantPort: 59029, wantPort: 59029,
}, },
{ {
name: "stun.sipgate.net:10000", name: "stun-sipgate-net-10000",
data: []byte{ data: []byte{
0x01, 0x01, 0x00, 0x44, 0x21, 0x12, 0xa4, 0x42, 0x01, 0x01, 0x00, 0x44, 0x21, 0x12, 0xa4, 0x42,
0x48, 0x2e, 0xb6, 0x47, 0x15, 0xe8, 0xb2, 0x8e, 0x48, 0x2e, 0xb6, 0x47, 0x15, 0xe8, 0xb2, 0x8e,
@ -82,7 +82,7 @@ var responseTests = []struct {
wantPort: 58539, wantPort: 58539,
}, },
{ {
name: "stun.powervoip.com:3478", name: "stun-powervoip-com-3478",
data: []byte{ data: []byte{
0x01, 0x01, 0x00, 0x24, 0x21, 0x12, 0xa4, 0x42, 0x01, 0x01, 0x00, 0x24, 0x21, 0x12, 0xa4, 0x42,
0x7e, 0x57, 0x96, 0x68, 0x29, 0xf4, 0x44, 0x60, 0x7e, 0x57, 0x96, 0x68, 0x29, 0xf4, 0x44, 0x60,
@ -100,7 +100,7 @@ var responseTests = []struct {
wantPort: 59859, wantPort: 59859,
}, },
{ {
name: "in-process pion server", name: "in-process-pion-server",
data: []byte{ data: []byte{
0x01, 0x01, 0x00, 0x24, 0x21, 0x12, 0xa4, 0x42, 0x01, 0x01, 0x00, 0x24, 0x21, 0x12, 0xa4, 0x42,
0xeb, 0xc2, 0xd3, 0x6e, 0xf4, 0x71, 0x21, 0x7c, 0xeb, 0xc2, 0xd3, 0x6e, 0xf4, 0x71, 0x21, 0x7c,
@ -119,7 +119,7 @@ var responseTests = []struct {
wantPort: 61300, wantPort: 61300,
}, },
{ {
name: "stuntman-server ipv6", name: "stuntman-server-ipv6",
data: []byte{ data: []byte{
0x01, 0x01, 0x00, 0x48, 0x21, 0x12, 0xa4, 0x42, 0x01, 0x01, 0x00, 0x48, 0x21, 0x12, 0xa4, 0x42,
0x06, 0xf5, 0x66, 0x85, 0xd2, 0x8a, 0xf3, 0xe6, 0x06, 0xf5, 0x66, 0x85, 0xd2, 0x8a, 0xf3, 0xe6,

@ -28,7 +28,7 @@ func TestSynologyProxyFromConfigCached(t *testing.T) {
tstest.Replace(t, &synologyProxyConfigPath, filepath.Join(t.TempDir(), "proxy.conf")) tstest.Replace(t, &synologyProxyConfigPath, filepath.Join(t.TempDir(), "proxy.conf"))
t.Run("no config file", func(t *testing.T) { t.Run("no-config-file", func(t *testing.T) {
if _, err := os.Stat(synologyProxyConfigPath); err == nil { if _, err := os.Stat(synologyProxyConfigPath); err == nil {
t.Fatalf("%s must not exist for this test", synologyProxyConfigPath) t.Fatalf("%s must not exist for this test", synologyProxyConfigPath)
} }
@ -52,7 +52,7 @@ func TestSynologyProxyFromConfigCached(t *testing.T) {
} }
}) })
t.Run("config file updated", func(t *testing.T) { t.Run("config-file-updated", func(t *testing.T) {
cache.updated = time.Now() cache.updated = time.Now()
cache.httpProxy = nil cache.httpProxy = nil
cache.httpsProxy = nil cache.httpsProxy = nil
@ -84,7 +84,7 @@ https_port=443
} }
}) })
t.Run("config file removed", func(t *testing.T) { t.Run("config-file-removed", func(t *testing.T) {
cache.updated = time.Now() cache.updated = time.Now()
cache.httpProxy = urlMustParse("http://127.0.0.1/") cache.httpProxy = urlMustParse("http://127.0.0.1/")
cache.httpsProxy = urlMustParse("http://127.0.0.1/") cache.httpsProxy = urlMustParse("http://127.0.0.1/")
@ -108,7 +108,7 @@ https_port=443
} }
}) })
t.Run("picks proxy from request scheme", func(t *testing.T) { t.Run("picks-proxy-from-request-scheme", func(t *testing.T) {
cache.updated = time.Now() cache.updated = time.Now()
cache.httpProxy = nil cache.httpProxy = nil
cache.httpsProxy = nil cache.httpsProxy = nil
@ -164,7 +164,7 @@ func TestSynologyProxiesFromConfig(t *testing.T) {
return openReader, openErr return openReader, openErr
}) })
t.Run("with config", func(t *testing.T) { t.Run("with-config", func(t *testing.T) {
mc := &mustCloser{Reader: strings.NewReader(` mc := &mustCloser{Reader: strings.NewReader(`
proxy_user=foo proxy_user=foo
proxy_pwd=bar proxy_pwd=bar
@ -200,7 +200,7 @@ http_port=80
}) })
t.Run("nonexistent config", func(t *testing.T) { t.Run("nonexistent-config", func(t *testing.T) {
openReader = nil openReader = nil
openErr = os.ErrNotExist openErr = os.ErrNotExist
@ -216,7 +216,7 @@ http_port=80
} }
}) })
t.Run("error opening config", func(t *testing.T) { t.Run("error-opening-config", func(t *testing.T) {
openReader = nil openReader = nil
openErr = errors.New("example error") openErr = errors.New("example error")

@ -97,7 +97,7 @@ func TestSetSelfProxy(t *testing.T) {
wantHTTPS string wantHTTPS string
}{ }{
{ {
name: "no self proxy", name: "no-self-proxy",
env: map[string]string{ env: map[string]string{
"HTTP_PROXY": "127.0.0.1:1234", "HTTP_PROXY": "127.0.0.1:1234",
"HTTPS_PROXY": "127.0.0.1:1234", "HTTPS_PROXY": "127.0.0.1:1234",
@ -107,7 +107,7 @@ func TestSetSelfProxy(t *testing.T) {
wantHTTPS: "127.0.0.1:1234", wantHTTPS: "127.0.0.1:1234",
}, },
{ {
name: "skip proxies", name: "skip-proxies",
env: map[string]string{ env: map[string]string{
"HTTP_PROXY": "127.0.0.1:1234", "HTTP_PROXY": "127.0.0.1:1234",
"HTTPS_PROXY": "127.0.0.1:5678", "HTTPS_PROXY": "127.0.0.1:5678",
@ -117,7 +117,7 @@ func TestSetSelfProxy(t *testing.T) {
wantHTTPS: "", // skipped wantHTTPS: "", // skipped
}, },
{ {
name: "localhost normalization of env var", name: "localhost-normalization-of-env-var",
env: map[string]string{ env: map[string]string{
"HTTP_PROXY": "localhost:1234", "HTTP_PROXY": "localhost:1234",
"HTTPS_PROXY": "[::1]:5678", "HTTPS_PROXY": "[::1]:5678",
@ -127,7 +127,7 @@ func TestSetSelfProxy(t *testing.T) {
wantHTTPS: "", // skipped wantHTTPS: "", // skipped
}, },
{ {
name: "localhost normalization of addr", name: "localhost-normalization-of-addr",
env: map[string]string{ env: map[string]string{
"HTTP_PROXY": "127.0.0.1:1234", "HTTP_PROXY": "127.0.0.1:1234",
"HTTPS_PROXY": "127.0.0.1:1234", "HTTPS_PROXY": "127.0.0.1:1234",
@ -137,7 +137,7 @@ func TestSetSelfProxy(t *testing.T) {
wantHTTPS: "", // skipped wantHTTPS: "", // skipped
}, },
{ {
name: "no ports", name: "no-ports",
env: map[string]string{ env: map[string]string{
"HTTP_PROXY": "myproxy", "HTTP_PROXY": "myproxy",
"HTTPS_PROXY": "myproxy", "HTTPS_PROXY": "myproxy",

@ -28,32 +28,32 @@ func TestServerEndpointJSONUnmarshal(t *testing.T) {
wantErr: false, wantErr: false,
}, },
{ {
name: "invalid ServerDisco", name: "invalid-ServerDisco",
json: []byte(`{"ServerDisco":"1","LamportID":18446744073709551615,"AddrPorts":["127.0.0.1:1","127.0.0.2:2"],"VNI":16777215,"BindLifetime":"30s","SteadyStateLifetime":"5m0s"}`), json: []byte(`{"ServerDisco":"1","LamportID":18446744073709551615,"AddrPorts":["127.0.0.1:1","127.0.0.2:2"],"VNI":16777215,"BindLifetime":"30s","SteadyStateLifetime":"5m0s"}`),
wantErr: true, wantErr: true,
}, },
{ {
name: "invalid LamportID", name: "invalid-LamportID",
json: []byte(`{"ServerDisco":"discokey:003cd7453e04a653eb0e7a18f206fc353180efadb2facfd05ebd6982a1392c7f","LamportID":1.1,"AddrPorts":["127.0.0.1:1","127.0.0.2:2"],"VNI":16777215,"BindLifetime":"30s","SteadyStateLifetime":"5m0s"}`), json: []byte(`{"ServerDisco":"discokey:003cd7453e04a653eb0e7a18f206fc353180efadb2facfd05ebd6982a1392c7f","LamportID":1.1,"AddrPorts":["127.0.0.1:1","127.0.0.2:2"],"VNI":16777215,"BindLifetime":"30s","SteadyStateLifetime":"5m0s"}`),
wantErr: true, wantErr: true,
}, },
{ {
name: "invalid AddrPorts", name: "invalid-AddrPorts",
json: []byte(`{"ServerDisco":"discokey:003cd7453e04a653eb0e7a18f206fc353180efadb2facfd05ebd6982a1392c7f","LamportID":18446744073709551615,"AddrPorts":["127.0.0.1.1:1","127.0.0.2:2"],"VNI":16777215,"BindLifetime":"30s","SteadyStateLifetime":"5m0s"}`), json: []byte(`{"ServerDisco":"discokey:003cd7453e04a653eb0e7a18f206fc353180efadb2facfd05ebd6982a1392c7f","LamportID":18446744073709551615,"AddrPorts":["127.0.0.1.1:1","127.0.0.2:2"],"VNI":16777215,"BindLifetime":"30s","SteadyStateLifetime":"5m0s"}`),
wantErr: true, wantErr: true,
}, },
{ {
name: "invalid VNI", name: "invalid-VNI",
json: []byte(`{"ServerDisco":"discokey:003cd7453e04a653eb0e7a18f206fc353180efadb2facfd05ebd6982a1392c7f","LamportID":18446744073709551615,"AddrPorts":["127.0.0.1:1","127.0.0.2:2"],"VNI":18446744073709551615,"BindLifetime":"30s","SteadyStateLifetime":"5m0s"}`), json: []byte(`{"ServerDisco":"discokey:003cd7453e04a653eb0e7a18f206fc353180efadb2facfd05ebd6982a1392c7f","LamportID":18446744073709551615,"AddrPorts":["127.0.0.1:1","127.0.0.2:2"],"VNI":18446744073709551615,"BindLifetime":"30s","SteadyStateLifetime":"5m0s"}`),
wantErr: true, wantErr: true,
}, },
{ {
name: "invalid BindLifetime", name: "invalid-BindLifetime",
json: []byte(`{"ServerDisco":"discokey:003cd7453e04a653eb0e7a18f206fc353180efadb2facfd05ebd6982a1392c7f","LamportID":18446744073709551615,"AddrPorts":["127.0.0.1:1","127.0.0.2:2"],"VNI":16777215,"BindLifetime":"5","SteadyStateLifetime":"5m0s"}`), json: []byte(`{"ServerDisco":"discokey:003cd7453e04a653eb0e7a18f206fc353180efadb2facfd05ebd6982a1392c7f","LamportID":18446744073709551615,"AddrPorts":["127.0.0.1:1","127.0.0.2:2"],"VNI":16777215,"BindLifetime":"5","SteadyStateLifetime":"5m0s"}`),
wantErr: true, wantErr: true,
}, },
{ {
name: "invalid SteadyStateLifetime", name: "invalid-SteadyStateLifetime",
json: []byte(`{"ServerDisco":"discokey:003cd7453e04a653eb0e7a18f206fc353180efadb2facfd05ebd6982a1392c7f","LamportID":18446744073709551615,"AddrPorts":["127.0.0.1:1","127.0.0.2:2"],"VNI":16777215,"BindLifetime":"30s","SteadyStateLifetime":"5"}`), json: []byte(`{"ServerDisco":"discokey:003cd7453e04a653eb0e7a18f206fc353180efadb2facfd05ebd6982a1392c7f","LamportID":18446744073709551615,"AddrPorts":["127.0.0.1:1","127.0.0.2:2"],"VNI":16777215,"BindLifetime":"30s","SteadyStateLifetime":"5"}`),
wantErr: true, wantErr: true,
}, },
@ -79,7 +79,7 @@ func TestServerEndpointJSONMarshal(t *testing.T) {
serverEndpoint ServerEndpoint serverEndpoint ServerEndpoint
}{ }{
{ {
name: "valid roundtrip", name: "valid-roundtrip",
serverEndpoint: ServerEndpoint{ serverEndpoint: ServerEndpoint{
ServerDisco: key.NewDisco().Public(), ServerDisco: key.NewDisco().Public(),
LamportID: uint64(math.MaxUint64), LamportID: uint64(math.MaxUint64),

@ -196,15 +196,15 @@ func TestServer(t *testing.T) {
forceClientsMixedAF bool forceClientsMixedAF bool
}{ }{
{ {
name: "over ipv4", name: "over-ipv4",
staticAddrs: []netip.Addr{netip.MustParseAddr("127.0.0.1")}, staticAddrs: []netip.Addr{netip.MustParseAddr("127.0.0.1")},
}, },
{ {
name: "over ipv6", name: "over-ipv6",
staticAddrs: []netip.Addr{netip.MustParseAddr("::1")}, staticAddrs: []netip.Addr{netip.MustParseAddr("::1")},
}, },
{ {
name: "mixed address families", name: "mixed-address-families",
staticAddrs: []netip.Addr{netip.MustParseAddr("127.0.0.1"), netip.MustParseAddr("::1")}, staticAddrs: []netip.Addr{netip.MustParseAddr("127.0.0.1"), netip.MustParseAddr("::1")},
forceClientsMixedAF: true, forceClientsMixedAF: true,
}, },

@ -41,25 +41,25 @@ func TestPostRequestContentTypeValidation(t *testing.T) {
wantErr bool wantErr bool
}{ }{
{ {
name: "API routes should accept `application/json` content-type", name: "API-accept-application-json",
browserRoute: false, browserRoute: false,
contentType: "application/json", contentType: "application/json",
wantErr: false, wantErr: false,
}, },
{ {
name: "API routes should reject `application/x-www-form-urlencoded` content-type", name: "API-reject-form-urlencoded",
browserRoute: false, browserRoute: false,
contentType: "application/x-www-form-urlencoded", contentType: "application/x-www-form-urlencoded",
wantErr: true, wantErr: true,
}, },
{ {
name: "Browser routes should accept `application/x-www-form-urlencoded` content-type", name: "browser-accept-form-urlencoded",
browserRoute: true, browserRoute: true,
contentType: "application/x-www-form-urlencoded", contentType: "application/x-www-form-urlencoded",
wantErr: false, wantErr: false,
}, },
{ {
name: "non Browser routes should accept `application/json` content-type", name: "browser-accept-application-json",
browserRoute: true, browserRoute: true,
contentType: "application/json", contentType: "application/json",
wantErr: false, wantErr: false,
@ -106,21 +106,21 @@ func TestAPIMuxCrossOriginResourceSharingHeaders(t *testing.T) {
corsMethods []string corsMethods []string
}{ }{
{ {
name: "do not set CORS headers for non-OPTIONS requests", name: "no-CORS-headers-for-non-OPTIONS",
corsOrigins: []string{"https://foobar.com"}, corsOrigins: []string{"https://foobar.com"},
corsMethods: []string{"GET", "POST", "HEAD"}, corsMethods: []string{"GET", "POST", "HEAD"},
httpMethod: "GET", httpMethod: "GET",
wantCORSHeaders: false, wantCORSHeaders: false,
}, },
{ {
name: "set CORS headers for non-OPTIONS requests", name: "CORS-headers-for-OPTIONS",
corsOrigins: []string{"https://foobar.com"}, corsOrigins: []string{"https://foobar.com"},
corsMethods: []string{"GET", "POST", "HEAD"}, corsMethods: []string{"GET", "POST", "HEAD"},
httpMethod: "OPTIONS", httpMethod: "OPTIONS",
wantCORSHeaders: true, wantCORSHeaders: true,
}, },
{ {
name: "do not serve CORS headers for OPTIONS requests with no configured origins", name: "no-CORS-headers-for-OPTIONS-without-origins",
httpMethod: "OPTIONS", httpMethod: "OPTIONS",
wantCORSHeaders: false, wantCORSHeaders: false,
}, },
@ -162,19 +162,19 @@ func TestCSRFProtection(t *testing.T) {
wantStatus int wantStatus int
}{ }{
{ {
name: "POST requests to non-API routes require CSRF token and fail if not provided", name: "non-API-POST-without-CSRF-fails",
apiRoute: false, apiRoute: false,
passCSRFToken: false, passCSRFToken: false,
wantStatus: http.StatusForbidden, wantStatus: http.StatusForbidden,
}, },
{ {
name: "POST requests to non-API routes require CSRF token and pass if provided", name: "non-API-POST-with-CSRF-passes",
apiRoute: false, apiRoute: false,
passCSRFToken: true, passCSRFToken: true,
wantStatus: http.StatusOK, wantStatus: http.StatusOK,
}, },
{ {
name: "POST requests to /api/ routes do not require CSRF token", name: "API-POST-without-CSRF-passes",
apiRoute: true, apiRoute: true,
passCSRFToken: false, passCSRFToken: false,
wantStatus: http.StatusOK, wantStatus: http.StatusOK,
@ -246,11 +246,11 @@ func TestContentSecurityPolicyHeader(t *testing.T) {
wantCSP string wantCSP string
}{ }{
{ {
name: "default CSP", name: "default-CSP",
wantCSP: `base-uri 'self'; block-all-mixed-content; default-src 'self'; form-action 'self'; frame-ancestors 'none';`, wantCSP: `base-uri 'self'; block-all-mixed-content; default-src 'self'; form-action 'self'; frame-ancestors 'none';`,
}, },
{ {
name: "custom CSP", name: "custom-CSP",
csp: CSP{ csp: CSP{
"default-src": {"'self'", "https://tailscale.com"}, "default-src": {"'self'", "https://tailscale.com"},
"upgrade-insecure-requests": nil, "upgrade-insecure-requests": nil,
@ -258,7 +258,7 @@ func TestContentSecurityPolicyHeader(t *testing.T) {
wantCSP: `default-src 'self' https://tailscale.com; upgrade-insecure-requests;`, wantCSP: `default-src 'self' https://tailscale.com; upgrade-insecure-requests;`,
}, },
{ {
name: "`/api/*` routes do not get CSP headers", name: "api-routes-no-CSP-headers",
apiRoute: true, apiRoute: true,
wantCSP: "", wantCSP: "",
}, },
@ -301,12 +301,12 @@ func TestCSRFCookieSecureMode(t *testing.T) {
wantSecure bool wantSecure bool
}{ }{
{ {
name: "CSRF cookie should be secure when server is in secure context", name: "secure-context-cookie-secure",
secureMode: true, secureMode: true,
wantSecure: true, wantSecure: true,
}, },
{ {
name: "CSRF cookie should not be secure when server is not in secure context", name: "non-secure-context-cookie-not-secure",
secureMode: false, secureMode: false,
wantSecure: false, wantSecure: false,
}, },
@ -343,12 +343,12 @@ func TestRefererPolicy(t *testing.T) {
wantRefererPolicy bool wantRefererPolicy bool
}{ }{
{ {
name: "BrowserMux routes get Referer-Policy headers", name: "BrowserMux-gets-Referer-Policy",
browserRoute: true, browserRoute: true,
wantRefererPolicy: true, wantRefererPolicy: true,
}, },
{ {
name: "APIMux routes do not get Referer-Policy headers", name: "APIMux-no-Referer-Policy",
browserRoute: false, browserRoute: false,
wantRefererPolicy: false, wantRefererPolicy: false,
}, },
@ -420,54 +420,54 @@ func TestRouting(t *testing.T) {
want string want string
}{ }{
{ {
desc: "only browser mux", desc: "only-browser-mux",
browserPatterns: []string{"/"}, browserPatterns: []string{"/"},
requestPath: "/index.html", requestPath: "/index.html",
want: "browser", want: "browser",
}, },
{ {
desc: "only API mux", desc: "only-API-mux",
apiPatterns: []string{"/api/"}, apiPatterns: []string{"/api/"},
requestPath: "/api/foo", requestPath: "/api/foo",
want: "api", want: "api",
}, },
{ {
desc: "browser mux match", desc: "browser-mux-match",
browserPatterns: []string{"/content/"}, browserPatterns: []string{"/content/"},
apiPatterns: []string{"/api/"}, apiPatterns: []string{"/api/"},
requestPath: "/content/index.html", requestPath: "/content/index.html",
want: "browser", want: "browser",
}, },
{ {
desc: "API mux match", desc: "API-mux-match",
browserPatterns: []string{"/content/"}, browserPatterns: []string{"/content/"},
apiPatterns: []string{"/api/"}, apiPatterns: []string{"/api/"},
requestPath: "/api/foo", requestPath: "/api/foo",
want: "api", want: "api",
}, },
{ {
desc: "browser wildcard match", desc: "browser-wildcard-match",
browserPatterns: []string{"/"}, browserPatterns: []string{"/"},
apiPatterns: []string{"/api/"}, apiPatterns: []string{"/api/"},
requestPath: "/index.html", requestPath: "/index.html",
want: "browser", want: "browser",
}, },
{ {
desc: "API wildcard match", desc: "API-wildcard-match",
browserPatterns: []string{"/content/"}, browserPatterns: []string{"/content/"},
apiPatterns: []string{"/"}, apiPatterns: []string{"/"},
requestPath: "/api/foo", requestPath: "/api/foo",
want: "api", want: "api",
}, },
{ {
desc: "path conflict", desc: "path-conflict",
browserPatterns: []string{"/foo/"}, browserPatterns: []string{"/foo/"},
apiPatterns: []string{"/foo/bar/"}, apiPatterns: []string{"/foo/bar/"},
requestPath: "/foo/bar/baz", requestPath: "/foo/bar/baz",
want: "api", want: "api",
}, },
{ {
desc: "no match", desc: "no-match",
browserPatterns: []string{"/foo/"}, browserPatterns: []string{"/foo/"},
apiPatterns: []string{"/bar/"}, apiPatterns: []string{"/bar/"},
requestPath: "/baz", requestPath: "/baz",
@ -521,43 +521,43 @@ func TestGetMoreSpecificPattern(t *testing.T) {
want: unknownHandler, want: unknownHandler,
}, },
{ {
desc: "identical prefix", desc: "identical-prefix",
a: "/foo/bar/", a: "/foo/bar/",
b: "/foo/bar/", b: "/foo/bar/",
want: unknownHandler, want: unknownHandler,
}, },
{ {
desc: "trailing slash", desc: "trailing-slash",
a: "/foo", a: "/foo",
b: "/foo/", // path.Clean will strip the trailing slash. b: "/foo/", // path.Clean will strip the trailing slash.
want: unknownHandler, want: unknownHandler,
}, },
{ {
desc: "same prefix", desc: "same-prefix",
a: "/foo/bar/quux", a: "/foo/bar/quux",
b: "/foo/bar/", // path.Clean will strip the trailing slash. b: "/foo/bar/", // path.Clean will strip the trailing slash.
want: apiHandler, want: apiHandler,
}, },
{ {
desc: "almost same prefix, but not a path component", desc: "almost-same-prefix-not-path-component",
a: "/goat/sheep/cheese", a: "/goat/sheep/cheese",
b: "/goat/sheepcheese/", // path.Clean will strip the trailing slash. b: "/goat/sheepcheese/", // path.Clean will strip the trailing slash.
want: apiHandler, want: apiHandler,
}, },
{ {
desc: "attempt to make less-specific pattern look more specific", desc: "traversal-less-specific-pattern",
a: "/goat/cat/buddy", a: "/goat/cat/buddy",
b: "/goat/../../../../../../../cat", // path.Clean catches this foolishness b: "/goat/../../../../../../../cat", // path.Clean catches this foolishness
want: apiHandler, want: apiHandler,
}, },
{ {
desc: "2 names for / (1)", desc: "two-names-for-root-1",
a: "/", a: "/",
b: "/../../../../../../", b: "/../../../../../../",
want: unknownHandler, want: unknownHandler,
}, },
{ {
desc: "2 names for / (2)", desc: "two-names-for-root-2",
a: "/", a: "/",
b: "///////", b: "///////",
want: unknownHandler, want: unknownHandler,
@ -586,15 +586,15 @@ func TestStrictTransportSecurityOptions(t *testing.T) {
expect string expect string
}{ }{
{ {
name: "off by default", name: "off-by-default",
}, },
{ {
name: "default HSTS options in the secure context", name: "default-HSTS-in-secure-context",
secureContext: true, secureContext: true,
expect: DefaultStrictTransportSecurityOptions, expect: DefaultStrictTransportSecurityOptions,
}, },
{ {
name: "custom options sent in the secure context", name: "custom-options-in-secure-context",
options: DefaultStrictTransportSecurityOptions + "; includeSubDomains", options: DefaultStrictTransportSecurityOptions + "; includeSubDomains",
secureContext: true, secureContext: true,
expect: DefaultStrictTransportSecurityOptions + "; includeSubDomains", expect: DefaultStrictTransportSecurityOptions + "; includeSubDomains",

@ -35,7 +35,7 @@ func TestConnectToRecorder(t *testing.T) {
wantErr bool wantErr bool
}{ }{
{ {
desc: "v1 recorder", desc: "v1-recorder",
setup: func(t *testing.T) (*http.ServeMux, <-chan []byte) { setup: func(t *testing.T) (*http.ServeMux, <-chan []byte) {
uploadHash := make(chan []byte, 1) uploadHash := make(chan []byte, 1)
mux := http.NewServeMux() mux := http.NewServeMux()
@ -50,7 +50,7 @@ func TestConnectToRecorder(t *testing.T) {
}, },
}, },
{ {
desc: "v2 recorder", desc: "v2-recorder",
http2: true, http2: true,
setup: func(t *testing.T) (*http.ServeMux, <-chan []byte) { setup: func(t *testing.T) (*http.ServeMux, <-chan []byte) {
uploadHash := make(chan []byte, 1) uploadHash := make(chan []byte, 1)
@ -100,7 +100,7 @@ func TestConnectToRecorder(t *testing.T) {
}, },
}, },
{ {
desc: "v2 recorder no acks", desc: "v2-recorder-no-acks",
http2: true, http2: true,
wantErr: true, wantErr: true,
setup: func(t *testing.T) (*http.ServeMux, <-chan []byte) { setup: func(t *testing.T) (*http.ServeMux, <-chan []byte) {

@ -16,4 +16,4 @@
) { ) {
src = ./.; src = ./.;
}).shellNix }).shellNix
# nix-direnv cache busting line: sha256-39axT5Q0+fNTcMgZCMLMNfJEJN46wMaaKDgfI+Uj+Ps= # nix-direnv cache busting line: sha256-VsVMvTEblVx/HNbuCVxC9UgKpriRwixswUSKVGLMf3Q=

@ -111,25 +111,25 @@ func TestFilterEnv(t *testing.T) {
wantErrMessage string wantErrMessage string
}{ }{
{ {
name: "simple direct matches", name: "simple-direct-matches",
acceptEnv: []string{"FOO", "FOO2", "FOO_3"}, acceptEnv: []string{"FOO", "FOO2", "FOO_3"},
environ: []string{"FOO=BAR", "FOO2=BAZ", "FOO_3=123", "FOOOO4-2=AbCdEfG"}, environ: []string{"FOO=BAR", "FOO2=BAZ", "FOO_3=123", "FOOOO4-2=AbCdEfG"},
expectedFiltered: []string{"FOO=BAR", "FOO2=BAZ", "FOO_3=123"}, expectedFiltered: []string{"FOO=BAR", "FOO2=BAZ", "FOO_3=123"},
}, },
{ {
name: "bare wildcard", name: "bare-wildcard",
acceptEnv: []string{"*"}, acceptEnv: []string{"*"},
environ: []string{"FOO=BAR", "FOO2=BAZ", "FOO_3=123", "FOOOO4-2=AbCdEfG"}, environ: []string{"FOO=BAR", "FOO2=BAZ", "FOO_3=123", "FOOOO4-2=AbCdEfG"},
expectedFiltered: []string{"FOO=BAR", "FOO2=BAZ", "FOO_3=123", "FOOOO4-2=AbCdEfG"}, expectedFiltered: []string{"FOO=BAR", "FOO2=BAZ", "FOO_3=123", "FOOOO4-2=AbCdEfG"},
}, },
{ {
name: "complex matches", name: "complex-matches",
acceptEnv: []string{"FO?", "FOOO*", "FO*5?7"}, acceptEnv: []string{"FO?", "FOOO*", "FO*5?7"},
environ: []string{"FOO=BAR", "FOO2=BAZ", "FOO_3=123", "FOOOO4-2=AbCdEfG", "FO1-kmndGamc79567=ABC", "FO57=BAR2"}, environ: []string{"FOO=BAR", "FOO2=BAZ", "FOO_3=123", "FOOOO4-2=AbCdEfG", "FO1-kmndGamc79567=ABC", "FO57=BAR2"},
expectedFiltered: []string{"FOO=BAR", "FOOOO4-2=AbCdEfG", "FO1-kmndGamc79567=ABC"}, expectedFiltered: []string{"FOO=BAR", "FOOOO4-2=AbCdEfG", "FO1-kmndGamc79567=ABC"},
}, },
{ {
name: "environ format invalid", name: "environ-format-invalid",
acceptEnv: []string{"FO?", "FOOO*", "FO*5?7"}, acceptEnv: []string{"FO?", "FOOO*", "FO*5?7"},
environ: []string{"FOOBAR"}, environ: []string{"FOOBAR"},
expectedFiltered: nil, expectedFiltered: nil,

@ -30,7 +30,7 @@ func BenchmarkShardedInt(b *testing.B) {
}) })
}) })
b.Run("sharded int", func(b *testing.B) { b.Run("sharded-int", func(b *testing.B) {
m := NewShardedInt() m := NewShardedInt()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
@ -60,7 +60,7 @@ func TestShardedInt(t *testing.T) {
} }
}) })
t.Run("high concurrency", func(t *testing.T) { t.Run("high-concurrency", func(t *testing.T) {
m := NewShardedInt() m := NewShardedInt()
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
numWorkers := 1000 numWorkers := 1000
@ -83,7 +83,7 @@ func TestShardedInt(t *testing.T) {
} }
}) })
t.Run("encoding.TextAppender", func(t *testing.T) { t.Run("encoding-TextAppender", func(t *testing.T) {
m := NewShardedInt() m := NewShardedInt()
m.Add(1) m.Add(1)
b := make([]byte, 0, 10) b := make([]byte, 0, 10)

@ -18,31 +18,35 @@ func TestProtoPortRangeParsing(t *testing.T) {
return PortRange{First: s, Last: e} return PortRange{First: s, Last: e}
} }
tests := []struct { tests := []struct {
in string name string
out ProtoPortRange in string
err error out ProtoPortRange
err error
}{ }{
{in: "tcp:80", out: ProtoPortRange{Proto: int(ipproto.TCP), Ports: pr(80, 80)}}, {name: "tcp-80", in: "tcp:80", out: ProtoPortRange{Proto: int(ipproto.TCP), Ports: pr(80, 80)}},
{in: "80", out: ProtoPortRange{Ports: pr(80, 80)}}, {name: "80", in: "80", out: ProtoPortRange{Ports: pr(80, 80)}},
{in: "*", out: ProtoPortRange{Ports: PortRangeAny}}, {name: "star", in: "*", out: ProtoPortRange{Ports: PortRangeAny}},
{in: "*:*", out: ProtoPortRange{Ports: PortRangeAny}}, {name: "star-star", in: "*:*", out: ProtoPortRange{Ports: PortRangeAny}},
{in: "tcp:*", out: ProtoPortRange{Proto: int(ipproto.TCP), Ports: PortRangeAny}}, {name: "tcp-star", in: "tcp:*", out: ProtoPortRange{Proto: int(ipproto.TCP), Ports: PortRangeAny}},
{ {
in: "tcp:", name: "tcp-empty-port",
err: vizerror.Errorf("invalid port list: %#v", ""), in: "tcp:",
err: vizerror.Errorf("invalid port list: %#v", ""),
}, },
{ {
in: ":80", name: "empty-proto-80",
err: errEmptyProtocol, in: ":80",
err: errEmptyProtocol,
}, },
{ {
in: "", name: "empty-string",
err: errEmptyString, in: "",
err: errEmptyString,
}, },
} }
for _, tc := range tests { for _, tc := range tests {
t.Run(tc.in, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
var ppr ProtoPortRange var ppr ProtoPortRange
err := ppr.UnmarshalText([]byte(tc.in)) err := ppr.UnmarshalText([]byte(tc.in))
if tc.err != err { if tc.err != err {

@ -841,12 +841,12 @@ func TestMarshalToRawMessageAndBack(t *testing.T) {
capType: PeerCapability("foo"), capType: PeerCapability("foo"),
}, },
{ {
name: "some values", name: "some-values",
val: testRule{Ports: []int{80, 443}, Name: "foo"}, val: testRule{Ports: []int{80, 443}, Name: "foo"},
capType: PeerCapability("foo"), capType: PeerCapability("foo"),
}, },
{ {
name: "all values", name: "all-values",
val: testRule{Ports: []int{80, 443}, Name: "foo", ToggleOn: true, Groups: inner{Groups: []string{"foo", "bar"}}, Addrs: []netip.AddrPort{testip}}, val: testRule{Ports: []int{80, 443}, Name: "foo", ToggleOn: true, Groups: inner{Groups: []string{"foo", "bar"}}, Addrs: []netip.AddrPort{testip}},
capType: PeerCapability("foo"), capType: PeerCapability("foo"),
}, },

@ -185,7 +185,7 @@ func TestMarkActiveChain(t *testing.T) {
expectLastActiveIdx: 0, expectLastActiveIdx: 0,
}, },
{ {
name: "simple truncate", name: "simple-truncate",
minChain: 2, minChain: 2,
chain: []aumTemplate{ chain: []aumTemplate{
{AUM: AUM{MessageKind: AUMCheckpoint, State: &State{}}}, {AUM: AUM{MessageKind: AUMCheckpoint, State: &State{}}},
@ -196,7 +196,7 @@ func TestMarkActiveChain(t *testing.T) {
expectLastActiveIdx: 1, expectLastActiveIdx: 1,
}, },
{ {
name: "long truncate", name: "long-truncate",
minChain: 5, minChain: 5,
chain: []aumTemplate{ chain: []aumTemplate{
{AUM: AUM{MessageKind: AUMCheckpoint, State: &State{}}}, {AUM: AUM{MessageKind: AUMCheckpoint, State: &State{}}},
@ -211,7 +211,7 @@ func TestMarkActiveChain(t *testing.T) {
expectLastActiveIdx: 2, expectLastActiveIdx: 2,
}, },
{ {
name: "truncate finding checkpoint", name: "truncate-finding-checkpoint",
minChain: 2, minChain: 2,
chain: []aumTemplate{ chain: []aumTemplate{
{AUM: AUM{MessageKind: AUMCheckpoint, State: &State{}}}, {AUM: AUM{MessageKind: AUMCheckpoint, State: &State{}}},
@ -342,7 +342,7 @@ func TestMarkAncestorIntersectionAUMs(t *testing.T) {
wantRetained: []string{"A"}, wantRetained: []string{"A"},
}, },
{ {
name: "no adjustment", name: "no-adjustment",
chain: newTestchain(t, ` chain: newTestchain(t, `
DEAD -> A -> B -> C DEAD -> A -> B -> C
A.template = checkpoint A.template = checkpoint
@ -380,7 +380,7 @@ func TestMarkAncestorIntersectionAUMs(t *testing.T) {
wantDeleted: []string{"A", "B"}, wantDeleted: []string{"A", "B"},
}, },
{ {
name: "fork finding earlier checkpoint", name: "fork-finding-earlier-checkpoint",
chain: newTestchain(t, ` chain: newTestchain(t, `
A -> B -> C -> D -> E -> F A -> B -> C -> D -> E -> F
| -> FORK | -> FORK
@ -403,7 +403,7 @@ func TestMarkAncestorIntersectionAUMs(t *testing.T) {
wantDeleted: []string{"A"}, wantDeleted: []string{"A"},
}, },
{ {
name: "fork multi", name: "fork-multi",
chain: newTestchain(t, ` chain: newTestchain(t, `
A -> B -> C -> D -> E A -> B -> C -> D -> E
| -> DEADFORK | -> DEADFORK
@ -429,7 +429,7 @@ func TestMarkAncestorIntersectionAUMs(t *testing.T) {
wantDeleted: []string{"A", "B", "DEADFORK"}, wantDeleted: []string{"A", "B", "DEADFORK"},
}, },
{ {
name: "fork multi 2", name: "fork-multi-2",
chain: newTestchain(t, ` chain: newTestchain(t, `
A -> B -> C -> D -> E -> F -> G A -> B -> C -> D -> E -> F -> G

@ -68,14 +68,17 @@ func authForPeers(self *ipnstate.PeerStatus, peers []*ipnstate.PeerStatus) *auth
func TestAuthRefreshErrorsNotRunning(t *testing.T) { func TestAuthRefreshErrorsNotRunning(t *testing.T) {
tests := []struct { tests := []struct {
name string
in *ipnstate.Status in *ipnstate.Status
expected string expected string
}{ }{
{ {
name: "no-status",
in: nil, in: nil,
expected: "no status", expected: "no status",
}, },
{ {
name: "ts-server-not-running",
in: &ipnstate.Status{ in: &ipnstate.Status{
BackendState: "NeedsMachineAuth", BackendState: "NeedsMachineAuth",
}, },
@ -84,7 +87,7 @@ func TestAuthRefreshErrorsNotRunning(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.expected, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
ctx := t.Context() ctx := t.Context()
a := authForStatus(tt.in) a := authForStatus(tt.in)
err := a.Refresh(ctx) err := a.Refresh(ctx)
@ -127,22 +130,22 @@ func TestAuthAllowsHost(t *testing.T) {
expected bool expected bool
}{ }{
{ {
name: "tagged with different tag", name: "tagged-different-tag",
peerStatus: peers[0], peerStatus: peers[0],
expected: false, expected: false,
}, },
{ {
name: "not tagged", name: "not-tagged",
peerStatus: peers[1], peerStatus: peers[1],
expected: false, expected: false,
}, },
{ {
name: "tags includes testTag", name: "tags-include-testTag",
peerStatus: peers[2], peerStatus: peers[2],
expected: true, expected: true,
}, },
{ {
name: "only tag is testTag", name: "only-testTag",
peerStatus: peers[3], peerStatus: peers[3],
expected: true, expected: true,
}, },
@ -201,12 +204,12 @@ func TestAuthSelfAllowed(t *testing.T) {
expected bool expected bool
}{ }{
{ {
name: "self has different tag", name: "self-different-tag",
in: []string{"woo"}, in: []string{"woo"},
expected: false, expected: false,
}, },
{ {
name: "selfs tags include testTag", name: "self-tags-include-testTag",
in: []string{"woo", testTag}, in: []string{"woo", testTag},
expected: true, expected: true,
}, },

@ -2933,7 +2933,7 @@ func TestResolveAuthKey(t *testing.T) {
wantErrContains string wantErrContains string
}{ }{
{ {
name: "successful resolution via OAuth client secret", name: "success-oauth-client-secret",
clientSecret: "tskey-client-secret-123", clientSecret: "tskey-client-secret-123",
oauthAvailable: true, oauthAvailable: true,
resolveViaOAuth: func(ctx context.Context, clientSecret string, tags []string) (string, error) { resolveViaOAuth: func(ctx context.Context, clientSecret string, tags []string) (string, error) {
@ -2946,7 +2946,7 @@ func TestResolveAuthKey(t *testing.T) {
wantErrContains: "", wantErrContains: "",
}, },
{ {
name: "failing resolution via OAuth client secret", name: "fail-oauth-client-secret",
clientSecret: "tskey-client-secret-123", clientSecret: "tskey-client-secret-123",
oauthAvailable: true, oauthAvailable: true,
resolveViaOAuth: func(ctx context.Context, clientSecret string, tags []string) (string, error) { resolveViaOAuth: func(ctx context.Context, clientSecret string, tags []string) (string, error) {
@ -2955,7 +2955,7 @@ func TestResolveAuthKey(t *testing.T) {
wantErrContains: "resolution failed", wantErrContains: "resolution failed",
}, },
{ {
name: "successful resolution via federated ID token", name: "success-federated-id-token",
clientID: "client-id-123", clientID: "client-id-123",
idToken: "id-token-456", idToken: "id-token-456",
wifAvailable: true, wifAvailable: true,
@ -2972,7 +2972,7 @@ func TestResolveAuthKey(t *testing.T) {
wantErrContains: "", wantErrContains: "",
}, },
{ {
name: "successful resolution via federated audience", name: "success-federated-audience",
clientID: "client-id-123", clientID: "client-id-123",
audience: "api.tailscale.com", audience: "api.tailscale.com",
wifAvailable: true, wifAvailable: true,
@ -2989,7 +2989,7 @@ func TestResolveAuthKey(t *testing.T) {
wantErrContains: "", wantErrContains: "",
}, },
{ {
name: "failing resolution via federated ID token", name: "fail-federated-id-token",
clientID: "client-id-123", clientID: "client-id-123",
idToken: "id-token-456", idToken: "id-token-456",
wifAvailable: true, wifAvailable: true,
@ -2999,7 +2999,7 @@ func TestResolveAuthKey(t *testing.T) {
wantErrContains: "resolution failed", wantErrContains: "resolution failed",
}, },
{ {
name: "empty client ID with ID token", name: "empty-client-id-with-token",
clientID: "", clientID: "",
idToken: "id-token-456", idToken: "id-token-456",
wifAvailable: true, wifAvailable: true,
@ -3009,7 +3009,7 @@ func TestResolveAuthKey(t *testing.T) {
wantErrContains: "empty", wantErrContains: "empty",
}, },
{ {
name: "empty client ID with audience", name: "empty-client-id-with-audience",
clientID: "", clientID: "",
audience: "api.tailscale.com", audience: "api.tailscale.com",
wifAvailable: true, wifAvailable: true,
@ -3019,7 +3019,7 @@ func TestResolveAuthKey(t *testing.T) {
wantErrContains: "empty", wantErrContains: "empty",
}, },
{ {
name: "empty ID token", name: "empty-id-token",
clientID: "client-id-123", clientID: "client-id-123",
idToken: "", idToken: "",
wifAvailable: true, wifAvailable: true,
@ -3029,7 +3029,7 @@ func TestResolveAuthKey(t *testing.T) {
wantErrContains: "empty", wantErrContains: "empty",
}, },
{ {
name: "audience with ID token", name: "audience-with-id-token",
clientID: "client-id-123", clientID: "client-id-123",
idToken: "id-token-456", idToken: "id-token-456",
audience: "api.tailscale.com", audience: "api.tailscale.com",
@ -3040,7 +3040,7 @@ func TestResolveAuthKey(t *testing.T) {
wantErrContains: "only one of ID token and audience", wantErrContains: "only one of ID token and audience",
}, },
{ {
name: "workload identity resolution skipped if resolution via OAuth token succeeds", name: "wif-skipped-oauth-succeeds",
clientSecret: "tskey-client-secret-123", clientSecret: "tskey-client-secret-123",
oauthAvailable: true, oauthAvailable: true,
resolveViaOAuth: func(ctx context.Context, clientSecret string, tags []string) (string, error) { resolveViaOAuth: func(ctx context.Context, clientSecret string, tags []string) (string, error) {
@ -3057,7 +3057,7 @@ func TestResolveAuthKey(t *testing.T) {
wantErrContains: "", wantErrContains: "",
}, },
{ {
name: "workload identity resolution skipped if resolution via OAuth token fails", name: "wif-skipped-oauth-fails",
clientID: "tskey-client-id-123", clientID: "tskey-client-id-123",
idToken: "", idToken: "",
oauthAvailable: true, oauthAvailable: true,
@ -3071,7 +3071,7 @@ func TestResolveAuthKey(t *testing.T) {
wantErrContains: "failed", wantErrContains: "failed",
}, },
{ {
name: "authkey set and no resolution available", name: "authkey-set-no-resolution",
authKey: "tskey-auth-123", authKey: "tskey-auth-123",
oauthAvailable: false, oauthAvailable: false,
wifAvailable: false, wifAvailable: false,
@ -3079,14 +3079,14 @@ func TestResolveAuthKey(t *testing.T) {
wantErrContains: "", wantErrContains: "",
}, },
{ {
name: "no authkey set and no resolution available", name: "no-authkey-no-resolution",
oauthAvailable: false, oauthAvailable: false,
wifAvailable: false, wifAvailable: false,
wantAuthKey: "", wantAuthKey: "",
wantErrContains: "", wantErrContains: "",
}, },
{ {
name: "authkey is client secret and resolution via OAuth client secret succeeds", name: "authkey-client-secret-oauth-succeeds",
authKey: "tskey-client-secret-123", authKey: "tskey-client-secret-123",
oauthAvailable: true, oauthAvailable: true,
resolveViaOAuth: func(ctx context.Context, clientSecret string, tags []string) (string, error) { resolveViaOAuth: func(ctx context.Context, clientSecret string, tags []string) (string, error) {
@ -3099,7 +3099,7 @@ func TestResolveAuthKey(t *testing.T) {
wantErrContains: "", wantErrContains: "",
}, },
{ {
name: "authkey is client secret but resolution via OAuth client secret fails", name: "authkey-client-secret-oauth-fails",
authKey: "tskey-client-secret-123", authKey: "tskey-client-secret-123",
oauthAvailable: true, oauthAvailable: true,
resolveViaOAuth: func(ctx context.Context, clientSecret string, tags []string) (string, error) { resolveViaOAuth: func(ctx context.Context, clientSecret string, tags []string) (string, error) {
@ -3282,12 +3282,12 @@ func TestListenUnspecifiedAddr(t *testing.T) {
t.Run("Netstack", func(t *testing.T) { t.Run("Netstack", func(t *testing.T) {
lt := setupTwoClientTest(t, false) lt := setupTwoClientTest(t, false)
t.Run("0.0.0.0", func(t *testing.T) { testUnspec(t, lt, "0.0.0.0:8080", "8080") }) t.Run("v4-unspec", func(t *testing.T) { testUnspec(t, lt, "0.0.0.0:8080", "8080") })
t.Run("::", func(t *testing.T) { testUnspec(t, lt, "[::]:8081", "8081") }) t.Run("::", func(t *testing.T) { testUnspec(t, lt, "[::]:8081", "8081") })
}) })
t.Run("TUN", func(t *testing.T) { t.Run("TUN", func(t *testing.T) {
lt := setupTwoClientTest(t, true) lt := setupTwoClientTest(t, true)
t.Run("0.0.0.0", func(t *testing.T) { testUnspec(t, lt, "0.0.0.0:8080", "8080") }) t.Run("v4-unspec", func(t *testing.T) { testUnspec(t, lt, "0.0.0.0:8080", "8080") })
t.Run("::", func(t *testing.T) { testUnspec(t, lt, "[::]:8081", "8081") }) t.Run("::", func(t *testing.T) { testUnspec(t, lt, "[::]:8081", "8081") })
}) })
} }

@ -22,7 +22,7 @@ func TestClockWithDefinedStartTime(t *testing.T) {
wants []time.Time // The return values of sequential calls to Now(). wants []time.Time // The return values of sequential calls to Now().
}{ }{
{ {
name: "increment ms", name: "increment-ms",
start: time.Unix(12345, 1000), start: time.Unix(12345, 1000),
step: 1000, step: 1000,
wants: []time.Time{ wants: []time.Time{
@ -33,7 +33,7 @@ func TestClockWithDefinedStartTime(t *testing.T) {
}, },
}, },
{ {
name: "increment second", name: "increment-second",
start: time.Unix(12345, 1000), start: time.Unix(12345, 1000),
step: time.Second, step: time.Second,
wants: []time.Time{ wants: []time.Time{
@ -44,7 +44,7 @@ func TestClockWithDefinedStartTime(t *testing.T) {
}, },
}, },
{ {
name: "no increment", name: "no-increment",
start: time.Unix(12345, 1000), start: time.Unix(12345, 1000),
wants: []time.Time{ wants: []time.Time{
time.Unix(12345, 1000), time.Unix(12345, 1000),
@ -91,7 +91,7 @@ func TestClockWithDefaultStartTime(t *testing.T) {
wants []time.Duration // The return values of sequential calls to Now() after added to Start() wants []time.Duration // The return values of sequential calls to Now() after added to Start()
}{ }{
{ {
name: "increment ms", name: "increment-ms",
step: 1000, step: 1000,
wants: []time.Duration{ wants: []time.Duration{
0, 0,
@ -101,7 +101,7 @@ func TestClockWithDefaultStartTime(t *testing.T) {
}, },
}, },
{ {
name: "increment second", name: "increment-second",
step: time.Second, step: time.Second,
wants: []time.Duration{ wants: []time.Duration{
0 * time.Second, 0 * time.Second,
@ -111,7 +111,7 @@ func TestClockWithDefaultStartTime(t *testing.T) {
}, },
}, },
{ {
name: "no increment", name: "no-increment",
wants: []time.Duration{0, 0, 0, 0}, wants: []time.Duration{0, 0, 0, 0},
}, },
} }
@ -177,7 +177,7 @@ func TestClockSetStep(t *testing.T) {
wants []time.Time // The return values of sequential calls to Now(). wants []time.Time // The return values of sequential calls to Now().
}{ }{
{ {
name: "increment ms then s", name: "increment-ms-then-s",
start: time.Unix(12345, 1000), start: time.Unix(12345, 1000),
step: 1000, step: 1000,
stepChanges: []stepInfo{ stepChanges: []stepInfo{
@ -198,7 +198,7 @@ func TestClockSetStep(t *testing.T) {
}, },
}, },
{ {
name: "multiple changes over time", name: "multiple-changes-over-time",
start: time.Unix(12345, 1000), start: time.Unix(12345, 1000),
step: 1, step: 1,
stepChanges: []stepInfo{ stepChanges: []stepInfo{
@ -227,7 +227,7 @@ func TestClockSetStep(t *testing.T) {
}, },
}, },
{ {
name: "multiple changes at once", name: "multiple-changes-at-once",
start: time.Unix(12345, 1000), start: time.Unix(12345, 1000),
step: 1, step: 1,
stepChanges: []stepInfo{ stepChanges: []stepInfo{
@ -252,7 +252,7 @@ func TestClockSetStep(t *testing.T) {
}, },
}, },
{ {
name: "changes at start", name: "changes-at-start",
start: time.Unix(12345, 1000), start: time.Unix(12345, 1000),
step: 0, step: 0,
stepChanges: []stepInfo{ stepChanges: []stepInfo{
@ -325,7 +325,7 @@ func TestClockAdvance(t *testing.T) {
wants []time.Time // The return values of sequential calls to Now(). wants []time.Time // The return values of sequential calls to Now().
}{ }{
{ {
name: "increment ms then advance 1s", name: "increment-ms-then-advance-1s",
start: time.Unix(12345, 1000), start: time.Unix(12345, 1000),
step: 1000, step: 1000,
advances: []advanceInfo{ advances: []advanceInfo{
@ -346,7 +346,7 @@ func TestClockAdvance(t *testing.T) {
}, },
}, },
{ {
name: "multiple advances over time", name: "multiple-advances-over-time",
start: time.Unix(12345, 1000), start: time.Unix(12345, 1000),
step: 1, step: 1,
advances: []advanceInfo{ advances: []advanceInfo{
@ -375,7 +375,7 @@ func TestClockAdvance(t *testing.T) {
}, },
}, },
{ {
name: "multiple advances at once", name: "multiple-advances-at-once",
start: time.Unix(12345, 1000), start: time.Unix(12345, 1000),
step: 1, step: 1,
advances: []advanceInfo{ advances: []advanceInfo{
@ -400,7 +400,7 @@ func TestClockAdvance(t *testing.T) {
}, },
}, },
{ {
name: "changes at start", name: "changes-at-start",
start: time.Unix(12345, 1000), start: time.Unix(12345, 1000),
step: 5, step: 5,
advances: []advanceInfo{ advances: []advanceInfo{
@ -489,7 +489,7 @@ func TestSingleTicker(t *testing.T) {
steps []testStep steps []testStep
}{ }{
{ {
name: "no tick advance", name: "no-tick-advance",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
period: time.Second, period: time.Second,
steps: []testStep{ steps: []testStep{
@ -500,7 +500,7 @@ func TestSingleTicker(t *testing.T) {
}, },
}, },
{ {
name: "no tick step", name: "no-tick-step",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
step: time.Second - 1, step: time.Second - 1,
period: time.Second, period: time.Second,
@ -514,7 +514,7 @@ func TestSingleTicker(t *testing.T) {
}, },
}, },
{ {
name: "single tick advance exact", name: "single-tick-advance-exact",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
period: time.Second, period: time.Second,
steps: []testStep{ steps: []testStep{
@ -526,7 +526,7 @@ func TestSingleTicker(t *testing.T) {
}, },
}, },
{ {
name: "single tick advance extra", name: "single-tick-advance-extra",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
period: time.Second, period: time.Second,
steps: []testStep{ steps: []testStep{
@ -538,7 +538,7 @@ func TestSingleTicker(t *testing.T) {
}, },
}, },
{ {
name: "single tick step exact", name: "single-tick-step-exact",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
step: time.Second, step: time.Second,
period: time.Second, period: time.Second,
@ -553,7 +553,7 @@ func TestSingleTicker(t *testing.T) {
}, },
}, },
{ {
name: "single tick step extra", name: "single-tick-step-extra",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
step: time.Second + 1, step: time.Second + 1,
period: time.Second, period: time.Second,
@ -568,7 +568,7 @@ func TestSingleTicker(t *testing.T) {
}, },
}, },
{ {
name: "single tick per advance", name: "single-tick-per-advance",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
period: 3 * time.Second, period: 3 * time.Second,
steps: []testStep{ steps: []testStep{
@ -597,7 +597,7 @@ func TestSingleTicker(t *testing.T) {
}, },
}, },
{ {
name: "single tick per step", name: "single-tick-per-step",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
step: 2 * time.Second, step: 2 * time.Second,
period: 3 * time.Second, period: 3 * time.Second,
@ -626,7 +626,7 @@ func TestSingleTicker(t *testing.T) {
}, },
}, },
{ {
name: "multiple tick per advance", name: "multiple-tick-per-advance",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
period: time.Second, period: time.Second,
channelSize: 3, channelSize: 3,
@ -655,7 +655,7 @@ func TestSingleTicker(t *testing.T) {
}, },
}, },
{ {
name: "multiple tick per step", name: "multiple-tick-per-step",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
step: 3 * time.Second, step: 3 * time.Second,
period: 2 * time.Second, period: 2 * time.Second,
@ -723,7 +723,7 @@ func TestSingleTicker(t *testing.T) {
}, },
}, },
{ {
name: "reset while running", name: "reset-while-running",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
period: 2 * time.Second, period: 2 * time.Second,
steps: []testStep{ steps: []testStep{
@ -763,7 +763,7 @@ func TestSingleTicker(t *testing.T) {
}, },
}, },
{ {
name: "reset while stopped", name: "reset-while-stopped",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
step: time.Second, step: time.Second,
period: 2 * time.Second, period: 2 * time.Second,
@ -803,7 +803,7 @@ func TestSingleTicker(t *testing.T) {
}, },
}, },
{ {
name: "reset absolute", name: "reset-absolute",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
step: time.Second, step: time.Second,
period: 2 * time.Second, period: 2 * time.Second,
@ -841,7 +841,7 @@ func TestSingleTicker(t *testing.T) {
}, },
}, },
{ {
name: "follow real time", name: "follow-real-time",
realTimeOpts: new(ClockOpts), realTimeOpts: new(ClockOpts),
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
period: 2 * time.Second, period: 2 * time.Second,
@ -965,7 +965,7 @@ func TestSingleTimer(t *testing.T) {
steps []testStep steps []testStep
}{ }{
{ {
name: "no tick advance", name: "no-tick-advance",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
delay: time.Second, delay: time.Second,
steps: []testStep{ steps: []testStep{
@ -976,7 +976,7 @@ func TestSingleTimer(t *testing.T) {
}, },
}, },
{ {
name: "no tick step", name: "no-tick-step",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
step: time.Second - 1, step: time.Second - 1,
delay: time.Second, delay: time.Second,
@ -990,7 +990,7 @@ func TestSingleTimer(t *testing.T) {
}, },
}, },
{ {
name: "single tick advance exact", name: "single-tick-advance-exact",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
delay: time.Second, delay: time.Second,
steps: []testStep{ steps: []testStep{
@ -1006,7 +1006,7 @@ func TestSingleTimer(t *testing.T) {
}, },
}, },
{ {
name: "single tick advance extra", name: "single-tick-advance-extra",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
delay: time.Second, delay: time.Second,
steps: []testStep{ steps: []testStep{
@ -1022,7 +1022,7 @@ func TestSingleTimer(t *testing.T) {
}, },
}, },
{ {
name: "single tick step exact", name: "single-tick-step-exact",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
step: time.Second, step: time.Second,
delay: time.Second, delay: time.Second,
@ -1040,7 +1040,7 @@ func TestSingleTimer(t *testing.T) {
}, },
}, },
{ {
name: "single tick step extra", name: "single-tick-step-extra",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
step: time.Second + 1, step: time.Second + 1,
delay: time.Second, delay: time.Second,
@ -1058,7 +1058,7 @@ func TestSingleTimer(t *testing.T) {
}, },
}, },
{ {
name: "reset for single tick per advance", name: "reset-for-single-tick-per-advance",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
delay: 3 * time.Second, delay: 3 * time.Second,
steps: []testStep{ steps: []testStep{
@ -1093,7 +1093,7 @@ func TestSingleTimer(t *testing.T) {
}, },
}, },
{ {
name: "reset for single tick per step", name: "reset-for-single-tick-per-step",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
step: 2 * time.Second, step: 2 * time.Second,
delay: 3 * time.Second, delay: 3 * time.Second,
@ -1124,7 +1124,7 @@ func TestSingleTimer(t *testing.T) {
}, },
}, },
{ {
name: "reset while active", name: "reset-while-active",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
step: 2 * time.Second, step: 2 * time.Second,
delay: 3 * time.Second, delay: 3 * time.Second,
@ -1155,7 +1155,7 @@ func TestSingleTimer(t *testing.T) {
}, },
}, },
{ {
name: "stop after fire", name: "stop-after-fire",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
step: 2 * time.Second, step: 2 * time.Second,
delay: time.Second, delay: time.Second,
@ -1181,7 +1181,7 @@ func TestSingleTimer(t *testing.T) {
}, },
}, },
{ {
name: "stop before fire", name: "stop-before-fire",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
step: 2 * time.Second, step: 2 * time.Second,
delay: time.Second, delay: time.Second,
@ -1207,7 +1207,7 @@ func TestSingleTimer(t *testing.T) {
}, },
}, },
{ {
name: "stop after reset", name: "stop-after-reset",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
step: 2 * time.Second, step: 2 * time.Second,
delay: time.Second, delay: time.Second,
@ -1235,7 +1235,7 @@ func TestSingleTimer(t *testing.T) {
}, },
}, },
{ {
name: "reset while running", name: "reset-while-running",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
delay: 2 * time.Second, delay: 2 * time.Second,
steps: []testStep{ steps: []testStep{
@ -1275,7 +1275,7 @@ func TestSingleTimer(t *testing.T) {
}, },
}, },
{ {
name: "reset while stopped", name: "reset-while-stopped",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
step: time.Second, step: time.Second,
delay: 2 * time.Second, delay: 2 * time.Second,
@ -1310,7 +1310,7 @@ func TestSingleTimer(t *testing.T) {
}, },
}, },
{ {
name: "reset absolute", name: "reset-absolute",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
step: time.Second, step: time.Second,
delay: 2 * time.Second, delay: 2 * time.Second,
@ -1344,7 +1344,7 @@ func TestSingleTimer(t *testing.T) {
}, },
}, },
{ {
name: "follow real time", name: "follow-real-time",
realTimeOpts: new(ClockOpts), realTimeOpts: new(ClockOpts),
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
delay: 2 * time.Second, delay: 2 * time.Second,
@ -1705,7 +1705,7 @@ func TestClockFollowRealTime(t *testing.T) {
wants []time.Time // The return values of sequential calls to Now(). wants []time.Time // The return values of sequential calls to Now().
}{ }{
{ {
name: "increment ms then advance 1s", name: "increment-ms-then-advance-1s",
start: time.Unix(12345, 1000), start: time.Unix(12345, 1000),
wantStart: time.Unix(12345, 1000), wantStart: time.Unix(12345, 1000),
advances: []advanceInfo{ advances: []advanceInfo{
@ -1750,7 +1750,7 @@ func TestClockFollowRealTime(t *testing.T) {
}, },
}, },
{ {
name: "multiple advances over time", name: "multiple-advances-over-time",
start: time.Unix(12345, 1000), start: time.Unix(12345, 1000),
wantStart: time.Unix(12345, 1000), wantStart: time.Unix(12345, 1000),
advances: []advanceInfo{ advances: []advanceInfo{
@ -1795,7 +1795,7 @@ func TestClockFollowRealTime(t *testing.T) {
}, },
}, },
{ {
name: "multiple advances at once", name: "multiple-advances-at-once",
start: time.Unix(12345, 1000), start: time.Unix(12345, 1000),
wantStart: time.Unix(12345, 1000), wantStart: time.Unix(12345, 1000),
advances: []advanceInfo{ advances: []advanceInfo{
@ -1828,7 +1828,7 @@ func TestClockFollowRealTime(t *testing.T) {
}, },
}, },
{ {
name: "changes at start", name: "changes-at-start",
start: time.Unix(12345, 1000), start: time.Unix(12345, 1000),
wantStart: time.Unix(12345, 1000), wantStart: time.Unix(12345, 1000),
advances: []advanceInfo{ advances: []advanceInfo{
@ -1861,7 +1861,7 @@ func TestClockFollowRealTime(t *testing.T) {
}, },
}, },
{ {
name: "start from current time", name: "start-from-current-time",
realTimeClockOpts: ClockOpts{ realTimeClockOpts: ClockOpts{
Start: time.Unix(12345, 0), Start: time.Unix(12345, 0),
}, },
@ -1966,7 +1966,7 @@ func TestAfterFunc(t *testing.T) {
steps []testStep steps []testStep
}{ }{
{ {
name: "no tick advance", name: "no-tick-advance",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
delay: time.Second, delay: time.Second,
steps: []testStep{ steps: []testStep{
@ -1977,7 +1977,7 @@ func TestAfterFunc(t *testing.T) {
}, },
}, },
{ {
name: "no tick step", name: "no-tick-step",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
step: time.Second - 1, step: time.Second - 1,
delay: time.Second, delay: time.Second,
@ -1991,7 +1991,7 @@ func TestAfterFunc(t *testing.T) {
}, },
}, },
{ {
name: "single tick advance exact", name: "single-tick-advance-exact",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
delay: time.Second, delay: time.Second,
steps: []testStep{ steps: []testStep{
@ -2007,7 +2007,7 @@ func TestAfterFunc(t *testing.T) {
}, },
}, },
{ {
name: "single tick advance extra", name: "single-tick-advance-extra",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
delay: time.Second, delay: time.Second,
steps: []testStep{ steps: []testStep{
@ -2023,7 +2023,7 @@ func TestAfterFunc(t *testing.T) {
}, },
}, },
{ {
name: "single tick step exact", name: "single-tick-step-exact",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
step: time.Second, step: time.Second,
delay: time.Second, delay: time.Second,
@ -2041,7 +2041,7 @@ func TestAfterFunc(t *testing.T) {
}, },
}, },
{ {
name: "single tick step extra", name: "single-tick-step-extra",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
step: time.Second + 1, step: time.Second + 1,
delay: time.Second, delay: time.Second,
@ -2059,7 +2059,7 @@ func TestAfterFunc(t *testing.T) {
}, },
}, },
{ {
name: "reset for single tick per advance", name: "reset-for-single-tick-per-advance",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
delay: 3 * time.Second, delay: 3 * time.Second,
steps: []testStep{ steps: []testStep{
@ -2094,7 +2094,7 @@ func TestAfterFunc(t *testing.T) {
}, },
}, },
{ {
name: "reset for single tick per step", name: "reset-for-single-tick-per-step",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
step: 2 * time.Second, step: 2 * time.Second,
delay: 3 * time.Second, delay: 3 * time.Second,
@ -2125,7 +2125,7 @@ func TestAfterFunc(t *testing.T) {
}, },
}, },
{ {
name: "reset while active", name: "reset-while-active",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
step: 2 * time.Second, step: 2 * time.Second,
delay: 3 * time.Second, delay: 3 * time.Second,
@ -2156,7 +2156,7 @@ func TestAfterFunc(t *testing.T) {
}, },
}, },
{ {
name: "stop after fire", name: "stop-after-fire",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
step: 2 * time.Second, step: 2 * time.Second,
delay: time.Second, delay: time.Second,
@ -2182,7 +2182,7 @@ func TestAfterFunc(t *testing.T) {
}, },
}, },
{ {
name: "stop before fire", name: "stop-before-fire",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
step: 2 * time.Second, step: 2 * time.Second,
delay: time.Second, delay: time.Second,
@ -2208,7 +2208,7 @@ func TestAfterFunc(t *testing.T) {
}, },
}, },
{ {
name: "stop after reset", name: "stop-after-reset",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
step: 2 * time.Second, step: 2 * time.Second,
delay: time.Second, delay: time.Second,
@ -2236,7 +2236,7 @@ func TestAfterFunc(t *testing.T) {
}, },
}, },
{ {
name: "reset while running", name: "reset-while-running",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
delay: 2 * time.Second, delay: 2 * time.Second,
steps: []testStep{ steps: []testStep{
@ -2270,7 +2270,7 @@ func TestAfterFunc(t *testing.T) {
}, },
}, },
{ {
name: "reset while stopped", name: "reset-while-stopped",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
step: time.Second, step: time.Second,
delay: 2 * time.Second, delay: 2 * time.Second,
@ -2303,7 +2303,7 @@ func TestAfterFunc(t *testing.T) {
}, },
}, },
{ {
name: "reset absolute", name: "reset-absolute",
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
step: time.Second, step: time.Second,
delay: 2 * time.Second, delay: 2 * time.Second,
@ -2333,7 +2333,7 @@ func TestAfterFunc(t *testing.T) {
}, },
}, },
{ {
name: "follow real time", name: "follow-real-time",
realTimeOpts: new(ClockOpts), realTimeOpts: new(ClockOpts),
start: time.Unix(12345, 0), start: time.Unix(12345, 0),
delay: 2 * time.Second, delay: 2 * time.Second,

@ -355,7 +355,7 @@ func (h *Harness) testDistro(t *testing.T, d Distro, ipm ipMapping) {
}) })
}) })
t.Run("tailscale status", func(t *testing.T) { t.Run("tailscale-status", func(t *testing.T) {
dur := 100 * time.Millisecond dur := 100 * time.Millisecond
var outp []byte var outp []byte
var err error var err error
@ -383,7 +383,7 @@ func (h *Harness) testDistro(t *testing.T, d Distro, ipm ipMapping) {
t.Fatalf("error: %v", err) t.Fatalf("error: %v", err)
}) })
t.Run("dump routes", func(t *testing.T) { t.Run("dump-routes", func(t *testing.T) {
sess, err := cli.NewSession() sess, err := cli.NewSession()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)

@ -22,7 +22,7 @@ func TestPrintGoroutines(t *testing.T) {
want: "goroutine profile: total 0", want: "goroutine profile: total 0",
}, },
{ {
name: "single goroutine", name: "single-goroutine",
in: `goroutine profile: total 1 in: `goroutine profile: total 1
1 @ 0x47bc0e 0x458e57 0x847587 0x483da1 1 @ 0x47bc0e 0x458e57 0x847587 0x483da1
# 0x847586 database/sql.(*DB).connectionOpener+0x86 database/sql/sql.go:1261 # 0x847586 database/sql.(*DB).connectionOpener+0x86 database/sql/sql.go:1261
@ -34,7 +34,7 @@ func TestPrintGoroutines(t *testing.T) {
`, `,
}, },
{ {
name: "multiple goroutines sorted", name: "multiple-goroutines-sorted",
in: `goroutine profile: total 14 in: `goroutine profile: total 14
7 @ 0x47bc0e 0x413705 0x4132b2 0x10fda4d 0x483da1 7 @ 0x47bc0e 0x413705 0x4132b2 0x10fda4d 0x483da1
# 0x10fda4c github.com/user/pkg.RoutineA+0x16c pkg/a.go:443 # 0x10fda4c github.com/user/pkg.RoutineA+0x16c pkg/a.go:443
@ -70,7 +70,7 @@ func TestDiffPprofGoroutines(t *testing.T) {
want string want string
}{ }{
{ {
name: "no difference", name: "no-difference",
x: `goroutine profile: total 1 x: `goroutine profile: total 1
1 @ 0x47bc0e 0x458e57 0x847587 0x483da1 1 @ 0x47bc0e 0x458e57 0x847587 0x483da1
# 0x847586 database/sql.(*DB).connectionOpener+0x86 database/sql/sql.go:1261`, # 0x847586 database/sql.(*DB).connectionOpener+0x86 database/sql/sql.go:1261`,
@ -81,7 +81,7 @@ func TestDiffPprofGoroutines(t *testing.T) {
want: "", want: "",
}, },
{ {
name: "different counts", name: "different-counts",
x: `goroutine profile: total 1 x: `goroutine profile: total 1
1 @ 0x47bc0e 0x458e57 0x847587 0x483da1 1 @ 0x47bc0e 0x458e57 0x847587 0x483da1
# 0x847586 database/sql.(*DB).connectionOpener+0x86 database/sql/sql.go:1261 # 0x847586 database/sql.(*DB).connectionOpener+0x86 database/sql/sql.go:1261
@ -99,7 +99,7 @@ func TestDiffPprofGoroutines(t *testing.T) {
`, `,
}, },
{ {
name: "new goroutine", name: "new-goroutine",
x: `goroutine profile: total 1 x: `goroutine profile: total 1
1 @ 0x47bc0e 0x458e57 0x847587 0x483da1 1 @ 0x47bc0e 0x458e57 0x847587 0x483da1
# 0x847586 database/sql.(*DB).connectionOpener+0x86 database/sql/sql.go:1261 # 0x847586 database/sql.(*DB).connectionOpener+0x86 database/sql/sql.go:1261
@ -119,7 +119,7 @@ func TestDiffPprofGoroutines(t *testing.T) {
`, `,
}, },
{ {
name: "removed goroutine", name: "removed-goroutine",
x: `goroutine profile: total 2 x: `goroutine profile: total 2
1 @ 0x47bc0e 0x458e57 0x847587 0x483da1 1 @ 0x47bc0e 0x458e57 0x847587 0x483da1
# 0x847586 database/sql.(*DB).connectionOpener+0x86 database/sql/sql.go:1261 # 0x847586 database/sql.(*DB).connectionOpener+0x86 database/sql/sql.go:1261
@ -139,7 +139,7 @@ func TestDiffPprofGoroutines(t *testing.T) {
`, `,
}, },
{ {
name: "removed many goroutine", name: "removed-many-goroutine",
x: `goroutine profile: total 2 x: `goroutine profile: total 2
1 @ 0x47bc0e 0x458e57 0x847587 0x483da1 1 @ 0x47bc0e 0x458e57 0x847587 0x483da1
# 0x847586 database/sql.(*DB).connectionOpener+0x86 database/sql/sql.go:1261 # 0x847586 database/sql.(*DB).connectionOpener+0x86 database/sql/sql.go:1261
@ -159,13 +159,13 @@ func TestDiffPprofGoroutines(t *testing.T) {
`, `,
}, },
{ {
name: "invalid input x", name: "invalid-input-x",
x: "invalid", x: "invalid",
y: "goroutine profile: total 0\n", y: "goroutine profile: total 0\n",
want: "- invalid\n+ goroutine profile: total 0\n", want: "- invalid\n+ goroutine profile: total 0\n",
}, },
{ {
name: "invalid input y", name: "invalid-input-y",
x: "goroutine profile: total 0\n", x: "goroutine profile: total 0\n",
y: "invalid", y: "invalid",
want: "- goroutine profile: total 0\n+ invalid\n", want: "- goroutine profile: total 0\n+ invalid\n",
@ -193,13 +193,13 @@ func TestParseGoroutines(t *testing.T) {
wantCount int wantCount int
}{ }{
{ {
name: "empty profile", name: "empty-profile",
in: "goroutine profile: total 0\n", in: "goroutine profile: total 0\n",
wantHeader: "goroutine profile: total 0", wantHeader: "goroutine profile: total 0",
wantCount: 0, wantCount: 0,
}, },
{ {
name: "single goroutine", name: "single-goroutine",
in: `goroutine profile: total 1 in: `goroutine profile: total 1
1 @ 0x47bc0e 0x458e57 0x847587 0x483da1 1 @ 0x47bc0e 0x458e57 0x847587 0x483da1
# 0x847586 database/sql.(*DB).connectionOpener+0x86 database/sql/sql.go:1261 # 0x847586 database/sql.(*DB).connectionOpener+0x86 database/sql/sql.go:1261
@ -208,7 +208,7 @@ func TestParseGoroutines(t *testing.T) {
wantCount: 1, wantCount: 1,
}, },
{ {
name: "multiple goroutines", name: "multiple-goroutines",
in: `goroutine profile: total 14 in: `goroutine profile: total 14
7 @ 0x47bc0e 0x413705 0x4132b2 0x10fda4d 0x483da1 7 @ 0x47bc0e 0x413705 0x4132b2 0x10fda4d 0x483da1
# 0x10fda4c github.com/user/pkg.RoutineA+0x16c pkg/a.go:443 # 0x10fda4c github.com/user/pkg.RoutineA+0x16c pkg/a.go:443
@ -220,7 +220,7 @@ func TestParseGoroutines(t *testing.T) {
wantCount: 2, wantCount: 2,
}, },
{ {
name: "invalid format", name: "invalid-format",
in: "invalid", in: "invalid",
wantHeader: "invalid", wantHeader: "invalid",
}, },

@ -85,7 +85,7 @@ func TestStdHandler(t *testing.T) {
wantBody string wantBody string
}{ }{
{ {
name: "handler returns 200", name: "handler-returns-200",
rh: handlerCode(200), rh: handlerCode(200),
r: req(bgCtx, "http://example.com/"), r: req(bgCtx, "http://example.com/"),
wantCode: 200, wantCode: 200,
@ -102,7 +102,7 @@ func TestStdHandler(t *testing.T) {
}, },
{ {
name: "handler returns 200 with request ID", name: "handler-returns-200-with-request-ID",
rh: handlerCode(200), rh: handlerCode(200),
r: req(bgCtx, "http://example.com/"), r: req(bgCtx, "http://example.com/"),
wantCode: 200, wantCode: 200,
@ -119,7 +119,7 @@ func TestStdHandler(t *testing.T) {
}, },
{ {
name: "handler returns 404", name: "handler-returns-404",
rh: handlerCode(404), rh: handlerCode(404),
r: req(bgCtx, "http://example.com/foo"), r: req(bgCtx, "http://example.com/foo"),
wantCode: 404, wantCode: 404,
@ -135,7 +135,7 @@ func TestStdHandler(t *testing.T) {
}, },
{ {
name: "handler returns 404 with request ID", name: "handler-returns-404-with-request-ID",
rh: handlerCode(404), rh: handlerCode(404),
r: req(bgCtx, "http://example.com/foo"), r: req(bgCtx, "http://example.com/foo"),
wantCode: 404, wantCode: 404,
@ -151,7 +151,7 @@ func TestStdHandler(t *testing.T) {
}, },
{ {
name: "handler returns 404 via HTTPError", name: "handler-returns-404-via-HTTPError",
rh: handlerErr(0, Error(404, "not found", testErr)), rh: handlerErr(0, Error(404, "not found", testErr)),
r: req(bgCtx, "http://example.com/foo"), r: req(bgCtx, "http://example.com/foo"),
wantCode: 404, wantCode: 404,
@ -169,7 +169,7 @@ func TestStdHandler(t *testing.T) {
}, },
{ {
name: "handler returns 404 via HTTPError with request ID", name: "handler-returns-404-via-HTTPError-with-request-ID",
rh: handlerErr(0, Error(404, "not found", testErr)), rh: handlerErr(0, Error(404, "not found", testErr)),
r: req(RequestIDKey.WithValue(bgCtx, exampleRequestID), "http://example.com/foo"), r: req(RequestIDKey.WithValue(bgCtx, exampleRequestID), "http://example.com/foo"),
wantCode: 404, wantCode: 404,
@ -188,7 +188,7 @@ func TestStdHandler(t *testing.T) {
}, },
{ {
name: "handler returns 404 with nil child error", name: "handler-returns-404-nil-child-error",
rh: handlerErr(0, Error(404, "not found", nil)), rh: handlerErr(0, Error(404, "not found", nil)),
r: req(bgCtx, "http://example.com/foo"), r: req(bgCtx, "http://example.com/foo"),
wantCode: 404, wantCode: 404,
@ -206,7 +206,7 @@ func TestStdHandler(t *testing.T) {
}, },
{ {
name: "handler returns 404 with request ID and nil child error", name: "handler-returns-404-request-ID-nil-child-error",
rh: handlerErr(0, Error(404, "not found", nil)), rh: handlerErr(0, Error(404, "not found", nil)),
r: req(RequestIDKey.WithValue(bgCtx, exampleRequestID), "http://example.com/foo"), r: req(RequestIDKey.WithValue(bgCtx, exampleRequestID), "http://example.com/foo"),
wantCode: 404, wantCode: 404,
@ -225,7 +225,7 @@ func TestStdHandler(t *testing.T) {
}, },
{ {
name: "handler returns user-visible error", name: "handler-returns-user-visible-error",
rh: handlerErr(0, vizerror.New("visible error")), rh: handlerErr(0, vizerror.New("visible error")),
r: req(bgCtx, "http://example.com/foo"), r: req(bgCtx, "http://example.com/foo"),
wantCode: 500, wantCode: 500,
@ -243,7 +243,7 @@ func TestStdHandler(t *testing.T) {
}, },
{ {
name: "handler returns user-visible error with request ID", name: "handler-returns-user-visible-error-with-request-ID",
rh: handlerErr(0, vizerror.New("visible error")), rh: handlerErr(0, vizerror.New("visible error")),
r: req(RequestIDKey.WithValue(bgCtx, exampleRequestID), "http://example.com/foo"), r: req(RequestIDKey.WithValue(bgCtx, exampleRequestID), "http://example.com/foo"),
wantCode: 500, wantCode: 500,
@ -262,7 +262,7 @@ func TestStdHandler(t *testing.T) {
}, },
{ {
name: "handler returns user-visible error wrapped by private error", name: "handler-returns-vizerror-wrapped-by-private-error",
rh: handlerErr(0, fmt.Errorf("private internal error: %w", vizerror.New("visible error"))), rh: handlerErr(0, fmt.Errorf("private internal error: %w", vizerror.New("visible error"))),
r: req(bgCtx, "http://example.com/foo"), r: req(bgCtx, "http://example.com/foo"),
wantCode: 500, wantCode: 500,
@ -280,7 +280,7 @@ func TestStdHandler(t *testing.T) {
}, },
{ {
name: "handler returns JSON-formatted HTTPError", name: "handler-returns-JSON-formatted-HTTPError",
rh: ReturnHandlerFunc(func(w http.ResponseWriter, r *http.Request) error { rh: ReturnHandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
h := Error(http.StatusBadRequest, `{"isjson": true}`, errors.New("uh")) h := Error(http.StatusBadRequest, `{"isjson": true}`, errors.New("uh"))
h.Header = http.Header{"Content-Type": {"application/json"}} h.Header = http.Header{"Content-Type": {"application/json"}}
@ -303,7 +303,7 @@ func TestStdHandler(t *testing.T) {
}, },
{ {
name: "handler returns user-visible error wrapped by private error with request ID", name: "handler-returns-vizerror-wrapped-by-private-error-with-request-ID",
rh: handlerErr(0, fmt.Errorf("private internal error: %w", vizerror.New("visible error"))), rh: handlerErr(0, fmt.Errorf("private internal error: %w", vizerror.New("visible error"))),
r: req(RequestIDKey.WithValue(bgCtx, exampleRequestID), "http://example.com/foo"), r: req(RequestIDKey.WithValue(bgCtx, exampleRequestID), "http://example.com/foo"),
wantCode: 500, wantCode: 500,
@ -322,7 +322,7 @@ func TestStdHandler(t *testing.T) {
}, },
{ {
name: "handler returns generic error", name: "handler-returns-generic-error",
rh: handlerErr(0, testErr), rh: handlerErr(0, testErr),
r: req(bgCtx, "http://example.com/foo"), r: req(bgCtx, "http://example.com/foo"),
wantCode: 500, wantCode: 500,
@ -340,7 +340,7 @@ func TestStdHandler(t *testing.T) {
}, },
{ {
name: "handler returns generic error with request ID", name: "handler-returns-generic-error-with-request-ID",
rh: handlerErr(0, testErr), rh: handlerErr(0, testErr),
r: req(RequestIDKey.WithValue(bgCtx, exampleRequestID), "http://example.com/foo"), r: req(RequestIDKey.WithValue(bgCtx, exampleRequestID), "http://example.com/foo"),
wantCode: 500, wantCode: 500,
@ -359,7 +359,7 @@ func TestStdHandler(t *testing.T) {
}, },
{ {
name: "handler returns error after writing response", name: "handler-returns-error-after-writing-response",
rh: handlerErr(200, testErr), rh: handlerErr(200, testErr),
r: req(bgCtx, "http://example.com/foo"), r: req(bgCtx, "http://example.com/foo"),
wantCode: 200, wantCode: 200,
@ -376,7 +376,7 @@ func TestStdHandler(t *testing.T) {
}, },
{ {
name: "handler returns error after writing response with request ID", name: "handler-returns-error-after-writing-response-with-request-ID",
rh: handlerErr(200, testErr), rh: handlerErr(200, testErr),
r: req(RequestIDKey.WithValue(bgCtx, exampleRequestID), "http://example.com/foo"), r: req(RequestIDKey.WithValue(bgCtx, exampleRequestID), "http://example.com/foo"),
wantCode: 200, wantCode: 200,
@ -394,7 +394,7 @@ func TestStdHandler(t *testing.T) {
}, },
{ {
name: "handler returns HTTPError after writing response", name: "handler-returns-HTTPError-after-writing-response",
rh: handlerErr(200, Error(404, "not found", testErr)), rh: handlerErr(200, Error(404, "not found", testErr)),
r: req(bgCtx, "http://example.com/foo"), r: req(bgCtx, "http://example.com/foo"),
wantCode: 200, wantCode: 200,
@ -411,7 +411,7 @@ func TestStdHandler(t *testing.T) {
}, },
{ {
name: "handler does nothing", name: "handler-does-nothing",
rh: handlerFunc(func(http.ResponseWriter, *http.Request) error { return nil }), rh: handlerFunc(func(http.ResponseWriter, *http.Request) error { return nil }),
r: req(bgCtx, "http://example.com/foo"), r: req(bgCtx, "http://example.com/foo"),
wantCode: 200, wantCode: 200,
@ -427,7 +427,7 @@ func TestStdHandler(t *testing.T) {
}, },
{ {
name: "handler hijacks conn", name: "handler-hijacks-conn",
rh: handlerFunc(func(w http.ResponseWriter, r *http.Request) error { rh: handlerFunc(func(w http.ResponseWriter, r *http.Request) error {
_, _, err := w.(http.Hijacker).Hijack() _, _, err := w.(http.Hijacker).Hijack()
if err != nil { if err != nil {
@ -450,7 +450,7 @@ func TestStdHandler(t *testing.T) {
}, },
{ {
name: "error handler gets run", name: "error-handler-gets-run",
rh: handlerErr(0, Error(404, "not found", nil)), // status code changed in errHandler rh: handlerErr(0, Error(404, "not found", nil)), // status code changed in errHandler
r: req(bgCtx, "http://example.com/"), r: req(bgCtx, "http://example.com/"),
wantCode: 200, wantCode: 200,
@ -472,7 +472,7 @@ func TestStdHandler(t *testing.T) {
}, },
{ {
name: "error handler gets run with request ID", name: "error-handler-gets-run-with-request-ID",
rh: handlerErr(0, Error(404, "not found", nil)), // status code changed in errHandler rh: handlerErr(0, Error(404, "not found", nil)), // status code changed in errHandler
r: req(RequestIDKey.WithValue(bgCtx, exampleRequestID), "http://example.com/"), r: req(RequestIDKey.WithValue(bgCtx, exampleRequestID), "http://example.com/"),
wantCode: 200, wantCode: 200,

@ -33,13 +33,13 @@ func TestResolverEqual(t *testing.T) {
want: true, want: true,
}, },
{ {
name: "nil vs non-nil", name: "nil-vs-non-nil",
a: nil, a: nil,
b: &Resolver{}, b: &Resolver{},
want: false, want: false,
}, },
{ {
name: "non-nil vs nil", name: "non-nil-vs-nil",
a: &Resolver{}, a: &Resolver{},
b: nil, b: nil,
want: false, want: false,
@ -51,13 +51,13 @@ func TestResolverEqual(t *testing.T) {
want: true, want: true,
}, },
{ {
name: "not equal addrs", name: "not-equal-addrs",
a: &Resolver{Addr: "dns.example.com"}, a: &Resolver{Addr: "dns.example.com"},
b: &Resolver{Addr: "dns2.example.com"}, b: &Resolver{Addr: "dns2.example.com"},
want: false, want: false,
}, },
{ {
name: "not equal bootstrap", name: "not-equal-bootstrap",
a: &Resolver{ a: &Resolver{
Addr: "dns.example.com", Addr: "dns.example.com",
BootstrapResolution: []netip.Addr{netip.MustParseAddr("8.8.8.8")}, BootstrapResolution: []netip.Addr{netip.MustParseAddr("8.8.8.8")},
@ -69,13 +69,13 @@ func TestResolverEqual(t *testing.T) {
want: false, want: false,
}, },
{ {
name: "equal UseWithExitNode", name: "equal-UseWithExitNode",
a: &Resolver{Addr: "dns.example.com", UseWithExitNode: true}, a: &Resolver{Addr: "dns.example.com", UseWithExitNode: true},
b: &Resolver{Addr: "dns.example.com", UseWithExitNode: true}, b: &Resolver{Addr: "dns.example.com", UseWithExitNode: true},
want: true, want: true,
}, },
{ {
name: "not equal UseWithExitNode", name: "not-equal-UseWithExitNode",
a: &Resolver{Addr: "dns.example.com", UseWithExitNode: true}, a: &Resolver{Addr: "dns.example.com", UseWithExitNode: true},
b: &Resolver{Addr: "dns.example.com", UseWithExitNode: false}, b: &Resolver{Addr: "dns.example.com", UseWithExitNode: false},
want: false, want: false,

@ -16,77 +16,77 @@ func TestCompare(t *testing.T) {
want int want int
}{ }{
{ {
name: "both empty", name: "both-empty",
want: 0, want: 0,
}, },
{ {
name: "v1 empty", name: "v1-empty",
v2: "1.2.3", v2: "1.2.3",
want: -1, want: -1,
}, },
{ {
name: "v2 empty", name: "v2-empty",
v1: "1.2.3", v1: "1.2.3",
want: 1, want: 1,
}, },
{ {
name: "semver major", name: "semver-major",
v1: "2.0.0", v1: "2.0.0",
v2: "1.9.9", v2: "1.9.9",
want: 1, want: 1,
}, },
{ {
name: "semver major", name: "semver-major",
v1: "2.0.0", v1: "2.0.0",
v2: "1.9.9", v2: "1.9.9",
want: 1, want: 1,
}, },
{ {
name: "semver minor", name: "semver-minor",
v1: "1.9.0", v1: "1.9.0",
v2: "1.8.9", v2: "1.8.9",
want: 1, want: 1,
}, },
{ {
name: "semver patch", name: "semver-patch",
v1: "1.9.9", v1: "1.9.9",
v2: "1.9.8", v2: "1.9.8",
want: 1, want: 1,
}, },
{ {
name: "semver equal", name: "semver-equal",
v1: "1.9.8", v1: "1.9.8",
v2: "1.9.8", v2: "1.9.8",
want: 0, want: 0,
}, },
{ {
name: "tailscale major", name: "tailscale-major",
v1: "1.0-0", v1: "1.0-0",
v2: "0.97-105", v2: "0.97-105",
want: 1, want: 1,
}, },
{ {
name: "tailscale minor", name: "tailscale-minor",
v1: "0.98-0", v1: "0.98-0",
v2: "0.97-105", v2: "0.97-105",
want: 1, want: 1,
}, },
{ {
name: "tailscale patch", name: "tailscale-patch",
v1: "0.97-120", v1: "0.97-120",
v2: "0.97-105", v2: "0.97-105",
want: 1, want: 1,
}, },
{ {
name: "tailscale equal", name: "tailscale-equal",
v1: "0.97-105", v1: "0.97-105",
v2: "0.97-105", v2: "0.97-105",
want: 0, want: 0,
}, },
{ {
name: "tailscale weird extra field", name: "tailscale-weird-extra-field",
v1: "0.96.1-0", // more fields == larger v1: "0.96.1-0", // more fields == larger
v2: "0.96-105", v2: "0.96-105",
want: 1, want: 1,
@ -96,7 +96,7 @@ func TestCompare(t *testing.T) {
// of strconv.ParseUint with these characters would have lead us to // of strconv.ParseUint with these characters would have lead us to
// panic. We're now only looking at ascii numbers, so test these are // panic. We're now only looking at ascii numbers, so test these are
// compared as text. // compared as text.
name: "only ascii numbers", name: "only-ascii-numbers",
v1: "۱۱", // 2x EXTENDED ARABIC-INDIC DIGIT ONE v1: "۱۱", // 2x EXTENDED ARABIC-INDIC DIGIT ONE
v2: "۲", // 1x EXTENDED ARABIC-INDIC DIGIT TWO v2: "۲", // 1x EXTENDED ARABIC-INDIC DIGIT TWO
want: -1, want: -1,
@ -104,55 +104,55 @@ func TestCompare(t *testing.T) {
// A few specific OS version tests below. // A few specific OS version tests below.
{ {
name: "windows version", name: "windows-version",
v1: "10.0.19045.3324", v1: "10.0.19045.3324",
v2: "10.0.18362", v2: "10.0.18362",
want: 1, want: 1,
}, },
{ {
name: "windows 11 is everything above 10.0.22000", name: "windows-11-above-10_0_22000",
v1: "10.0.22631.2262", v1: "10.0.22631.2262",
v2: "10.0.22000", v2: "10.0.22000",
want: 1, want: 1,
}, },
{ {
name: "android short version", name: "android-short-version",
v1: "10", v1: "10",
v2: "7", v2: "7",
want: 1, want: 1,
}, },
{ {
name: "android longer version", name: "android-longer-version",
v1: "7.1.2", v1: "7.1.2",
v2: "7", v2: "7",
want: 1, want: 1,
}, },
{ {
name: "iOS version", name: "iOS-version",
v1: "15.6.1", v1: "15.6.1",
v2: "15.6", v2: "15.6",
want: 1, want: 1,
}, },
{ {
name: "Linux short kernel version", name: "linux-short-kernel-version",
v1: "4.4.302+", v1: "4.4.302+",
v2: "4.0", v2: "4.0",
want: 1, want: 1,
}, },
{ {
name: "Linux long kernel version", name: "linux-long-kernel-version",
v1: "4.14.255-311-248.529.amzn2.x86_64", v1: "4.14.255-311-248.529.amzn2.x86_64",
v2: "4.0", v2: "4.0",
want: 1, want: 1,
}, },
{ {
name: "FreeBSD version", name: "freebsd-version",
v1: "14.0-CURRENT", v1: "14.0-CURRENT",
v2: "14", v2: "14",
want: 1, want: 1,
}, },
{ {
name: "Synology version", name: "synology-version",
v1: "Synology 6.2.4; kernel=3.10.105", v1: "Synology 6.2.4; kernel=3.10.105",
v2: "Synology 6", v2: "Synology 6",
want: 1, want: 1,

@ -360,17 +360,17 @@ func TestGetTypeHasher(t *testing.T) {
out32: "\x01\x04\x00\x00\x00\x00\x00\x00\x00\x01\x03\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00*\v\x00\x00\x00\x00\x00\x00\x0010.1.3.4/32\v\x00\x00\x00\x00\x00\x00\x0010.0.0.0/24\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x001.2.3.4/32\x01 \x00\x00\x00\x01\x00\x02\x00\x01\x04\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04!\x01\x01\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00foo\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\v\x00\x00\x00\x00\x00\x00\x00foooooooooo\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\f\x00\x00\x00\x00\x00\x00\x00baaaaaarrrrr\x00\x01\x00\x02\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\v\x00\x00\x00\x00\x00\x00\x00foooooooooo\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\f\x00\x00\x00\x00\x00\x00\x00baaaaaarrrrr\x00\x01\x00\x02\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\v\x00\x00\x00\x00\x00\x00\x00foooooooooo\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\f\x00\x00\x00\x00\x00\x00\x00baaaaaarrrrr\x00\x01\x00\x02\x00\x00\x00", out32: "\x01\x04\x00\x00\x00\x00\x00\x00\x00\x01\x03\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00*\v\x00\x00\x00\x00\x00\x00\x0010.1.3.4/32\v\x00\x00\x00\x00\x00\x00\x0010.0.0.0/24\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x001.2.3.4/32\x01 \x00\x00\x00\x01\x00\x02\x00\x01\x04\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04!\x01\x01\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00foo\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\v\x00\x00\x00\x00\x00\x00\x00foooooooooo\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\f\x00\x00\x00\x00\x00\x00\x00baaaaaarrrrr\x00\x01\x00\x02\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\v\x00\x00\x00\x00\x00\x00\x00foooooooooo\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\f\x00\x00\x00\x00\x00\x00\x00baaaaaarrrrr\x00\x01\x00\x02\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\v\x00\x00\x00\x00\x00\x00\x00foooooooooo\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\f\x00\x00\x00\x00\x00\x00\x00baaaaaarrrrr\x00\x01\x00\x02\x00\x00\x00",
}, },
{ {
name: "netip.Addr", name: "netip-Addr",
val: netip.MustParseAddr("fe80::123%foo"), val: netip.MustParseAddr("fe80::123%foo"),
out: u64(16+3) + u64(0x80fe) + u64(0x2301<<48) + "foo", out: u64(16+3) + u64(0x80fe) + u64(0x2301<<48) + "foo",
}, },
{ {
name: "ptr-netip.Addr", name: "ptr-netip-Addr",
val: &someIP, val: &someIP,
out: u8(1) + u64(4) + u32(0x04030201), out: u8(1) + u64(4) + u32(0x04030201),
}, },
{ {
name: "ptr-nil-netip.Addr", name: "ptr-nil-netip-Addr",
val: (*netip.Addr)(nil), val: (*netip.Addr)(nil),
out: "\x00", out: "\x00",
}, },
@ -469,7 +469,7 @@ func TestGetTypeHasher(t *testing.T) {
out: "\x01\x01\x00\x00\x00\x02\x00\x00\x00\x03\x04\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00\a\b\x00\x00\x00", out: "\x01\x01\x00\x00\x00\x02\x00\x00\x00\x03\x04\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00\a\b\x00\x00\x00",
}, },
{ {
name: "tailcfg.Node", name: "tailcfg-Node",
val: &tailcfg.Node{}, val: &tailcfg.Node{},
out: "ANY", // magic value; just check it doesn't fail to hash out: "ANY", // magic value; just check it doesn't fail to hash
out32: "ANY", out32: "ANY",

@ -36,17 +36,17 @@ func TestExpectFilter(t *testing.T) {
wantErr string // if non-empty, an error is expected containing this text wantErr string // if non-empty, an error is expected containing this text
}{ }{
{ {
name: "single event", name: "single-event",
events: []int{42}, events: []int{42},
expectFunc: eventbustest.Type[EventFoo](), expectFunc: eventbustest.Type[EventFoo](),
}, },
{ {
name: "multiple events, single expectation", name: "multiple-events-single-expectation",
events: []int{42, 1, 2, 3, 4, 5}, events: []int{42, 1, 2, 3, 4, 5},
expectFunc: eventbustest.Type[EventFoo](), expectFunc: eventbustest.Type[EventFoo](),
}, },
{ {
name: "filter on event with function", name: "filter-on-event-with-function",
events: []int{24, 42}, events: []int{24, 42},
expectFunc: func(event EventFoo) (bool, error) { expectFunc: func(event EventFoo) (bool, error) {
if event.Value == 42 { if event.Value == 42 {
@ -77,7 +77,7 @@ func TestExpectFilter(t *testing.T) {
wantErr: "value > 10", wantErr: "value > 10",
}, },
{ {
name: "first event has to be func", name: "first-event-has-to-be-func",
events: []int{24, 42}, events: []int{24, 42},
expectFunc: func(event EventFoo) (bool, error) { expectFunc: func(event EventFoo) (bool, error) {
if event.Value != 42 { if event.Value != 42 {
@ -99,7 +99,7 @@ func TestExpectFilter(t *testing.T) {
wantErr: "wrong result (-got, +want)", wantErr: "wrong result (-got, +want)",
}, },
{ {
name: "no events", name: "no-events",
events: []int{}, events: []int{},
expectFunc: func(event EventFoo) (bool, error) { expectFunc: func(event EventFoo) (bool, error) {
return true, nil return true, nil
@ -151,37 +151,37 @@ func TestExpectEvents(t *testing.T) {
wantErr bool wantErr bool
}{ }{
{ {
name: "No expectations", name: "no-expectations",
events: []any{EventFoo{}}, events: []any{EventFoo{}},
expectEvents: []any{}, expectEvents: []any{},
wantErr: true, wantErr: true,
}, },
{ {
name: "One event", name: "one-event",
events: []any{EventFoo{}}, events: []any{EventFoo{}},
expectEvents: []any{eventbustest.Type[EventFoo]()}, expectEvents: []any{eventbustest.Type[EventFoo]()},
wantErr: false, wantErr: false,
}, },
{ {
name: "Two events", name: "two-events",
events: []any{EventFoo{}, EventBar{}}, events: []any{EventFoo{}, EventBar{}},
expectEvents: []any{eventbustest.Type[EventFoo](), eventbustest.Type[EventBar]()}, expectEvents: []any{eventbustest.Type[EventFoo](), eventbustest.Type[EventBar]()},
wantErr: false, wantErr: false,
}, },
{ {
name: "Two expected events with another in the middle", name: "two-expected-events-with-another-in-middle",
events: []any{EventFoo{}, EventBaz{}, EventBar{}}, events: []any{EventFoo{}, EventBaz{}, EventBar{}},
expectEvents: []any{eventbustest.Type[EventFoo](), eventbustest.Type[EventBar]()}, expectEvents: []any{eventbustest.Type[EventFoo](), eventbustest.Type[EventBar]()},
wantErr: false, wantErr: false,
}, },
{ {
name: "Missing event", name: "missing-event",
events: []any{EventFoo{}, EventBaz{}}, events: []any{EventFoo{}, EventBaz{}},
expectEvents: []any{eventbustest.Type[EventFoo](), eventbustest.Type[EventBar]()}, expectEvents: []any{eventbustest.Type[EventFoo](), eventbustest.Type[EventBar]()},
wantErr: true, wantErr: true,
}, },
{ {
name: "One event with specific value", name: "one-event-with-specific-value",
events: []any{EventFoo{42}}, events: []any{EventFoo{42}},
expectEvents: []any{ expectEvents: []any{
func(ev EventFoo) (bool, error) { func(ev EventFoo) (bool, error) {
@ -194,7 +194,7 @@ func TestExpectEvents(t *testing.T) {
wantErr: false, wantErr: false,
}, },
{ {
name: "Two event with one specific value", name: "two-events-with-one-specific-value",
events: []any{EventFoo{43}, EventFoo{42}}, events: []any{EventFoo{43}, EventFoo{42}},
expectEvents: []any{ expectEvents: []any{
func(ev EventFoo) (bool, error) { func(ev EventFoo) (bool, error) {
@ -207,7 +207,7 @@ func TestExpectEvents(t *testing.T) {
wantErr: false, wantErr: false,
}, },
{ {
name: "One event with wrong value", name: "one-event-with-wrong-value",
events: []any{EventFoo{43}}, events: []any{EventFoo{43}},
expectEvents: []any{ expectEvents: []any{
func(ev EventFoo) (bool, error) { func(ev EventFoo) (bool, error) {
@ -220,7 +220,7 @@ func TestExpectEvents(t *testing.T) {
wantErr: true, wantErr: true,
}, },
{ {
name: "Two events with specific values", name: "two-events-with-specific-values",
events: []any{EventFoo{42}, EventFoo{42}, EventBar{"42"}}, events: []any{EventFoo{42}, EventFoo{42}, EventBar{"42"}},
expectEvents: []any{ expectEvents: []any{
func(ev EventFoo) (bool, error) { func(ev EventFoo) (bool, error) {
@ -283,37 +283,37 @@ func TestExpectExactlyEventsFilter(t *testing.T) {
wantErr bool wantErr bool
}{ }{
{ {
name: "No expectations", name: "no-expectations",
events: []any{EventFoo{}}, events: []any{EventFoo{}},
expectEvents: []any{}, expectEvents: []any{},
wantErr: true, wantErr: true,
}, },
{ {
name: "One event", name: "one-event",
events: []any{EventFoo{}}, events: []any{EventFoo{}},
expectEvents: []any{eventbustest.Type[EventFoo]()}, expectEvents: []any{eventbustest.Type[EventFoo]()},
wantErr: false, wantErr: false,
}, },
{ {
name: "Two events", name: "two-events",
events: []any{EventFoo{}, EventBar{}}, events: []any{EventFoo{}, EventBar{}},
expectEvents: []any{eventbustest.Type[EventFoo](), eventbustest.Type[EventBar]()}, expectEvents: []any{eventbustest.Type[EventFoo](), eventbustest.Type[EventBar]()},
wantErr: false, wantErr: false,
}, },
{ {
name: "Two expected events with another in the middle", name: "two-expected-events-with-another-in-middle",
events: []any{EventFoo{}, EventBaz{}, EventBar{}}, events: []any{EventFoo{}, EventBaz{}, EventBar{}},
expectEvents: []any{eventbustest.Type[EventFoo](), eventbustest.Type[EventBar]()}, expectEvents: []any{eventbustest.Type[EventFoo](), eventbustest.Type[EventBar]()},
wantErr: true, wantErr: true,
}, },
{ {
name: "Missing event", name: "missing-event",
events: []any{EventFoo{}, EventBaz{}}, events: []any{EventFoo{}, EventBaz{}},
expectEvents: []any{eventbustest.Type[EventFoo](), eventbustest.Type[EventBar]()}, expectEvents: []any{eventbustest.Type[EventFoo](), eventbustest.Type[EventBar]()},
wantErr: true, wantErr: true,
}, },
{ {
name: "One event with value", name: "one-event-with-value",
events: []any{EventFoo{42}}, events: []any{EventFoo{42}},
expectEvents: []any{ expectEvents: []any{
func(ev EventFoo) (bool, error) { func(ev EventFoo) (bool, error) {
@ -326,7 +326,7 @@ func TestExpectExactlyEventsFilter(t *testing.T) {
wantErr: false, wantErr: false,
}, },
{ {
name: "Two event with one specific value", name: "two-events-with-one-specific-value",
events: []any{EventFoo{43}, EventFoo{42}}, events: []any{EventFoo{43}, EventFoo{42}},
expectEvents: []any{ expectEvents: []any{
func(ev EventFoo) (bool, error) { func(ev EventFoo) (bool, error) {
@ -339,7 +339,7 @@ func TestExpectExactlyEventsFilter(t *testing.T) {
wantErr: true, wantErr: true,
}, },
{ {
name: "One event with wrong value", name: "one-event-with-wrong-value",
events: []any{EventFoo{43}}, events: []any{EventFoo{43}},
expectEvents: []any{ expectEvents: []any{
func(ev EventFoo) (bool, error) { func(ev EventFoo) (bool, error) {
@ -352,7 +352,7 @@ func TestExpectExactlyEventsFilter(t *testing.T) {
wantErr: true, wantErr: true,
}, },
{ {
name: "Two events with specific values", name: "two-events-with-specific-values",
events: []any{EventFoo{42}, EventFoo{42}, EventBar{"42"}}, events: []any{EventFoo{42}, EventFoo{42}, EventBar{"42"}},
expectEvents: []any{ expectEvents: []any{
func(ev EventFoo) (bool, error) { func(ev EventFoo) (bool, error) {

@ -960,32 +960,32 @@ func TestPickFirewallModeFromInstalledRules(t *testing.T) {
want FirewallMode want FirewallMode
}{ }{
{ {
name: "using iptables legacy", name: "using-iptables-legacy",
det: &testFWDetector{iptRuleCount: 1}, det: &testFWDetector{iptRuleCount: 1},
want: FirewallModeIPTables, want: FirewallModeIPTables,
}, },
{ {
name: "using nftables", name: "using-nftables",
det: &testFWDetector{nftRuleCount: 1}, det: &testFWDetector{nftRuleCount: 1},
want: FirewallModeNfTables, want: FirewallModeNfTables,
}, },
{ {
name: "using both iptables and nftables", name: "using-both-iptables-and-nftables",
det: &testFWDetector{iptRuleCount: 2, nftRuleCount: 2}, det: &testFWDetector{iptRuleCount: 2, nftRuleCount: 2},
want: FirewallModeNfTables, want: FirewallModeNfTables,
}, },
{ {
name: "not using any firewall, both available", name: "no-firewall-both-available",
det: &testFWDetector{}, det: &testFWDetector{},
want: FirewallModeNfTables, want: FirewallModeNfTables,
}, },
{ {
name: "not using any firewall, iptables available only", name: "no-firewall-iptables-only",
det: &testFWDetector{iptRuleCount: 1, nftErr: errors.New("nft error")}, det: &testFWDetector{iptRuleCount: 1, nftErr: errors.New("nft error")},
want: FirewallModeIPTables, want: FirewallModeIPTables,
}, },
{ {
name: "not using any firewall, nftables available only", name: "no-firewall-nftables-only",
det: &testFWDetector{iptErr: errors.New("iptables error"), nftRuleCount: 1}, det: &testFWDetector{iptErr: errors.New("iptables error"), nftRuleCount: 1},
want: FirewallModeNfTables, want: FirewallModeNfTables,
}, },

@ -226,42 +226,42 @@ func TestPolicyScopeContains(t *testing.T) {
wantAStrictlyContainsB: false, wantAStrictlyContainsB: false,
}, },
{ {
name: "UserScope(1234)/UserScope(1234)", name: "UserScope-1234/UserScope-1234",
scopeA: UserScopeOf("1234"), scopeA: UserScopeOf("1234"),
scopeB: UserScopeOf("1234"), scopeB: UserScopeOf("1234"),
wantAContainsB: true, wantAContainsB: true,
wantAStrictlyContainsB: false, wantAStrictlyContainsB: false,
}, },
{ {
name: "UserScope(1234)/UserScope(5678)", name: "UserScope-1234/UserScope-5678",
scopeA: UserScopeOf("1234"), scopeA: UserScopeOf("1234"),
scopeB: UserScopeOf("5678"), scopeB: UserScopeOf("5678"),
wantAContainsB: false, wantAContainsB: false,
wantAStrictlyContainsB: false, wantAStrictlyContainsB: false,
}, },
{ {
name: "ProfileScope(A)/UserScope(A/1234)", name: "ProfileScope-A/UserScope-A-1234",
scopeA: PolicyScope{kind: ProfileSetting, profileID: "A"}, scopeA: PolicyScope{kind: ProfileSetting, profileID: "A"},
scopeB: PolicyScope{kind: UserSetting, userID: "1234", profileID: "A"}, scopeB: PolicyScope{kind: UserSetting, userID: "1234", profileID: "A"},
wantAContainsB: true, wantAContainsB: true,
wantAStrictlyContainsB: true, wantAStrictlyContainsB: true,
}, },
{ {
name: "ProfileScope(A)/UserScope(B/1234)", name: "ProfileScope-A/UserScope-B-1234",
scopeA: PolicyScope{kind: ProfileSetting, profileID: "A"}, scopeA: PolicyScope{kind: ProfileSetting, profileID: "A"},
scopeB: PolicyScope{kind: UserSetting, userID: "1234", profileID: "B"}, scopeB: PolicyScope{kind: UserSetting, userID: "1234", profileID: "B"},
wantAContainsB: false, wantAContainsB: false,
wantAStrictlyContainsB: false, wantAStrictlyContainsB: false,
}, },
{ {
name: "UserScope(1234)/UserScope(A/1234)", name: "UserScope-1234/UserScope-A-1234",
scopeA: PolicyScope{kind: UserSetting, userID: "1234"}, scopeA: PolicyScope{kind: UserSetting, userID: "1234"},
scopeB: PolicyScope{kind: UserSetting, userID: "1234", profileID: "A"}, scopeB: PolicyScope{kind: UserSetting, userID: "1234", profileID: "A"},
wantAContainsB: true, wantAContainsB: true,
wantAStrictlyContainsB: true, wantAStrictlyContainsB: true,
}, },
{ {
name: "UserScope(1234)/UserScope(A/5678)", name: "UserScope-1234/UserScope-A-5678",
scopeA: PolicyScope{kind: UserSetting, userID: "1234"}, scopeA: PolicyScope{kind: UserSetting, userID: "1234"},
scopeB: PolicyScope{kind: UserSetting, userID: "5678", profileID: "A"}, scopeB: PolicyScope{kind: UserSetting, userID: "5678", profileID: "A"},
wantAContainsB: false, wantAContainsB: false,

@ -44,7 +44,7 @@ func TestGetString(t *testing.T) {
wantMetrics []metrics.TestState wantMetrics []metrics.TestState
}{ }{
{ {
name: "read existing value", name: "read-existing-value",
key: pkey.AdminConsoleVisibility, key: pkey.AdminConsoleVisibility,
handlerValue: "hide", handlerValue: "hide",
wantValue: "hide", wantValue: "hide",
@ -54,13 +54,13 @@ func TestGetString(t *testing.T) {
}, },
}, },
{ {
name: "read non-existing value", name: "read-non-existing-value",
key: pkey.EnableServerMode, key: pkey.EnableServerMode,
handlerError: ErrNotConfigured, handlerError: ErrNotConfigured,
wantError: nil, wantError: nil,
}, },
{ {
name: "read non-existing value, non-blank default", name: "read-non-existing-value-non-blank-default",
key: pkey.EnableServerMode, key: pkey.EnableServerMode,
handlerError: ErrNotConfigured, handlerError: ErrNotConfigured,
defaultValue: "test", defaultValue: "test",
@ -68,7 +68,7 @@ func TestGetString(t *testing.T) {
wantError: nil, wantError: nil,
}, },
{ {
name: "reading value returns other error", name: "reading-value-returns-other-error",
key: pkey.NetworkDevicesVisibility, key: pkey.NetworkDevicesVisibility,
handlerError: someOtherError, handlerError: someOtherError,
wantError: someOtherError, wantError: someOtherError,
@ -124,27 +124,27 @@ func TestGetUint64(t *testing.T) {
wantError error wantError error
}{ }{
{ {
name: "read existing value", name: "read-existing-value",
key: pkey.LogSCMInteractions, key: pkey.LogSCMInteractions,
handlerValue: 1, handlerValue: 1,
wantValue: 1, wantValue: 1,
}, },
{ {
name: "read non-existing value", name: "read-non-existing-value",
key: pkey.LogSCMInteractions, key: pkey.LogSCMInteractions,
handlerValue: 0, handlerValue: 0,
handlerError: ErrNotConfigured, handlerError: ErrNotConfigured,
wantValue: 0, wantValue: 0,
}, },
{ {
name: "read non-existing value, non-zero default", name: "read-non-existing-value-non-zero-default",
key: pkey.LogSCMInteractions, key: pkey.LogSCMInteractions,
defaultValue: 2, defaultValue: 2,
handlerError: ErrNotConfigured, handlerError: ErrNotConfigured,
wantValue: 2, wantValue: 2,
}, },
{ {
name: "reading value returns other error", name: "reading-value-returns-other-error",
key: pkey.FlushDNSOnSessionUnlock, key: pkey.FlushDNSOnSessionUnlock,
handlerError: someOtherError, handlerError: someOtherError,
wantError: someOtherError, wantError: someOtherError,
@ -191,7 +191,7 @@ func TestGetBoolean(t *testing.T) {
wantMetrics []metrics.TestState wantMetrics []metrics.TestState
}{ }{
{ {
name: "read existing value", name: "read-existing-value",
key: pkey.FlushDNSOnSessionUnlock, key: pkey.FlushDNSOnSessionUnlock,
handlerValue: true, handlerValue: true,
wantValue: true, wantValue: true,
@ -201,14 +201,14 @@ func TestGetBoolean(t *testing.T) {
}, },
}, },
{ {
name: "read non-existing value", name: "read-non-existing-value",
key: pkey.LogSCMInteractions, key: pkey.LogSCMInteractions,
handlerValue: false, handlerValue: false,
handlerError: ErrNotConfigured, handlerError: ErrNotConfigured,
wantValue: false, wantValue: false,
}, },
{ {
name: "reading value returns other error", name: "reading-value-returns-other-error",
key: pkey.FlushDNSOnSessionUnlock, key: pkey.FlushDNSOnSessionUnlock,
handlerError: someOtherError, handlerError: someOtherError,
wantError: someOtherError, // expect error... wantError: someOtherError, // expect error...
@ -266,7 +266,7 @@ func TestGetPreferenceOption(t *testing.T) {
wantMetrics []metrics.TestState wantMetrics []metrics.TestState
}{ }{
{ {
name: "always by policy", name: "always-by-policy",
key: pkey.EnableIncomingConnections, key: pkey.EnableIncomingConnections,
handlerValue: "always", handlerValue: "always",
wantValue: ptype.AlwaysByPolicy, wantValue: ptype.AlwaysByPolicy,
@ -276,7 +276,7 @@ func TestGetPreferenceOption(t *testing.T) {
}, },
}, },
{ {
name: "never by policy", name: "never-by-policy",
key: pkey.EnableIncomingConnections, key: pkey.EnableIncomingConnections,
handlerValue: "never", handlerValue: "never",
wantValue: ptype.NeverByPolicy, wantValue: ptype.NeverByPolicy,
@ -286,7 +286,7 @@ func TestGetPreferenceOption(t *testing.T) {
}, },
}, },
{ {
name: "use default", name: "use-default",
key: pkey.EnableIncomingConnections, key: pkey.EnableIncomingConnections,
handlerValue: "", handlerValue: "",
wantValue: ptype.ShowChoiceByPolicy, wantValue: ptype.ShowChoiceByPolicy,
@ -296,13 +296,13 @@ func TestGetPreferenceOption(t *testing.T) {
}, },
}, },
{ {
name: "read non-existing value", name: "read-non-existing-value",
key: pkey.EnableIncomingConnections, key: pkey.EnableIncomingConnections,
handlerError: ErrNotConfigured, handlerError: ErrNotConfigured,
wantValue: ptype.ShowChoiceByPolicy, wantValue: ptype.ShowChoiceByPolicy,
}, },
{ {
name: "other error is returned", name: "other-error-is-returned",
key: pkey.EnableIncomingConnections, key: pkey.EnableIncomingConnections,
handlerError: someOtherError, handlerError: someOtherError,
wantValue: ptype.ShowChoiceByPolicy, wantValue: ptype.ShowChoiceByPolicy,
@ -359,7 +359,7 @@ func TestGetVisibility(t *testing.T) {
wantMetrics []metrics.TestState wantMetrics []metrics.TestState
}{ }{
{ {
name: "hidden by policy", name: "hidden-by-policy",
key: pkey.AdminConsoleVisibility, key: pkey.AdminConsoleVisibility,
handlerValue: "hide", handlerValue: "hide",
wantValue: ptype.HiddenByPolicy, wantValue: ptype.HiddenByPolicy,
@ -369,7 +369,7 @@ func TestGetVisibility(t *testing.T) {
}, },
}, },
{ {
name: "visibility default", name: "visibility-default",
key: pkey.AdminConsoleVisibility, key: pkey.AdminConsoleVisibility,
handlerValue: "show", handlerValue: "show",
wantValue: ptype.VisibleByPolicy, wantValue: ptype.VisibleByPolicy,
@ -379,14 +379,14 @@ func TestGetVisibility(t *testing.T) {
}, },
}, },
{ {
name: "read non-existing value", name: "read-non-existing-value",
key: pkey.AdminConsoleVisibility, key: pkey.AdminConsoleVisibility,
handlerValue: "show", handlerValue: "show",
handlerError: ErrNotConfigured, handlerError: ErrNotConfigured,
wantValue: ptype.VisibleByPolicy, wantValue: ptype.VisibleByPolicy,
}, },
{ {
name: "other error is returned", name: "other-error-is-returned",
key: pkey.AdminConsoleVisibility, key: pkey.AdminConsoleVisibility,
handlerValue: "show", handlerValue: "show",
handlerError: someOtherError, handlerError: someOtherError,
@ -445,7 +445,7 @@ func TestGetDuration(t *testing.T) {
wantMetrics []metrics.TestState wantMetrics []metrics.TestState
}{ }{
{ {
name: "read existing value", name: "read-existing-value",
key: pkey.KeyExpirationNoticeTime, key: pkey.KeyExpirationNoticeTime,
handlerValue: "2h", handlerValue: "2h",
wantValue: 2 * time.Hour, wantValue: 2 * time.Hour,
@ -456,7 +456,7 @@ func TestGetDuration(t *testing.T) {
}, },
}, },
{ {
name: "invalid duration value", name: "invalid-duration-value",
key: pkey.KeyExpirationNoticeTime, key: pkey.KeyExpirationNoticeTime,
handlerValue: "-20", handlerValue: "-20",
wantValue: 24 * time.Hour, wantValue: 24 * time.Hour,
@ -468,21 +468,21 @@ func TestGetDuration(t *testing.T) {
}, },
}, },
{ {
name: "read non-existing value", name: "read-non-existing-value",
key: pkey.KeyExpirationNoticeTime, key: pkey.KeyExpirationNoticeTime,
handlerError: ErrNotConfigured, handlerError: ErrNotConfigured,
wantValue: 24 * time.Hour, wantValue: 24 * time.Hour,
defaultValue: 24 * time.Hour, defaultValue: 24 * time.Hour,
}, },
{ {
name: "read non-existing value different default", name: "read-non-existing-value-different-default",
key: pkey.KeyExpirationNoticeTime, key: pkey.KeyExpirationNoticeTime,
handlerError: ErrNotConfigured, handlerError: ErrNotConfigured,
wantValue: 0 * time.Second, wantValue: 0 * time.Second,
defaultValue: 0 * time.Second, defaultValue: 0 * time.Second,
}, },
{ {
name: "other error is returned", name: "other-error-is-returned",
key: pkey.KeyExpirationNoticeTime, key: pkey.KeyExpirationNoticeTime,
handlerError: someOtherError, handlerError: someOtherError,
wantValue: 24 * time.Hour, wantValue: 24 * time.Hour,
@ -541,7 +541,7 @@ func TestGetStringArray(t *testing.T) {
wantMetrics []metrics.TestState wantMetrics []metrics.TestState
}{ }{
{ {
name: "read existing value", name: "read-existing-value",
key: pkey.AllowedSuggestedExitNodes, key: pkey.AllowedSuggestedExitNodes,
handlerValue: []string{"foo", "bar"}, handlerValue: []string{"foo", "bar"},
wantValue: []string{"foo", "bar"}, wantValue: []string{"foo", "bar"},
@ -551,13 +551,13 @@ func TestGetStringArray(t *testing.T) {
}, },
}, },
{ {
name: "read non-existing value", name: "read-non-existing-value",
key: pkey.AllowedSuggestedExitNodes, key: pkey.AllowedSuggestedExitNodes,
handlerError: ErrNotConfigured, handlerError: ErrNotConfigured,
wantError: nil, wantError: nil,
}, },
{ {
name: "read non-existing value, non nil default", name: "read-non-existing-value-non-nil-default",
key: pkey.AllowedSuggestedExitNodes, key: pkey.AllowedSuggestedExitNodes,
handlerError: ErrNotConfigured, handlerError: ErrNotConfigured,
defaultValue: []string{"foo", "bar"}, defaultValue: []string{"foo", "bar"},
@ -565,7 +565,7 @@ func TestGetStringArray(t *testing.T) {
wantError: nil, wantError: nil,
}, },
{ {
name: "reading value returns other error", name: "reading-value-returns-other-error",
key: pkey.AllowedSuggestedExitNodes, key: pkey.AllowedSuggestedExitNodes,
handlerError: someOtherError, handlerError: someOtherError,
wantError: someOtherError, wantError: someOtherError,

@ -180,7 +180,7 @@ func Test_endpoint_maybeProbeUDPLifetimeLocked(t *testing.T) {
wantMaybe bool wantMaybe bool
}{ }{
{ {
name: "nil probeUDPLifetime", name: "nil-probeUDPLifetime",
localDisco: higher, localDisco: higher,
remoteDisco: &lower, remoteDisco: &lower,
probeUDPLifetimeFn: func() *probeUDPLifetime { probeUDPLifetimeFn: func() *probeUDPLifetime {
@ -189,28 +189,28 @@ func Test_endpoint_maybeProbeUDPLifetimeLocked(t *testing.T) {
bestAddr: addr, bestAddr: addr,
}, },
{ {
name: "local higher disco key", name: "local-higher-disco-key",
localDisco: higher, localDisco: higher,
remoteDisco: &lower, remoteDisco: &lower,
probeUDPLifetimeFn: newProbeUDPLifetime, probeUDPLifetimeFn: newProbeUDPLifetime,
bestAddr: addr, bestAddr: addr,
}, },
{ {
name: "remote no disco key", name: "remote-no-disco-key",
localDisco: higher, localDisco: higher,
remoteDisco: nil, remoteDisco: nil,
probeUDPLifetimeFn: newProbeUDPLifetime, probeUDPLifetimeFn: newProbeUDPLifetime,
bestAddr: addr, bestAddr: addr,
}, },
{ {
name: "invalid bestAddr", name: "invalid-bestAddr",
localDisco: lower, localDisco: lower,
remoteDisco: &higher, remoteDisco: &higher,
probeUDPLifetimeFn: newProbeUDPLifetime, probeUDPLifetimeFn: newProbeUDPLifetime,
bestAddr: addrQuality{}, bestAddr: addrQuality{},
}, },
{ {
name: "cycle started too recently", name: "cycle-started-too-recently",
localDisco: lower, localDisco: lower,
remoteDisco: &higher, remoteDisco: &higher,
probeUDPLifetimeFn: func() *probeUDPLifetime { probeUDPLifetimeFn: func() *probeUDPLifetime {
@ -222,7 +222,7 @@ func Test_endpoint_maybeProbeUDPLifetimeLocked(t *testing.T) {
bestAddr: addr, bestAddr: addr,
}, },
{ {
name: "maybe cliff 0 cycle not active", name: "maybe-cliff-0-cycle-not-active",
localDisco: lower, localDisco: lower,
remoteDisco: &higher, remoteDisco: &higher,
probeUDPLifetimeFn: func() *probeUDPLifetime { probeUDPLifetimeFn: func() *probeUDPLifetime {
@ -238,7 +238,7 @@ func Test_endpoint_maybeProbeUDPLifetimeLocked(t *testing.T) {
wantMaybe: true, wantMaybe: true,
}, },
{ {
name: "maybe cliff 0", name: "maybe-cliff-0",
localDisco: lower, localDisco: lower,
remoteDisco: &higher, remoteDisco: &higher,
probeUDPLifetimeFn: func() *probeUDPLifetime { probeUDPLifetimeFn: func() *probeUDPLifetime {
@ -254,7 +254,7 @@ func Test_endpoint_maybeProbeUDPLifetimeLocked(t *testing.T) {
wantMaybe: true, wantMaybe: true,
}, },
{ {
name: "maybe cliff 1", name: "maybe-cliff-1",
localDisco: lower, localDisco: lower,
remoteDisco: &higher, remoteDisco: &higher,
probeUDPLifetimeFn: func() *probeUDPLifetime { probeUDPLifetimeFn: func() *probeUDPLifetime {
@ -270,7 +270,7 @@ func Test_endpoint_maybeProbeUDPLifetimeLocked(t *testing.T) {
wantMaybe: true, wantMaybe: true,
}, },
{ {
name: "maybe cliff 2", name: "maybe-cliff-2",
localDisco: lower, localDisco: lower,
remoteDisco: &higher, remoteDisco: &higher,
probeUDPLifetimeFn: func() *probeUDPLifetime { probeUDPLifetimeFn: func() *probeUDPLifetime {
@ -341,13 +341,13 @@ func Test_epAddr_isDirectUDP(t *testing.T) {
want: true, want: true,
}, },
{ {
name: "false derp magic addr", name: "false-derp-magic-addr",
ap: netip.AddrPortFrom(tailcfg.DerpMagicIPAddr, 0), ap: netip.AddrPortFrom(tailcfg.DerpMagicIPAddr, 0),
vni: packet.VirtualNetworkID{}, vni: packet.VirtualNetworkID{},
want: false, want: false,
}, },
{ {
name: "false vni set", name: "false-vni-set",
ap: netip.MustParseAddrPort("192.0.2.1:7"), ap: netip.MustParseAddrPort("192.0.2.1:7"),
vni: vni, vni: vni,
want: false, want: false,
@ -397,42 +397,42 @@ func Test_endpoint_udpRelayEndpointReady(t *testing.T) {
wantBestAddr addrQuality wantBestAddr addrQuality
}{ }{
{ {
name: "bestAddr trusted direct", name: "bestAddr-trusted-direct",
curBestAddr: directAddrQuality, curBestAddr: directAddrQuality,
trustBestAddrUntil: mono.Now().Add(1 * time.Hour), trustBestAddrUntil: mono.Now().Add(1 * time.Hour),
maybeBest: peerRelayAddrQuality, maybeBest: peerRelayAddrQuality,
wantBestAddr: directAddrQuality, wantBestAddr: directAddrQuality,
}, },
{ {
name: "bestAddr untrusted direct", name: "bestAddr-untrusted-direct",
curBestAddr: directAddrQuality, curBestAddr: directAddrQuality,
trustBestAddrUntil: mono.Now().Add(-1 * time.Hour), trustBestAddrUntil: mono.Now().Add(-1 * time.Hour),
maybeBest: peerRelayAddrQuality, maybeBest: peerRelayAddrQuality,
wantBestAddr: peerRelayAddrQuality, wantBestAddr: peerRelayAddrQuality,
}, },
{ {
name: "maybeBest same relay server higher latency bestAddr trusted", name: "maybeBest-same-relay-higher-latency-trusted",
curBestAddr: peerRelayAddrQuality, curBestAddr: peerRelayAddrQuality,
trustBestAddrUntil: mono.Now().Add(1 * time.Hour), trustBestAddrUntil: mono.Now().Add(1 * time.Hour),
maybeBest: peerRelayAddrQualityHigherLatencySameServer, maybeBest: peerRelayAddrQualityHigherLatencySameServer,
wantBestAddr: peerRelayAddrQualityHigherLatencySameServer, wantBestAddr: peerRelayAddrQualityHigherLatencySameServer,
}, },
{ {
name: "maybeBest diff relay server higher latency bestAddr trusted", name: "maybeBest-diff-relay-higher-latency-trusted",
curBestAddr: peerRelayAddrQuality, curBestAddr: peerRelayAddrQuality,
trustBestAddrUntil: mono.Now().Add(1 * time.Hour), trustBestAddrUntil: mono.Now().Add(1 * time.Hour),
maybeBest: peerRelayAddrQualityHigherLatencyDiffServer, maybeBest: peerRelayAddrQualityHigherLatencyDiffServer,
wantBestAddr: peerRelayAddrQuality, wantBestAddr: peerRelayAddrQuality,
}, },
{ {
name: "maybeBest diff relay server lower latency bestAddr trusted", name: "maybeBest-diff-relay-lower-latency-trusted",
curBestAddr: peerRelayAddrQuality, curBestAddr: peerRelayAddrQuality,
trustBestAddrUntil: mono.Now().Add(1 * time.Hour), trustBestAddrUntil: mono.Now().Add(1 * time.Hour),
maybeBest: peerRelayAddrQualityLowerLatencyDiffServer, maybeBest: peerRelayAddrQualityLowerLatencyDiffServer,
wantBestAddr: peerRelayAddrQualityLowerLatencyDiffServer, wantBestAddr: peerRelayAddrQualityLowerLatencyDiffServer,
}, },
{ {
name: "maybeBest diff relay server equal latency bestAddr trusted", name: "maybeBest-diff-relay-equal-latency-trusted",
curBestAddr: peerRelayAddrQuality, curBestAddr: peerRelayAddrQuality,
trustBestAddrUntil: mono.Now().Add(1 * time.Hour), trustBestAddrUntil: mono.Now().Add(1 * time.Hour),
maybeBest: peerRelayAddrQualityEqualLatencyDiffServer, maybeBest: peerRelayAddrQualityEqualLatencyDiffServer,

@ -184,19 +184,19 @@ func TestBpfDiscardV4(t *testing.T) {
accept bool accept bool
}{ }{
{ {
name: "base accepted datagram", name: "base-accepted-datagram",
replace: map[int]byte{}, replace: map[int]byte{},
accept: true, accept: true,
}, },
{ {
name: "more fragments", name: "more-fragments",
replace: map[int]byte{ replace: map[int]byte{
6: 0x20, 6: 0x20,
}, },
accept: false, accept: false,
}, },
{ {
name: "some fragment", name: "some-fragment",
replace: map[int]byte{ replace: map[int]byte{
7: 0x01, 7: 0x01,
}, },

@ -1217,7 +1217,7 @@ func testTwoDevicePing(t *testing.T, d *devices) {
} }
outerT := t outerT := t
t.Run("ping 1.0.0.1", func(t *testing.T) { t.Run("ping-1_0_0_1", func(t *testing.T) {
setT(t) setT(t)
defer setT(outerT) defer setT(outerT)
ping1(t) ping1(t)
@ -1225,7 +1225,7 @@ func testTwoDevicePing(t *testing.T, d *devices) {
checkStats(t, m2, m2Conns) checkStats(t, m2, m2Conns)
}) })
t.Run("ping 1.0.0.2", func(t *testing.T) { t.Run("ping-1_0_0_2", func(t *testing.T) {
setT(t) setT(t)
defer setT(outerT) defer setT(outerT)
ping2(t) ping2(t)
@ -1233,7 +1233,7 @@ func testTwoDevicePing(t *testing.T, d *devices) {
checkStats(t, m2, m2Conns) checkStats(t, m2, m2Conns)
}) })
t.Run("ping 1.0.0.2 via SendPacket", func(t *testing.T) { t.Run("ping-1_0_0_2-via-SendPacket", func(t *testing.T) {
setT(t) setT(t)
defer setT(outerT) defer setT(outerT)
msg1to2 := tuntest.Ping(netip.MustParseAddr("1.0.0.2"), netip.MustParseAddr("1.0.0.1")) msg1to2 := tuntest.Ping(netip.MustParseAddr("1.0.0.2"), netip.MustParseAddr("1.0.0.1"))
@ -1251,7 +1251,7 @@ func testTwoDevicePing(t *testing.T, d *devices) {
checkStats(t, m2, m2Conns) checkStats(t, m2, m2Conns)
}) })
t.Run("no-op dev1 reconfig", func(t *testing.T) { t.Run("no-op-dev1-reconfig", func(t *testing.T) {
setT(t) setT(t)
defer setT(outerT) defer setT(outerT)
if err := m1.Reconfig(m1cfg); err != nil { if err := m1.Reconfig(m1cfg); err != nil {
@ -2731,7 +2731,7 @@ func TestAddrForSendLockedForWireGuardOnly(t *testing.T) {
want epAddr want epAddr
}{ }{
{ {
name: "no endpoints", name: "no-endpoints",
sendInitialPing: false, sendInitialPing: false,
validAddr: false, validAddr: false,
sendFollowUpPing: false, sendFollowUpPing: false,
@ -2740,7 +2740,7 @@ func TestAddrForSendLockedForWireGuardOnly(t *testing.T) {
want: epAddr{}, want: epAddr{},
}, },
{ {
name: "singular endpoint does not request ping", name: "singular-endpoint-no-ping-request",
sendInitialPing: false, sendInitialPing: false,
validAddr: true, validAddr: true,
sendFollowUpPing: false, sendFollowUpPing: false,
@ -2754,7 +2754,7 @@ func TestAddrForSendLockedForWireGuardOnly(t *testing.T) {
want: epAddr{ap: netip.MustParseAddrPort("1.1.1.1:111")}, want: epAddr{ap: netip.MustParseAddrPort("1.1.1.1:111")},
}, },
{ {
name: "ping sent within wireguardPingInterval should not request ping", name: "ping-within-wireguardPingInterval-no-request",
sendInitialPing: true, sendInitialPing: true,
validAddr: true, validAddr: true,
sendFollowUpPing: false, sendFollowUpPing: false,
@ -2772,7 +2772,7 @@ func TestAddrForSendLockedForWireGuardOnly(t *testing.T) {
want: epAddr{ap: netip.MustParseAddrPort("1.1.1.1:111")}, want: epAddr{ap: netip.MustParseAddrPort("1.1.1.1:111")},
}, },
{ {
name: "ping sent outside of wireguardPingInterval should request ping", name: "ping-outside-wireguardPingInterval-requests-ping",
sendInitialPing: true, sendInitialPing: true,
validAddr: true, validAddr: true,
sendFollowUpPing: true, sendFollowUpPing: true,
@ -2790,7 +2790,7 @@ func TestAddrForSendLockedForWireGuardOnly(t *testing.T) {
want: epAddr{ap: netip.MustParseAddrPort("1.1.1.1:111")}, want: epAddr{ap: netip.MustParseAddrPort("1.1.1.1:111")},
}, },
{ {
name: "choose lowest latency for useable IPv4 and IPv6", name: "choose-lowest-latency-v4-and-v6",
sendInitialPing: true, sendInitialPing: true,
validAddr: true, validAddr: true,
sendFollowUpPing: false, sendFollowUpPing: false,
@ -2808,7 +2808,7 @@ func TestAddrForSendLockedForWireGuardOnly(t *testing.T) {
want: epAddr{ap: netip.MustParseAddrPort("[2345:0425:2CA1:0000:0000:0567:5673:23b5]:222")}, want: epAddr{ap: netip.MustParseAddrPort("[2345:0425:2CA1:0000:0000:0567:5673:23b5]:222")},
}, },
{ {
name: "choose IPv6 address when latency is the same for v4 and v6", name: "choose-IPv6-when-equal-latency",
sendInitialPing: true, sendInitialPing: true,
validAddr: true, validAddr: true,
sendFollowUpPing: false, sendFollowUpPing: false,
@ -3378,73 +3378,73 @@ func Test_packetLooksLike(t *testing.T) {
wantIsGeneveEncap bool wantIsGeneveEncap bool
}{ }{
{ {
name: "STUN binding success response", name: "STUN-binding-success-response",
msg: stun.Response(stun.NewTxID(), netip.MustParseAddrPort("127.0.0.1:1")), msg: stun.Response(stun.NewTxID(), netip.MustParseAddrPort("127.0.0.1:1")),
wantPacketLooksLikeType: packetLooksLikeSTUNBinding, wantPacketLooksLikeType: packetLooksLikeSTUNBinding,
wantIsGeneveEncap: false, wantIsGeneveEncap: false,
}, },
{ {
name: "naked disco", name: "naked-disco",
msg: nakedDisco, msg: nakedDisco,
wantPacketLooksLikeType: packetLooksLikeDisco, wantPacketLooksLikeType: packetLooksLikeDisco,
wantIsGeneveEncap: false, wantIsGeneveEncap: false,
}, },
{ {
name: "geneve encap disco", name: "geneve-encap-disco",
msg: geneveEncapDisco, msg: geneveEncapDisco,
wantPacketLooksLikeType: packetLooksLikeDisco, wantPacketLooksLikeType: packetLooksLikeDisco,
wantIsGeneveEncap: true, wantIsGeneveEncap: true,
}, },
{ {
name: "geneve encap too short disco", name: "geneve-encap-too-short-disco",
msg: geneveEncapDisco[:len(geneveEncapDisco)-key.DiscoPublicRawLen], msg: geneveEncapDisco[:len(geneveEncapDisco)-key.DiscoPublicRawLen],
wantPacketLooksLikeType: packetLooksLikeWireGuard, wantPacketLooksLikeType: packetLooksLikeWireGuard,
wantIsGeneveEncap: false, wantIsGeneveEncap: false,
}, },
{ {
name: "geneve encap disco nonzero geneve version", name: "geneve-encap-disco-nonzero-geneve-version",
msg: geneveEncapDiscoNonZeroGeneveVersion, msg: geneveEncapDiscoNonZeroGeneveVersion,
wantPacketLooksLikeType: packetLooksLikeWireGuard, wantPacketLooksLikeType: packetLooksLikeWireGuard,
wantIsGeneveEncap: false, wantIsGeneveEncap: false,
}, },
{ {
name: "geneve encap disco nonzero geneve reserved bits", name: "geneve-encap-disco-nonzero-geneve-reserved-bits",
msg: geneveEncapDiscoNonZeroGeneveReservedBits, msg: geneveEncapDiscoNonZeroGeneveReservedBits,
wantPacketLooksLikeType: packetLooksLikeWireGuard, wantPacketLooksLikeType: packetLooksLikeWireGuard,
wantIsGeneveEncap: false, wantIsGeneveEncap: false,
}, },
{ {
name: "geneve encap disco nonzero geneve vni lsb", name: "geneve-encap-disco-nonzero-geneve-vni-lsb",
msg: geneveEncapDiscoNonZeroGeneveVNILSB, msg: geneveEncapDiscoNonZeroGeneveVNILSB,
wantPacketLooksLikeType: packetLooksLikeWireGuard, wantPacketLooksLikeType: packetLooksLikeWireGuard,
wantIsGeneveEncap: false, wantIsGeneveEncap: false,
}, },
{ {
name: "geneve encap wireguard", name: "geneve-encap-wireguard",
msg: geneveEncapWireGuard, msg: geneveEncapWireGuard,
wantPacketLooksLikeType: packetLooksLikeWireGuard, wantPacketLooksLikeType: packetLooksLikeWireGuard,
wantIsGeneveEncap: true, wantIsGeneveEncap: true,
}, },
{ {
name: "naked WireGuard Initiation type", name: "naked-WireGuard-Initiation-type",
msg: nakedWireGuardInitiation, msg: nakedWireGuardInitiation,
wantPacketLooksLikeType: packetLooksLikeWireGuard, wantPacketLooksLikeType: packetLooksLikeWireGuard,
wantIsGeneveEncap: false, wantIsGeneveEncap: false,
}, },
{ {
name: "naked WireGuard Response type", name: "naked-WireGuard-Response-type",
msg: nakedWireGuardResponse, msg: nakedWireGuardResponse,
wantPacketLooksLikeType: packetLooksLikeWireGuard, wantPacketLooksLikeType: packetLooksLikeWireGuard,
wantIsGeneveEncap: false, wantIsGeneveEncap: false,
}, },
{ {
name: "naked WireGuard Cookie Reply type", name: "naked-WireGuard-Cookie-Reply-type",
msg: nakedWireGuardCookieReply, msg: nakedWireGuardCookieReply,
wantPacketLooksLikeType: packetLooksLikeWireGuard, wantPacketLooksLikeType: packetLooksLikeWireGuard,
wantIsGeneveEncap: false, wantIsGeneveEncap: false,
}, },
{ {
name: "naked WireGuard Transport type", name: "naked-WireGuard-Transport-type",
msg: nakedWireGuardTransport, msg: nakedWireGuardTransport,
wantPacketLooksLikeType: packetLooksLikeWireGuard, wantPacketLooksLikeType: packetLooksLikeWireGuard,
wantIsGeneveEncap: false, wantIsGeneveEncap: false,
@ -3481,22 +3481,22 @@ func Test_looksLikeInitiationMsg(t *testing.T) {
want bool want bool
}{ }{
{ {
name: "valid initiation", name: "valid-initiation",
b: initMsg, b: initMsg,
want: true, want: true,
}, },
{ {
name: "invalid message type field", name: "invalid-message-type-field",
b: initMsgSizeTransportType, b: initMsgSizeTransportType,
want: false, want: false,
}, },
{ {
name: "too small", name: "too-small",
b: initMsg[:device.MessageInitiationSize-1], b: initMsg[:device.MessageInitiationSize-1],
want: false, want: false,
}, },
{ {
name: "too big", name: "too-big",
b: append(initMsg, 0), b: append(initMsg, 0),
want: false, want: false,
}, },
@ -3538,7 +3538,7 @@ func Test_nodeHasCap(t *testing.T) {
want bool want bool
}{ }{
{ {
name: "match v4", name: "match-v4",
filt: filter.New([]filtertype.Match{ filt: filter.New([]filtertype.Match{
{ {
Srcs: []netip.Prefix{netip.MustParsePrefix("2.2.2.2/32")}, Srcs: []netip.Prefix{netip.MustParsePrefix("2.2.2.2/32")},
@ -3556,7 +3556,7 @@ func Test_nodeHasCap(t *testing.T) {
want: true, want: true,
}, },
{ {
name: "match v6", name: "match-v6",
filt: filter.New([]filtertype.Match{ filt: filter.New([]filtertype.Match{
{ {
Srcs: []netip.Prefix{netip.MustParsePrefix("::2/128")}, Srcs: []netip.Prefix{netip.MustParsePrefix("::2/128")},
@ -3574,7 +3574,7 @@ func Test_nodeHasCap(t *testing.T) {
want: true, want: true,
}, },
{ {
name: "no match CapMatch Dst", name: "no-match-CapMatch-Dst",
filt: filter.New([]filtertype.Match{ filt: filter.New([]filtertype.Match{
{ {
Srcs: []netip.Prefix{netip.MustParsePrefix("::2/128")}, Srcs: []netip.Prefix{netip.MustParsePrefix("::2/128")},
@ -3592,7 +3592,7 @@ func Test_nodeHasCap(t *testing.T) {
want: false, want: false,
}, },
{ {
name: "no match peer cap", name: "no-match-peer-cap",
filt: filter.New([]filtertype.Match{ filt: filter.New([]filtertype.Match{
{ {
Srcs: []netip.Prefix{netip.MustParsePrefix("::2/128")}, Srcs: []netip.Prefix{netip.MustParsePrefix("::2/128")},
@ -3610,7 +3610,7 @@ func Test_nodeHasCap(t *testing.T) {
want: false, want: false,
}, },
{ {
name: "nil src", name: "nil-src",
filt: filter.New([]filtertype.Match{ filt: filter.New([]filtertype.Match{
{ {
Srcs: []netip.Prefix{netip.MustParsePrefix("2.2.2.2/32")}, Srcs: []netip.Prefix{netip.MustParsePrefix("2.2.2.2/32")},
@ -3628,7 +3628,7 @@ func Test_nodeHasCap(t *testing.T) {
want: false, want: false,
}, },
{ {
name: "nil dst", name: "nil-dst",
filt: filter.New([]filtertype.Match{ filt: filter.New([]filtertype.Match{
{ {
Srcs: []netip.Prefix{netip.MustParsePrefix("2.2.2.2/32")}, Srcs: []netip.Prefix{netip.MustParsePrefix("2.2.2.2/32")},
@ -3706,7 +3706,7 @@ func TestConn_SetNetworkMap_updateRelayServersSet(t *testing.T) {
wantRelayClientEnabled bool wantRelayClientEnabled bool
}{ }{
{ {
name: "candidate relay server", name: "candidate-relay-server",
filt: filter.New([]filtertype.Match{ filt: filter.New([]filtertype.Match{
{ {
Srcs: peerNodeCandidateRelay.Addresses, Srcs: peerNodeCandidateRelay.Addresses,
@ -3730,7 +3730,7 @@ func TestConn_SetNetworkMap_updateRelayServersSet(t *testing.T) {
wantRelayClientEnabled: true, wantRelayClientEnabled: true,
}, },
{ {
name: "no candidate relay server because self has tailcfg.NodeAttrDisableRelayClient", name: "no-candidate-self-has-DisableRelayClient", // self has tailcfg.NodeAttrDisableRelayClient
filt: filter.New([]filtertype.Match{ filt: filter.New([]filtertype.Match{
{ {
Srcs: peerNodeCandidateRelay.Addresses, Srcs: peerNodeCandidateRelay.Addresses,
@ -3748,7 +3748,7 @@ func TestConn_SetNetworkMap_updateRelayServersSet(t *testing.T) {
wantRelayClientEnabled: false, wantRelayClientEnabled: false,
}, },
{ {
name: "no candidate relay server because self has tailcfg.NodeAttrOnlyTCP443", name: "no-candidate-self-has-OnlyTCP443", // self has tailcfg.NodeAttrOnlyTCP443
filt: filter.New([]filtertype.Match{ filt: filter.New([]filtertype.Match{
{ {
Srcs: peerNodeCandidateRelay.Addresses, Srcs: peerNodeCandidateRelay.Addresses,
@ -3766,7 +3766,7 @@ func TestConn_SetNetworkMap_updateRelayServersSet(t *testing.T) {
wantRelayClientEnabled: false, wantRelayClientEnabled: false,
}, },
{ {
name: "self candidate relay server", name: "self-candidate-relay-server",
filt: filter.New([]filtertype.Match{ filt: filter.New([]filtertype.Match{
{ {
Srcs: selfNode.Addresses, Srcs: selfNode.Addresses,
@ -3790,7 +3790,7 @@ func TestConn_SetNetworkMap_updateRelayServersSet(t *testing.T) {
wantRelayClientEnabled: true, wantRelayClientEnabled: true,
}, },
{ {
name: "no candidate relay server", name: "no-candidate-relay-server",
filt: filter.New([]filtertype.Match{ filt: filter.New([]filtertype.Match{
{ {
Srcs: peerNodeNotCandidateRelayCapVer.Addresses, Srcs: peerNodeNotCandidateRelayCapVer.Addresses,
@ -3911,7 +3911,7 @@ func TestConn_receiveIP(t *testing.T) {
wantNoteRecvActivityCalled bool wantNoteRecvActivityCalled bool
}{ }{
{ {
name: "naked disco", name: "naked-disco",
b: looksLikeNakedDisco, b: looksLikeNakedDisco,
ipp: netip.MustParseAddrPort("127.0.0.1:7777"), ipp: netip.MustParseAddrPort("127.0.0.1:7777"),
cache: &epAddrEndpointCache{}, cache: &epAddrEndpointCache{},
@ -3923,7 +3923,7 @@ func TestConn_receiveIP(t *testing.T) {
wantNoteRecvActivityCalled: false, wantNoteRecvActivityCalled: false,
}, },
{ {
name: "geneve encap disco", name: "geneve-encap-disco",
b: looksLikeGeneveDisco, b: looksLikeGeneveDisco,
ipp: netip.MustParseAddrPort("127.0.0.1:7777"), ipp: netip.MustParseAddrPort("127.0.0.1:7777"),
cache: &epAddrEndpointCache{}, cache: &epAddrEndpointCache{},
@ -3935,7 +3935,7 @@ func TestConn_receiveIP(t *testing.T) {
wantNoteRecvActivityCalled: false, wantNoteRecvActivityCalled: false,
}, },
{ {
name: "STUN binding", name: "STUN-binding",
b: looksLikeSTUNBinding, b: looksLikeSTUNBinding,
ipp: netip.MustParseAddrPort("127.0.0.1:7777"), ipp: netip.MustParseAddrPort("127.0.0.1:7777"),
cache: &epAddrEndpointCache{}, cache: &epAddrEndpointCache{},
@ -3947,7 +3947,7 @@ func TestConn_receiveIP(t *testing.T) {
wantNoteRecvActivityCalled: false, wantNoteRecvActivityCalled: false,
}, },
{ {
name: "naked WireGuard init lazyEndpoint empty peerMap", name: "naked-WireGuard-init-lazyEndpoint-empty-peerMap",
b: looksLikeNakedWireGuardInit, b: looksLikeNakedWireGuardInit,
ipp: netip.MustParseAddrPort("127.0.0.1:7777"), ipp: netip.MustParseAddrPort("127.0.0.1:7777"),
cache: &epAddrEndpointCache{}, cache: &epAddrEndpointCache{},
@ -3959,7 +3959,7 @@ func TestConn_receiveIP(t *testing.T) {
wantNoteRecvActivityCalled: false, wantNoteRecvActivityCalled: false,
}, },
{ {
name: "naked WireGuard init endpoint matching peerMap entry", name: "naked-WireGuard-init-endpoint-matching-peerMap-entry",
b: looksLikeNakedWireGuardInit, b: looksLikeNakedWireGuardInit,
ipp: netip.MustParseAddrPort("127.0.0.1:7777"), ipp: netip.MustParseAddrPort("127.0.0.1:7777"),
cache: &epAddrEndpointCache{}, cache: &epAddrEndpointCache{},
@ -3973,7 +3973,7 @@ func TestConn_receiveIP(t *testing.T) {
wantNoteRecvActivityCalled: true, wantNoteRecvActivityCalled: true,
}, },
{ {
name: "geneve WireGuard init lazyEndpoint empty peerMap", name: "geneve-WireGuard-init-lazyEndpoint-empty-peerMap",
b: looksLikeGeneveWireGuardInit, b: looksLikeGeneveWireGuardInit,
ipp: netip.MustParseAddrPort("127.0.0.1:7777"), ipp: netip.MustParseAddrPort("127.0.0.1:7777"),
cache: &epAddrEndpointCache{}, cache: &epAddrEndpointCache{},
@ -3985,7 +3985,7 @@ func TestConn_receiveIP(t *testing.T) {
wantNoteRecvActivityCalled: false, wantNoteRecvActivityCalled: false,
}, },
{ {
name: "geneve WireGuard init lazyEndpoint matching peerMap activity noted", name: "geneve-WireGuard-init-lazyEndpoint-matching-peerMap-activity-noted",
b: looksLikeGeneveWireGuardInit, b: looksLikeGeneveWireGuardInit,
ipp: netip.MustParseAddrPort("127.0.0.1:7777"), ipp: netip.MustParseAddrPort("127.0.0.1:7777"),
cache: &epAddrEndpointCache{}, cache: &epAddrEndpointCache{},
@ -4001,7 +4001,7 @@ func TestConn_receiveIP(t *testing.T) {
wantNoteRecvActivityCalled: true, wantNoteRecvActivityCalled: true,
}, },
{ {
name: "geneve WireGuard init lazyEndpoint matching peerMap no activity noted", name: "geneve-WireGuard-init-lazyEndpoint-matching-peerMap-no-activity-noted",
b: looksLikeGeneveWireGuardInit, b: looksLikeGeneveWireGuardInit,
ipp: netip.MustParseAddrPort("127.0.0.1:7777"), ipp: netip.MustParseAddrPort("127.0.0.1:7777"),
cache: &epAddrEndpointCache{}, cache: &epAddrEndpointCache{},
@ -4151,25 +4151,25 @@ func Test_lazyEndpoint_InitiationMessagePublicKey(t *testing.T) {
wantNoteRecvActivityCalled bool wantNoteRecvActivityCalled bool
}{ }{
{ {
name: "noteRecvActivity called", name: "noteRecvActivity-called",
callWithPeerMapKey: true, callWithPeerMapKey: true,
maybeEPMatchingKey: false, maybeEPMatchingKey: false,
wantNoteRecvActivityCalled: true, wantNoteRecvActivityCalled: true,
}, },
{ {
name: "maybeEP early return", name: "maybeEP-early-return",
callWithPeerMapKey: true, callWithPeerMapKey: true,
maybeEPMatchingKey: true, maybeEPMatchingKey: true,
wantNoteRecvActivityCalled: false, wantNoteRecvActivityCalled: false,
}, },
{ {
name: "not in peerMap early return", name: "not-in-peerMap-early-return",
callWithPeerMapKey: false, callWithPeerMapKey: false,
maybeEPMatchingKey: false, maybeEPMatchingKey: false,
wantNoteRecvActivityCalled: false, wantNoteRecvActivityCalled: false,
}, },
{ {
name: "not in peerMap maybeEP early return", name: "not-in-peerMap-maybeEP-early-return",
callWithPeerMapKey: false, callWithPeerMapKey: false,
maybeEPMatchingKey: true, maybeEPMatchingKey: true,
wantNoteRecvActivityCalled: false, wantNoteRecvActivityCalled: false,
@ -4232,25 +4232,25 @@ func Test_lazyEndpoint_FromPeer(t *testing.T) {
wantEpAddrInPeerMap bool wantEpAddrInPeerMap bool
}{ }{
{ {
name: "epAddr in peerMap", name: "epAddr-in-peerMap",
callWithPeerMapKey: true, callWithPeerMapKey: true,
maybeEPMatchingKey: false, maybeEPMatchingKey: false,
wantEpAddrInPeerMap: true, wantEpAddrInPeerMap: true,
}, },
{ {
name: "maybeEP early return", name: "maybeEP-early-return",
callWithPeerMapKey: true, callWithPeerMapKey: true,
maybeEPMatchingKey: true, maybeEPMatchingKey: true,
wantEpAddrInPeerMap: false, wantEpAddrInPeerMap: false,
}, },
{ {
name: "not in peerMap early return", name: "not-in-peerMap-early-return",
callWithPeerMapKey: false, callWithPeerMapKey: false,
maybeEPMatchingKey: false, maybeEPMatchingKey: false,
wantEpAddrInPeerMap: false, wantEpAddrInPeerMap: false,
}, },
{ {
name: "not in peerMap maybeEP early return", name: "not-in-peerMap-maybeEP-early-return",
callWithPeerMapKey: false, callWithPeerMapKey: false,
maybeEPMatchingKey: true, maybeEPMatchingKey: true,
wantEpAddrInPeerMap: false, wantEpAddrInPeerMap: false,

@ -141,7 +141,7 @@ func TestRelayManager_handleNewServerEndpointRunLoop(t *testing.T) {
}{ }{
{ {
// Test for http://go/corp/32978 // Test for http://go/corp/32978
name: "eq server+ep neq VNI higher lamport", name: "eq-server-ep-neq-VNI-higher-lamport",
events: []newRelayServerEndpointEvent{ events: []newRelayServerEndpointEvent{
serverAendpointALamport1VNI1, serverAendpointALamport1VNI1,
serverAendpointALamport2VNI2, serverAendpointALamport2VNI2,
@ -151,7 +151,7 @@ func TestRelayManager_handleNewServerEndpointRunLoop(t *testing.T) {
}, },
}, },
{ {
name: "eq server+ep neq VNI lower lamport", name: "eq-server-ep-neq-VNI-lower-lamport",
events: []newRelayServerEndpointEvent{ events: []newRelayServerEndpointEvent{
serverAendpointALamport2VNI2, serverAendpointALamport2VNI2,
serverAendpointALamport1VNI1, serverAendpointALamport1VNI1,
@ -161,7 +161,7 @@ func TestRelayManager_handleNewServerEndpointRunLoop(t *testing.T) {
}, },
}, },
{ {
name: "eq server+vni neq ep lower lamport", name: "eq-server-vni-neq-ep-lower-lamport",
events: []newRelayServerEndpointEvent{ events: []newRelayServerEndpointEvent{
serverAendpointALamport2VNI2, serverAendpointALamport2VNI2,
serverAendpointBLamport1VNI2, serverAendpointBLamport1VNI2,
@ -171,7 +171,7 @@ func TestRelayManager_handleNewServerEndpointRunLoop(t *testing.T) {
}, },
}, },
{ {
name: "eq server+vni neq ep higher lamport", name: "eq-server-vni-neq-ep-higher-lamport",
events: []newRelayServerEndpointEvent{ events: []newRelayServerEndpointEvent{
serverAendpointBLamport1VNI2, serverAendpointBLamport1VNI2,
serverAendpointALamport2VNI2, serverAendpointALamport2VNI2,
@ -181,7 +181,7 @@ func TestRelayManager_handleNewServerEndpointRunLoop(t *testing.T) {
}, },
}, },
{ {
name: "eq server+endpoint+vni higher lamport", name: "eq-server-endpoint-vni-higher-lamport",
events: []newRelayServerEndpointEvent{ events: []newRelayServerEndpointEvent{
serverAendpointALamport1VNI1, serverAendpointALamport1VNI1,
serverAendpointALamport2VNI1, serverAendpointALamport2VNI1,
@ -191,7 +191,7 @@ func TestRelayManager_handleNewServerEndpointRunLoop(t *testing.T) {
}, },
}, },
{ {
name: "eq server+endpoint+vni lower lamport", name: "eq-server-endpoint-vni-lower-lamport",
events: []newRelayServerEndpointEvent{ events: []newRelayServerEndpointEvent{
serverAendpointALamport2VNI1, serverAendpointALamport2VNI1,
serverAendpointALamport1VNI1, serverAendpointALamport1VNI1,
@ -201,7 +201,7 @@ func TestRelayManager_handleNewServerEndpointRunLoop(t *testing.T) {
}, },
}, },
{ {
name: "eq endpoint+vni+lamport neq server", name: "eq-endpoint-vni-lamport-neq-server",
events: []newRelayServerEndpointEvent{ events: []newRelayServerEndpointEvent{
serverAendpointALamport1VNI1, serverAendpointALamport1VNI1,
serverBendpointALamport1VNI1, serverBendpointALamport1VNI1,
@ -212,7 +212,7 @@ func TestRelayManager_handleNewServerEndpointRunLoop(t *testing.T) {
}, },
}, },
{ {
name: "trusted last best with matching server", name: "trusted-last-best-with-matching-server",
events: []newRelayServerEndpointEvent{ events: []newRelayServerEndpointEvent{
serverAendpointALamport1VNI1LastBestMatching, serverAendpointALamport1VNI1LastBestMatching,
}, },

@ -54,13 +54,13 @@ ip rule add -6 pref 5270 table 52
want string want string
}{ }{
{ {
name: "no config", name: "no-config",
in: nil, in: nil,
want: ` want: `
up` + basic, up` + basic,
}, },
{ {
name: "local addr only", name: "local-addr-only",
in: &Config{ in: &Config{
LocalAddrs: mustCIDRs("100.101.102.103/10"), LocalAddrs: mustCIDRs("100.101.102.103/10"),
NetfilterMode: netfilterOff, NetfilterMode: netfilterOff,
@ -71,7 +71,7 @@ ip addr add 100.101.102.103/10 dev tailscale0` + basic,
}, },
{ {
name: "addr and routes", name: "addr-and-routes",
in: &Config{ in: &Config{
LocalAddrs: mustCIDRs("100.101.102.103/10"), LocalAddrs: mustCIDRs("100.101.102.103/10"),
Routes: mustCIDRs("100.100.100.100/32", "192.168.16.0/24"), Routes: mustCIDRs("100.100.100.100/32", "192.168.16.0/24"),
@ -85,7 +85,7 @@ ip route add 192.168.16.0/24 dev tailscale0 table 52` + basic,
}, },
{ {
name: "addr and routes and subnet routes", name: "addr-routes-and-subnet-routes",
in: &Config{ in: &Config{
LocalAddrs: mustCIDRs("100.101.102.103/10"), LocalAddrs: mustCIDRs("100.101.102.103/10"),
Routes: mustCIDRs("100.100.100.100/32", "192.168.16.0/24"), Routes: mustCIDRs("100.100.100.100/32", "192.168.16.0/24"),
@ -100,7 +100,7 @@ ip route add 192.168.16.0/24 dev tailscale0 table 52` + basic,
}, },
{ {
name: "addr and routes and subnet routes with netfilter", name: "addr-routes-subnet-routes-with-netfilter",
in: &Config{ in: &Config{
LocalAddrs: mustCIDRs("100.101.102.104/10"), LocalAddrs: mustCIDRs("100.101.102.104/10"),
Routes: mustCIDRs("100.100.100.100/32", "10.0.0.0/8"), Routes: mustCIDRs("100.100.100.100/32", "10.0.0.0/8"),
@ -141,7 +141,7 @@ v6/nat/ts-postrouting -m mark --mark 0x40000/0xff0000 -j MASQUERADE
`, `,
}, },
{ {
name: "addr and routes and subnet routes with netfilter but no stateful filtering", name: "addr-routes-subnet-routes-netfilter-no-stateful",
in: &Config{ in: &Config{
LocalAddrs: mustCIDRs("100.101.102.104/10"), LocalAddrs: mustCIDRs("100.101.102.104/10"),
Routes: mustCIDRs("100.100.100.100/32", "10.0.0.0/8"), Routes: mustCIDRs("100.100.100.100/32", "10.0.0.0/8"),
@ -180,7 +180,7 @@ v6/nat/ts-postrouting -m mark --mark 0x40000/0xff0000 -j MASQUERADE
`, `,
}, },
{ {
name: "addr and routes with netfilter", name: "addr-and-routes-with-netfilter",
in: &Config{ in: &Config{
LocalAddrs: mustCIDRs("100.101.102.104/10"), LocalAddrs: mustCIDRs("100.101.102.104/10"),
Routes: mustCIDRs("100.100.100.100/32", "10.0.0.0/8"), Routes: mustCIDRs("100.100.100.100/32", "10.0.0.0/8"),
@ -215,7 +215,7 @@ v6/nat/POSTROUTING -j ts-postrouting
}, },
{ {
name: "addr and routes and subnet routes with netfilter but no SNAT", name: "addr-routes-subnet-routes-netfilter-no-SNAT",
in: &Config{ in: &Config{
LocalAddrs: mustCIDRs("100.101.102.104/10"), LocalAddrs: mustCIDRs("100.101.102.104/10"),
Routes: mustCIDRs("100.100.100.100/32", "10.0.0.0/8"), Routes: mustCIDRs("100.100.100.100/32", "10.0.0.0/8"),
@ -251,7 +251,7 @@ v6/nat/POSTROUTING -j ts-postrouting
`, `,
}, },
{ {
name: "addr and routes with netfilter", name: "addr-and-routes-with-netfilter-2",
in: &Config{ in: &Config{
LocalAddrs: mustCIDRs("100.101.102.104/10"), LocalAddrs: mustCIDRs("100.101.102.104/10"),
Routes: mustCIDRs("100.100.100.100/32", "10.0.0.0/8"), Routes: mustCIDRs("100.100.100.100/32", "10.0.0.0/8"),
@ -286,7 +286,7 @@ v6/nat/POSTROUTING -j ts-postrouting
}, },
{ {
name: "addr and routes with half netfilter", name: "addr-and-routes-with-half-netfilter",
in: &Config{ in: &Config{
LocalAddrs: mustCIDRs("100.101.102.104/10"), LocalAddrs: mustCIDRs("100.101.102.104/10"),
Routes: mustCIDRs("100.100.100.100/32", "10.0.0.0/8"), Routes: mustCIDRs("100.100.100.100/32", "10.0.0.0/8"),
@ -310,7 +310,7 @@ v6/filter/ts-forward -o tailscale0 -j ACCEPT
`, `,
}, },
{ {
name: "addr and routes with netfilter2", name: "addr-and-routes-with-netfilter2",
in: &Config{ in: &Config{
LocalAddrs: mustCIDRs("100.101.102.104/10"), LocalAddrs: mustCIDRs("100.101.102.104/10"),
Routes: mustCIDRs("100.100.100.100/32", "10.0.0.0/8"), Routes: mustCIDRs("100.100.100.100/32", "10.0.0.0/8"),
@ -344,7 +344,7 @@ v6/nat/POSTROUTING -j ts-postrouting
`, `,
}, },
{ {
name: "addr, routes, and local routes with netfilter", name: "addr-routes-local-routes-with-netfilter",
in: &Config{ in: &Config{
LocalAddrs: mustCIDRs("100.101.102.104/10"), LocalAddrs: mustCIDRs("100.101.102.104/10"),
Routes: mustCIDRs("100.100.100.100/32", "0.0.0.0/0"), Routes: mustCIDRs("100.100.100.100/32", "0.0.0.0/0"),
@ -380,7 +380,7 @@ v6/nat/POSTROUTING -j ts-postrouting
`, `,
}, },
{ {
name: "addr, routes, and local routes with no netfilter", name: "addr-routes-local-routes-no-netfilter",
in: &Config{ in: &Config{
LocalAddrs: mustCIDRs("100.101.102.104/10"), LocalAddrs: mustCIDRs("100.101.102.104/10"),
Routes: mustCIDRs("100.100.100.100/32", "0.0.0.0/0"), Routes: mustCIDRs("100.100.100.100/32", "0.0.0.0/0"),
@ -396,7 +396,7 @@ ip route add throw 10.0.0.0/8 table 52
ip route add throw 192.168.0.0/24 table 52` + basic, ip route add throw 192.168.0.0/24 table 52` + basic,
}, },
{ {
name: "subnet routes with connmark for rp_filter", name: "subnet-routes-connmark-for-rp_filter",
in: &Config{ in: &Config{
LocalAddrs: mustCIDRs("100.101.102.104/10"), LocalAddrs: mustCIDRs("100.101.102.104/10"),
Routes: mustCIDRs("100.100.100.100/32"), Routes: mustCIDRs("100.100.100.100/32"),
@ -433,7 +433,7 @@ v6/nat/ts-postrouting -m mark --mark 0x40000/0xff0000 -j MASQUERADE
`, `,
}, },
{ {
name: "subnet routes (connmark always enabled)", name: "subnet-routes-connmark-always-enabled",
in: &Config{ in: &Config{
LocalAddrs: mustCIDRs("100.101.102.104/10"), LocalAddrs: mustCIDRs("100.101.102.104/10"),
Routes: mustCIDRs("100.100.100.100/32"), Routes: mustCIDRs("100.100.100.100/32"),
@ -470,7 +470,7 @@ v6/nat/ts-postrouting -m mark --mark 0x40000/0xff0000 -j MASQUERADE
`, `,
}, },
{ {
name: "connmark with stateful filtering", name: "connmark-with-stateful-filtering",
in: &Config{ in: &Config{
LocalAddrs: mustCIDRs("100.101.102.104/10"), LocalAddrs: mustCIDRs("100.101.102.104/10"),
Routes: mustCIDRs("100.100.100.100/32"), Routes: mustCIDRs("100.100.100.100/32"),

@ -26,7 +26,7 @@ func TestWatchdog(t *testing.T) {
maxWaitMultiple = 15 maxWaitMultiple = 15
} }
t.Run("default watchdog does not fire", func(t *testing.T) { t.Run("default-watchdog-does-not-fire", func(t *testing.T) {
t.Parallel() t.Parallel()
bus := eventbustest.NewBus(t) bus := eventbustest.NewBus(t)
ht := health.NewTracker(bus) ht := health.NewTracker(bus)
@ -55,7 +55,7 @@ func TestWatchdogMetrics(t *testing.T) {
wantCounts map[watchdogEvent]int64 wantCounts map[watchdogEvent]int64
}{ }{
{ {
name: "single event types", name: "single-event-types",
events: []watchdogEvent{RequestStatus, PeerForIPEvent, Ping}, events: []watchdogEvent{RequestStatus, PeerForIPEvent, Ping},
wantCounts: map[watchdogEvent]int64{ wantCounts: map[watchdogEvent]int64{
RequestStatus: 1, RequestStatus: 1,
@ -64,7 +64,7 @@ func TestWatchdogMetrics(t *testing.T) {
}, },
}, },
{ {
name: "repeated events", name: "repeated-events",
events: []watchdogEvent{RequestStatus, RequestStatus, Ping, RequestStatus}, events: []watchdogEvent{RequestStatus, RequestStatus, Ping, RequestStatus},
wantCounts: map[watchdogEvent]int64{ wantCounts: map[watchdogEvent]int64{
RequestStatus: 3, RequestStatus: 3,

@ -90,14 +90,14 @@ func TestDeviceConfig(t *testing.T) {
} }
} }
t.Run("device1 config", func(t *testing.T) { t.Run("device1-config", func(t *testing.T) {
if err := ReconfigDevice(device1, cfg1, t.Logf); err != nil { if err := ReconfigDevice(device1, cfg1, t.Logf); err != nil {
t.Fatal(err) t.Fatal(err)
} }
cmp(t, device1, cfg1) cmp(t, device1, cfg1)
}) })
t.Run("device2 config", func(t *testing.T) { t.Run("device2-config", func(t *testing.T) {
if err := ReconfigDevice(device2, cfg2, t.Logf); err != nil { if err := ReconfigDevice(device2, cfg2, t.Logf); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -105,7 +105,7 @@ func TestDeviceConfig(t *testing.T) {
}) })
// This is only to test that Config and Reconfig are properly synchronized. // This is only to test that Config and Reconfig are properly synchronized.
t.Run("device2 config/reconfig", func(t *testing.T) { t.Run("device2-config-reconfig", func(t *testing.T) {
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(2) wg.Add(2)
@ -122,7 +122,7 @@ func TestDeviceConfig(t *testing.T) {
wg.Wait() wg.Wait()
}) })
t.Run("device1 modify peer", func(t *testing.T) { t.Run("device1-modify-peer", func(t *testing.T) {
cfg1.Peers[0].DiscoKey = key.DiscoPublicFromRaw32(mem.B([]byte{0: 1, 31: 0})) cfg1.Peers[0].DiscoKey = key.DiscoPublicFromRaw32(mem.B([]byte{0: 1, 31: 0}))
if err := ReconfigDevice(device1, cfg1, t.Logf); err != nil { if err := ReconfigDevice(device1, cfg1, t.Logf); err != nil {
t.Fatal(err) t.Fatal(err)
@ -130,7 +130,7 @@ func TestDeviceConfig(t *testing.T) {
cmp(t, device1, cfg1) cmp(t, device1, cfg1)
}) })
t.Run("device1 replace endpoint", func(t *testing.T) { t.Run("device1-replace-endpoint", func(t *testing.T) {
cfg1.Peers[0].DiscoKey = key.DiscoPublicFromRaw32(mem.B([]byte{0: 2, 31: 0})) cfg1.Peers[0].DiscoKey = key.DiscoPublicFromRaw32(mem.B([]byte{0: 2, 31: 0}))
if err := ReconfigDevice(device1, cfg1, t.Logf); err != nil { if err := ReconfigDevice(device1, cfg1, t.Logf); err != nil {
t.Fatal(err) t.Fatal(err)
@ -138,7 +138,7 @@ func TestDeviceConfig(t *testing.T) {
cmp(t, device1, cfg1) cmp(t, device1, cfg1)
}) })
t.Run("device1 add new peer", func(t *testing.T) { t.Run("device1-add-new-peer", func(t *testing.T) {
cfg1.Peers = append(cfg1.Peers, Peer{ cfg1.Peers = append(cfg1.Peers, Peer{
PublicKey: k3, PublicKey: k3,
AllowedIPs: []netip.Prefix{ip3}, AllowedIPs: []netip.Prefix{ip3},
@ -178,7 +178,7 @@ func TestDeviceConfig(t *testing.T) {
} }
}) })
t.Run("device1 remove peer", func(t *testing.T) { t.Run("device1-remove-peer", func(t *testing.T) {
removeKey := cfg1.Peers[len(cfg1.Peers)-1].PublicKey removeKey := cfg1.Peers[len(cfg1.Peers)-1].PublicKey
cfg1.Peers = cfg1.Peers[:len(cfg1.Peers)-1] cfg1.Peers = cfg1.Peers[:len(cfg1.Peers)-1]

Loading…
Cancel
Save