mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-02-15 23:45:39 -05:00
**What type of PR is this?**
Feature
**What does this PR do? Why is it needed?**
Introduces a `completion` subcommand to `beacon-chain` and `validator`
that outputs shell completion scripts. Supports Bash, Zsh, and Fish
shells.
```bash
# Load completions in current session
source <(beacon-chain completion bash)
# Persist for future sessions
beacon-chain completion zsh > "${fpath[1]}/_beacon-chain"
validator completion fish > ~/.config/fish/completions/validator.fish
```
Once loaded, users can press TAB to complete subcommands, nested
commands, and flags. Flag completion supports prefix matching (e.g.,
typing `--exec<TAB>` suggests `--execution-endpoint`,
`--execution-headers`).
**Which issues(s) does this PR fix?**
Fixes #16244
**Other notes for review**
The implementation adds three files to the existing `cmd` package:
- `completion.go` - Defines `CompletionCommand()` returning a
`*cli.Command` with `bash`, `zsh`, `fish` subcommands
- `completion_scripts.go` - Contains the shell script templates
- `completion_test.go` - Unit tests for command structure and script
content
Changes to `beacon-chain` and `validator`:
- Import `cmd.CompletionCommand("binary-name")` in the Commands slice
- Set `EnableBashCompletion: true` on the cli.App to activate
urfave/cli's `--generate-bash-completion` hidden flag
The shell scripts call the binary with `--generate-bash-completion`
appended to get context-aware suggestions. This means completions
automatically reflect the current binary's flags and commands.
**Acknowledgements**
- [x] I have read
[CONTRIBUTING.md](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md).
- [x] I have included a uniquely named [changelog fragment
file](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md#maintaining-changelogmd).
- [x] I have added a description with sufficient context for reviewers
to understand this PR.
- [x] I have tested that my changes work as expected and I added a
testing plan to the PR description (if applicable).
Signed-off-by: Willian Paixao <willian@ufpa.br>
106 lines
3.7 KiB
Go
106 lines
3.7 KiB
Go
package cmd
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/OffchainLabs/prysm/v7/testing/assert"
|
|
"github.com/OffchainLabs/prysm/v7/testing/require"
|
|
"github.com/urfave/cli/v2"
|
|
)
|
|
|
|
func TestCompletionCommand(t *testing.T) {
|
|
t.Run("creates command with correct name", func(t *testing.T) {
|
|
cmd := CompletionCommand("beacon-chain")
|
|
require.Equal(t, "completion", cmd.Name)
|
|
})
|
|
|
|
t.Run("has three subcommands", func(t *testing.T) {
|
|
cmd := CompletionCommand("beacon-chain")
|
|
require.Equal(t, 3, len(cmd.Subcommands))
|
|
|
|
names := make([]string, len(cmd.Subcommands))
|
|
for i, sub := range cmd.Subcommands {
|
|
names[i] = sub.Name
|
|
}
|
|
assert.DeepEqual(t, []string{"bash", "zsh", "fish"}, names)
|
|
})
|
|
|
|
t.Run("description contains binary name", func(t *testing.T) {
|
|
cmd := CompletionCommand("validator")
|
|
assert.Equal(t, true, strings.Contains(cmd.Description, "validator"))
|
|
})
|
|
}
|
|
|
|
func TestBashCompletionScript(t *testing.T) {
|
|
script := bashCompletionScript("beacon-chain")
|
|
|
|
assert.Equal(t, true, strings.Contains(script, "beacon-chain"), "script should contain binary name")
|
|
assert.Equal(t, true, strings.Contains(script, "_beacon_chain_completions"), "script should contain function name with underscores")
|
|
assert.Equal(t, true, strings.Contains(script, "complete -o bashdefault"), "script should contain complete command")
|
|
assert.Equal(t, true, strings.Contains(script, "--generate-bash-completion"), "script should use generate-bash-completion flag")
|
|
}
|
|
|
|
func TestZshCompletionScript(t *testing.T) {
|
|
script := zshCompletionScript("validator")
|
|
|
|
assert.Equal(t, true, strings.Contains(script, "#compdef validator"), "script should contain compdef directive")
|
|
assert.Equal(t, true, strings.Contains(script, "_validator"), "script should contain function name")
|
|
assert.Equal(t, true, strings.Contains(script, "--generate-bash-completion"), "script should use generate-bash-completion flag")
|
|
}
|
|
|
|
func TestFishCompletionScript(t *testing.T) {
|
|
script := fishCompletionScript("beacon-chain")
|
|
|
|
assert.Equal(t, true, strings.Contains(script, "complete -c beacon-chain"), "script should contain complete command")
|
|
assert.Equal(t, true, strings.Contains(script, "__fish_beacon_chain_complete"), "script should contain function name with underscores")
|
|
assert.Equal(t, true, strings.Contains(script, "--generate-bash-completion"), "script should use generate-bash-completion flag")
|
|
}
|
|
|
|
func TestScriptFunctionNames(t *testing.T) {
|
|
// Test that hyphens are converted to underscores in function names
|
|
bashScript := bashCompletionScript("beacon-chain")
|
|
assert.Equal(t, true, strings.Contains(bashScript, "_beacon_chain_completions"))
|
|
assert.Equal(t, false, strings.Contains(bashScript, "_beacon-chain_completions"))
|
|
|
|
zshScript := zshCompletionScript("beacon-chain")
|
|
assert.Equal(t, true, strings.Contains(zshScript, "_beacon_chain"))
|
|
|
|
fishScript := fishCompletionScript("beacon-chain")
|
|
assert.Equal(t, true, strings.Contains(fishScript, "__fish_beacon_chain_complete"))
|
|
}
|
|
|
|
func TestCompletionSubcommandActions(t *testing.T) {
|
|
// Test that Action functions execute without errors
|
|
cmd := CompletionCommand("beacon-chain")
|
|
|
|
tests := []struct {
|
|
name string
|
|
subcommand string
|
|
}{
|
|
{"bash action executes", "bash"},
|
|
{"zsh action executes", "zsh"},
|
|
{"fish action executes", "fish"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
var subCmd *cli.Command
|
|
for _, sub := range cmd.Subcommands {
|
|
if sub.Name == tt.subcommand {
|
|
subCmd = sub
|
|
break
|
|
}
|
|
}
|
|
require.NotNil(t, subCmd, "subcommand should exist")
|
|
require.NotNil(t, subCmd.Action, "subcommand should have an action")
|
|
|
|
// Action should not return an error; use a real cli.Context
|
|
app := &cli.App{}
|
|
ctx := cli.NewContext(app, nil, nil)
|
|
err := subCmd.Action(ctx)
|
|
require.NoError(t, err)
|
|
})
|
|
}
|
|
}
|