Add queue listing and removal/blocklisting to the shared arr client. The Downloads view now fetches SABnzbd and both arr queues in parallel, shows status, progress and import problems, flags items needing attention in the summary, and supports removing or blocklisting items. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
158 lines
5.1 KiB
Go
158 lines
5.1 KiB
Go
package ui
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.stinnesbeck.com/nils/arr-tui/internal/arr"
|
|
"git.stinnesbeck.com/nils/arr-tui/internal/radarr"
|
|
"git.stinnesbeck.com/nils/arr-tui/internal/sonarr"
|
|
)
|
|
|
|
func TestMovieLabel(t *testing.T) {
|
|
if got := movieLabel(radarr.Movie{Title: "Alien", Year: 1979}); got != "Alien (1979)" {
|
|
t.Errorf("got %q", got)
|
|
}
|
|
if got := movieLabel(radarr.Movie{ID: 3, Title: "Alien", Year: 1979}); !strings.Contains(got, "✓") {
|
|
t.Errorf("in-library marker missing: %q", got)
|
|
}
|
|
// tview color tags in titles must be escaped
|
|
if got := movieLabel(radarr.Movie{Title: "[red]x"}); strings.Contains(got, "[red]x") {
|
|
t.Errorf("title not escaped: %q", got)
|
|
}
|
|
}
|
|
|
|
func TestMovieDetails(t *testing.T) {
|
|
got := movieDetails(radarr.Movie{
|
|
ID: 1, Title: "Alien", Year: 1979, Runtime: 117, Genres: []string{"Horror", "Sci-Fi"},
|
|
Overview: "In space...", Ratings: radarr.Ratings{IMDB: &radarr.Rating{Value: 8.5}},
|
|
})
|
|
for _, want := range []string{"Alien (1979)", "★ 8.5", "1h57m", "Horror, Sci-Fi", "In library, not downloaded", "In space..."} {
|
|
if !strings.Contains(got, want) {
|
|
t.Errorf("details missing %q:\n%s", want, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDefaultIndexes(t *testing.T) {
|
|
profiles := []arr.QualityProfile{{Name: "Any"}, {Name: "HD-1080p"}}
|
|
if profileIndex(profiles, "hd-1080p") != 1 || profileIndex(profiles, "nope") != 0 {
|
|
t.Error("profileIndex")
|
|
}
|
|
folders := []arr.RootFolder{{Path: "/a"}, {Path: "/movies/"}}
|
|
if folderIndex(folders, "/movies") != 1 || folderIndex(folders, "") != 0 {
|
|
t.Error("folderIndex")
|
|
}
|
|
}
|
|
|
|
func TestSeriesLabelAndDetails(t *testing.T) {
|
|
s := sonarr.Series{
|
|
Title: "Breaking Bad", Year: 2008, Network: "AMC", Status: "ended", Runtime: 47,
|
|
Genres: []string{"Crime"},
|
|
Ratings: sonarr.Rating{Value: 9.3},
|
|
Seasons: []sonarr.Season{{SeasonNumber: 0}, {SeasonNumber: 1}},
|
|
}
|
|
if got := seriesLabel(s); got != "Breaking Bad (2008)" {
|
|
t.Errorf("label = %q", got)
|
|
}
|
|
got := seriesDetails(s)
|
|
for _, want := range []string{"Breaking Bad (2008)", "★ 9.3", "1 season ·", "47 min", "Network: AMC", "Status: ended"} {
|
|
if !strings.Contains(got, want) {
|
|
t.Errorf("details missing %q:\n%s", want, got)
|
|
}
|
|
}
|
|
|
|
s.ID = 1
|
|
s.Statistics = sonarr.Statistics{EpisodeCount: 10, EpisodeFileCount: 4}
|
|
if got := seriesDetails(s); !strings.Contains(got, "4/10 episodes") {
|
|
t.Errorf("progress missing:\n%s", got)
|
|
}
|
|
s.Statistics.EpisodeFileCount = 10
|
|
if got := seriesDetails(s); !strings.Contains(got, "all episodes downloaded") {
|
|
t.Errorf("complete marker missing:\n%s", got)
|
|
}
|
|
}
|
|
|
|
func TestResultKeysAndWithID(t *testing.T) {
|
|
var r result = movieResult{radarr.Movie{TMDBID: 603, Title: "x"}}
|
|
if r.key() != "tmdb:603" || r.inLibrary() {
|
|
t.Fatalf("movie key/inLibrary: %q %v", r.key(), r.inLibrary())
|
|
}
|
|
if added := r.withID(5); !added.inLibrary() || r.inLibrary() {
|
|
t.Error("withID must return a flagged copy and leave the original alone")
|
|
}
|
|
r = seriesResult{sonarr.Series{TVDBID: 81189}}
|
|
if r.key() != "tvdb:81189" {
|
|
t.Errorf("series key = %q", r.key())
|
|
}
|
|
}
|
|
|
|
func TestFormatSpeed(t *testing.T) {
|
|
tests := map[float64]string{0: "0 KB/s", 0.4: "0 KB/s", 512: "512 KB/s", 1536: "1.5 MB/s"}
|
|
for in, want := range tests {
|
|
if got := formatSpeed(in); got != want {
|
|
t.Errorf("formatSpeed(%v) = %q, want %q", in, got, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestProgressBar(t *testing.T) {
|
|
if got := progressBar(50, 10); got != "█████░░░░░ 50%" {
|
|
t.Errorf("got %q", got)
|
|
}
|
|
if got := progressBar(150, 4); got != "████ 100%" {
|
|
t.Errorf("clamp high: %q", got)
|
|
}
|
|
if got := progressBar(-5, 4); got != "░░░░ 0%" {
|
|
t.Errorf("clamp low: %q", got)
|
|
}
|
|
}
|
|
|
|
func TestFormatAge(t *testing.T) {
|
|
now := time.Unix(1_000_000, 0)
|
|
tests := map[int64]string{
|
|
0: "",
|
|
now.Unix() - 10: "just now",
|
|
now.Unix() - 5*60: "5m ago",
|
|
now.Unix() - 3*3600: "3h ago",
|
|
now.Unix() - 3*24*3600: "3d ago",
|
|
}
|
|
for in, want := range tests {
|
|
if got := formatAge(in, now); got != want {
|
|
t.Errorf("formatAge(%d) = %q, want %q", in, got, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestTruncate(t *testing.T) {
|
|
if got := truncate("short", 10); got != "short" {
|
|
t.Errorf("got %q", got)
|
|
}
|
|
if got := truncate("Aborted, cannot be completed", 10); got != "Aborted, …" {
|
|
t.Errorf("got %q", got)
|
|
}
|
|
if got := truncate("äöüäöü", 4); got != "äöü…" {
|
|
t.Errorf("rune handling: %q", got)
|
|
}
|
|
}
|
|
|
|
func TestArrStatusText(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
item arr.QueueItem
|
|
want string
|
|
}{
|
|
{"downloading", arr.QueueItem{TrackedDownloadState: "downloading", TrackedDownloadStatus: "ok"}, "Downloading"},
|
|
{"blocked", arr.QueueItem{TrackedDownloadState: "importBlocked", TrackedDownloadStatus: "warning"}, "[red]Import blocked[-]"},
|
|
{"warning", arr.QueueItem{TrackedDownloadState: "importPending", TrackedDownloadStatus: "warning"}, "[yellow]Import pending[-]"},
|
|
{"error", arr.QueueItem{Status: "failed", TrackedDownloadStatus: "error"}, "[red]Failed[-]"},
|
|
{"fallback to status", arr.QueueItem{Status: "queued"}, "Queued"},
|
|
}
|
|
for _, tc := range tests {
|
|
if got := arrStatusText(tc.item); got != tc.want {
|
|
t.Errorf("%s: got %q, want %q", tc.name, got, tc.want)
|
|
}
|
|
}
|
|
}
|