18 "github.com/stretchr/testify/require"
21// fakeTimeoutErr is a minimal net.Error implementation whose Timeout()
22// returns true. It is used to verify that IsStaleSocketErr never
23// classifies a timeout as stale.
24type fakeTimeoutErr struct{}
26func (fakeTimeoutErr) Error() string { return "fake timeout" }
27func (fakeTimeoutErr) Timeout() bool { return true }
28func (fakeTimeoutErr) Temporary() bool { return true }
30func TestIsStaleSocketErr(t *testing.T) {
38 {name: "nil", err: nil, want: false},
39 {name: "ECONNREFUSED", err: syscall.ECONNREFUSED, want: true},
41 name: "wrapped ECONNREFUSED",
42 err: fmt.Errorf("dial: %w", syscall.ECONNREFUSED),
45 {name: "fs.ErrNotExist", err: fs.ErrNotExist, want: true},
47 name: "wrapped fs.ErrNotExist",
48 err: fmt.Errorf("stat: %w", fs.ErrNotExist),
51 {name: "timeout net.Error", err: fakeTimeoutErr{}, want: false},
52 {name: "generic error", err: errors.New("boom"), want: false},
55 for _, tc := range cases {
56 t.Run(tc.name, func(t *testing.T) {
58 require.Equal(t, tc.want, IsStaleSocketErr(tc.err))
63func TestDefaultHost_XDGRuntimeDir(t *testing.T) {
65 t.Setenv("XDG_RUNTIME_DIR", dir)
69 require.True(t, strings.HasPrefix(host, "unix://"),
70 "DefaultHost should return a unix:// URL, got %q", host)
71 path := strings.TrimPrefix(host, "unix://")
73 // The composed path may exceed maxUnixSocketPathLen and fall back
74 // to /tmp; only assert containment when it did not. Recompose the
75 // path under dir (rather than checking the returned path length,
76 // which is short again after a /tmp fallback) to decide whether a
77 // fallback happened. The socket is named crush-<uid>.sock.
78 composed := filepath.Join(dir, filepath.Base(path))
79 if len(composed) <= maxUnixSocketPathLen {
80 require.True(t, strings.HasPrefix(path, dir),
81 "socket path %q should live under %q", path, dir)
83 require.True(t, strings.HasSuffix(path, ".sock"),
84 "socket path %q should end in .sock", path)
85 require.Contains(t, filepath.Base(path), "crush",
86 "socket filename should contain 'crush'")
89func TestDefaultHost_FallbackTemp(t *testing.T) {
90 t.Setenv("XDG_RUNTIME_DIR", "")
94 require.True(t, strings.HasPrefix(host, "unix://"),
95 "DefaultHost should return a unix:// URL, got %q", host)
96 path := strings.TrimPrefix(host, "unix://")
97 require.NotEmpty(t, path, "fallback socket path must be non-empty")
98 require.True(t, strings.HasSuffix(path, ".sock"),
99 "socket path %q should end in .sock", path)
100 require.Contains(t, filepath.Base(path), "crush",
101 "socket filename should contain 'crush'")
104// staleSocketPath creates a deterministic stale unix socket file on
105// disk: the socket node exists but no goroutine is accepting on it.
106// It does so by binding a listener, disabling unlink-on-close, then
107// closing the listener. The path is returned so the caller can probe
108// it. A leftover file is best-effort removed via t.Cleanup.
109func staleSocketPath(t *testing.T, path string) {
111 ln, err := net.Listen("unix", path) //nolint:noctx
112 require.NoError(t, err)
113 ul, ok := ln.(*net.UnixListener)
114 require.True(t, ok, "expected *net.UnixListener, got %T", ln)
115 ul.SetUnlinkOnClose(false)
116 require.NoError(t, ul.Close())
118 // Verify it is actually stale: dialing should fail.
119 conn, dialErr := net.DialTimeout("unix", path, 200*time.Millisecond) //nolint:noctx
122 t.Fatalf("expected stale socket at %q to refuse connections", path)
124 require.True(t, IsStaleSocketErr(dialErr),
125 "expected stale-socket dial error, got %v", dialErr)
132func TestListen_RemovesStaleSocket(t *testing.T) {
133 // t.TempDir() yields a path that may already be near the macOS
134 // sun_path limit; use a short filename to stay well under it.
136 path := filepath.Join(dir, "s.sock")
138 staleSocketPath(t, path)
140 // Confirm the stale node is present before we call listen.
141 _, statErr := os.Stat(path)
142 require.NoError(t, statErr, "stale socket file should exist on disk")
144 ln, removedStale, err := listen("unix", path)
145 require.NoError(t, err)
146 require.NotNil(t, ln)
147 require.True(t, removedStale, "listen should report removedStale=true")
153func TestListen_LiveSocketNotRemoved(t *testing.T) {
155 path := filepath.Join(dir, "s.sock")
157 ln1, err := net.Listen("unix", path) //nolint:noctx
158 require.NoError(t, err)
160 // Drain accepts so the listener stays alive and responsive without
161 // blocking the test on a stray connection.
162 var wg sync.WaitGroup
167 c, err := ln1.Accept()
179 ln2, removedStale, err := listen("unix", path)
183 require.Error(t, err, "listen on a live socket must fail")
184 require.False(t, removedStale,
185 "a live socket must never be removed (got removedStale=true)")
187 // The live socket file must still be on disk and dialable.
188 _, statErr := os.Stat(path)
189 require.NoError(t, statErr, "live socket file should still exist")
190 conn, dialErr := net.DialTimeout("unix", path, 200*time.Millisecond) //nolint:noctx
191 require.NoError(t, dialErr, "live socket should still accept dials")
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list