Files
commandkit/completion_test.go

82 lines
4.0 KiB
Go

package commandkit
import (
"context"
"reflect"
"testing"
commandmeta "gitea.maximumdirect.net/eric/CommandKit/internal/commandmeta"
)
// Existing consumer code can compare these metadata values or use them as keys.
var (
_ map[FlagMeta]struct{}
_ map[ArgumentMeta]struct{}
_ = (FlagMeta{}) == (FlagMeta{})
_ = (ArgumentMeta{}) == (ArgumentMeta{})
)
func TestCompletionHintsFollowCapturedBindings(t *testing.T) {
type inner struct{ values Supplied[[]string] }
type outer struct{ flags FlagValues[inner] }
choices := []CompletionChoice{{Value: "alpha", Description: "first"}}
hint := CompleteChoices(choices...)
choices[0] = CompletionChoice{Value: "changed"}
parse := func(value string) (string, error) { return value, nil }
repeated := RepeatFlag(FlagMeta{Name: "tag", Completion: hint}, nil, parse,
func(input *inner, value Supplied[[]string]) { input.values = value })
captured := CaptureFlags([]Flag[inner]{repeated}, func(input *outer) *FlagValues[inner] { return &input.flags })
command := NewCommand(CommandSpec[outer]{
Name: "run",
Flags: append(captured,
ValueFlag(FlagMeta{Name: "file", Completion: CompleteFiles()}, (*string)(nil), parse, func(*outer, Supplied[string]) {}),
BoolFlag(FlagMeta{Name: "enabled"}, false, func(*outer, Supplied[bool]) {}),
ValueFlag(FlagMeta{Name: "empty", Completion: CompleteChoices()}, (*string)(nil), parse, func(*outer, Supplied[string]) {})),
Arguments: []Argument[outer]{
ValueArgument(ArgumentMeta{Name: "directory", Completion: CompleteDirectories()}, false, parse, func(*outer, Supplied[string]) {}),
RepeatArgument(ArgumentMeta{Name: "values", Completion: hint}, 0, -1, parse, func(*outer, Supplied[[]string]) {}),
},
})
flags := command.record.payload.bridgeFlags()
arguments := command.record.payload.bridgeArguments()
want := commandmeta.CompletionHint{Kind: commandmeta.CompletionChoices, Choices: []commandmeta.CompletionChoice{{Value: "alpha", Description: "first"}}}
if !flags[0].Repeated || !reflect.DeepEqual(flags[0].Completion, want) || !reflect.DeepEqual(arguments[1].Completion, want) {
t.Fatalf("captured hints: flags=%#v arguments=%#v", flags, arguments)
}
if flags[1].Completion.Kind != commandmeta.CompletionFiles || flags[2].Completion.Kind != commandmeta.CompletionNone || flags[3].Completion.Kind != commandmeta.CompletionChoices || len(flags[3].Completion.Choices) != 0 || arguments[0].Completion.Kind != commandmeta.CompletionDirectories {
t.Fatalf("hint strategies: flags=%#v arguments=%#v", flags, arguments)
}
flags[0].Completion.Choices[0].Value = "mutated"
if got := command.record.payload.bridgeFlags()[0].Completion; !reflect.DeepEqual(got, want) {
t.Fatalf("bridged choices changed captured hint: %#v", got)
}
if got := bridgeCompletion(NoCompletion()); got.Kind != commandmeta.CompletionNone || !reflect.DeepEqual(got, bridgeCompletion(CompletionHint{})) {
t.Fatalf("no completion: %#v", got)
}
for _, flag := range append(commandmeta.ReportingFlags(), commandmeta.HelpFlag()) {
if !reflect.DeepEqual(flag.Completion.Choices, []commandmeta.CompletionChoice{{Value: "true"}, {Value: "false"}}) {
t.Fatalf("reporting flag choices: %#v", flag)
}
}
}
func TestCompletionChoicesDoNotRestrictExecution(t *testing.T) {
type input struct{ mode Supplied[string] }
var received input
command := NewCommand(CommandSpec[input]{
Name: "run",
Flags: []Flag[input]{ValueFlag(FlagMeta{Name: "mode", Completion: CompleteChoices(CompletionChoice{Value: "suggested"})}, (*string)(nil),
func(value string) (string, error) { return value, nil },
func(i *input, value Supplied[string]) { i.mode = value })},
Run: func(_ Invocation, value input) Outcome { received = value; return Success() },
})
app, err := New(Identity{Name: "example"}, command)
if err != nil {
t.Fatal(err)
}
outcome := app.Execute(context.Background(), []string{"run", "--mode=other"}, Streams{})
if outcome.Status != 0 || !received.mode.Set || received.mode.Value != "other" {
t.Fatalf("outcome=%#v input=%#v", outcome, received)
}
}