package cmd import ( "os" "path/filepath" "strings" "testing" ) // writeEnv creates a .env in dir and returns its path. func writeEnv(t *testing.T, dir, content string) string { t.Helper() path := filepath.Join(dir, envFile) if err := os.WriteFile(path, []byte(content), 0o600); err != nil { t.Fatalf("write .env: %v", err) } return path } // inDir runs fn with the working directory set to dir. func inDir(t *testing.T, dir string, fn func()) { t.Helper() old, err := os.Getwd() if err != nil { t.Fatalf("getwd: %v", err) } if err := os.Chdir(dir); err != nil { t.Fatalf("chdir: %v", err) } t.Cleanup(func() { if err := os.Chdir(old); err != nil { t.Fatalf("restore cwd: %v", err) } }) fn() } // clearEnv removes every credential variable for the duration of the test. func clearEnv(t *testing.T) { t.Helper() for _, k := range []string{envUser, envPassword, envContext, envToken, envEndpoint} { t.Setenv(k, "") if err := os.Unsetenv(k); err != nil { t.Fatalf("unset %s: %v", k, err) } } } func TestLoadReadsEnvFile(t *testing.T) { dir := t.TempDir() writeEnv(t, dir, "SCHLUNDTECH_USER=u\nSCHLUNDTECH_PASSWORD=p\nSCHLUNDTECH_CONTEXT=10\n") clearEnv(t) inDir(t, dir, func() { creds, warning, err := loadCredentials() if err != nil { t.Fatalf("Load: %v", err) } if creds.User != "u" || creds.Password != "p" || creds.Context != "10" { t.Errorf("creds = %+v", creds) } if warning != "" { t.Errorf("warning = %q, want none for a 0600 file", warning) } }) } // The real environment must win, so a single invocation can be redirected // without editing the file. func TestEnvironmentWinsOverEnvFile(t *testing.T) { dir := t.TempDir() writeEnv(t, dir, "SCHLUNDTECH_USER=from-file\nSCHLUNDTECH_PASSWORD=p\nSCHLUNDTECH_CONTEXT=10\n") clearEnv(t) t.Setenv(envUser, "from-environment") inDir(t, dir, func() { creds, _, err := loadCredentials() if err != nil { t.Fatalf("Load: %v", err) } if creds.User != "from-environment" { t.Errorf("user = %q, want the environment to win", creds.User) } }) } // A missing .env is fine: the environment alone is enough. func TestMissingEnvFileIsNotAnError(t *testing.T) { dir := t.TempDir() clearEnv(t) t.Setenv(envUser, "u") t.Setenv(envPassword, "p") t.Setenv(envContext, "10") inDir(t, dir, func() { creds, warning, err := loadCredentials() if err != nil { t.Fatalf("Load: %v", err) } if creds.User != "u" { t.Errorf("user = %q", creds.User) } if warning != "" { t.Errorf("warning = %q, want none", warning) } }) } // The file holds a password, so a permissive mode deserves a warning. func TestLoosePermissionsWarn(t *testing.T) { if os.Getuid() == 0 { t.Skip("running as root: the permission check would not be meaningful") } dir := t.TempDir() path := writeEnv(t, dir, "SCHLUNDTECH_USER=u\nSCHLUNDTECH_PASSWORD=p\nSCHLUNDTECH_CONTEXT=10\n") if err := os.Chmod(path, 0o644); err != nil { t.Fatalf("chmod: %v", err) } clearEnv(t) inDir(t, dir, func() { _, warning, err := loadCredentials() if err != nil { t.Fatalf("Load: %v", err) } if !strings.Contains(warning, "chmod 600") { t.Errorf("warning = %q, want it to suggest chmod 600", warning) } }) } func TestMissingVariablesAreAllReported(t *testing.T) { clearEnv(t) _, err := credentialsFromEnv() if err == nil { t.Fatal("expected an error with nothing set") } msg := err.Error() for _, want := range []string{envUser, envPassword, envContext} { if !strings.Contains(msg, want) { t.Errorf("error %q does not mention %s", msg, want) } } // The message must point at the file, not just the variables. if !strings.Contains(msg, envFile) { t.Errorf("error %q does not mention %s", msg, envFile) } } // A password in the file must never leak into an error message. func TestErrorDoesNotLeakPassword(t *testing.T) { dir := t.TempDir() writeEnv(t, dir, "SCHLUNDTECH_PASSWORD=hunter2-do-not-print\n") clearEnv(t) inDir(t, dir, func() { _, _, err := loadCredentials() if err == nil { t.Fatal("expected an error") } if strings.Contains(err.Error(), "hunter2-do-not-print") { t.Errorf("error leaked the password: %q", err) } }) } func TestMalformedEnvFileIsReported(t *testing.T) { dir := t.TempDir() writeEnv(t, dir, "this is not a valid line\n") clearEnv(t) inDir(t, dir, func() { _, _, err := loadCredentials() if err == nil { t.Fatal("expected a parse error") } if !strings.Contains(err.Error(), envFile) { t.Errorf("error = %q, want it to name the file", err) } }) } func TestOptionalVariablesMayStayEmpty(t *testing.T) { clearEnv(t) t.Setenv(envUser, "u") t.Setenv(envPassword, "p") t.Setenv(envContext, "10") creds, err := credentialsFromEnv() if err != nil { t.Fatalf("FromEnv: %v", err) } if creds.Token != "" { t.Errorf("token = %q, want empty", creds.Token) } if creds.Endpoint != "" { t.Errorf("endpoint = %q, want empty", creds.Endpoint) } }