Files
beads/cmd/bd/dolt_singleprocess_test.go
beads/crew/emma bb4549abdd fix(daemon): allow read-only daemon commands with Dolt backend
The daemon guard was blocking ALL daemon commands when using Dolt
backend, including read-only commands like `status`, `stop`, `logs`.

Changes:
- Rename guard to `guardDaemonStartForDolt` (more accurate)
- Remove `PersistentPreRunE` from `daemonCmd` and `daemonsCmd`
- Add `PreRunE` guard only to `daemonStartCmd` and `daemonsRestartCmd`
- Update test to use new function name and test start command

Now:
- `bd daemon status` works with Dolt backend
- `bd daemon start` blocked unless `--federation` flag
- `bd daemon start --federation` works (starts dolt sql-server)

Fixes: bd-n7o47

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 18:06:06 -08:00

135 lines
3.9 KiB
Go

package main
import (
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
"time"
)
func writeDoltWorkspace(t *testing.T, workspaceDir string) (beadsDir string, doltDir string) {
t.Helper()
beadsDir = filepath.Join(workspaceDir, ".beads")
doltDir = filepath.Join(beadsDir, "dolt")
if err := os.MkdirAll(doltDir, 0o700); err != nil {
t.Fatalf("mkdir: %v", err)
}
metadata := `{
"database": "dolt",
"backend": "dolt"
}`
if err := os.WriteFile(filepath.Join(beadsDir, "metadata.json"), []byte(metadata), 0o600); err != nil {
t.Fatalf("write metadata.json: %v", err)
}
return beadsDir, doltDir
}
func TestDoltSingleProcess_ShouldAutoStartDaemonFalse(t *testing.T) {
oldDBPath := dbPath
t.Cleanup(func() { dbPath = oldDBPath })
dbPath = ""
ws := t.TempDir()
beadsDir, _ := writeDoltWorkspace(t, ws)
t.Setenv("BEADS_DIR", beadsDir)
// Ensure the finder sees a workspace root (and not the repo running tests).
oldWD, _ := os.Getwd()
_ = os.Chdir(ws)
t.Cleanup(func() { _ = os.Chdir(oldWD) })
if shouldAutoStartDaemon() {
t.Fatalf("expected shouldAutoStartDaemon() to be false for dolt backend")
}
}
func TestDoltSingleProcess_TryAutoStartDoesNotCreateStartlock(t *testing.T) {
oldDBPath := dbPath
t.Cleanup(func() { dbPath = oldDBPath })
dbPath = ""
ws := t.TempDir()
beadsDir, _ := writeDoltWorkspace(t, ws)
t.Setenv("BEADS_DIR", beadsDir)
socketPath := filepath.Join(ws, "bd.sock")
lockPath := socketPath + ".startlock"
ok := tryAutoStartDaemon(socketPath)
if ok {
t.Fatalf("expected tryAutoStartDaemon() to return false for dolt backend")
}
if _, err := os.Stat(lockPath); err == nil {
t.Fatalf("expected startlock not to be created for dolt backend: %s", lockPath)
} else if !os.IsNotExist(err) {
t.Fatalf("stat startlock: %v", err)
}
}
func TestDoltSingleProcess_DaemonGuardBlocksStartCommand(t *testing.T) {
oldDBPath := dbPath
t.Cleanup(func() { dbPath = oldDBPath })
dbPath = ""
ws := t.TempDir()
beadsDir, _ := writeDoltWorkspace(t, ws)
t.Setenv("BEADS_DIR", beadsDir)
// Use daemonStartCmd which has the guard attached.
// Ensure help and federation flags exist (cobra adds them during execution).
cmd := daemonStartCmd
cmd.Flags().Bool("help", false, "help")
// Note: federation flag is already registered in init()
err := guardDaemonStartForDolt(cmd, nil)
if err == nil {
t.Fatalf("expected daemon guard error for dolt backend without --federation")
}
if !strings.Contains(err.Error(), "single-process") {
t.Fatalf("expected error to mention single-process, got: %v", err)
}
}
// This test uses a helper subprocess because startDaemon calls os.Exit on failure.
func TestDoltSingleProcess_StartDaemonGuardrailExitsNonZero(t *testing.T) {
if os.Getenv("BD_TEST_HELPER_STARTDAEMON") == "1" {
// Helper mode: set up environment and invoke startDaemon (should os.Exit(1)).
ws := os.Getenv("BD_TEST_WORKSPACE")
_, doltDir := writeDoltWorkspace(t, ws)
// Ensure FindDatabasePath can resolve.
_ = os.Chdir(ws)
_ = os.Setenv("BEADS_DB", doltDir)
dbPath = ""
pidFile := filepath.Join(ws, ".beads", "daemon.pid")
startDaemon(5*time.Second, false, false, false, false, false, "", pidFile, "info", false, false, 0, 0)
return
}
ws := t.TempDir()
// Pre-create workspace structure so helper can just use it.
_, doltDir := writeDoltWorkspace(t, ws)
exe, err := os.Executable()
if err != nil {
t.Fatalf("os.Executable: %v", err)
}
cmd := exec.Command(exe, "-test.run", "^TestDoltSingleProcess_StartDaemonGuardrailExitsNonZero$", "-test.v")
cmd.Env = append(os.Environ(),
"BD_TEST_HELPER_STARTDAEMON=1",
"BD_TEST_WORKSPACE="+ws,
"BEADS_DB="+doltDir,
)
out, err := cmd.CombinedOutput()
if err == nil {
t.Fatalf("expected non-zero exit; output:\n%s", string(out))
}
if !strings.Contains(string(out), "daemon mode is not supported") {
t.Fatalf("expected output to mention daemon unsupported; got:\n%s", string(out))
}
}