package bonfire import ( "bytes" "context" "io" "slices" "strings" "testing" ) func TestValidBareRuntimeCommandNames(t *testing.T) { for _, name := range []string{"serve", "migrate", "migrate:rollback", "migrate:status", "build", "dev", "new"} { if _, err := NewRoot("app", []Command{{Name: name}}, io.Discard); err != nil { t.Fatalf("%s: %v", name, err) } } } func capturingCommand(name string, got *[]string) Command { return Command{ Name: name, Description: "Capture args", Passthrough: true, Run: func(ctx context.Context, in Input, out Output) error { *got = append([]string(nil), in.Args()...) return nil }, } } func TestPassthroughCommandReceivesRawArgs(t *testing.T) { var got []string var out, errW bytes.Buffer root, err := NewRootIO("app", []Command{capturingCommand("serve", &got)}, strings.NewReader(""), &out, &errW) if err != nil { t.Fatal(err) } args := []string{"--addr", ":8123", "-p", "--help", "-h", "--", "x"} root.SetArgs(append([]string{"serve"}, args...)) if err := root.Execute(); err != nil { t.Fatalf("Execute: %v", err) } if !slices.Equal(got, args) { t.Fatalf("args = %q, want %q", got, args) } if out.Len() != 0 || errW.Len() != 0 { t.Fatalf("passthrough printed output: stdout=%q stderr=%q", out.String(), errW.String()) } } func TestPassthroughCommandThroughCall(t *testing.T) { var got []string var buf bytes.Buffer args := []string{"--addr", ":8123", "--help"} if err := Call(context.Background(), []Command{capturingCommand("serve", &got)}, "serve", args, &buf); err != nil { t.Fatalf("Call: %v", err) } if !slices.Equal(got, args) { t.Fatalf("args = %q, want %q", got, args) } } func TestPassthroughCommandIgnoresArgCount(t *testing.T) { var got []string cmd := capturingCommand("queue:clear", &got) cmd.Args = []Arg{{Name: "queue", Required: true}} root, err := NewRoot("app", []Command{cmd}, io.Discard) if err != nil { t.Fatal(err) } root.SetArgs([]string{"queue:clear"}) if err := root.Execute(); err != nil { t.Fatalf("passthrough with zero args: %v", err) } if len(got) != 0 { t.Fatalf("args = %q, want none", got) } } func TestNonPassthroughCommandRejectsUnknownFlag(t *testing.T) { root, err := NewRoot("app", []Command{{Name: "migrate", Run: func(context.Context, Input, Output) error { return nil }}}, io.Discard) if err != nil { t.Fatal(err) } root.SetArgs([]string{"migrate", "--addr", ":8123"}) err = root.Execute() if err == nil || !strings.Contains(err.Error(), "unknown flag: --addr") { t.Fatalf("err = %v, want unknown flag: --addr", err) } } func TestHelpShowsLongOrDescription(t *testing.T) { for _, tc := range []struct { name string cmd Command want string }{ {"long", Command{Name: "serve", Description: "Short text", Long: "Long text"}, "Long text"}, {"description", Command{Name: "serve", Description: "Short text"}, "Short text"}, } { t.Run(tc.name, func(t *testing.T) { var buf bytes.Buffer root, err := NewRoot("app", []Command{tc.cmd}, &buf) if err != nil { t.Fatal(err) } root.SetArgs([]string{"help", "serve"}) if err := root.Execute(); err != nil { t.Fatal(err) } if !strings.Contains(buf.String(), tc.want) { t.Fatalf("help missing %q:\n%s", tc.want, buf.String()) } }) } }