Files
commandkit/completion_install_test.go

109 lines
4.4 KiB
Go

package commandkit
import (
"bytes"
"context"
"errors"
"io"
"strings"
"testing"
)
func TestCompletionDeclarationGeneratesScripts(t *testing.T) {
for _, name := range []string{"app", "my-app", "tool.exe", "café"} {
if !validCompletionExecutable(name) {
t.Fatalf("supported executable rejected: %q", name)
}
}
app, err := New(Identity{Name: "Human Readable Name"}, Completion("my-app"))
if err != nil {
t.Fatal(err)
}
if !app.routeSnapshot().completion {
t.Fatal("completion not present in snapshot")
}
for _, shell := range []string{"bash", "zsh", "fish"} {
var output, diagnostics bytes.Buffer
outcome := app.Execute(context.Background(), []string{"completion", shell}, Streams{Out: &output, Err: &diagnostics})
if outcome.Status != 0 || !strings.Contains(output.String(), "my-app") || strings.Contains(output.String(), "Human Readable Name") {
t.Fatalf("%s outcome=%#v output=%q diagnostics=%q", shell, outcome, output.String(), diagnostics.String())
}
output.Reset()
outcome = app.Execute(context.Background(), []string{"completion", shell, "--quiet"}, Streams{Out: &output, Err: &diagnostics})
if outcome.Status != 0 || output.Len() != 0 {
t.Fatalf("quiet %s outcome=%#v output=%q", shell, outcome, output.String())
}
}
var help bytes.Buffer
outcome := app.Execute(context.Background(), []string{"--help"}, Streams{Out: &help})
if outcome.Status != 0 || !strings.Contains(help.String(), "completion") || strings.Contains(help.String(), "__complete") {
t.Fatalf("help outcome=%#v output=%q", outcome, help.String())
}
writeErr := errors.New("destination failed")
outcome = app.Execute(context.Background(), []string{"completion", "bash"}, Streams{Out: completionErrorWriter{writeErr}, Err: io.Discard})
if outcome.Status != 1 || !errors.Is(outcome.Presentation.Output.WriteError, writeErr) {
t.Fatalf("script delivery failure: %#v", outcome)
}
}
type completionErrorWriter struct{ err error }
func (w completionErrorWriter) Write([]byte) (int, error) { return 0, w.err }
func TestCompletionInstallationCollisionsAndRollback(t *testing.T) {
run := NewCommand(CommandSpec[struct{}]{Name: "run", Run: func(Invocation, struct{}) Outcome { return Success() }})
query := NewCommand(CommandSpec[struct{}]{Name: "__complete", Run: func(Invocation, struct{}) Outcome { return Success() }})
for _, declarations := range [][]Declaration{
{Completion("app"), query},
{query, Completion("app")},
{Completion("app"), run, Alias("__complete nested", "run")},
{run, Alias("__complete nested", "run"), Completion("app")},
{Completion("app"), Group("completion", "collision")},
{Group("outer", "", Completion("app"))},
{Completion("dir/app")},
{Completion("two words")},
} {
if _, err := New(Identity{Name: "example"}, declarations...); err == nil {
t.Fatalf("accepted conflicting declarations: %#v", declarations)
}
}
app, err := New(Identity{Name: "example"}, run)
if err != nil {
t.Fatal(err)
}
before := app.routeSnapshot()
if err := app.Register(Completion("app"), query); err == nil || app.routeSnapshot() != before {
t.Fatalf("failed batch changed snapshot: %v", err)
}
if err := app.Register(Completion("app")); err != nil {
t.Fatal(err)
}
if app.routeSnapshot() == before || !app.routeSnapshot().completion {
t.Fatal("late completion installation did not refresh snapshot")
}
if err := app.Register(query); err == nil {
t.Fatal("query command accepted after completion installation")
}
if err := app.Register(Alias("__complete nested", "run")); err == nil {
t.Fatal("query alias accepted after completion installation")
}
withoutCompletion, err := New(Identity{Name: "example"}, query)
if err != nil || withoutCompletion.routeSnapshot().completion {
t.Fatalf("ordinary query-named command: %v", err)
}
if outcome := withoutCompletion.Execute(context.Background(), []string{"__complete"}, Streams{}); outcome.Status != 0 {
t.Fatalf("ordinary command outcome: %#v", outcome)
}
if err := withoutCompletion.Register(Completion("app")); err == nil || withoutCompletion.routeSnapshot().completion {
t.Fatal("completion accepted an existing query command")
}
aliased, err := New(Identity{Name: "example"}, run, Alias("__complete nested", "run"))
if err != nil {
t.Fatal(err)
}
if err := aliased.Register(Completion("app")); err == nil || aliased.routeSnapshot().completion {
t.Fatal("completion accepted an existing query alias")
}
}