feat: show release info on anime detail page

This commit is contained in:
2026-07-01 18:53:35 +02:00
parent d61ed31d1e
commit 08f58cf377
4 changed files with 207 additions and 65 deletions

View File

@@ -115,20 +115,6 @@ func TestReleasedEpisodeCount(t *testing.T) {
}
}
func TestListedEpisodeCount(t *testing.T) {
episodes := []domain.EpisodeData{
{MalID: 1, Title: "Episode 1"},
{MalID: 2, Title: "Episode 2"},
{MalID: 3, Title: "Recap", IsRecap: true},
{Title: "missing id"},
}
got := listedEpisodeCount(episodes)
if got != 2 {
t.Fatalf("listedEpisodeCount() = %d, want 2", got)
}
}
func TestAnimeEpisodeCountUsesCanonicalEpisodes(t *testing.T) {
episodeSvc := &stubEpisodeService{
episodes: domain.CanonicalEpisodeList{
@@ -160,6 +146,117 @@ func TestAnimeEpisodeCountUsesCanonicalEpisodes(t *testing.T) {
}
}
func TestAnimeReleaseInfoUsesCanonicalEpisodes(t *testing.T) {
episodeSvc := &stubEpisodeService{
episodes: domain.CanonicalEpisodeList{
Source: "AllAnime",
Episodes: []domain.CanonicalEpisode{
{Number: 1},
{Number: 2},
{Number: 3},
},
},
}
handler := NewAnimeHandler(nil, nil, episodeSvc, nil)
got := handler.animeReleaseInfo(context.Background(), domain.Anime{Anime: jikan.Anime{
MalID: 59970,
Status: "Currently Airing",
Airing: true,
Episodes: 12,
}}, time.Date(2026, time.July, 1, 12, 0, 0, 0, time.UTC))
if got.Count != 3 || got.Label != "Available episodes" || got.Status != "Currently Airing" {
t.Fatalf("animeReleaseInfo() = %+v, want 3 available episodes and current status", got)
}
if episodeSvc.called != 1 {
t.Fatalf("GetCanonicalEpisodes() calls = %d, want 1", episodeSvc.called)
}
}
func TestAnimeReleaseInfoDoesNotCallJikanFallbackAvailable(t *testing.T) {
episodeSvc := &stubEpisodeService{
episodes: domain.CanonicalEpisodeList{
Source: "jikan_fallback",
Episodes: []domain.CanonicalEpisode{
{Number: 1},
},
},
}
handler := NewAnimeHandler(nil, nil, episodeSvc, nil)
got := handler.animeReleaseInfo(context.Background(), domain.Anime{Anime: jikan.Anime{
MalID: 62076,
Status: "Currently Airing",
Airing: true,
}}, time.Date(2026, time.July, 10, 12, 0, 0, 0, time.UTC))
if got.Count != 1 || got.Label != "Estimated aired episodes" {
t.Fatalf("animeReleaseInfo() = %+v, want estimated aired episode count", got)
}
}
func TestAnimeReleaseInfoMarksAiringAnimeWithoutCanonicalEpisodesAsNotYetAired(t *testing.T) {
episodeSvc := &stubEpisodeService{
episodes: domain.CanonicalEpisodeList{
Source: "jikan_fallback",
ReleaseChecked: true,
Episodes: []domain.CanonicalEpisode{},
},
}
handler := NewAnimeHandler(nil, nil, episodeSvc, nil)
got := handler.animeReleaseInfo(context.Background(), domain.Anime{Anime: jikan.Anime{
MalID: 62076,
Status: "Currently Airing",
Airing: true,
Episodes: 6,
Aired: jikan.Aired{From: "2026-06-03T00:00:00+00:00"},
}}, time.Date(2026, time.July, 1, 12, 0, 0, 0, time.UTC))
if got.Count != 0 || got.Label != "" || got.Status != "Not yet aired" {
t.Fatalf("animeReleaseInfo() = %+v, want not-yet-aired status without count", got)
}
}
func TestAnimeEpisodeCountStopsWhenCanonicalEpisodesAreEmpty(t *testing.T) {
episodeSvc := &stubEpisodeService{
episodes: domain.CanonicalEpisodeList{
Source: "AllAnime",
Episodes: []domain.CanonicalEpisode{},
},
}
handler := NewAnimeHandler(nil, nil, episodeSvc, nil)
got := handler.animeEpisodeCount(context.Background(), domain.Anime{Anime: jikan.Anime{
MalID: 62076,
Airing: true,
Episodes: 12,
Aired: jikan.Aired{From: "2026-06-03T00:00:00+00:00"},
}}, time.Date(2026, time.July, 1, 12, 0, 0, 0, time.UTC))
if got.Count != 0 || got.Label != "" {
t.Fatalf("animeEpisodeCount() = %+v, want empty display", got)
}
if episodeSvc.called != 1 {
t.Fatalf("GetCanonicalEpisodes() calls = %d, want 1", episodeSvc.called)
}
}
func TestAnimeInitialReleaseInfoDoesNotTrustCurrentlyAiringMetadata(t *testing.T) {
got := animeInitialReleaseInfo(domain.Anime{Anime: jikan.Anime{
MalID: 62076,
Status: "Currently Airing",
Airing: true,
Episodes: 6,
Aired: jikan.Aired{From: "2026-06-03T00:00:00+00:00"},
}}, time.Date(2026, time.July, 1, 12, 0, 0, 0, time.UTC))
if got.Count != 0 || got.Label != "" || got.Status != "" {
t.Fatalf("animeInitialReleaseInfo() = %+v, want empty unverified airing display", got)
}
}
func TestAnimeEpisodeCountFallsBackToMetadata(t *testing.T) {
episodeSvc := &stubEpisodeService{err: errors.New("provider unavailable")}
handler := NewAnimeHandler(nil, nil, episodeSvc, nil)
@@ -175,7 +272,7 @@ func TestAnimeEpisodeCountFallsBackToMetadata(t *testing.T) {
}
}
func TestAnimeInitialEpisodeCountDoesNotCallEpisodeService(t *testing.T) {
func TestAnimeInitialEpisodeCountDoesNotTrustCurrentlyAiringMetadata(t *testing.T) {
episodeSvc := &stubEpisodeService{
episodes: domain.CanonicalEpisodeList{
Episodes: []domain.CanonicalEpisode{{Number: 1}, {Number: 2}, {Number: 3}},
@@ -185,12 +282,13 @@ func TestAnimeInitialEpisodeCountDoesNotCallEpisodeService(t *testing.T) {
got := animeInitialEpisodeCount(domain.Anime{Anime: jikan.Anime{
MalID: 59970,
Airing: true,
Status: "Currently Airing",
Episodes: 12,
Aired: jikan.Aired{From: "2026-04-03T00:00:00+00:00"},
}}, time.Date(2026, time.June, 21, 0, 0, 0, 0, time.UTC))
if got.Count != 12 || got.Label != "Total episodes" {
t.Fatalf("animeInitialEpisodeCount() = %+v, want count=12 label=%q", got, "Total episodes")
if got.Count != 0 || got.Label != "" {
t.Fatalf("animeInitialEpisodeCount() = %+v, want empty display", got)
}
if episodeSvc.called != 0 {
t.Fatalf("GetCanonicalEpisodes() calls = %d, want 0", episodeSvc.called)