diff --git a/internal/cli/admin.go b/internal/cli/admin.go index 804d220..b0c7f0e 100644 --- a/internal/cli/admin.go +++ b/internal/cli/admin.go @@ -330,7 +330,19 @@ func runWhitelist(args []string, role store.Role, out, errOut io.Writer) int { return 2 } dir := store.Direction(args[0]) + if dir != store.DirIn && dir != store.DirOut { + fmt.Fprintf(errOut, "whitelist direction must be \"in\" or \"out\", got %q\n", args[0]) + fmt.Fprintln(errOut, "usage: emcli whitelist [flags]") + return 2 + } sub, rest := args[1], args[2:] + switch sub { + case "add", "remove", "list": // valid + default: + fmt.Fprintf(errOut, "unknown whitelist subcommand %q (want add|remove|list)\n", sub) + fmt.Fprintln(errOut, "usage: emcli whitelist [flags]") + return 2 + } fs := flag.NewFlagSet("whitelist", flag.ContinueOnError) fs.SetOutput(errOut) account := fs.String("account", "", "account name") @@ -371,9 +383,6 @@ func runWhitelist(args []string, role store.Role, out, errOut io.Writer) int { for _, a := range addrs { fmt.Fprintln(out, a) } - default: - fmt.Fprintf(errOut, "unknown whitelist subcommand %q\n", sub) - return 2 } return 0 } diff --git a/internal/cli/admin_test.go b/internal/cli/admin_test.go index 415751e..e836c36 100644 --- a/internal/cli/admin_test.go +++ b/internal/cli/admin_test.go @@ -110,6 +110,51 @@ func TestAccountEditPartialPreservesOtherFields(t *testing.T) { } } +// A missing direction (e.g. `whitelist list`) must report the real problem — +// the in|out direction — not the misleading "--account is required". +func TestWhitelistMissingDirectionReported(t *testing.T) { + adminEnv(t) + code, _, errOut := run(t, "whitelist", "list", "--account", "bobby") + if code == 0 { + t.Fatal("missing direction must be a usage error") + } + if strings.Contains(errOut, "--account is required") { + t.Fatalf("misleading error; want a direction complaint, got: %q", errOut) + } + if !strings.Contains(errOut, "in") || !strings.Contains(errOut, "out") { + t.Fatalf("error should name the in|out direction, got: %q", errOut) + } +} + +// A missing subcommand (e.g. `whitelist out --account x`) must report the real +// problem — the add|remove|list subcommand — not "--account is required". +func TestWhitelistMissingSubcommandReported(t *testing.T) { + adminEnv(t) + code, _, errOut := run(t, "whitelist", "out", "--account", "bobby") + if code == 0 { + t.Fatal("missing subcommand must be a usage error") + } + if strings.Contains(errOut, "--account is required") { + t.Fatalf("misleading error; want a subcommand complaint, got: %q", errOut) + } + if !strings.Contains(errOut, "add") || !strings.Contains(errOut, "list") { + t.Fatalf("error should name the add|remove|list subcommand, got: %q", errOut) + } +} + +// The happy path still works after the direction/subcommand validation. +func TestWhitelistListWorks(t *testing.T) { + adminEnv(t) + run(t, "account", "add", "--name", "bobby", "--imap-host", "h", "--username", "u@x.com") + if code, _, e := run(t, "whitelist", "out", "add", "--account", "bobby", "--address", "@x.com"); code != 0 { + t.Fatalf("add failed: %s", e) + } + code, out, _ := run(t, "whitelist", "out", "list", "--account", "bobby") + if code != 0 || !strings.Contains(out, "@x.com") { + t.Fatalf("list: code=%d out=%q", code, out) + } +} + func TestAuditListCoreRenders(t *testing.T) { st, err := store.Open(filepath.Join(t.TempDir(), "e.db")) if err != nil {