fix: validate BEADS_DIR env var for project files (bd-420)

Extends the fix from PR #424 to also validate the BEADS_DIR environment
variable. Previously, only the directory tree search was validated, but
BEADS_DIR could still point to a daemon-only directory.

Changes:
- Add validation to BEADS_DIR path using hasBeadsProjectFiles()
- Add comprehensive test for BEADS_DIR validation
- Test verifies both rejection of daemon-only dirs and acceptance of valid dirs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-11-30 12:36:27 -08:00
parent 95fe36fb53
commit 54928b78a1
2 changed files with 53 additions and 1 deletions

View File

@@ -275,7 +275,10 @@ func FindBeadsDir() string {
if beadsDir := os.Getenv("BEADS_DIR"); beadsDir != "" {
absBeadsDir := utils.CanonicalizePath(beadsDir)
if info, err := os.Stat(absBeadsDir); err == nil && info.IsDir() {
return absBeadsDir
// Validate directory contains actual project files (bd-420)
if hasBeadsProjectFiles(absBeadsDir) {
return absBeadsDir
}
}
}

View File

@@ -410,6 +410,55 @@ func TestFindBeadsDirSkipsDaemonRegistry(t *testing.T) {
}
}
// TestFindBeadsDirValidatesBeadsDirEnv verifies that BEADS_DIR env var
// is validated for project files (bd-420)
func TestFindBeadsDirValidatesBeadsDirEnv(t *testing.T) {
// Save original state
originalEnv := os.Getenv("BEADS_DIR")
defer func() {
if originalEnv != "" {
os.Setenv("BEADS_DIR", originalEnv)
} else {
os.Unsetenv("BEADS_DIR")
}
}()
// Create temp directory with only daemon registry files
tmpDir, err := os.MkdirTemp("", "beads-env-test-*")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpDir)
if err := os.WriteFile(filepath.Join(tmpDir, "registry.json"), []byte("[]"), 0644); err != nil {
t.Fatal(err)
}
// Set BEADS_DIR to daemon-only directory
os.Setenv("BEADS_DIR", tmpDir)
// Should NOT return the daemon-only directory
result := FindBeadsDir()
if result != "" {
resultResolved, _ := filepath.EvalSymlinks(result)
tmpDirResolved, _ := filepath.EvalSymlinks(tmpDir)
if resultResolved == tmpDirResolved {
t.Errorf("FindBeadsDir() should skip BEADS_DIR with only daemon files, got %q", result)
}
}
// Now add a project file
if err := os.WriteFile(filepath.Join(tmpDir, "beads.db"), []byte{}, 0644); err != nil {
t.Fatal(err)
}
// Should now return the directory
result = FindBeadsDir()
if result == "" {
t.Error("FindBeadsDir() should return BEADS_DIR with project files")
}
}
func TestFindDatabasePathHomeDefault(t *testing.T) {
// This test verifies that if no database is found, it falls back to home directory
// We can't reliably test this without modifying the home directory, so we'll skip