package paths import ( "os" "path/filepath" "strings" "testing" ) func TestResolveUsesXDGOverrides(t *testing.T) { dir := t.TempDir() t.Setenv("XDG_CONFIG_HOME", filepath.Join(dir, "config")) t.Setenv("XDG_STATE_HOME", filepath.Join(dir, "state")) t.Setenv("XDG_CACHE_HOME", filepath.Join(dir, "cache")) t.Setenv("XDG_RUNTIME_DIR", filepath.Join(dir, "run")) layout, err := Resolve() if err != nil { t.Fatalf("Resolve: %v", err) } if layout.ConfigDir != filepath.Join(dir, "config", "banger") { t.Errorf("ConfigDir = %q", layout.ConfigDir) } if layout.StateDir != filepath.Join(dir, "state", "banger") { t.Errorf("StateDir = %q", layout.StateDir) } if layout.CacheDir != filepath.Join(dir, "cache", "banger") { t.Errorf("CacheDir = %q", layout.CacheDir) } if layout.RuntimeDir != filepath.Join(dir, "run", "banger") { t.Errorf("RuntimeDir = %q", layout.RuntimeDir) } if !strings.HasSuffix(layout.SocketPath, "bangerd.sock") { t.Errorf("SocketPath = %q", layout.SocketPath) } if !strings.HasSuffix(layout.DBPath, "state.db") { t.Errorf("DBPath = %q", layout.DBPath) } } func TestResolveFallsBackWhenRuntimeUnset(t *testing.T) { t.Setenv("XDG_RUNTIME_DIR", "") layout, err := Resolve() if err != nil { t.Fatalf("Resolve: %v", err) } if !strings.Contains(layout.RuntimeDir, "banger-runtime-") { t.Errorf("expected fallback runtime dir, got %q", layout.RuntimeDir) } } func TestEnsureCreatesAllDirs(t *testing.T) { base := t.TempDir() layout := Layout{ ConfigDir: filepath.Join(base, "config"), StateDir: filepath.Join(base, "state"), CacheDir: filepath.Join(base, "cache"), RuntimeDir: filepath.Join(base, "runtime"), VMsDir: filepath.Join(base, "state/vms"), ImagesDir: filepath.Join(base, "state/images"), KernelsDir: filepath.Join(base, "state/kernels"), OCICacheDir: filepath.Join(base, "cache/oci"), } if err := Ensure(layout); err != nil { t.Fatalf("Ensure: %v", err) } for _, dir := range []string{ layout.ConfigDir, layout.StateDir, layout.CacheDir, layout.RuntimeDir, layout.VMsDir, layout.ImagesDir, layout.KernelsDir, layout.OCICacheDir, } { info, err := os.Stat(dir) if err != nil { t.Errorf("stat %q: %v", dir, err) continue } if !info.IsDir() { t.Errorf("%q is not a directory", dir) } } // Idempotent. if err := Ensure(layout); err != nil { t.Fatalf("Ensure (second run): %v", err) } } func TestBangerdPathEnvOverride(t *testing.T) { t.Setenv("BANGER_DAEMON_BIN", "/tmp/custom-bangerd") got, err := BangerdPath() if err != nil { t.Fatalf("BangerdPath: %v", err) } if got != "/tmp/custom-bangerd" { t.Errorf("got %q, want /tmp/custom-bangerd", got) } } func TestBangerdPathFindsSiblingBinary(t *testing.T) { t.Setenv("BANGER_DAEMON_BIN", "") root := t.TempDir() sibling := filepath.Join(root, "bangerd") if err := os.WriteFile(sibling, []byte("#!/bin/sh\n"), 0o755); err != nil { t.Fatalf("WriteFile: %v", err) } original := executablePath executablePath = func() (string, error) { return filepath.Join(root, "banger"), nil } t.Cleanup(func() { executablePath = original }) got, err := BangerdPath() if err != nil { t.Fatalf("BangerdPath: %v", err) } if got != sibling { t.Errorf("got %q, want %q", got, sibling) } } func TestBangerdPathNotFound(t *testing.T) { t.Setenv("BANGER_DAEMON_BIN", "") root := t.TempDir() original := executablePath executablePath = func() (string, error) { return filepath.Join(root, "banger"), nil } t.Cleanup(func() { executablePath = original }) if _, err := BangerdPath(); err == nil { t.Fatal("expected error when no sibling bangerd exists") } }