Files
beads/internal/beads/beads_symlink_test.go
Abhinav Gupta ac8ef9b9e3 test: replace manual os.Chdir with t.Chdir in tests (#457)
Replaces manual working directory save/restore patterns
with Go's built-in `t.Chdir()` helper across 23 test files.

The manual pattern involved calling `os.Getwd()` to save
the original directory, using `defer os.Chdir(origWd)` for
restoration, and manually handling errors during directory
changes. This boilerplate has been replaced with single
`t.Chdir(path)` calls that handle cleanup automatically.

The `t.Chdir()` method automatically restores the working
directory when the test completes, eliminating the need for
manual defer statements and error handling.

Total:
~75 instances replaced (assuming Claude's math is right)

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-04 11:21:43 -08:00

158 lines
4.1 KiB
Go

//go:build integration
// +build integration
package beads
import (
"os"
"path/filepath"
"testing"
)
// TestFindAllDatabases_SymlinkDeduplication verifies that FindAllDatabases
// properly deduplicates databases when symlinks are present in the path
func TestFindAllDatabases_SymlinkDeduplication(t *testing.T) {
// Create a temporary directory structure
tmpDir, err := os.MkdirTemp("", "beads-symlink-test")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpDir)
// Resolve symlinks (macOS /var -> /private/var, etc.)
tmpDir, err = filepath.EvalSymlinks(tmpDir)
if err != nil {
t.Fatal(err)
}
// Create structure:
// tmpDir/
// real/
// .beads/test.db
// symlink_to_real -> real/
// subdir/
// (working directory here)
// Create real directory with .beads database
realDir := filepath.Join(tmpDir, "real")
if err := os.MkdirAll(realDir, 0750); err != nil {
t.Fatal(err)
}
beadsDir := filepath.Join(realDir, ".beads")
if err := os.MkdirAll(beadsDir, 0750); err != nil {
t.Fatal(err)
}
dbPath := filepath.Join(beadsDir, "test.db")
if err := os.WriteFile(dbPath, []byte("fake db"), 0600); err != nil {
t.Fatal(err)
}
// Create symlink to real directory
symlinkDir := filepath.Join(tmpDir, "symlink_to_real")
if err := os.Symlink(realDir, symlinkDir); err != nil {
t.Skip("Cannot create symlinks on this system (may require admin on Windows)")
}
// Create subdirectory as working directory
subdir := filepath.Join(symlinkDir, "subdir")
if err := os.MkdirAll(subdir, 0750); err != nil {
t.Fatal(err)
}
// Change to subdir (which is inside the symlinked directory)
t.Chdir(subdir)
// Call FindAllDatabases
databases := FindAllDatabases()
// Should find exactly ONE database, not two
// Without the fix, it would find the same database twice:
// - Once via symlink_to_real/.beads/test.db
// - Once via real/.beads/test.db (when walking up to parent)
if len(databases) != 1 {
t.Errorf("expected 1 database (with deduplication), got %d", len(databases))
for i, db := range databases {
t.Logf(" Database %d: %s", i, db.Path)
}
}
// Verify it's the database we expect
resolvedDbPath, err := filepath.EvalSymlinks(dbPath)
if err == nil {
// Check if the found database matches the canonical path
foundDbPath, err := filepath.EvalSymlinks(databases[0].Path)
if err == nil && foundDbPath != resolvedDbPath {
t.Errorf("expected database %s, got %s", resolvedDbPath, foundDbPath)
}
}
}
// TestFindAllDatabases_MultipleSymlinksToSameDB tests that multiple symlinks
// pointing to the same database are properly deduplicated
func TestFindAllDatabases_MultipleSymlinksToSameDB(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "beads-multisymlink-test")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpDir)
// Resolve symlinks
tmpDir, err = filepath.EvalSymlinks(tmpDir)
if err != nil {
t.Fatal(err)
}
// Create structure:
// tmpDir/
// real/
// .beads/test.db
// link1 -> real/
// link2 -> real/
// workdir/
// Create real directory with database
realDir := filepath.Join(tmpDir, "real")
beadsDir := filepath.Join(realDir, ".beads")
if err := os.MkdirAll(beadsDir, 0750); err != nil {
t.Fatal(err)
}
dbPath := filepath.Join(beadsDir, "test.db")
if err := os.WriteFile(dbPath, []byte("fake db"), 0600); err != nil {
t.Fatal(err)
}
// Create multiple symlinks
link1 := filepath.Join(tmpDir, "link1")
if err := os.Symlink(realDir, link1); err != nil {
t.Skip("Cannot create symlinks on this system")
}
link2 := filepath.Join(tmpDir, "link2")
if err := os.Symlink(realDir, link2); err != nil {
t.Fatal(err)
}
// Create working directory
workdir := filepath.Join(tmpDir, "workdir")
if err := os.MkdirAll(workdir, 0750); err != nil {
t.Fatal(err)
}
// Change to working directory
t.Chdir(workdir)
// Find databases
databases := FindAllDatabases()
// Should find exactly 1 database (all paths resolve to the same real database)
if len(databases) != 1 {
t.Errorf("expected 1 database with deduplication, got %d", len(databases))
for i, db := range databases {
t.Logf(" Database %d: %s", i, db.Path)
}
}
}