You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
dotctl/test/link_test.go

283 lines
8.9 KiB
Go

package test
import (
"bytes"
"os"
"path/filepath"
"testing"
"github.com/Marcusk19/dotctl/cmd"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestLinkDotfiles_MissingTarget(t *testing.T) {
dotfileRoot := t.TempDir()
targetRoot := t.TempDir()
// Create source dir in dotfileRoot
dotPath := filepath.Join(dotfileRoot, "nvim")
require.NoError(t, os.MkdirAll(dotPath, 0755))
// Target does not exist yet
targetPath := filepath.Join(targetRoot, ".config", "nvim")
links := map[string]string{"nvim": targetPath}
var buf bytes.Buffer
result := cmd.LinkDotfiles(&buf, dotfileRoot, links, false, false, false)
assert.Equal(t, 1, result.Linked, "should have linked 1")
assert.Equal(t, 0, result.Skipped)
assert.Equal(t, 0, result.Backed)
// Verify symlink was actually created and points to the right place
linkTarget, err := os.Readlink(targetPath)
require.NoError(t, err, "symlink should exist at targetPath")
assert.Equal(t, dotPath, linkTarget, "symlink should point to dotPath")
}
func TestLinkDotfiles_AlreadyLinked(t *testing.T) {
dotfileRoot := t.TempDir()
targetRoot := t.TempDir()
dotPath := filepath.Join(dotfileRoot, "nvim")
require.NoError(t, os.MkdirAll(dotPath, 0755))
targetPath := filepath.Join(targetRoot, ".config", "nvim")
require.NoError(t, os.MkdirAll(filepath.Dir(targetPath), 0755))
// Pre-create correct symlink
require.NoError(t, os.Symlink(dotPath, targetPath))
links := map[string]string{"nvim": targetPath}
var buf bytes.Buffer
result := cmd.LinkDotfiles(&buf, dotfileRoot, links, false, false, false)
assert.Equal(t, 0, result.Linked)
assert.Equal(t, 1, result.Skipped, "should have skipped 1 (already linked)")
assert.Equal(t, 0, result.Backed)
// Symlink should still point to dotPath
linkTarget, err := os.Readlink(targetPath)
require.NoError(t, err)
assert.Equal(t, dotPath, linkTarget)
assert.Contains(t, buf.String(), "already linked")
}
func TestLinkDotfiles_WrongSymlink_NoOverwrite(t *testing.T) {
dotfileRoot := t.TempDir()
targetRoot := t.TempDir()
dotPath := filepath.Join(dotfileRoot, "nvim")
require.NoError(t, os.MkdirAll(dotPath, 0755))
wrongPath := filepath.Join(t.TempDir(), "wrong")
require.NoError(t, os.MkdirAll(wrongPath, 0755))
targetPath := filepath.Join(targetRoot, ".config", "nvim")
require.NoError(t, os.MkdirAll(filepath.Dir(targetPath), 0755))
// Pre-create symlink pointing to the wrong place
require.NoError(t, os.Symlink(wrongPath, targetPath))
links := map[string]string{"nvim": targetPath}
var buf bytes.Buffer
result := cmd.LinkDotfiles(&buf, dotfileRoot, links, false, false, false)
assert.Equal(t, 0, result.Linked)
assert.Equal(t, 1, result.Skipped, "should have skipped 1 (wrong symlink, no overwrite)")
assert.Equal(t, 0, result.Backed)
// Symlink should still point to the wrong path (unchanged)
linkTarget, err := os.Readlink(targetPath)
require.NoError(t, err)
assert.Equal(t, wrongPath, linkTarget)
}
func TestLinkDotfiles_WrongSymlink_Overwrite(t *testing.T) {
dotfileRoot := t.TempDir()
targetRoot := t.TempDir()
dotPath := filepath.Join(dotfileRoot, "nvim")
require.NoError(t, os.MkdirAll(dotPath, 0755))
wrongPath := filepath.Join(t.TempDir(), "wrong")
require.NoError(t, os.MkdirAll(wrongPath, 0755))
targetPath := filepath.Join(targetRoot, ".config", "nvim")
require.NoError(t, os.MkdirAll(filepath.Dir(targetPath), 0755))
// Pre-create symlink pointing to the wrong place
require.NoError(t, os.Symlink(wrongPath, targetPath))
links := map[string]string{"nvim": targetPath}
var buf bytes.Buffer
result := cmd.LinkDotfiles(&buf, dotfileRoot, links, true, false, false)
assert.Equal(t, 1, result.Linked, "should have relinked 1")
assert.Equal(t, 0, result.Skipped)
assert.Equal(t, 0, result.Backed)
// Symlink should now point to dotPath
linkTarget, err := os.Readlink(targetPath)
require.NoError(t, err)
assert.Equal(t, dotPath, linkTarget, "symlink should now point to dotPath")
}
func TestLinkDotfiles_RealFile_WithBackup(t *testing.T) {
dotfileRoot := t.TempDir()
targetRoot := t.TempDir()
dotPath := filepath.Join(dotfileRoot, "nvim")
require.NoError(t, os.MkdirAll(dotPath, 0755))
targetPath := filepath.Join(targetRoot, ".config", "nvim")
require.NoError(t, os.MkdirAll(filepath.Dir(targetPath), 0755))
// Create a real file at the target path
require.NoError(t, os.WriteFile(targetPath, []byte("original content"), 0644))
links := map[string]string{"nvim": targetPath}
var buf bytes.Buffer
result := cmd.LinkDotfiles(&buf, dotfileRoot, links, false, false, false)
assert.Equal(t, 0, result.Linked)
assert.Equal(t, 0, result.Skipped)
assert.Equal(t, 1, result.Backed, "should have backed up 1")
// Backup file should exist with original content
backupPath := targetPath + ".dotctl.bak"
backupContent, err := os.ReadFile(backupPath)
require.NoError(t, err, "backup file should exist")
assert.Equal(t, "original content", string(backupContent))
// Target should now be a symlink to dotPath
linkTarget, err := os.Readlink(targetPath)
require.NoError(t, err, "target should be a symlink now")
assert.Equal(t, dotPath, linkTarget)
}
func TestLinkDotfiles_RealFile_NoBackup(t *testing.T) {
dotfileRoot := t.TempDir()
targetRoot := t.TempDir()
dotPath := filepath.Join(dotfileRoot, "nvim")
require.NoError(t, os.MkdirAll(dotPath, 0755))
targetPath := filepath.Join(targetRoot, ".config", "nvim")
require.NoError(t, os.MkdirAll(filepath.Dir(targetPath), 0755))
// Create a real file at the target path
require.NoError(t, os.WriteFile(targetPath, []byte("original content"), 0644))
links := map[string]string{"nvim": targetPath}
var buf bytes.Buffer
result := cmd.LinkDotfiles(&buf, dotfileRoot, links, false, true, false)
assert.Equal(t, 0, result.Linked)
assert.Equal(t, 1, result.Skipped, "should have skipped 1 (noBackup)")
assert.Equal(t, 0, result.Backed)
// Original file should be untouched
content, err := os.ReadFile(targetPath)
require.NoError(t, err)
assert.Equal(t, "original content", string(content))
// No backup file should exist
backupPath := targetPath + ".dotctl.bak"
_, err = os.Stat(backupPath)
assert.True(t, os.IsNotExist(err), "backup file should not exist")
}
func TestLinkDotfiles_DryRun(t *testing.T) {
dotfileRoot := t.TempDir()
targetRoot := t.TempDir()
dotPath := filepath.Join(dotfileRoot, "nvim")
require.NoError(t, os.MkdirAll(dotPath, 0755))
targetPath := filepath.Join(targetRoot, ".config", "nvim")
// Do NOT create parent dirs - dryRun should not create them either
links := map[string]string{"nvim": targetPath}
var buf bytes.Buffer
result := cmd.LinkDotfiles(&buf, dotfileRoot, links, false, false, true)
assert.Equal(t, 1, result.Linked, "should count 1 linked even in dry run")
assert.Equal(t, 0, result.Skipped)
assert.Equal(t, 0, result.Backed)
// No symlink should actually be created
_, err := os.Lstat(targetPath)
assert.True(t, os.IsNotExist(err), "symlink should NOT be created in dry run")
assert.Contains(t, buf.String(), "would link")
}
func TestLinkDotfiles_SkipsGitAndDotctl(t *testing.T) {
dotfileRoot := t.TempDir()
targetRoot := t.TempDir()
links := map[string]string{
".git": filepath.Join(targetRoot, ".git"),
"dotctl": filepath.Join(targetRoot, "dotctl"),
}
var buf bytes.Buffer
result := cmd.LinkDotfiles(&buf, dotfileRoot, links, false, false, false)
assert.Equal(t, 0, result.Linked)
assert.Equal(t, 0, result.Skipped)
assert.Equal(t, 0, result.Backed)
// Nothing should be created
_, err := os.Lstat(filepath.Join(targetRoot, ".git"))
assert.True(t, os.IsNotExist(err))
_, err = os.Lstat(filepath.Join(targetRoot, "dotctl"))
assert.True(t, os.IsNotExist(err))
}
func TestLinkDotfiles_EmptyTargetPath(t *testing.T) {
dotfileRoot := t.TempDir()
links := map[string]string{"nvim": ""}
var buf bytes.Buffer
result := cmd.LinkDotfiles(&buf, dotfileRoot, links, false, false, false)
assert.Equal(t, 0, result.Linked)
assert.Equal(t, 1, result.Skipped, "empty target should be skipped")
assert.Equal(t, 0, result.Backed)
}
func TestLinkDotfiles_MultipleEntries(t *testing.T) {
dotfileRoot := t.TempDir()
targetRoot := t.TempDir()
// Create two source dirs
require.NoError(t, os.MkdirAll(filepath.Join(dotfileRoot, "nvim"), 0755))
require.NoError(t, os.MkdirAll(filepath.Join(dotfileRoot, "zsh"), 0755))
nvimTarget := filepath.Join(targetRoot, ".config", "nvim")
zshTarget := filepath.Join(targetRoot, ".zshrc")
links := map[string]string{
"nvim": nvimTarget,
"zsh": zshTarget,
}
var buf bytes.Buffer
result := cmd.LinkDotfiles(&buf, dotfileRoot, links, false, false, false)
assert.Equal(t, 2, result.Linked, "should have linked 2 entries")
assert.Equal(t, 0, result.Skipped)
assert.Equal(t, 0, result.Backed)
// Verify both symlinks
linkTarget, err := os.Readlink(nvimTarget)
require.NoError(t, err)
assert.Equal(t, filepath.Join(dotfileRoot, "nvim"), linkTarget)
linkTarget, err = os.Readlink(zshTarget)
require.NoError(t, err)
assert.Equal(t, filepath.Join(dotfileRoot, "zsh"), linkTarget)
}