package cli import ( "net" "os" "path/filepath" "strings" "testing" "time" ) func TestSocketPathPrefersTheRuntimeDirectory(t *testing.T) { t.Setenv("XDG_RUNTIME_DIR", "/run/user/1000") got, err := socketPath("", "/home/user/.werkdock") if err != nil || got != "/run/user/1000/werkdock.sock" { t.Errorf("got %q, %v", got, err) } // an argument wins, in podman's spelling or plain for _, arg := range []string{"/tmp/other.sock", "unix:///tmp/other.sock"} { if got, err := socketPath(arg, "/store"); err != nil || got != "/tmp/other.sock" { t.Errorf("%q: got %q, %v", arg, got, err) } } t.Setenv("XDG_RUNTIME_DIR", "") if got, err := socketPath("", "/home/user/.werkdock"); err != nil || got != "/home/user/.werkdock/werkdock.sock" { t.Errorf("without a runtime directory: got %q, %v", got, err) } } // A unix socket path has a hard kernel limit; a long home directory // would otherwise fail with an obscure message far from the cause. func TestSocketPathRefusesAPathTooLongForAUnixSocket(t *testing.T) { long := "/" + strings.Repeat("directory/", 12) + "werkdock.sock" _, err := socketPath(long, "/store") if err == nil || !strings.Contains(err.Error(), "too long") { t.Errorf("got %v", err) } } func TestListenReplacesADeadSocketButNotALiveOne(t *testing.T) { dir := t.TempDir() socket := filepath.Join(dir, "werkdock.sock") // a file left behind by a service that died if err := os.WriteFile(socket, nil, 0o600); err != nil { t.Fatal(err) } ln, err := listen(socket) if err != nil { t.Fatalf("stale socket not replaced: %v", err) } defer ln.Close() info, err := os.Stat(socket) if err != nil { t.Fatal(err) } if perm := info.Mode().Perm(); perm != 0o600 { t.Errorf("the socket is the API: mode %o, want 600", perm) } // and now a second service must not take it over accepted := make(chan struct{}, 1) go func() { if conn, err := ln.Accept(); err == nil { conn.Close() accepted <- struct{}{} } }() if _, err := listen(socket); err == nil || !strings.Contains(err.Error(), "already listening") { t.Errorf("second service: %v", err) } select { case <-accepted: case <-time.After(2 * time.Second): t.Error("the live check never connected") } if _, err := net.Dial("unix", socket); err != nil { t.Errorf("the first service lost its socket: %v", err) } }