Release listing and grab in the shared arr client, a full-screen release list (sort, hide rejected, rejection reasons, confirmation before grabbing rejected releases; Usenet only), and a season/episode picker for Sonarr. Overlays are now a stack so dialogs can open above the release list. Add a fake Newznab indexer to the dev setup. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
111 lines
3.1 KiB
Go
111 lines
3.1 KiB
Go
package ui
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"git.stinnesbeck.com/nils/arr-tui/internal/arr"
|
|
"git.stinnesbeck.com/nils/arr-tui/internal/sonarr"
|
|
)
|
|
|
|
func sampleReleases() []arr.Release {
|
|
return []arr.Release{
|
|
{GUID: "rej-big", Size: 40 << 30, AgeMinutes: 60, QualityWeight: 2000, Rejections: []string{"no space"}},
|
|
{GUID: "ok-720", Size: 4 << 30, AgeMinutes: 600, QualityWeight: 1000, Approved: true},
|
|
{GUID: "ok-1080", Size: 8 << 30, AgeMinutes: 100, QualityWeight: 1800, Approved: true},
|
|
{GUID: "ok-1080-score", Size: 6 << 30, AgeMinutes: 900, QualityWeight: 1800, CustomFormatScore: 50, Approved: true},
|
|
}
|
|
}
|
|
|
|
func guids(rs []arr.Release) []string {
|
|
out := make([]string, len(rs))
|
|
for i, r := range rs {
|
|
out[i] = r.GUID
|
|
}
|
|
return out
|
|
}
|
|
|
|
func equal(a, b []string) bool {
|
|
if len(a) != len(b) {
|
|
return false
|
|
}
|
|
for i := range a {
|
|
if a[i] != b[i] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func TestVisibleReleasesSort(t *testing.T) {
|
|
all := sampleReleases()
|
|
tests := []struct {
|
|
name string
|
|
hide bool
|
|
mode int
|
|
want []string
|
|
}{
|
|
{"best: approved first, score, quality", false, sortBest, []string{"ok-1080-score", "ok-1080", "ok-720", "rej-big"}},
|
|
{"size desc", false, sortSize, []string{"rej-big", "ok-1080", "ok-1080-score", "ok-720"}},
|
|
{"newest first", false, sortAge, []string{"rej-big", "ok-1080", "ok-720", "ok-1080-score"}},
|
|
{"hide rejected", true, sortBest, []string{"ok-1080-score", "ok-1080", "ok-720"}},
|
|
}
|
|
for _, tc := range tests {
|
|
if got := guids(visibleReleases(all, tc.hide, tc.mode)); !equal(got, tc.want) {
|
|
t.Errorf("%s: got %v, want %v", tc.name, got, tc.want)
|
|
}
|
|
}
|
|
if all[0].GUID != "rej-big" {
|
|
t.Error("visibleReleases must not reorder its input")
|
|
}
|
|
}
|
|
|
|
func TestReleaseMark(t *testing.T) {
|
|
tests := map[string]arr.Release{
|
|
"[green]✓[-]": {Approved: true},
|
|
"[red]✗[-]": {Rejected: true, Rejections: []string{"x"}},
|
|
"[yellow]~[-]": {Rejected: true, TemporarilyRejected: true},
|
|
}
|
|
for want, r := range tests {
|
|
if got := releaseMark(r); got != want {
|
|
t.Errorf("releaseMark(%+v) = %q, want %q", r, got, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFormatSizeAndAge(t *testing.T) {
|
|
sizes := map[int64]string{0: "-", 2048: "2 KB", 5 << 20: "5 MB", 3<<30 + 1<<29: "3.5 GB"}
|
|
for in, want := range sizes {
|
|
if got := formatSize(in); got != want {
|
|
t.Errorf("formatSize(%d) = %q, want %q", in, got, want)
|
|
}
|
|
}
|
|
ages := map[float64]string{5: "5m", 120: "2h", 47 * 60: "47h", 3 * 24 * 60: "3d"}
|
|
for in, want := range ages {
|
|
if got := formatReleaseAge(in); got != want {
|
|
t.Errorf("formatReleaseAge(%v) = %q, want %q", in, got, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSummarizeSeasons(t *testing.T) {
|
|
eps := []sonarr.Episode{
|
|
{SeasonNumber: 1, EpisodeNumber: 1, HasFile: true},
|
|
{SeasonNumber: 1, EpisodeNumber: 2},
|
|
{SeasonNumber: 0, EpisodeNumber: 1},
|
|
{SeasonNumber: 2, EpisodeNumber: 1},
|
|
}
|
|
got := summarizeSeasons(eps)
|
|
want := []seasonSummary{{2, 1, 0}, {1, 2, 1}, {0, 1, 0}}
|
|
if len(got) != len(want) {
|
|
t.Fatalf("got %+v", got)
|
|
}
|
|
for i := range want {
|
|
if got[i] != want[i] {
|
|
t.Errorf("season %d: got %+v, want %+v", i, got[i], want[i])
|
|
}
|
|
}
|
|
if seasonLabel(0) != "Specials" || seasonLabel(3) != "Season 3" {
|
|
t.Error("seasonLabel")
|
|
}
|
|
}
|