Files
kportal/internal/complete/completion_test.go
T
2026-06-21 12:59:55 +01:00

328 lines
7.7 KiB
Go

package complete
import (
"os"
"path/filepath"
"strings"
"testing"
)
func TestGenerateBash(t *testing.T) {
script, err := Generate(ShellBash)
if err != nil {
t.Fatalf("Generate(ShellBash) failed: %v", err)
}
// Verify script contains key elements
if !strings.Contains(script, "_kportal()") {
t.Error("Bash script missing _kportal function")
}
if !strings.Contains(script, "complete -F _kportal kportal") {
t.Error("Bash script missing completion registration")
}
if !strings.Contains(script, "--version") {
t.Error("Bash script missing --version flag")
}
if !strings.Contains(script, "generate") {
t.Error("Bash script missing generate subcommand")
}
}
func TestGenerateZsh(t *testing.T) {
script, err := Generate(ShellZsh)
if err != nil {
t.Fatalf("Generate(ShellZsh) failed: %v", err)
}
// Verify script contains key elements
if !strings.Contains(script, "#compdef kportal") {
t.Error("Zsh script missing #compdef directive")
}
if !strings.Contains(script, "_kportal()") {
t.Error("Zsh script missing _kportal function")
}
if !strings.Contains(script, "'generate:") {
t.Error("Zsh script missing generate subcommand")
}
if !strings.Contains(script, "'--context[") {
t.Error("Zsh script missing --context flag")
}
}
func TestGenerateFish(t *testing.T) {
script, err := Generate(ShellFish)
if err != nil {
t.Fatalf("Generate(ShellFish) failed: %v", err)
}
// Verify script contains key elements
if !strings.Contains(script, "complete -c kportal") {
t.Error("Fish script missing complete directive")
}
if !strings.Contains(script, "-n '__fish_use_subcommand'") {
t.Error("Fish script missing subcommand detection")
}
if !strings.Contains(script, "-l context") {
t.Error("Fish script missing --context flag")
}
}
func TestGenerateUnsupported(t *testing.T) {
_, err := Generate(Shell("unsupported"))
if err == nil {
t.Error("Expected error for unsupported shell")
}
}
func TestAutoDetectShell(t *testing.T) {
tests := []struct {
name string
shellEnv string
expected Shell
}{
{"bash", "/bin/bash", ShellBash},
{"zsh", "/usr/bin/zsh", ShellZsh},
{"fish", "/usr/local/bin/fish", ShellFish},
{"tcsh", "/bin/tcsh", ShellBash}, // Falls back to bash
{"empty", "", ShellBash}, // Falls back to bash
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
detected := autoDetectShellFromEnv(tt.shellEnv)
if detected != tt.expected {
t.Errorf("AutoDetectShell() = %v, want %v", detected, tt.expected)
}
})
}
}
// autoDetectShellFromEnv is a test helper that simulates shell detection
func autoDetectShellFromEnv(shellEnv string) Shell {
if shellEnv == "" {
return ShellBash
}
if strings.HasSuffix(shellEnv, "/bash") {
return ShellBash
}
if strings.HasSuffix(shellEnv, "/zsh") {
return ShellZsh
}
if strings.HasSuffix(shellEnv, "/fish") {
return ShellFish
}
return ShellBash
}
func TestInstaller(t *testing.T) {
// Test with bash
installer := NewInstaller(ShellBash)
if installer.prefixDir == "" {
t.Error("Installer should have a prefix directory")
}
// Test filename generation
t.Run("filename", func(t *testing.T) {
tests := []struct {
shell Shell
expected string
}{
{ShellBash, "_kportal"},
{ShellZsh, "_kportal"},
{ShellFish, "kportal.fish"},
}
for _, tt := range tests {
inst := NewInstaller(tt.shell)
got := inst.getCompletionFilename()
if got != tt.expected {
t.Errorf("getCompletionFilename() for %v = %v, want %v", tt.shell, got, tt.expected)
}
}
})
}
func TestInstallCompletion(t *testing.T) {
// Temp directory (auto-removed by the test framework)
tempDir := t.TempDir()
// Test installation to temp directory
installer := &Installer{
shell: ShellBash,
prefixDir: tempDir,
}
// Should fail because file doesn't exist (doesn't matter, just test the method exists)
_ = installer.Install()
}
func TestPrint(t *testing.T) {
// Test that Print doesn't crash and outputs something
orig := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
err := Print(ShellBash)
_ = w.Close()
os.Stdout = orig
if err != nil {
t.Fatalf("Print(ShellBash) failed: %v", err)
}
// Read output
buf := make([]byte, 1024)
n, _ := r.Read(buf)
output := string(buf[:n])
if !strings.Contains(output, "_kportal") {
t.Error("Print output should contain _kportal")
}
}
func TestGetCompletionFilename(t *testing.T) {
tests := []struct {
shell Shell
expected string
}{
{ShellBash, "_kportal"},
{ShellZsh, "_kportal"},
{ShellFish, "kportal.fish"},
{Shell("unknown"), "_kportal"}, // Falls back
}
for _, tt := range tests {
inst := &Installer{shell: tt.shell}
got := inst.getCompletionFilename()
if got != tt.expected {
t.Errorf("getCompletionFilename() for %v = %v, want %v", tt.shell, got, tt.expected)
}
}
}
func TestBashCompletionIncludesContextCompletion(t *testing.T) {
script, err := Generate(ShellBash)
if err != nil {
t.Fatalf("Generate(ShellBash) failed: %v", err)
}
// Should include kubectl context completion
if !strings.Contains(script, "kubectl config get-contexts") {
t.Error("Bash completion should include kubectl context completion")
}
}
func TestZshCompletionIncludesContextCompletion(t *testing.T) {
script, err := Generate(ShellZsh)
if err != nil {
t.Fatalf("Generate(ShellZsh) failed: %v", err)
}
// Should include kubectl context completion
if !strings.Contains(script, "kubectl config get-contexts") {
t.Error("Zsh completion should include kubectl context completion")
}
}
func TestFishCompletionIncludesContextCompletion(t *testing.T) {
script, err := Generate(ShellFish)
if err != nil {
t.Fatalf("Generate(ShellFish) failed: %v", err)
}
// Should include kubectl context completion
if !strings.Contains(script, "kubectl config get-contexts") {
t.Error("Fish completion should include kubectl context completion")
}
}
func TestAllFlagsPresent(t *testing.T) {
script, err := Generate(ShellBash)
if err != nil {
t.Fatalf("Generate(ShellBash) failed: %v", err)
}
// Check all main flags are present. The main command exposes single-dash
// -c/-v (no --config/--verbose long forms); word flags use --.
flags := []string{
"-c",
"-v",
"--version",
"--update",
"--check",
"--headless",
"--log-format",
"--convert",
"--convert-output",
}
for _, flag := range flags {
if !strings.Contains(script, flag) {
t.Errorf("Bash completion missing flag: %s", flag)
}
}
}
func TestSubcommandsPresent(t *testing.T) {
for _, shell := range []Shell{ShellBash, ShellZsh, ShellFish} {
script, err := Generate(shell)
if err != nil {
t.Fatalf("Generate(%v) failed: %v", shell, err)
}
for _, sub := range []string{"generate", "completion"} {
if !strings.Contains(script, sub) {
t.Errorf("%s completion missing %q subcommand", shell, sub)
}
}
}
}
func TestGenerateFlagsPresent(t *testing.T) {
script, err := Generate(ShellBash)
if err != nil {
t.Fatalf("Generate(ShellBash) failed: %v", err)
}
generateFlags := []string{
"--context",
"--config",
"--dry-run",
}
for _, flag := range generateFlags {
if !strings.Contains(script, flag) {
t.Errorf("Bash completion missing generate flag: %s", flag)
}
}
}
func TestCompletionScriptPermissions(t *testing.T) {
// Create temp file and verify permissions handling
tempDir := t.TempDir()
installer := &Installer{
shell: ShellBash,
prefixDir: tempDir,
}
filename := filepath.Join(tempDir, installer.getCompletionFilename())
// Write a test file
err := os.WriteFile(filename, []byte("test"), 0o600)
if err != nil {
t.Fatalf("Failed to write test file: %v", err)
}
// Verify file exists and is readable
data, err := os.ReadFile(filename)
if err != nil {
t.Fatalf("Failed to read test file: %v", err)
}
if string(data) != "test" {
t.Error("File content mismatch")
}
}