diff --git a/cmd/bd/completions_test.go b/cmd/bd/completions_test.go index d9202e39..e6e916d7 100644 --- a/cmd/bd/completions_test.go +++ b/cmd/bd/completions_test.go @@ -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 diff --git a/cmd/bd/main.go b/cmd/bd/main.go index 312aa7f5..bda51aeb 100644 --- a/cmd/bd/main.go +++ b/cmd/bd/main.go @@ -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",