fix(cli): route invalid browse args to cobra
- commit
- 1630cf54dad8e01052baf720c07f3c2a0dd75327
- author
- brice · · 2026-04-08 12:05 UTC
- parent
- 7396109
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
4 files · +44 -0
M cmd/dumber/main.go +7 -0
| @@ -7,6 +7,7 @@ import ( | |||
| 7 | 7 | "os" | |
| 8 | 8 | "os/signal" | |
| 9 | 9 | "runtime" | |
| 10 | + | "strings" | |
| 10 | 11 | "syscall" | |
| 11 | 12 | ||
| 12 | 13 | "github.com/bnema/dumber/internal/application/port" | |
| @@ -63,7 +64,13 @@ func launchModeFromArgs(args []string) (launchMode, string) { | |||
| 63 | 64 | if len(args) > 1 { | |
| 64 | 65 | switch args[1] { | |
| 65 | 66 | case "browse": | |
| 67 | + | if len(args) > 3 { | |
| 68 | + | return launchModeCLI, "" | |
| 69 | + | } | |
| 66 | 70 | if len(args) > 2 { | |
| 71 | + | if strings.HasPrefix(args[2], "-") { | |
| 72 | + | return launchModeCLI, "" | |
| 73 | + | } | |
| 67 | 74 | return launchModeBrowse, args[2] | |
| 68 | 75 | } | |
| 69 | 76 | return launchModeBrowse, "" | |
| @@ -7,6 +7,7 @@ import ( | |||||
| 7 | "os" | 7 | "os" | ||
| 8 | "os/signal" | 8 | "os/signal" | ||
| 9 | "runtime" | 9 | "runtime" | ||
| 10 | + | "strings" | |||
| 10 | "syscall" | 11 | "syscall" | ||
| 11 | 12 | ||||
| 12 | "github.com/bnema/dumber/internal/application/port" | 13 | "github.com/bnema/dumber/internal/application/port" | ||
| @@ -63,7 +64,13 @@ func launchModeFromArgs(args []string) (launchMode, string) { | |||||
| 63 | if len(args) > 1 { | 64 | if len(args) > 1 { | ||
| 64 | switch args[1] { | 65 | switch args[1] { | ||
| 65 | case "browse": | 66 | case "browse": | ||
| 67 | + | if len(args) > 3 { | |||
| 68 | + | return launchModeCLI, "" | |||
| 69 | + | } | |||
| 66 | if len(args) > 2 { | 70 | if len(args) > 2 { | ||
| 71 | + | if strings.HasPrefix(args[2], "-") { | |||
| 72 | + | return launchModeCLI, "" | |||
| 73 | + | } | |||
| 67 | return launchModeBrowse, args[2] | 74 | return launchModeBrowse, args[2] | ||
| 68 | } | 75 | } | ||
| 69 | return launchModeBrowse, "" | 76 | return launchModeBrowse, "" | ||
M cmd/dumber/main_test.go +20 -0
| @@ -22,6 +22,26 @@ func TestLaunchModeFromArgs_DetectsBrowseURL(t *testing.T) { | |||
| 22 | 22 | } | |
| 23 | 23 | } | |
| 24 | 24 | ||
| 25 | + | func TestLaunchModeFromArgs_BrowseHelpFallsBackToCLI(t *testing.T) { | |
| 26 | + | mode, browseURL := launchModeFromArgs([]string{"dumber", "browse", "--help"}) | |
| 27 | + | if mode != launchModeCLI { | |
| 28 | + | t.Fatalf("expected cli mode for browse help, got %q", mode) | |
| 29 | + | } | |
| 30 | + | if browseURL != "" { | |
| 31 | + | t.Fatalf("expected empty browse url for browse help, got %q", browseURL) | |
| 32 | + | } | |
| 33 | + | } | |
| 34 | + | ||
| 35 | + | func TestLaunchModeFromArgs_BrowseExtraPositionalFallsBackToCLI(t *testing.T) { | |
| 36 | + | mode, browseURL := launchModeFromArgs([]string{"dumber", "browse", "https://example.com", "extra"}) | |
| 37 | + | if mode != launchModeCLI { | |
| 38 | + | t.Fatalf("expected cli mode for browse extra args, got %q", mode) | |
| 39 | + | } | |
| 40 | + | if browseURL != "" { | |
| 41 | + | t.Fatalf("expected empty browse url for browse extra args, got %q", browseURL) | |
| 42 | + | } | |
| 43 | + | } | |
| 44 | + | ||
| 25 | 45 | func TestLaunchModeFromArgs_DefaultsToCLI(t *testing.T) { | |
| 26 | 46 | mode, browseURL := launchModeFromArgs([]string{"dumber"}) | |
| 27 | 47 | if mode != launchModeCLI { | |
| @@ -22,6 +22,26 @@ func TestLaunchModeFromArgs_DetectsBrowseURL(t *testing.T) { | |||||
| 22 | } | 22 | } | ||
| 23 | } | 23 | } | ||
| 24 | 24 | ||||
| 25 | + | func TestLaunchModeFromArgs_BrowseHelpFallsBackToCLI(t *testing.T) { | |||
| 26 | + | mode, browseURL := launchModeFromArgs([]string{"dumber", "browse", "--help"}) | |||
| 27 | + | if mode != launchModeCLI { | |||
| 28 | + | t.Fatalf("expected cli mode for browse help, got %q", mode) | |||
| 29 | + | } | |||
| 30 | + | if browseURL != "" { | |||
| 31 | + | t.Fatalf("expected empty browse url for browse help, got %q", browseURL) | |||
| 32 | + | } | |||
| 33 | + | } | |||
| 34 | + | ||||
| 35 | + | func TestLaunchModeFromArgs_BrowseExtraPositionalFallsBackToCLI(t *testing.T) { | |||
| 36 | + | mode, browseURL := launchModeFromArgs([]string{"dumber", "browse", "https://example.com", "extra"}) | |||
| 37 | + | if mode != launchModeCLI { | |||
| 38 | + | t.Fatalf("expected cli mode for browse extra args, got %q", mode) | |||
| 39 | + | } | |||
| 40 | + | if browseURL != "" { | |||
| 41 | + | t.Fatalf("expected empty browse url for browse extra args, got %q", browseURL) | |||
| 42 | + | } | |||
| 43 | + | } | |||
| 44 | + | ||||
| 25 | func TestLaunchModeFromArgs_DefaultsToCLI(t *testing.T) { | 45 | func TestLaunchModeFromArgs_DefaultsToCLI(t *testing.T) { | ||
| 26 | mode, browseURL := launchModeFromArgs([]string{"dumber"}) | 46 | mode, browseURL := launchModeFromArgs([]string{"dumber"}) | ||
| 27 | if mode != launchModeCLI { | 47 | if mode != launchModeCLI { | ||
M internal/cli/cmd/omnibox_test.go +16 -0
| @@ -11,3 +11,19 @@ func TestRootCommand_RegistersOmniboxCommand(t *testing.T) { | |||
| 11 | 11 | t.Fatalf("expected omnibox command, got %#v", cmd) | |
| 12 | 12 | } | |
| 13 | 13 | } | |
| 14 | + | ||
| 15 | + | func TestRootCommand_BrowseCommandRejectsExtraArgs(t *testing.T) { | |
| 16 | + | cmd, _, err := rootCmd.Find([]string{"browse"}) | |
| 17 | + | if err != nil { | |
| 18 | + | t.Fatalf("expected browse command to be registered: %v", err) | |
| 19 | + | } | |
| 20 | + | if cmd == nil || cmd.Name() != "browse" { | |
| 21 | + | t.Fatalf("expected browse command, got %#v", cmd) | |
| 22 | + | } | |
| 23 | + | if cmd.Args == nil { | |
| 24 | + | t.Fatal("expected browse command to define argument validation") | |
| 25 | + | } | |
| 26 | + | if err := cmd.Args(cmd, []string{"https://example.com", "extra"}); err == nil { | |
| 27 | + | t.Fatal("expected browse command to reject extra args") | |
| 28 | + | } | |
| 29 | + | } | |
| @@ -11,3 +11,19 @@ func TestRootCommand_RegistersOmniboxCommand(t *testing.T) { | |||||
| 11 | t.Fatalf("expected omnibox command, got %#v", cmd) | 11 | t.Fatalf("expected omnibox command, got %#v", cmd) | ||
| 12 | } | 12 | } | ||
| 13 | } | 13 | } | ||
| 14 | + | ||||
| 15 | + | func TestRootCommand_BrowseCommandRejectsExtraArgs(t *testing.T) { | |||
| 16 | + | cmd, _, err := rootCmd.Find([]string{"browse"}) | |||
| 17 | + | if err != nil { | |||
| 18 | + | t.Fatalf("expected browse command to be registered: %v", err) | |||
| 19 | + | } | |||
| 20 | + | if cmd == nil || cmd.Name() != "browse" { | |||
| 21 | + | t.Fatalf("expected browse command, got %#v", cmd) | |||
| 22 | + | } | |||
| 23 | + | if cmd.Args == nil { | |||
| 24 | + | t.Fatal("expected browse command to define argument validation") | |||
| 25 | + | } | |||
| 26 | + | if err := cmd.Args(cmd, []string{"https://example.com", "extra"}); err == nil { | |||
| 27 | + | t.Fatal("expected browse command to reject extra args") | |||
| 28 | + | } | |||
| 29 | + | } | |||
M internal/cli/cmd/root.go +1 -0
| @@ -107,6 +107,7 @@ func GetApp() *cli.App { | |||
| 107 | 107 | var browseCmd = &cobra.Command{ | |
| 108 | 108 | Use: "browse [url]", | |
| 109 | 109 | Short: "Launch the graphical browser", | |
| 110 | + | Args: cobra.MaximumNArgs(1), | |
| 110 | 111 | Long: `Launch the GTK4 graphical browser. | |
| 111 | 112 | ||
| 112 | 113 | If a URL is provided, navigate to it. Otherwise, open the homepage. | |
| @@ -107,6 +107,7 @@ func GetApp() *cli.App { | |||||
| 107 | var browseCmd = &cobra.Command{ | 107 | var browseCmd = &cobra.Command{ | ||
| 108 | Use: "browse [url]", | 108 | Use: "browse [url]", | ||
| 109 | Short: "Launch the graphical browser", | 109 | Short: "Launch the graphical browser", | ||
| 110 | + | Args: cobra.MaximumNArgs(1), | |||
| 110 | Long: `Launch the GTK4 graphical browser. | 111 | Long: `Launch the GTK4 graphical browser. | ||
| 111 | 112 | ||||
| 112 | If a URL is provided, navigate to it. Otherwise, open the homepage. | 113 | If a URL is provided, navigate to it. Otherwise, open the homepage. | ||