fix(init): prevent data loss when reinitializing with existing content

This commit is contained in:
Yar Kravtsov
2025-07-30 10:41:03 +03:00
parent ab97fa86dc
commit 43b68bc071
5 changed files with 602 additions and 9 deletions

View File

@@ -3,6 +3,7 @@ package core
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
@@ -1432,6 +1433,170 @@ func (suite *CoreTestSuite) TestPreviewAddValidation() {
suite.Contains(err.Error(), "already managed", "Error should mention already managed")
}
// Task 1.1: Tests for HasUserContent() method
func (suite *CoreTestSuite) TestHasUserContent_WithCommonTracker_ReturnsTrue() {
// Initialize lnk repository
err := suite.lnk.Init()
suite.Require().NoError(err)
// Create .lnk file to simulate existing content
lnkFile := filepath.Join(suite.tempDir, "lnk", ".lnk")
err = os.WriteFile(lnkFile, []byte(".bashrc\n"), 0644)
suite.Require().NoError(err)
// Call HasUserContent()
hasContent := suite.lnk.HasUserContent()
suite.True(hasContent, "Should detect common tracker file")
}
func (suite *CoreTestSuite) TestHasUserContent_WithHostTracker_ReturnsTrue() {
// Initialize lnk repository
err := suite.lnk.Init()
suite.Require().NoError(err)
// Create host-specific lnk instance
hostLnk := NewLnk(WithHost("testhost"))
// Create .lnk.hostname file to simulate host-specific content
lnkFile := filepath.Join(suite.tempDir, "lnk", ".lnk.testhost")
err = os.WriteFile(lnkFile, []byte(".vimrc\n"), 0644)
suite.Require().NoError(err)
// Call HasUserContent()
hasContent := hostLnk.HasUserContent()
suite.True(hasContent, "Should detect host-specific tracker file")
}
func (suite *CoreTestSuite) TestHasUserContent_WithBothTrackers_ReturnsTrue() {
// Initialize lnk repository
err := suite.lnk.Init()
suite.Require().NoError(err)
// Create both common and host-specific tracker files
commonLnkFile := filepath.Join(suite.tempDir, "lnk", ".lnk")
err = os.WriteFile(commonLnkFile, []byte(".bashrc\n"), 0644)
suite.Require().NoError(err)
hostLnkFile := filepath.Join(suite.tempDir, "lnk", ".lnk.testhost")
err = os.WriteFile(hostLnkFile, []byte(".vimrc\n"), 0644)
suite.Require().NoError(err)
// Test with common instance
hasContent := suite.lnk.HasUserContent()
suite.True(hasContent, "Should detect common tracker file")
// Test with host-specific instance
hostLnk := NewLnk(WithHost("testhost"))
hasContent = hostLnk.HasUserContent()
suite.True(hasContent, "Should detect host-specific tracker file")
}
func (suite *CoreTestSuite) TestHasUserContent_EmptyDirectory_ReturnsFalse() {
// Initialize lnk repository
err := suite.lnk.Init()
suite.Require().NoError(err)
// Call HasUserContent() on empty repository
hasContent := suite.lnk.HasUserContent()
suite.False(hasContent, "Should return false for empty repository")
}
func (suite *CoreTestSuite) TestHasUserContent_NonTrackerFiles_ReturnsFalse() {
// Initialize lnk repository
err := suite.lnk.Init()
suite.Require().NoError(err)
// Create non-tracker files
randomFile := filepath.Join(suite.tempDir, "lnk", "random.txt")
err = os.WriteFile(randomFile, []byte("some content"), 0644)
suite.Require().NoError(err)
configFile := filepath.Join(suite.tempDir, "lnk", ".gitignore")
err = os.WriteFile(configFile, []byte("*.log"), 0644)
suite.Require().NoError(err)
// Call HasUserContent()
hasContent := suite.lnk.HasUserContent()
suite.False(hasContent, "Should return false when only non-tracker files exist")
}
// Task 2.1: Tests for enhanced InitWithRemote() safety check
func (suite *CoreTestSuite) TestInitWithRemote_HasUserContent_ReturnsError() {
// Initialize and add content first
err := suite.lnk.Init()
suite.Require().NoError(err)
// Create .lnk file to simulate existing content
lnkFile := filepath.Join(suite.tempDir, "lnk", ".lnk")
err = os.WriteFile(lnkFile, []byte(".bashrc\n"), 0644)
suite.Require().NoError(err)
// Try InitWithRemote - should fail
err = suite.lnk.InitWithRemote("https://github.com/test/dotfiles.git")
suite.Error(err, "Should fail when user content exists")
suite.Contains(err.Error(), "already contains managed files")
suite.Contains(err.Error(), "lnk pull")
// Verify .lnk file still exists (no deletion occurred)
suite.FileExists(lnkFile)
}
func (suite *CoreTestSuite) TestInitWithRemote_EmptyDirectory_Success() {
// Create a dummy remote directory for testing
remoteDir := filepath.Join(suite.tempDir, "remote")
err := os.MkdirAll(remoteDir, 0755)
suite.Require().NoError(err)
// Initialize a bare git repository as remote
cmd := exec.Command("git", "init", "--bare")
cmd.Dir = remoteDir
err = cmd.Run()
suite.Require().NoError(err)
// InitWithRemote should succeed on empty directory
err = suite.lnk.InitWithRemote(remoteDir)
suite.NoError(err, "Should succeed when no user content exists")
// Verify repository was cloned
lnkDir := filepath.Join(suite.tempDir, "lnk")
suite.DirExists(lnkDir)
gitDir := filepath.Join(lnkDir, ".git")
suite.DirExists(gitDir)
}
func (suite *CoreTestSuite) TestInitWithRemote_NoRemoteURL_BypassesSafetyCheck() {
// Initialize and add content first
err := suite.lnk.Init()
suite.Require().NoError(err)
// Create .lnk file to simulate existing content
lnkFile := filepath.Join(suite.tempDir, "lnk", ".lnk")
err = os.WriteFile(lnkFile, []byte(".bashrc\n"), 0644)
suite.Require().NoError(err)
// InitWithRemote with empty URL should bypass safety check (this is local init)
err = suite.lnk.InitWithRemote("")
suite.NoError(err, "Should bypass safety check when no remote URL provided")
}
func (suite *CoreTestSuite) TestInitWithRemote_ErrorMessage_ContainsSuggestedCommand() {
// Initialize and add content first
err := suite.lnk.Init()
suite.Require().NoError(err)
// Create host-specific content
hostLnk := NewLnk(WithHost("testhost"))
hostLnkFile := filepath.Join(suite.tempDir, "lnk", ".lnk.testhost")
err = os.WriteFile(hostLnkFile, []byte(".vimrc\n"), 0644)
suite.Require().NoError(err)
// Try InitWithRemote - should fail with helpful message
err = hostLnk.InitWithRemote("https://github.com/test/dotfiles.git")
suite.Error(err, "Should fail when user content exists")
suite.Contains(err.Error(), "lnk pull", "Should suggest pull command")
suite.Contains(err.Error(), "instead of", "Should explain alternative")
}
func TestCoreSuite(t *testing.T) {
suite.Run(t, new(CoreTestSuite))
}