cmd/loop: print homedir in help messages as ~

Make the output homedir-independent to facilitate tests.
This commit is contained in:
Boris Nagaev 2026-02-02 19:22:41 -05:00
parent ea03440f29
commit 61945bd9f9
No known key found for this signature in database
2 changed files with 130 additions and 10 deletions

View file

@ -0,0 +1,82 @@
package main
import (
"errors"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
)
// TestDefaultPathText verifies HOME-based path elision behavior used for help
// defaults without relying on the real environment.
func TestDefaultPathText(t *testing.T) {
sep := string(filepath.Separator)
home := filepath.Clean(sep + "home" + sep + "alice")
homeDir := func() (string, error) { return home, nil }
tests := []struct {
name string
value string
homeFn func() (string, error)
want string
}{
{
name: "empty value",
value: "",
homeFn: func() (string, error) {
return home, nil
},
want: "",
},
{
name: "nil homedir func",
value: home + sep + "data",
homeFn: nil,
want: home + sep + "data",
},
{
name: "homedir error",
value: home + sep + "data",
homeFn: func() (string, error) {
return "", errors.New("homedir error")
},
want: home + sep + "data",
},
{
name: "exact home",
value: home,
homeFn: homeDir,
want: "~",
},
{
name: "home prefix",
value: home + sep + "dir" + sep + "file",
homeFn: homeDir,
want: "~" + sep + "dir" + sep + "file",
},
{
name: "non-home path",
value: filepath.Clean(sep + "var" + sep + "tmp"),
homeFn: homeDir,
want: filepath.Clean(sep + "var" + sep + "tmp"),
},
{
name: "prefix but not path segment",
value: home + "x" + sep + "dir",
homeFn: homeDir,
want: home + "x" + sep + "dir",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
got := defaultPathText(test.value, test.homeFn)
require.Equalf(
t, test.want, got,
"defaultPathText(%q)", test.value,
)
})
}
}

View file

@ -52,10 +52,11 @@ var (
defaultInitiator = "loop-cli"
loopDirFlag = &cli.StringFlag{
Name: "loopdir",
Value: loopd.LoopDirBase,
Usage: "path to loop's base directory",
Sources: cli.EnvVars(envVarLoopDir),
Name: "loopdir",
Value: loopd.LoopDirBase,
DefaultText: defaultPathText(loopd.LoopDirBase, os.UserHomeDir),
Usage: "path to loop's base directory",
Sources: cli.EnvVars(envVarLoopDir),
}
networkFlag = &cli.StringFlag{
Name: "network",
@ -66,15 +67,21 @@ var (
}
tlsCertFlag = &cli.StringFlag{
Name: "tlscertpath",
Usage: "path to loop's TLS certificate",
Value: loopd.DefaultTLSCertPath,
Name: "tlscertpath",
Usage: "path to loop's TLS certificate",
Value: loopd.DefaultTLSCertPath,
DefaultText: defaultPathText(
loopd.DefaultTLSCertPath, os.UserHomeDir,
),
Sources: cli.EnvVars(envVarTLSCertPath),
}
macaroonPathFlag = &cli.StringFlag{
Name: "macaroonpath",
Usage: "path to macaroon file",
Value: loopd.DefaultMacaroonPath,
Name: "macaroonpath",
Usage: "path to macaroon file",
Value: loopd.DefaultMacaroonPath,
DefaultText: defaultPathText(
loopd.DefaultMacaroonPath, os.UserHomeDir,
),
Sources: cli.EnvVars(envVarMacaroonPath),
}
verboseFlag = &cli.BoolFlag{
@ -132,6 +139,37 @@ const (
envVarMacaroonPath = "LOOPCLI_MACAROONPATH"
)
// defaultPathText returns a help-friendly path string that replaces the user's
// home directory with "~". The homeDir function is injected so callers can
// control environment-dependent behavior in tests.
func defaultPathText(value string, homeDir func() (string, error)) string {
if value == "" {
return value
}
if homeDir == nil {
return value
}
home, err := homeDir()
if err != nil || home == "" {
return value
}
cleanHome := filepath.Clean(home)
cleanValue := filepath.Clean(value)
if cleanValue == cleanHome {
return "~"
}
prefix := cleanHome + string(filepath.Separator)
if suffix, ok := strings.CutPrefix(cleanValue, prefix); ok {
return "~" + string(filepath.Separator) + suffix
}
return value
}
func printJSON(resp any) {
b, err := json.Marshal(resp)
if err != nil {