package installer import ( "errors" "os" "path/filepath" "runtime" "testing" "gitgud.io/mike/mpv-manager/internal/assets" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestInstallEmbeddedThumbfast(t *testing.T) { installDir := t.TempDir() require.NoError(t, installEmbeddedThumbfast(installDir)) want, err := assets.ReadThumbfast() require.NoError(t, err) got, err := os.ReadFile(filepath.Join(installDir, "scripts", "thumbfast.lua")) require.NoError(t, err) assert.Equal(t, want, got) } func TestWriteFileAtomicallyPreservingReplacesExistingFile(t *testing.T) { targetPath := filepath.Join(t.TempDir(), "thumbfast.lua") require.NoError(t, os.WriteFile(targetPath, []byte("previous"), 0600)) require.NoError(t, writeFileAtomicallyPreserving(targetPath, []byte("replacement"), 0644)) content, err := os.ReadFile(targetPath) require.NoError(t, err) assert.Equal(t, "replacement", string(content)) info, err := os.Stat(targetPath) require.NoError(t, err) assert.True(t, info.Mode().IsRegular()) if runtime.GOOS != "windows" { assert.Equal(t, os.FileMode(0644), info.Mode().Perm()) } } func TestWriteFileAtomicallyPreservingRestoresExistingFileOnFailure(t *testing.T) { dir := t.TempDir() targetPath := filepath.Join(dir, "thumbfast.lua") require.NoError(t, os.WriteFile(targetPath, []byte("previous"), 0644)) replaceFailure := errors.New("simulated replacement failure") renameCalls := 0 rename := func(oldPath, newPath string) error { renameCalls++ if renameCalls == 2 { return replaceFailure } return os.Rename(oldPath, newPath) } err := writeFileAtomicallyPreservingWithRename(targetPath, []byte("replacement"), 0644, rename) require.ErrorIs(t, err, replaceFailure) assert.Equal(t, 3, renameCalls, "the third rename must restore the previous file") content, readErr := os.ReadFile(targetPath) require.NoError(t, readErr) assert.Equal(t, "previous", string(content)) entries, readDirErr := os.ReadDir(dir) require.NoError(t, readDirErr) require.Len(t, entries, 1) assert.Equal(t, "thumbfast.lua", entries[0].Name()) } func TestWriteFileAtomicallyPreservingKeepsBackupWhenRestoreFails(t *testing.T) { dir := t.TempDir() targetPath := filepath.Join(dir, "thumbfast.lua") require.NoError(t, os.WriteFile(targetPath, []byte("previous"), 0644)) replaceFailure := errors.New("simulated replacement failure") restoreFailure := errors.New("simulated restore failure") renameCalls := 0 rename := func(oldPath, newPath string) error { renameCalls++ switch renameCalls { case 2: return replaceFailure case 3: return restoreFailure } return os.Rename(oldPath, newPath) } err := writeFileAtomicallyPreservingWithRename(targetPath, []byte("replacement"), 0644, rename) require.Error(t, err) assert.ErrorIs(t, err, replaceFailure) assert.ErrorIs(t, err, restoreFailure) assert.Contains(t, err.Error(), "restore previous file") assert.Equal(t, 3, renameCalls) } func TestWriteFileAtomicallyPreservingErrorPaths(t *testing.T) { t.Run("target is a directory", func(t *testing.T) { err := writeFileAtomicallyPreserving(t.TempDir(), []byte("data"), 0644) require.Error(t, err) assert.Contains(t, err.Error(), "target is a directory") }) t.Run("missing parent directory", func(t *testing.T) { targetPath := filepath.Join(t.TempDir(), "missing", "file.lua") err := writeFileAtomicallyPreserving(targetPath, []byte("data"), 0644) require.Error(t, err) assert.Contains(t, err.Error(), "create temporary file") }) t.Run("backup rename failure", func(t *testing.T) { targetPath := filepath.Join(t.TempDir(), "thumbfast.lua") require.NoError(t, os.WriteFile(targetPath, []byte("previous"), 0644)) backupFailure := errors.New("simulated backup failure") rename := func(oldPath, newPath string) error { return backupFailure } err := writeFileAtomicallyPreservingWithRename(targetPath, []byte("replacement"), 0644, rename) require.ErrorIs(t, err, backupFailure) assert.Contains(t, err.Error(), "back up existing file") // The original file is untouched. content, readErr := os.ReadFile(targetPath) require.NoError(t, readErr) assert.Equal(t, "previous", string(content)) }) } func TestInstallEmbeddedThumbfast_BadInstallDir(t *testing.T) { // A regular file where the scripts dir should be makes MkdirAll fail. installDir := t.TempDir() require.NoError(t, os.WriteFile(filepath.Join(installDir, "scripts"), []byte("blocker"), 0644)) err := installEmbeddedThumbfast(installDir) require.Error(t, err) assert.Contains(t, err.Error(), "create scripts directory") }