Files
pmg/cmd/setup/doctor_test.go
T
Sahilb315 cd9b45b3bc fix: triage the unwritable config dir remedy by cause
The chown hint is only correct when another account created files
inside the current user's own home. When a leaked HOME or
XDG_CONFIG_HOME points at another user's home (e.g. sudo -u on GitHub
runners), following it would chown that user's directory and brick
their pmg instead. Classify the failure against the passwd home,
which the leaked environment cannot influence, and prescribe:

- dir inside own home: restore ownership with chown
- dir outside own home: fix the leaked environment, never chown
- explicit PMG_CONFIG_DIR: make it writable

Used by both the fatal event-log error and the doctor check, and the
docs troubleshooting now carries the same two-case triage.
2026-07-14 03:56:07 +05:30

153 lines
4.5 KiB
Go

package setup
import (
"os"
"os/exec"
"path/filepath"
"testing"
"github.com/safedep/pmg/config"
"github.com/safedep/pmg/internal/doctor"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestPathContainsDir(t *testing.T) {
assert.True(t, pathContainsDir([]string{"/usr/local/lib/pmg/bin/"}, "/usr/local/lib/pmg/bin"))
assert.False(t, pathContainsDir([]string{"/usr/local/bin"}, "/usr/local/lib/pmg/bin"))
assert.False(t, pathContainsDir([]string{"/usr/bin"}, ""))
}
func TestPathIsUnderDir(t *testing.T) {
assert.True(t, pathIsUnderDir("/usr/local/lib/pmg/bin/npm", "/usr/local/lib/pmg/bin"))
assert.False(t, pathIsUnderDir("/usr/local/bin/npm", "/usr/local/lib/pmg/bin"))
assert.False(t, pathIsUnderDir("/usr/local/lib/pmg/bin-extra/npm", "/usr/local/lib/pmg/bin"))
}
func TestSystemInstallAliasesPassDoesNotActivateInterception(t *testing.T) {
results := []doctor.CheckResult{
{Name: checkShellAliases, Status: doctor.StatusPass, Message: "No aliases (system install)"},
{Name: checkShimInPath, Status: doctor.StatusFail},
}
assert.False(t, isInterceptionActive(results))
}
func TestAliasesInstalledActivatesInterception(t *testing.T) {
results := []doctor.CheckResult{
{
Name: checkShellAliases,
Status: doctor.StatusPass,
Message: aliasesInstalledMessage,
ImpliesInterception: true,
},
{Name: checkShimInPath, Status: doctor.StatusFail},
}
assert.True(t, isInterceptionActive(results))
}
func TestShimInPathImpliesInterception(t *testing.T) {
results := []doctor.CheckResult{
{
Name: checkShimInPath,
Status: doctor.StatusPass,
Message: "Package managers resolve to System shim directory",
ImpliesInterception: true,
},
}
assert.True(t, isInterceptionActive(results))
}
func TestClassifyPackageManagerResolutions(t *testing.T) {
shimDir := "/usr/local/lib/pmg/bin"
lookPath := func(name string) (string, error) {
switch name {
case "npm":
return shimDir + "/npm", nil
case "pip":
return "/usr/bin/pip", nil
case "uv":
return "", exec.ErrNotFound
default:
return "", exec.ErrNotFound
}
}
under, shadowed := classifyPackageManagerResolutions(
[]string{"npm", "pip", "uv"},
[]string{shimDir},
lookPath,
)
assert.Equal(t, []string{"npm"}, under)
assert.Equal(t, []string{"pip"}, shadowed)
}
func TestClassifyPackageManagerResolutionsAcceptsEitherShimDir(t *testing.T) {
systemDir := "/usr/local/lib/pmg/bin"
userDir := "/home/dev/.pmg/bin"
lookPath := func(name string) (string, error) {
switch name {
case "npm":
return systemDir + "/npm", nil
case "pip":
return userDir + "/pip", nil
default:
return "/usr/bin/" + name, nil
}
}
under, shadowed := classifyPackageManagerResolutions(
[]string{"npm", "pip", "yarn"},
[]string{systemDir, userDir},
lookPath,
)
assert.ElementsMatch(t, []string{"npm", "pip"}, under)
assert.Equal(t, []string{"yarn"}, shadowed)
}
func TestCheckEventLogDirResult(t *testing.T) {
configDir := "/home/dev/.config/safedep/pmg"
t.Run("skipped when event logging disabled", func(t *testing.T) {
result := checkEventLogDirResult(true, t.TempDir(), configDir)
assert.Equal(t, doctor.StatusWarn, result.Status)
})
t.Run("missing directory fails", func(t *testing.T) {
result := checkEventLogDirResult(false, filepath.Join(t.TempDir(), "absent"), configDir)
assert.Equal(t, doctor.StatusFail, result.Status)
assert.Equal(t, "Event log directory not found", result.Message)
})
t.Run("file instead of directory fails", func(t *testing.T) {
path := filepath.Join(t.TempDir(), "logs")
require.NoError(t, os.WriteFile(path, []byte("x"), 0o644))
result := checkEventLogDirResult(false, path, configDir)
assert.Equal(t, doctor.StatusFail, result.Status)
})
t.Run("writable directory passes", func(t *testing.T) {
result := checkEventLogDirResult(false, t.TempDir(), configDir)
assert.Equal(t, doctor.StatusPass, result.Status)
})
t.Run("unwritable directory fails with triaged remedy", func(t *testing.T) {
if os.Geteuid() == 0 {
t.Skip("running as root: directory permissions are not enforced")
}
dir := t.TempDir()
require.NoError(t, os.Chmod(dir, 0o555))
t.Cleanup(func() {
require.NoError(t, os.Chmod(dir, 0o755))
})
result := checkEventLogDirResult(false, dir, configDir)
assert.Equal(t, doctor.StatusFail, result.Status)
assert.Equal(t, "Event log directory not writable", result.Message)
assert.Equal(t, config.UnwritableConfigDirRemedy(configDir), result.Fix)
})
}