2026-11-03 03:47:23 +00:00
|
|
|
package commandkit
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"reflect"
|
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
commandmeta "gitea.maximumdirect.net/eric/CommandKit/internal/commandmeta"
|
|
|
|
|
)
|
|
|
|
|
|
2026-11-03 13:12:22 +00:00
|
|
|
// Existing consumer code can compare these metadata values or use them as keys.
|
|
|
|
|
var (
|
|
|
|
|
_ map[FlagMeta]struct{}
|
|
|
|
|
_ map[ArgumentMeta]struct{}
|
|
|
|
|
_ = (FlagMeta{}) == (FlagMeta{})
|
|
|
|
|
_ = (ArgumentMeta{}) == (ArgumentMeta{})
|
|
|
|
|
)
|
|
|
|
|
|
2026-11-03 03:47:23 +00:00
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
}
|