0.2.76 - Icon Packs
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ponzischeme89/memby/server/internal/library"
|
||||
)
|
||||
|
||||
func episodeResult(season, episode int, title string) library.IngestResult {
|
||||
return library.IngestResult{
|
||||
Source: "sonarr",
|
||||
Kind: library.KindEpisode,
|
||||
Reason: library.ReasonImport,
|
||||
ItemID: "emby-" + title,
|
||||
Name: title,
|
||||
SeriesName: "The Bear",
|
||||
Season: season,
|
||||
Episode: episode,
|
||||
}
|
||||
}
|
||||
|
||||
func TestEpisodeSummaryNamesTheEpisodeBothWays(t *testing.T) {
|
||||
if got := episodeSummary(episodeResult(3, 5, "Children")); got != "S03E05 — Children" {
|
||||
t.Errorf("summary = %q, want the code and the title", got)
|
||||
}
|
||||
|
||||
// Emby records plenty of episodes under the show's own name, and "S03E05 — The Bear"
|
||||
// reads as a mistake where the code alone reads as an episode.
|
||||
same := episodeResult(3, 5, "The Bear")
|
||||
if got := episodeSummary(same); got != "S03E05" {
|
||||
t.Errorf("summary = %q, want the code alone when the title repeats the series", got)
|
||||
}
|
||||
|
||||
untitled := episodeResult(3, 5, "")
|
||||
if got := episodeSummary(untitled); got != "S03E05" {
|
||||
t.Errorf("summary = %q, want the code alone", got)
|
||||
}
|
||||
}
|
||||
|
||||
// A season pack is one piece of news. Every arrival replaces the same banner, which is
|
||||
// what the shared anchor is for, and the wording moves from the episode to the count.
|
||||
func TestIngestRunsCollapseASeasonPack(t *testing.T) {
|
||||
var runs ingestRuns
|
||||
now := time.Date(2026, 8, 18, 19, 4, 0, 0, time.UTC)
|
||||
|
||||
first := runs.record("bear|3", "emby-1", "S03E01", now, ingestRunWindow)
|
||||
if first.Count != 1 || first.Anchor != "emby-1" {
|
||||
t.Fatalf("first = %+v, want a run of one anchored on it", first)
|
||||
}
|
||||
if got := episodeRunMessage(first); got != "S03E01 is ready to watch." {
|
||||
t.Errorf("message = %q, want the episode named", got)
|
||||
}
|
||||
|
||||
second := runs.record("bear|3", "emby-2", "S03E02", now.Add(30*time.Second), ingestRunWindow)
|
||||
if second.Anchor != "emby-1" {
|
||||
t.Errorf("anchor = %q, want the run's first episode so the banner is replaced", second.Anchor)
|
||||
}
|
||||
if second.Count != 2 {
|
||||
t.Errorf("count = %d, want 2", second.Count)
|
||||
}
|
||||
if got := episodeRunMessage(second); got != "2 new episodes are ready to watch." {
|
||||
t.Errorf("message = %q, want the count once there is more than one", got)
|
||||
}
|
||||
}
|
||||
|
||||
// Next week's episode is its own news. Televisions dedupe on the alert id forever, so a
|
||||
// run that reused last week's anchor would be silently swallowed on every set in the house.
|
||||
func TestIngestRunsStartAfreshOnceTheWindowHasClosed(t *testing.T) {
|
||||
var runs ingestRuns
|
||||
now := time.Date(2026, 8, 18, 19, 4, 0, 0, time.UTC)
|
||||
|
||||
runs.record("bear|3", "emby-1", "S03E01", now, ingestRunWindow)
|
||||
later := runs.record("bear|3", "emby-2", "S03E02", now.Add(ingestRunWindow+time.Minute), ingestRunWindow)
|
||||
|
||||
if later.Anchor != "emby-2" {
|
||||
t.Errorf("anchor = %q, want a new run", later.Anchor)
|
||||
}
|
||||
if later.Count != 1 {
|
||||
t.Errorf("count = %d, want a run of one", later.Count)
|
||||
}
|
||||
}
|
||||
|
||||
// Two shows importing at once are two pieces of news, not one run of four episodes.
|
||||
func TestIngestRunsAreKeptPerSeason(t *testing.T) {
|
||||
var runs ingestRuns
|
||||
now := time.Date(2026, 8, 18, 19, 4, 0, 0, time.UTC)
|
||||
|
||||
runs.record(seasonRunKey("The Bear", 3), "bear-1", "S03E01", now, ingestRunWindow)
|
||||
other := runs.record(seasonRunKey("Slow Horses", 4), "horses-1", "S04E01", now, ingestRunWindow)
|
||||
|
||||
if other.Count != 1 || other.Anchor != "horses-1" {
|
||||
t.Fatalf("other show = %+v, want a run of its own", other)
|
||||
}
|
||||
// And the same show under a different spelling is still the same show, the rule the
|
||||
// schedule row and the ingest worker already match titles by.
|
||||
same := runs.record(seasonRunKey("the bear!", 3), "bear-2", "S03E02", now, ingestRunWindow)
|
||||
if same.Anchor != "bear-1" || same.Count != 2 {
|
||||
t.Fatalf("same season = %+v, want it folded into the first run", same)
|
||||
}
|
||||
}
|
||||
|
||||
// The tally is memory the gateway can afford to lose, so it must also be memory it cannot
|
||||
// grow without bound.
|
||||
func TestIngestRunsAreBounded(t *testing.T) {
|
||||
var runs ingestRuns
|
||||
now := time.Date(2026, 8, 18, 19, 4, 0, 0, time.UTC)
|
||||
for i := 0; i < trackedIngestRuns*2; i++ {
|
||||
runs.record(time.Duration(i).String(), "item", "S01E01", now, ingestRunWindow)
|
||||
}
|
||||
if len(runs.runs) > trackedIngestRuns {
|
||||
t.Fatalf("tracking %d runs, want a cap of %d", len(runs.runs), trackedIngestRuns)
|
||||
}
|
||||
}
|
||||
|
||||
// Only an import is news. An upgrade replaced a file that was already watchable, and a
|
||||
// rename or a delete is housekeeping — announcing any of them trains viewers to look away.
|
||||
func TestOnlyAnImportIsAnnounced(t *testing.T) {
|
||||
s := &Server{log: discardLogger()}
|
||||
for _, reason := range []string{
|
||||
library.ReasonUpgrade, library.ReasonRename, library.ReasonDelete,
|
||||
} {
|
||||
result := episodeResult(3, 5, "Children")
|
||||
result.Reason = reason
|
||||
// A nil cache would be reached by publishAlert if this announced anything; it
|
||||
// returns early on one, so the assertion is that nothing is recorded either.
|
||||
s.AnnounceLibraryIngest(t.Context(), result)
|
||||
if len(s.ingestRuns.runs) != 0 {
|
||||
t.Fatalf("%s was treated as news", reason)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A series-level result is a rename settling or a show written ahead of its first episode.
|
||||
// Neither is something anybody can press Play on.
|
||||
func TestASeriesRefreshIsNotAnnounced(t *testing.T) {
|
||||
s := &Server{log: discardLogger()}
|
||||
s.AnnounceLibraryIngest(t.Context(), library.IngestResult{
|
||||
Source: "sonarr", Kind: library.KindSeries, Reason: library.ReasonImport,
|
||||
ItemID: "series-1", Name: "The Bear", SeriesName: "The Bear",
|
||||
})
|
||||
if len(s.ingestRuns.runs) != 0 {
|
||||
t.Fatal("a series refresh was announced")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMovieRunMessageSaysTheFilmIsThere(t *testing.T) {
|
||||
// The wording is the whole point of moving the announcement behind the scan: the
|
||||
// banner published from the webhook could only ever promise the film was coming.
|
||||
run := ingestRun{Anchor: "a", Count: 1, Latest: "S01E01"}
|
||||
if strings.Contains(episodeRunMessage(run), "shortly") {
|
||||
t.Error("the message still promises rather than states")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user