Posters (the downloaded bytes, per size, 30 days, at most 200 MB) and casts (people.json, 7 days, at most 2000) are stored in the user's cache directory (os.UserCacheDir()/arr-tui) and used on the next start before asking the servers. Expired posters are pruned at startup, casts are written shortly after new ones arrive and on exit. Tests turn the cache off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
108 lines
3.4 KiB
Go
108 lines
3.4 KiB
Go
package ui
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.stinnesbeck.com/nils/arr-tui/internal/trakt"
|
|
)
|
|
|
|
func TestPostersOnDisk(t *testing.T) {
|
|
d := &diskCache{dir: t.TempDir()}
|
|
if _, ok := d.readPoster("k", false); ok {
|
|
t.Fatal("empty cache returned a poster")
|
|
}
|
|
d.writePoster("k", false, []byte("SMALL"))
|
|
d.writePoster("k", true, []byte("LARGE"))
|
|
if data, ok := d.readPoster("k", false); !ok || string(data) != "SMALL" {
|
|
t.Errorf("small: %q %v", data, ok)
|
|
}
|
|
if data, ok := d.readPoster("k", true); !ok || string(data) != "LARGE" {
|
|
t.Errorf("large copies are kept apart: %q %v", data, ok)
|
|
}
|
|
if info, _ := os.Stat(filepath.Dir(d.posterPath("k", false))); info.Mode().Perm() != 0o700 {
|
|
t.Errorf("directory mode %v", info.Mode().Perm())
|
|
}
|
|
|
|
// Too old: not used, and pruned.
|
|
old := time.Now().Add(-posterTTL - time.Hour)
|
|
_ = os.Chtimes(d.posterPath("k", false), old, old)
|
|
if _, ok := d.readPoster("k", false); ok {
|
|
t.Error("an expired poster was used")
|
|
}
|
|
d.prunePosters()
|
|
if _, err := os.Stat(d.posterPath("k", false)); !os.IsNotExist(err) {
|
|
t.Error("an expired poster was not pruned")
|
|
}
|
|
if _, ok := d.readPoster("k", true); !ok {
|
|
t.Error("a fresh poster was pruned")
|
|
}
|
|
|
|
// Off: nothing is read or written.
|
|
off := &diskCache{}
|
|
off.writePoster("k", false, []byte("x"))
|
|
if _, ok := off.readPoster("k", false); ok {
|
|
t.Error("cache without a directory returned a poster")
|
|
}
|
|
}
|
|
|
|
func TestPrunePostersKeepsTheNewestWithinTheCap(t *testing.T) {
|
|
d := &diskCache{dir: t.TempDir()}
|
|
big := make([]byte, posterCacheCap/2+1) // two of these do not fit
|
|
for i, key := range []string{"oldest", "middle", "newest"} {
|
|
d.writePoster(key, false, big)
|
|
at := time.Now().Add(time.Duration(i-3) * time.Hour)
|
|
_ = os.Chtimes(d.posterPath(key, false), at, at)
|
|
}
|
|
d.prunePosters()
|
|
for key, want := range map[string]bool{"oldest": false, "middle": false, "newest": true} {
|
|
if _, ok := d.readPoster(key, false); ok != want {
|
|
t.Errorf("%s kept: %v, want %v", key, ok, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPeopleOnDisk(t *testing.T) {
|
|
d := &diskCache{dir: t.TempDir()}
|
|
all := map[string]savedPeople{
|
|
"movie:tmdb:1": {Saved: time.Now(), Cast: []trakt.Person{{Name: "A", Role: "B"}}, Leaders: []trakt.Person{{Name: "C"}}},
|
|
"movie:tmdb:2": {Saved: time.Now().Add(-peopleTTL - time.Hour), Cast: []trakt.Person{{Name: "Old"}}},
|
|
"show:tvdb:3": {Saved: time.Now()}, // Trakt had no cast: remembered as such
|
|
}
|
|
if err := d.savePeople(all); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got := d.loadPeople()
|
|
if len(got) != 2 || got["movie:tmdb:1"].Cast[0].Role != "B" || got["movie:tmdb:1"].Leaders[0].Name != "C" {
|
|
t.Errorf("loaded %+v", got)
|
|
}
|
|
if _, ok := got["show:tvdb:3"]; !ok {
|
|
t.Error("an empty cast was not kept")
|
|
}
|
|
// A damaged file is ignored.
|
|
_ = os.WriteFile(d.peoplePath(), []byte("{not json"), 0o600)
|
|
if len(d.loadPeople()) != 0 {
|
|
t.Error("damaged file")
|
|
}
|
|
}
|
|
|
|
func TestPeopleCacheKeepsTheNewest(t *testing.T) {
|
|
a := newTestApp(t)
|
|
for i := 0; i < maxPeopleSaved+5; i++ {
|
|
a.remember(trakt.Ref{Kind: trakt.Movies, TMDB: i + 1}.Key(), trakt.People{})
|
|
a.people.saved[trakt.Ref{Kind: trakt.Movies, TMDB: i + 1}.Key()] = time.Unix(int64(i), 0)
|
|
}
|
|
a.people.timer.Stop()
|
|
if len(a.people.done) != maxPeopleSaved {
|
|
t.Errorf("%d casts kept", len(a.people.done))
|
|
}
|
|
if _, ok := a.people.done["movie:tmdb:1"]; ok {
|
|
t.Error("the oldest cast was kept")
|
|
}
|
|
if !a.people.dirty || len(a.peopleSnapshot()) != maxPeopleSaved {
|
|
t.Error("new casts must be written")
|
|
}
|
|
}
|