fix: allow shell completions to work without a database (#1118)

Add "__complete" to noDbCommands list so that Cobra's internal completion
command can run without requiring a beads database to be present.

Previously, running shell completions (e.g., `bd show <TAB>`) in a directory
without a .beads database would fail with "no beads database found" error.

Now completions return empty results gracefully when no database exists,
allowing basic command completion to work everywhere while issue ID
completion still requires a database.

Added integration test that verifies __complete command works without a database.
This commit is contained in:
Dmitry Verkhoturov
2026-01-16 03:22:55 +00:00
committed by GitHub
parent 5d93aaf722
commit c1a9dda173
2 changed files with 125 additions and 0 deletions

View File

@@ -2,6 +2,7 @@ package main
import (
"context"
"os"
"testing"
"time"
@@ -209,6 +210,128 @@ func TestIssueIDCompletion_NoStore(t *testing.T) {
}
}
func TestCompleteCommandWorksWithoutDatabase(t *testing.T) {
// This test verifies that shell completions work even without a beads database.
// The __complete command must be in noDbCommands list so that PersistentPreRun
// doesn't exit with "no beads database found" error.
//
// This test will FAIL on versions before the fix was applied, where __complete
// was not in the noDbCommands list.
// Create a temp directory with no .beads database
tmpDir := t.TempDir()
// Save original state
originalDBPath := dbPath
originalStore := store
defer func() {
dbPath = originalDBPath
store = originalStore
}()
// Reset state to simulate no database
store = nil
dbPath = ""
// Change to temp directory (no database present)
originalWd, err := os.Getwd()
if err != nil {
t.Fatalf("Failed to get working directory: %v", err)
}
if err := os.Chdir(tmpDir); err != nil {
t.Fatalf("Failed to change to temp directory: %v", err)
}
defer func() { _ = os.Chdir(originalWd) }()
// Test that issueIDCompletion returns gracefully (empty list, no error)
// when there's no database, rather than panicking or hanging
cmd := &cobra.Command{}
completions, directive := issueIDCompletion(cmd, []string{}, "")
// Should return empty completions, not panic
if completions == nil {
// nil is acceptable, convert to empty slice for consistent handling
completions = []string{}
}
// Should return NoFileComp directive
if directive != cobra.ShellCompDirectiveNoFileComp {
t.Errorf("Expected directive NoFileComp (%d), got %d",
cobra.ShellCompDirectiveNoFileComp, directive)
}
// Completions should be empty (no database to query)
if len(completions) != 0 {
t.Errorf("Expected 0 completions without database, got %d", len(completions))
}
}
func TestCompleteCommandInNoDbCommandsList(t *testing.T) {
// This integration test verifies that running `bd __complete show ""`
// does NOT fail with "no beads database found" error.
//
// The __complete command is Cobra's internal command for shell completions.
// It must be in the noDbCommands list to skip database initialization.
//
// Before the fix: this test would fail because __complete wasn't in noDbCommands,
// causing PersistentPreRun to exit with "no beads database found".
// After the fix: this test passes because __complete is in noDbCommands.
// Create a temp directory with no .beads database
tmpDir := t.TempDir()
// Change to temp directory (no database present)
originalWd, err := os.Getwd()
if err != nil {
t.Fatalf("Failed to get working directory: %v", err)
}
if err := os.Chdir(tmpDir); err != nil {
t.Fatalf("Failed to change to temp directory: %v", err)
}
defer func() { _ = os.Chdir(originalWd) }()
// Save and reset global state
originalDBPath := dbPath
originalStore := store
originalDaemonClient := daemonClient
defer func() {
dbPath = originalDBPath
store = originalStore
daemonClient = originalDaemonClient
}()
store = nil
dbPath = ""
daemonClient = nil
// Capture stdout/stderr
oldStdout := os.Stdout
oldStderr := os.Stderr
_, w, _ := os.Pipe()
os.Stdout = w
os.Stderr = w
defer func() {
os.Stdout = oldStdout
os.Stderr = oldStderr
}()
// Run __complete command (this is what shell completion scripts call)
rootCmd.SetArgs([]string{"__complete", "show", ""})
err = rootCmd.Execute()
// Close pipe to get output
_ = w.Close()
// The command should NOT fail - if __complete is in noDbCommands,
// PersistentPreRun will skip database initialization and the completion
// will return empty results gracefully
if err != nil {
t.Errorf("__complete command failed without database: %v\n"+
"This indicates __complete is not in noDbCommands list.\n"+
"Shell completions should work without requiring a database.", err)
}
}
func TestIssueIDCompletion_EmptyDatabase(t *testing.T) {
// Save original store and restore after test
originalStore := store

View File

@@ -355,6 +355,8 @@ var rootCmd = &cobra.Command{
// Skip database initialization for commands that don't need a database
noDbCommands := []string{
cmdDaemon,
"__complete", // Cobra's internal completion command (shell completions work without db)
"__completeNoDesc", // Cobra's completion without descriptions (used by fish)
"bash",
"completion",
"doctor",