package cli import ( "context" "errors" "os" "path/filepath" "strings" "testing" ) // clearGlobalEnv neutralizes the BUNNY_ env fallbacks so a test controls // resolution entirely through flags. func clearGlobalEnv(t *testing.T) { t.Helper() for _, k := range []string{"BUNNY_ZONE", "BUNNY_ENDPOINT", "BUNNY_ACCESS_KEY_FILE", "BUNNY_ACCESS_KEY"} { t.Setenv(k, "") } } func writeKeyFile(t *testing.T) string { t.Helper() p := filepath.Join(t.TempDir(), "key") if err := os.WriteFile(p, []byte("secret\n"), 0o600); err != nil { t.Fatal(err) } return p } func runMain(args []string) error { return newMainCommand().Run(context.Background(), args) } func TestMainMissingSubcommand(t *testing.T) { clearGlobalEnv(t) captureLogs(t) var ue usageError if err := runMain(nil); !errors.As(err, &ue) { t.Fatalf("expected usageError, got %v", err) } } func TestMainMissingZone(t *testing.T) { clearGlobalEnv(t) captureLogs(t) var ue usageError err := runMain([]string{"push"}) if !errors.As(err, &ue) || !strings.Contains(err.Error(), "zone") { t.Fatalf("expected missing-zone usageError, got %v", err) } } func TestMainMissingAccessKey(t *testing.T) { clearGlobalEnv(t) captureLogs(t) var ue usageError err := runMain([]string{"--zone", "z", "push"}) if !errors.As(err, &ue) || !strings.Contains(err.Error(), "access key") { t.Fatalf("expected missing-access-key usageError, got %v", err) } } func TestMainUnknownSubcommand(t *testing.T) { clearGlobalEnv(t) captureLogs(t) kf := writeKeyFile(t) var ue usageError err := runMain([]string{"--zone", "z", "--access-key-file", kf, "frob"}) if !errors.As(err, &ue) || !strings.Contains(err.Error(), "unknown subcommand") { t.Fatalf("expected unknown-subcommand usageError, got %v", err) } } func TestMainDispatchesToPrune(t *testing.T) { clearGlobalEnv(t) captureLogs(t) kf := writeKeyFile(t) // A fully-resolved global set reaches dispatch; the missing archive surfaces a // plain (non-usage) error, proving parse + validation + dispatch worked. err := runMain([]string{"--zone", "z", "--access-key-file", kf, "prune", "no-such.tgz"}) if err == nil || !strings.Contains(err.Error(), "open archive") { t.Fatalf("expected prune dispatch error, got %v", err) } } func TestMainAccessKeyFromEnv(t *testing.T) { clearGlobalEnv(t) captureLogs(t) t.Setenv("BUNNY_ACCESS_KEY", "literalkey") // With no --access-key-file, the literal BUNNY_ACCESS_KEY resolves the key // and resolution reaches prune. err := runMain([]string{"--zone", "z", "prune", "no-such.tgz"}) if err == nil || !strings.Contains(err.Error(), "open archive") { t.Fatalf("expected prune dispatch error (env access key resolved), got %v", err) } } func TestMainZoneFromEnv(t *testing.T) { clearGlobalEnv(t) captureLogs(t) t.Setenv("BUNNY_ZONE", "envzone") kf := writeKeyFile(t) // With zone supplied via env, resolution succeeds and reaches prune. err := runMain([]string{"--access-key-file", kf, "prune", "no-such.tgz"}) if err == nil || !strings.Contains(err.Error(), "open archive") { t.Fatalf("expected prune dispatch error (env zone resolved), got %v", err) } } func TestLookupEnv(t *testing.T) { t.Setenv("BUNNY_TEST_LOOKUP", "value") if got := lookupEnv("BUNNY_TEST_LOOKUP", "fallback"); got != "value" { t.Fatalf("set env: got %q", got) } t.Setenv("BUNNY_TEST_LOOKUP", "") if got := lookupEnv("BUNNY_TEST_LOOKUP", "fallback"); got != "fallback" { t.Fatalf("empty env should fall back: got %q", got) } }