Reuse adapter sessions during execution

This commit is contained in:
2026-10-09 01:42:03 +00:00
parent a852301801
commit a7960e5e94
6 changed files with 328 additions and 32 deletions

View File

@@ -21,8 +21,9 @@ func TestExecuteRunsTypedCommandsWithFreshInvocationState(t *testing.T) {
var inputs []input
var contexts []context.Context
var streams []commandkit.Streams
ctx := context.WithValue(context.Background(), struct{}{}, "invocation")
var out, diagnostic bytes.Buffer
firstContext := context.WithValue(context.Background(), struct{}{}, "first")
secondContext := context.WithValue(context.Background(), struct{}{}, "second")
var firstOut, secondOut, firstDiagnostic, secondDiagnostic bytes.Buffer
command, err := commandkit.NewCommand(commandkit.CommandSpec[input]{
Name: "run",
Flags: []commandkit.Flag[input]{
@@ -43,9 +44,9 @@ func TestExecuteRunsTypedCommandsWithFreshInvocationState(t *testing.T) {
if err != nil {
t.Fatal(err)
}
first := app.Execute(ctx, []string{"run", "--name=first", "--id=a,b", "--id=c"}, commandkit.Streams{Out: &out, Err: &diagnostic})
second := app.Execute(ctx, []string{"run"}, commandkit.Streams{Out: &out, Err: &diagnostic})
third := app.Execute(ctx, []string{"run"}, commandkit.Streams{})
first := app.Execute(firstContext, []string{"run", "--name=first", "--id=a,b", "--id=c"}, commandkit.Streams{Out: &firstOut, Err: &firstDiagnostic})
second := app.Execute(secondContext, []string{"run"}, commandkit.Streams{Out: &secondOut, Err: &secondDiagnostic})
third := app.Execute(context.Background(), []string{"run"}, commandkit.Streams{})
if first.Kind != commandkit.OutcomeSuccess || second.Kind != commandkit.OutcomeSuccess || third.Kind != commandkit.OutcomeSuccess {
t.Fatalf("outcomes = %#v, %#v, and %#v", first, second, third)
}
@@ -58,8 +59,96 @@ func TestExecuteRunsTypedCommandsWithFreshInvocationState(t *testing.T) {
if streams[2].Out != io.Discard || streams[2].Err != io.Discard {
t.Fatalf("nil streams were not replaced with io.Discard: %#v", streams[2])
}
if contexts[0] != ctx || streams[0].Out != &out || streams[0].Err != &diagnostic {
t.Fatalf("handler invocation context or streams were not forwarded")
if contexts[0] != firstContext || contexts[1] != secondContext || streams[0].Out != &firstOut || streams[0].Err != &firstDiagnostic || streams[1].Out != &secondOut || streams[1].Err != &secondDiagnostic {
t.Fatalf("handler invocation contexts or streams were not forwarded independently")
}
}
func TestExecuteUsesFreshSessionForHelpAfterFlagBearingInvocation(t *testing.T) {
type input struct {
name commandkit.Supplied[string]
enabled commandkit.Supplied[bool]
}
var received []input
command, err := commandkit.NewCommand(commandkit.CommandSpec[input]{
Name: "run",
Flags: []commandkit.Flag[input]{
commandkit.ValueFlag(commandkit.FlagMeta{Name: "name"}, (*string)(nil), func(value string) (string, error) {
return value, nil
}, func(target *input, value commandkit.Supplied[string]) { target.name = value }),
commandkit.BoolFlag(commandkit.FlagMeta{Name: "enabled"}, false, func(target *input, value commandkit.Supplied[bool]) { target.enabled = value }),
},
Run: func(_ commandkit.Invocation, value input) commandkit.Outcome {
received = append(received, value)
return commandkit.Success()
},
})
if err != nil {
t.Fatal(err)
}
app, err := commandkit.New(commandkit.Identity{Name: "example"}, command)
if err != nil {
t.Fatal(err)
}
var firstOutput, secondOutput, diagnostic bytes.Buffer
first := app.Execute(context.Background(), []string{"run", "--name=chosen", "--enabled=false"}, commandkit.Streams{Out: &firstOutput, Err: &diagnostic})
if first.Kind != commandkit.OutcomeSuccess {
t.Fatalf("flag-bearing invocation = %#v", first)
}
help := app.Execute(context.Background(), []string{"run", "--name=", "--help"}, commandkit.Streams{Out: &secondOutput, Err: &diagnostic})
if help.Kind != commandkit.OutcomeSuccess || !strings.Contains(secondOutput.String(), "--enabled") {
t.Fatalf("help invocation = %#v, output %q", help, secondOutput.String())
}
last := app.Execute(context.Background(), []string{"run"}, commandkit.Streams{})
if last.Kind != commandkit.OutcomeSuccess {
t.Fatalf("omitted-value invocation = %#v", last)
}
if len(received) != 2 || received[0].name.Value != "chosen" || !received[0].name.Set || received[0].enabled.Value || !received[0].enabled.Set {
t.Fatalf("first input = %#v", received)
}
if received[1].name.Set || received[1].name.Value != "" || received[1].enabled.Set {
t.Fatalf("later invocation inherited flag state: %#v", received[1])
}
}
func TestExecutePresentsHandlerUsageAfterRegisteringCommand(t *testing.T) {
usageCause := errors.New("handler requests usage")
var app *commandkit.App
var calls int
late, err := commandkit.NewCommand(commandkit.CommandSpec[struct{}]{
Name: "late",
Run: func(commandkit.Invocation, struct{}) commandkit.Outcome { return commandkit.Success() },
})
if err != nil {
t.Fatal(err)
}
command, err := commandkit.NewCommand(commandkit.CommandSpec[struct{}]{
Name: "run",
Run: func(commandkit.Invocation, struct{}) commandkit.Outcome {
calls++
if err := app.Register(late); err != nil {
return commandkit.Failure(err)
}
return commandkit.Usage(usageCause)
},
})
if err != nil {
t.Fatal(err)
}
app, err = commandkit.New(commandkit.Identity{Name: "example"}, command)
if err != nil {
t.Fatal(err)
}
var diagnostic bytes.Buffer
result := app.Execute(context.Background(), []string{"run"}, commandkit.Streams{Err: &diagnostic})
if result.Kind != commandkit.OutcomeUsage || !errors.Is(result.Cause, usageCause) || calls != 1 {
t.Fatalf("handler usage = %#v, calls %d", result, calls)
}
if !strings.Contains(diagnostic.String(), "Usage: example run") {
t.Fatalf("handler usage diagnostic = %q", diagnostic.String())
}
if result := app.Execute(context.Background(), []string{"late"}, commandkit.Streams{}); result.Kind != commandkit.OutcomeSuccess {
t.Fatalf("registered command execution = %#v", result)
}
}