package commandkit import ( "bytes" "context" "io" "reflect" "strings" "testing" internalcobra "gitea.maximumdirect.net/eric/CommandKit/internal/cobra" "gitea.maximumdirect.net/eric/promptkit" ) func TestAppParseUsesRegisteredBindingsWithTheAdapter(t *testing.T) { type input struct { scope Supplied[string] id Supplied[string] } show, err := NewCommand(CommandSpec[input]{ Name: "show", Flags: []Flag[input]{ValueFlag(FlagMeta{Name: "scope"}, (*string)(nil), func(value string) (string, error) { return value, nil }, func(value *input, supplied Supplied[string]) { value.scope = supplied })}, Arguments: []Argument[input]{ValueArgument(ArgumentMeta{Name: "id"}, true, func(value string) (string, error) { return value, nil }, func(value *input, supplied Supplied[string]) { value.id = supplied })}, Run: func(Invocation, input) Outcome { return Success() }, }) if err != nil { t.Fatal(err) } config, err := Group("config", "configuration", show) if err != nil { t.Fatal(err) } app, err := New(Identity{Name: "example"}, config) if err != nil { t.Fatal(err) } options := internalcobra.ParseOptions{Context: context.Background()} result := app.parse([]string{"config", "show", "one", "--scope", "app"}, options) if result.Failure != nil { t.Fatalf("parse failed: %s", result.Failure.Message) } if !reflect.DeepEqual(result.Path, []string{"config", "show"}) || !reflect.DeepEqual(result.FlagValues, [][]string{{"app"}}) || !reflect.DeepEqual(result.Operands, []string{"one"}) { t.Fatalf("parse result = %#v", result) } result = app.parse([]string{"config", "--scope", "app", "show"}, options) if result.Failure == nil { t.Fatal("accepted a child flag before the complete command path") } } func TestBridgeTreeRefreshesAfterSuccessfulRegistration(t *testing.T) { app, err := New(Identity{Name: "example"}) if err != nil { t.Fatal(err) } var output bytes.Buffer streams := Streams{Out: &output, Err: io.Discard} renderRootHelp := func() string { t.Helper() output.Reset() outcome := app.Execute(context.Background(), []string{"--help"}, streams) if outcome.Kind != OutcomeSuccess { t.Fatalf("root help outcome = %#v", outcome) } return output.String() } initial := renderRootHelp() if strings.Contains(initial, "custom") || strings.Contains(initial, "config") || strings.Contains(initial, "promptkit") { t.Fatalf("initial help unexpectedly lists future registrations: %q", initial) } custom, err := NewCommand(CommandSpec[struct{}]{Name: "custom", Run: func(Invocation, struct{}) Outcome { return Success() }}) if err != nil { t.Fatal(err) } if err := app.Register(custom); err != nil { t.Fatal(err) } providerCalls := 0 if err := RegisterConfig(app, ConfigSuite[struct{}]{Scopes: []ConfigScope[struct{}]{{ Name: "app", Operations: []ConfigOperation{ConfigShow}, Acquire: func(context.Context, ConfigRequest, struct{}) (Acquired[ConfigView], error) { providerCalls++ return Acquired[ConfigView]{}, nil }, }}}); err != nil { t.Fatal(err) } if err := RegisterPromptKit(app, PromptKitSuite[struct{}]{InspectPrompt: func(context.Context, Selection, struct{}, string, string) (Acquired[*promptkit.PromptInspection], error) { providerCalls++ return Acquired[*promptkit.PromptInspection]{}, nil }}); err != nil { t.Fatal(err) } updated := renderRootHelp() for _, name := range []string{"custom", "config", "promptkit"} { if !strings.Contains(updated, name) { t.Errorf("updated help omits %q: %q", name, updated) } } if providerCalls != 0 { t.Fatalf("help invoked inspection or configuration callbacks %d times", providerCalls) } late, err := NewCommand(CommandSpec[struct{}]{Name: "late"}) if err != nil { t.Fatal(err) } if err := app.Register(late, custom); err == nil { t.Fatal("Register accepted a batch with an existing-name collision") } if err := app.Register(); err != nil { t.Fatalf("empty registration failed: %v", err) } final := renderRootHelp() if !strings.Contains(final, "custom") || !strings.Contains(final, "config") || !strings.Contains(final, "promptkit") || strings.Contains(final, "late") { t.Fatalf("help after failed and empty registration = %q", final) } if outcome := app.Execute(context.Background(), []string{"custom"}, streams); outcome.Kind != OutcomeSuccess { t.Fatalf("new command did not parse after registration: %#v", outcome) } }