unarr/internal/engine/organize_expand_test.go
Deivid Soto 3e0f3a5a64
Some checks failed
Release / release (push) Failing after 0s
Release / docker (push) Has been skipped
Release / virustotal (push) Failing after 0s
feat(cli): upgrade command, rich status, and version cache
- Replace `upgrade` stub with real command (alias for `self-update`)
- Also register `update` as alias: `unarr update` works too
- Rewrite `status` to show full config, disk usage, daemon state, and
  update availability with colored sections
- Add version check cache (1h TTL) so `status` is instant on repeat runs
- Guard against division by zero on empty filesystems
- Guard against negative durations from clock skew
- Guard against stale PID via heartbeat recency check (2 min)
- Add comprehensive test coverage across agent, engine, upgrade, usenet,
  arr, library, mediaserver, and UI packages
- Improve Makefile coverage target to exclude cmd/ glue code
- Fix stream handler resource cleanup and ffprobe error handling
2026-03-31 22:05:43 +02:00

181 lines
4.2 KiB
Go

package engine
import (
"os"
"path/filepath"
"testing"
)
func TestReplaceFile(t *testing.T) {
tmp := t.TempDir()
backupDir := filepath.Join(tmp, "backups")
// Create "old" file
oldPath := filepath.Join(tmp, "movie.mkv")
os.WriteFile(oldPath, []byte("old content"), 0o644)
// Create "new" file
newPath := filepath.Join(tmp, "movie-new.mkv")
os.WriteFile(newPath, []byte("new better content"), 0o644)
err := replaceFile(oldPath, newPath, backupDir)
if err != nil {
t.Fatalf("replaceFile: %v", err)
}
// Old path should now contain new content
data, err := os.ReadFile(oldPath)
if err != nil {
t.Fatalf("read old path: %v", err)
}
if string(data) != "new better content" {
t.Errorf("old path content = %q, want 'new better content'", string(data))
}
// Backup should exist
entries, _ := os.ReadDir(backupDir)
if len(entries) != 1 {
t.Errorf("expected 1 backup file, got %d", len(entries))
}
// New file should be gone
if _, err := os.Stat(newPath); !os.IsNotExist(err) {
t.Error("new file should have been moved/deleted")
}
}
func TestReplaceFileOldNotFound(t *testing.T) {
tmp := t.TempDir()
err := replaceFile(filepath.Join(tmp, "nonexistent.mkv"), filepath.Join(tmp, "new.mkv"), "")
if err == nil {
t.Error("expected error when old file doesn't exist")
}
}
func TestCopyFile(t *testing.T) {
tmp := t.TempDir()
src := filepath.Join(tmp, "source.txt")
dst := filepath.Join(tmp, "dest.txt")
content := []byte("hello world copy test")
os.WriteFile(src, content, 0o644)
err := copyFile(src, dst)
if err != nil {
t.Fatalf("copyFile: %v", err)
}
data, err := os.ReadFile(dst)
if err != nil {
t.Fatalf("read dest: %v", err)
}
if string(data) != string(content) {
t.Errorf("dest content = %q, want %q", string(data), string(content))
}
}
func TestCopyFileSrcNotFound(t *testing.T) {
tmp := t.TempDir()
err := copyFile(filepath.Join(tmp, "nope.txt"), filepath.Join(tmp, "out.txt"))
if err == nil {
t.Error("expected error when source doesn't exist")
}
}
func TestOrganizeNoDirs(t *testing.T) {
r := &Result{FilePath: "/tmp/file.mkv", FileName: "file.mkv"}
task := &Task{Title: "Movie"}
path, err := organize(r, task, OrganizeConfig{Enabled: true})
if err != nil {
t.Fatal(err)
}
if path != "/tmp/file.mkv" {
t.Errorf("should return original path when no dirs configured, got %q", path)
}
}
func TestOrganizeNilResult(t *testing.T) {
task := &Task{Title: "Movie"}
path, err := organize(&Result{}, task, OrganizeConfig{Enabled: true})
if err != nil {
t.Fatal(err)
}
if path != "" {
t.Errorf("expected empty path for empty result, got %q", path)
}
}
func TestOrganizeMovieDirectory(t *testing.T) {
tmp := t.TempDir()
srcDir := filepath.Join(tmp, "src", "MovieDir")
os.MkdirAll(srcDir, 0o755)
os.WriteFile(filepath.Join(srcDir, "movie.mkv"), []byte("data"), 0o644)
moviesDir := filepath.Join(tmp, "Movies")
r := &Result{FilePath: srcDir, FileName: "MovieDir"}
task := &Task{Title: "My Movie 2023"}
path, err := organize(r, task, OrganizeConfig{
Enabled: true,
MoviesDir: moviesDir,
})
if err != nil {
t.Fatal(err)
}
if path == srcDir {
t.Error("directory should have moved")
}
if _, err := os.Stat(path); err != nil {
t.Errorf("organized directory should exist at %s", path)
}
}
func TestOrganizeSeasonOnly(t *testing.T) {
tmp := t.TempDir()
srcFile := filepath.Join(tmp, "Show.S01.Complete.mkv")
os.WriteFile(srcFile, []byte("data"), 0o644)
tvDir := filepath.Join(tmp, "TV")
r := &Result{FilePath: srcFile, FileName: "Show.S01.Complete.mkv"}
task := &Task{Title: "Show S01"}
path, err := organize(r, task, OrganizeConfig{
Enabled: true,
TVShowsDir: tvDir,
})
if err != nil {
t.Fatal(err)
}
dir := filepath.Dir(path)
if filepath.Base(dir) != "Season 01" {
t.Errorf("expected Season 01 directory, got %q", filepath.Base(dir))
}
}
func TestCleanTitleEdgeCases(t *testing.T) {
tests := []struct {
input string
want string
}{
{"", ""},
{"Simple Title", "Simple Title"},
{"Title (2023) 1080p BluRay", "Title"},
{"Title 720p HDTV", "Title"},
{"Title x264 HEVC", "Title"},
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
got := cleanTitle(tt.input)
if got != tt.want {
t.Errorf("cleanTitle(%q) = %q, want %q", tt.input, got, tt.want)
}
})
}
}