package api import ( "testing" "time" "github.com/ponzischeme89/memby/server/internal/radarr" ) func radarrDetailDay(year int, month time.Month, day int) time.Time { return time.Date(year, month, day, 0, 0, 0, 0, time.UTC) } func TestRadarrMovieID(t *testing.T) { for _, testCase := range []struct { raw string want int ok bool }{ {raw: "radarr:412", want: 412, ok: true}, {raw: " radarr:412 ", want: 412, ok: true}, {raw: "412", want: 412, ok: true}, {raw: "", ok: false}, {raw: "radarr:", ok: false}, {raw: "radarr:0", ok: false}, {raw: "radarr:-3", ok: false}, {raw: "abc123", ok: false}, } { id, ok := radarrMovieID(testCase.raw) if ok != testCase.ok || id != testCase.want { t.Fatalf("radarrMovieID(%q) = %d, %v; want %d, %v", testCase.raw, id, ok, testCase.want, testCase.ok) } } } // A published digital date is named to the day; the schedule row's cinema-plus-a-month // estimate is named only to the month, because it is a guess and a guess printed as // "14 November" is one somebody plans an evening around. func TestRadarrExpectedLabelPrecision(t *testing.T) { now := radarrDetailDay(2026, time.August, 19) published := radarrRelease{at: radarrDetailDay(2026, time.November, 14)} estimated := radarrRelease{at: radarrDetailDay(2026, time.November, 14), estimated: true} past := radarrRelease{at: radarrDetailDay(2026, time.March, 3)} if got := radarrExpectedLabel(published, true, now, time.UTC); got != "Expected 14 November 2026" { t.Fatalf("published: %q", got) } if got := radarrExpectedLabel(estimated, true, now, time.UTC); got != "Expected November 2026" { t.Fatalf("estimated: %q", got) } if got := radarrExpectedLabel(past, true, now, time.UTC); got != "Released 3 March 2026" { t.Fatalf("past: %q", got) } if got := radarrExpectedLabel(radarrRelease{}, false, now, time.UTC); got != "Release date not yet announced" { t.Fatalf("unknown: %q", got) } } func TestRadarrMovieStateReadsTheHouseholdsCopy(t *testing.T) { now := radarrDetailDay(2026, time.August, 19) future := radarrRelease{at: radarrDetailDay(2026, time.November, 14)} past := radarrRelease{at: radarrDetailDay(2026, time.March, 3)} for _, testCase := range []struct { name string movie radarr.Movie release radarrRelease hasRelease bool want string }{ {name: "coming soon", movie: radarr.Movie{Monitored: true}, release: future, hasRelease: true, want: "Coming Soon"}, {name: "out but not here", movie: radarr.Movie{Monitored: true}, release: past, hasRelease: true, want: "Not Yet Available"}, {name: "no date", movie: radarr.Movie{Monitored: true}, want: "Awaiting Release"}, {name: "unmonitored", movie: radarr.Movie{}, release: future, hasRelease: true, want: "Not Tracked"}, { name: "downloaded but unscanned", movie: radarr.Movie{Monitored: true, HasFile: true}, release: past, hasRelease: true, want: "Almost Ready", }, } { label, detail := radarrMovieState(testCase.movie, testCase.release, testCase.hasRelease, now) if label != testCase.want { t.Fatalf("%s: state = %q, want %q", testCase.name, label, testCase.want) } if detail == "" { t.Fatalf("%s: a state with no explanation under it", testCase.name) } } } func TestBuildRadarrMovieDetail(t *testing.T) { cinema := radarrDetailDay(2026, time.October, 2) digital := radarrDetailDay(2026, time.November, 14) movie := radarr.Movie{ ID: 412, TMDBID: 9001, Title: "The Quiet Coast", OriginalTitle: "The Quiet Coast", Overview: " A harbour town in winter. ", Year: 2026, Runtime: 118, Genres: []string{"Drama", "Mystery"}, Studio: "Kōwhai Pictures", Certification: "M", Status: "announced", Monitored: true, YouTubeTrailerID: "abc123", InCinemas: &cinema, DigitalRelease: &digital, Images: []radarr.Image{{CoverType: "poster"}, {CoverType: "fanart"}}, } detail := buildRadarrMovieDetail(movie, radarrDetailDay(2026, time.August, 19), time.UTC) if detail.ID != "radarr:412" { t.Fatalf("id: %q", detail.ID) } if detail.Overview != "A harbour town in winter." { t.Fatalf("overview: %q", detail.Overview) } // The same title twice is the common case and reads as a mistake when printed. if detail.OriginalTitle != "" { t.Fatalf("original title repeated: %q", detail.OriginalTitle) } if detail.ExpectedLabel != "Expected 14 November 2026" { t.Fatalf("expected label: %q", detail.ExpectedLabel) } if detail.StateLabel != "Coming Soon" { t.Fatalf("state: %q", detail.StateLabel) } if detail.LifecycleText != "ANNOUNCED" || detail.Lifecycle != "announced" { t.Fatalf("lifecycle: %q/%q", detail.Lifecycle, detail.LifecycleText) } if !detail.TrailerAvailable { t.Fatalf("a film with a trailer id must offer the action: %+v", detail) } if detail.AvailabilityNotice == "" { t.Fatal("the page must say it cannot be watched here") } if len(detail.ReleaseDates) != 2 || detail.ReleaseDates[0].Kind != "cinema" || detail.ReleaseDates[0].Value != "2 October 2026" || detail.ReleaseDates[1].Kind != "digital" || detail.ReleaseDates[1].Value != "14 November 2026" { t.Fatalf("release dates: %+v", detail.ReleaseDates) } // Never null on the wire: the television decodes these as lists. if detail.Genres == nil || detail.Ratings == nil { t.Fatalf("nil collections: %+v", detail) } } // A film with nothing but a cinema date is the case the precision rule exists for: the // schedule row places it a month later, and the page must not present that as a fact. func TestBuildRadarrMovieDetailCinemaOnly(t *testing.T) { cinema := radarrDetailDay(2026, time.October, 2) detail := buildRadarrMovieDetail( radarr.Movie{ID: 7, Title: "Harbour Lights", InCinemas: &cinema, Monitored: true, Status: "inCinemas"}, radarrDetailDay(2026, time.August, 19), time.UTC, ) if detail.ExpectedLabel != "Expected November 2026" { t.Fatalf("expected label: %q", detail.ExpectedLabel) } if len(detail.ReleaseDates) != 1 || detail.ReleaseDates[0].Kind != "cinema" { t.Fatalf("release dates: %+v", detail.ReleaseDates) } if detail.TrailerAvailable { t.Fatal("a film with no trailer id must not offer the action") } } func TestBuildRadarrMovieDetailUnannounced(t *testing.T) { detail := buildRadarrMovieDetail( radarr.Movie{ID: 9, Title: "Untitled", OriginalTitle: "Sans Titre", Monitored: true, Status: "tba"}, radarrDetailDay(2026, time.August, 19), time.UTC, ) if detail.ExpectedLabel != "Release date not yet announced" { t.Fatalf("expected label: %q", detail.ExpectedLabel) } if detail.StateLabel != "Awaiting Release" { t.Fatalf("state: %q", detail.StateLabel) } if len(detail.ReleaseDates) != 0 { t.Fatalf("release dates: %+v", detail.ReleaseDates) } if detail.OriginalTitle != "Sans Titre" { t.Fatalf("a differing original title is worth printing: %q", detail.OriginalTitle) } }