wip(tests): mock anilist client, local files, collection

This commit is contained in:
5rahim
2024-03-10 18:04:39 -04:00
parent 47a841c38a
commit 93a9f7cf0b
43 changed files with 885 additions and 332 deletions

1
.gitignore vendored
View File

@@ -11,6 +11,7 @@ web/
out/
assets/
test/testdata/**/*.json
test/sample
test/providers.json
test/db.json

View File

@@ -9,13 +9,34 @@ import (
"github.com/Yamashou/gqlgenc/graphqljson"
"github.com/goccy/go-json"
"github.com/rs/zerolog"
"github.com/seanime-app/seanime/internal/limiter"
"github.com/seanime-app/seanime/internal/util"
"io"
"net/http"
"strconv"
"sync"
"time"
)
type ClientWrapperInterface interface {
UpdateEntry(ctx context.Context, mediaID *int, status *MediaListStatus, score *float64, progress *int, repeat *int, private *bool, notes *string, hiddenFromStatusLists *bool, startedAt *FuzzyDateInput, completedAt *FuzzyDateInput, interceptors ...clientv2.RequestInterceptor) (*UpdateEntry, error)
UpdateMediaListEntry(ctx context.Context, mediaID *int, status *MediaListStatus, scoreRaw *int, progress *int, startedAt *FuzzyDateInput, completedAt *FuzzyDateInput, interceptors ...clientv2.RequestInterceptor) (*UpdateMediaListEntry, error)
UpdateMediaListEntryProgress(ctx context.Context, mediaID *int, progress *int, totalEpisodes *int) error
UpdateMediaListEntryStatus(ctx context.Context, mediaID *int, progress *int, status *MediaListStatus, scoreRaw *int, interceptors ...clientv2.RequestInterceptor) (*UpdateMediaListEntryStatus, error)
DeleteEntry(ctx context.Context, mediaListEntryID *int, interceptors ...clientv2.RequestInterceptor) (*DeleteEntry, error)
AnimeCollection(ctx context.Context, userName *string, interceptors ...clientv2.RequestInterceptor) (*AnimeCollection, error)
SearchAnimeShortMedia(ctx context.Context, page *int, perPage *int, sort []*MediaSort, search *string, status []*MediaStatus, interceptors ...clientv2.RequestInterceptor) (*SearchAnimeShortMedia, error)
BasicMediaByMalID(ctx context.Context, id *int, interceptors ...clientv2.RequestInterceptor) (*BasicMediaByMalID, error)
BasicMediaByID(ctx context.Context, id *int, interceptors ...clientv2.RequestInterceptor) (*BasicMediaByID, error)
BaseMediaByID(ctx context.Context, id *int, interceptors ...clientv2.RequestInterceptor) (*BaseMediaByID, error)
MediaDetailsByID(ctx context.Context, id *int, interceptors ...clientv2.RequestInterceptor) (*MediaDetailsByID, error)
CompleteMediaByID(ctx context.Context, id *int, interceptors ...clientv2.RequestInterceptor) (*CompleteMediaByID, error)
ListMedia(ctx context.Context, page *int, search *string, perPage *int, sort []*MediaSort, status []*MediaStatus, genres []*string, averageScoreGreater *int, season *MediaSeason, seasonYear *int, format *MediaFormat, interceptors ...clientv2.RequestInterceptor) (*ListMedia, error)
ListRecentMedia(ctx context.Context, page *int, perPage *int, airingAtGreater *int, airingAtLesser *int, interceptors ...clientv2.RequestInterceptor) (*ListRecentMedia, error)
GetViewer(ctx context.Context, interceptors ...clientv2.RequestInterceptor) (*GetViewer, error)
AddMediaToPlanning(mIds []int, rateLimiter *limiter.Limiter, logger *zerolog.Logger) error
}
type (
// ClientWrapper is a wrapper around the AniList API client.
ClientWrapper struct {
@@ -47,6 +68,92 @@ func NewClientWrapper(token string) *ClientWrapper {
return cw
}
func (cw *ClientWrapper) AddMediaToPlanning(mIds []int, rateLimiter *limiter.Limiter, logger *zerolog.Logger) error {
if len(mIds) == 0 {
logger.Debug().Msg("anilist: no media added to planning list")
return nil
}
if rateLimiter == nil {
return errors.New("anilist: no rate limiter provided")
}
status := MediaListStatusPlanning
scoreRaw := 0
progress := 0
wg := sync.WaitGroup{}
for _, _id := range mIds {
wg.Add(1)
go func(id int) {
rateLimiter.Wait()
defer wg.Done()
_, err := cw.Client.UpdateMediaListEntry(
context.Background(),
&id,
&status,
&scoreRaw,
&progress,
nil,
nil,
)
if err != nil {
logger.Error().Msg("anilist: An error occurred while adding media to planning list: " + err.Error())
}
}(_id)
}
wg.Wait()
logger.Debug().Any("count", len(mIds)).Msg("anilist: Media added to planning list")
return nil
}
func (cw *ClientWrapper) UpdateEntry(ctx context.Context, mediaID *int, status *MediaListStatus, score *float64, progress *int, repeat *int, private *bool, notes *string, hiddenFromStatusLists *bool, startedAt *FuzzyDateInput, completedAt *FuzzyDateInput, interceptors ...clientv2.RequestInterceptor) (*UpdateEntry, error) {
return cw.Client.UpdateEntry(ctx, mediaID, status, score, progress, repeat, private, notes, hiddenFromStatusLists, startedAt, completedAt, interceptors...)
}
func (cw *ClientWrapper) UpdateMediaListEntry(ctx context.Context, mediaID *int, status *MediaListStatus, scoreRaw *int, progress *int, startedAt *FuzzyDateInput, completedAt *FuzzyDateInput, interceptors ...clientv2.RequestInterceptor) (*UpdateMediaListEntry, error) {
return cw.Client.UpdateMediaListEntry(ctx, mediaID, status, scoreRaw, progress, startedAt, completedAt, interceptors...)
}
func (cw *ClientWrapper) UpdateMediaListEntryStatus(ctx context.Context, mediaID *int, progress *int, status *MediaListStatus, scoreRaw *int, interceptors ...clientv2.RequestInterceptor) (*UpdateMediaListEntryStatus, error) {
return cw.Client.UpdateMediaListEntryStatus(ctx, mediaID, progress, status, scoreRaw, interceptors...)
}
func (cw *ClientWrapper) DeleteEntry(ctx context.Context, mediaListEntryID *int, interceptors ...clientv2.RequestInterceptor) (*DeleteEntry, error) {
return cw.Client.DeleteEntry(ctx, mediaListEntryID, interceptors...)
}
func (cw *ClientWrapper) AnimeCollection(ctx context.Context, userName *string, interceptors ...clientv2.RequestInterceptor) (*AnimeCollection, error) {
cw.logger.Trace().Str("username", *userName).Msg("anilist: Fetching anime collection")
return cw.Client.AnimeCollection(ctx, userName, interceptors...)
}
func (cw *ClientWrapper) SearchAnimeShortMedia(ctx context.Context, page *int, perPage *int, sort []*MediaSort, search *string, status []*MediaStatus, interceptors ...clientv2.RequestInterceptor) (*SearchAnimeShortMedia, error) {
return cw.Client.SearchAnimeShortMedia(ctx, page, perPage, sort, search, status, interceptors...)
}
func (cw *ClientWrapper) BasicMediaByMalID(ctx context.Context, id *int, interceptors ...clientv2.RequestInterceptor) (*BasicMediaByMalID, error) {
return cw.Client.BasicMediaByMalID(ctx, id, interceptors...)
}
func (cw *ClientWrapper) BasicMediaByID(ctx context.Context, id *int, interceptors ...clientv2.RequestInterceptor) (*BasicMediaByID, error) {
return cw.Client.BasicMediaByID(ctx, id, interceptors...)
}
func (cw *ClientWrapper) BaseMediaByID(ctx context.Context, id *int, interceptors ...clientv2.RequestInterceptor) (*BaseMediaByID, error) {
cw.logger.Trace().Int("mediaId", *id).Msg("anilist: Fetching base media by ID")
return cw.Client.BaseMediaByID(ctx, id, interceptors...)
}
func (cw *ClientWrapper) MediaDetailsByID(ctx context.Context, id *int, interceptors ...clientv2.RequestInterceptor) (*MediaDetailsByID, error) {
return cw.Client.MediaDetailsByID(ctx, id, interceptors...)
}
func (cw *ClientWrapper) CompleteMediaByID(ctx context.Context, id *int, interceptors ...clientv2.RequestInterceptor) (*CompleteMediaByID, error) {
return cw.Client.CompleteMediaByID(ctx, id, interceptors...)
}
func (cw *ClientWrapper) ListMedia(ctx context.Context, page *int, search *string, perPage *int, sort []*MediaSort, status []*MediaStatus, genres []*string, averageScoreGreater *int, season *MediaSeason, seasonYear *int, format *MediaFormat, interceptors ...clientv2.RequestInterceptor) (*ListMedia, error) {
return cw.Client.ListMedia(ctx, page, search, perPage, sort, status, genres, averageScoreGreater, season, seasonYear, format, interceptors...)
}
func (cw *ClientWrapper) ListRecentMedia(ctx context.Context, page *int, perPage *int, airingAtGreater *int, airingAtLesser *int, interceptors ...clientv2.RequestInterceptor) (*ListRecentMedia, error) {
return cw.Client.ListRecentMedia(ctx, page, perPage, airingAtGreater, airingAtLesser, interceptors...)
}
func (cw *ClientWrapper) GetViewer(ctx context.Context, interceptors ...clientv2.RequestInterceptor) (*GetViewer, error) {
return cw.Client.GetViewer(ctx, interceptors...)
}
// customDoFunc is a custom request interceptor function that handles rate limiting and retries.
func (cw *ClientWrapper) customDoFunc(ctx context.Context, req *http.Request, gqlInfo *clientv2.GQLRequestInfo, res interface{}) (err error) {
var rlRemainingStr string

View File

@@ -10,8 +10,8 @@ import (
func TestGetBaseMediaById(t *testing.T) {
test_utils.InitTestProvider(t, test_utils.Anilist())
// Get Anilist client
acw := TestGetAnilistClientWrapper()
//acw := TestGetAnilistClientWrapper()
acw := TestGetMockAnilistClientWrapper() // MockClientWrapper
tests := []struct {
name string
@@ -25,7 +25,7 @@ func TestGetBaseMediaById(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
res, err := acw.Client.BaseMediaByID(context.Background(), &test.mediaId)
res, err := acw.BaseMediaByID(context.Background(), &test.mediaId)
assert.NoError(t, err)
assert.NotNil(t, res)
})

View File

@@ -13,7 +13,7 @@ func TestAddMediaToPlanning(t *testing.T) {
anilistClientWrapper := TestGetAnilistClientWrapper()
err := anilistClientWrapper.Client.AddMediaToPlanning(
err := anilistClientWrapper.AddMediaToPlanning(
[]int{131586},
limiter.NewAnilistLimiter(),
util.NewLogger(),

View File

@@ -17,8 +17,8 @@ func NewBaseMediaCache() *BaseMediaCache {
//----------------------------------------------------------------------------------------------------------------------
func GetBaseMediaById(anilistClient *Client, id int) (*BaseMedia, error) {
res, err := anilistClient.BaseMediaByID(context.Background(), &id)
func GetBaseMediaById(acw ClientWrapperInterface, id int) (*BaseMedia, error) {
res, err := acw.BaseMediaByID(context.Background(), &id)
if err != nil {
return nil, err
}

View File

@@ -28,9 +28,9 @@ func NewBaseMediaRelationTree() *BaseMediaRelationTree {
return &BaseMediaRelationTree{result.NewResultMap[int, *BaseMedia]()}
}
func (m *BasicMedia) FetchMediaTree(rel FetchMediaTreeRelation, acw *ClientWrapper, rl *limiter.Limiter, tree *BaseMediaRelationTree, cache *BaseMediaCache) error {
func (m *BasicMedia) FetchMediaTree(rel FetchMediaTreeRelation, acw ClientWrapperInterface, rl *limiter.Limiter, tree *BaseMediaRelationTree, cache *BaseMediaCache) error {
rl.Wait()
res, err := acw.Client.BaseMediaByID(context.Background(), &m.ID)
res, err := acw.BaseMediaByID(context.Background(), &m.ID)
if err != nil {
return err
}
@@ -40,7 +40,7 @@ func (m *BasicMedia) FetchMediaTree(rel FetchMediaTreeRelation, acw *ClientWrapp
// FetchMediaTree populates the BaseMediaRelationTree with the given media's sequels and prequels.
// It also takes a BaseMediaCache to store the fetched media in and avoid duplicate fetches.
// It also takes a limiter.Limiter to limit the number of requests made to the AniList API.
func (m *BaseMedia) FetchMediaTree(rel FetchMediaTreeRelation, acw *ClientWrapper, rl *limiter.Limiter, tree *BaseMediaRelationTree, cache *BaseMediaCache) error {
func (m *BaseMedia) FetchMediaTree(rel FetchMediaTreeRelation, acw ClientWrapperInterface, rl *limiter.Limiter, tree *BaseMediaRelationTree, cache *BaseMediaCache) error {
if tree.Has(m.ID) {
cache.Set(m.ID, m)
return nil
@@ -78,7 +78,7 @@ func (m *BaseMedia) FetchMediaTree(rel FetchMediaTreeRelation, acw *ClientWrappe
}
// processEdges fetches the next node(s) for each edge in parallel.
func processEdges(edges []*BaseMedia_Relations_Edges, rel FetchMediaTreeRelation, acw *ClientWrapper, rl *limiter.Limiter, tree *BaseMediaRelationTree, cache *BaseMediaCache, doneCh chan struct{}) {
func processEdges(edges []*BaseMedia_Relations_Edges, rel FetchMediaTreeRelation, acw ClientWrapperInterface, rl *limiter.Limiter, tree *BaseMediaRelationTree, cache *BaseMediaCache, doneCh chan struct{}) {
lop.ForEach(edges, func(edge *BaseMedia_Relations_Edges, _ int) {
processEdge(edge, rel, acw, rl, tree, cache)
})
@@ -87,13 +87,13 @@ func processEdges(edges []*BaseMedia_Relations_Edges, rel FetchMediaTreeRelation
}()
}
func processEdge(edge *BaseMedia_Relations_Edges, rel FetchMediaTreeRelation, acw *ClientWrapper, rl *limiter.Limiter, tree *BaseMediaRelationTree, cache *BaseMediaCache) {
func processEdge(edge *BaseMedia_Relations_Edges, rel FetchMediaTreeRelation, acw ClientWrapperInterface, rl *limiter.Limiter, tree *BaseMediaRelationTree, cache *BaseMediaCache) {
cacheV, ok := cache.Get(edge.GetNode().ID)
edgeBaseMedia := cacheV
if !ok {
rl.Wait()
// Fetch the next node
res, err := acw.Client.BaseMediaByID(context.Background(), &edge.GetNode().ID)
res, err := acw.BaseMediaByID(context.Background(), &edge.GetNode().ID)
if err == nil {
edgeBaseMedia = res.GetMedia()
cache.Set(edgeBaseMedia.ID, edgeBaseMedia)

View File

@@ -12,7 +12,7 @@ import (
func TestBaseMedia_FetchMediaTree(t *testing.T) {
test_utils.InitTestProvider(t, test_utils.Anilist())
acw := TestGetAnilistClientWrapper()
acw := TestGetMockAnilistClientWrapper()
lim := limiter.NewAnilistLimiter()
baseMediaCache := NewBaseMediaCache()
@@ -38,7 +38,7 @@ func TestBaseMedia_FetchMediaTree(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
mediaF, err := acw.Client.BaseMediaByID(context.Background(), &tt.mediaId)
mediaF, err := acw.BaseMediaByID(context.Background(), &tt.mediaId)
if assert.NoError(t, err) {
@@ -99,7 +99,7 @@ func TestBasicMedia_FetchMediaTree(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
mediaF, err := acw.Client.BasicMediaByID(context.Background(), &tt.mediaId)
mediaF, err := acw.BasicMediaByID(context.Background(), &tt.mediaId)
if assert.NoError(t, err) {

View File

@@ -1,16 +0,0 @@
package anilist
import (
"github.com/seanime-app/seanime/internal/test_utils"
)
// This file contains helper functions for testing the anilist package
func TestGetAnilistClientWrapper() *ClientWrapper {
return NewClientWrapper(test_utils.ConfigData.Provider.AnilistJwt)
}
func TestGetAnilistClientWrapperAndInfo() (*ClientWrapper, *test_utils.Config) {
cw := NewClientWrapper(test_utils.ConfigData.Provider.AnilistJwt)
return cw, test_utils.ConfigData
}

View File

@@ -0,0 +1,369 @@
package anilist
import (
"context"
"github.com/Yamashou/gqlgenc/clientv2"
"github.com/goccy/go-json"
"github.com/rs/zerolog"
"github.com/seanime-app/seanime/internal/limiter"
"github.com/seanime-app/seanime/internal/test_utils"
"github.com/seanime-app/seanime/internal/util"
"log"
"os"
)
// This file contains helper functions for testing the anilist package
// TestGetAnilistClientWrapper TESTING PURPOSES ONLY
func TestGetAnilistClientWrapper() ClientWrapperInterface {
return NewClientWrapper(test_utils.ConfigData.Provider.AnilistJwt)
}
func TestGetMockAnilistClientWrapper() ClientWrapperInterface {
return NewMockClientWrapper()
}
// MockClientWrapper is a mock implementation of the ClientWrapperInterface, used for tests.
// It uses the real implementation of the ClientWrapperInterface to make requests then populates a cache with the results.
// This is to avoid making repeated requests to the AniList API during tests but still have realistic data.
type MockClientWrapper struct {
realClientWrapper ClientWrapperInterface
logger *zerolog.Logger
}
func NewMockClientWrapper() *MockClientWrapper {
return &MockClientWrapper{
realClientWrapper: NewClientWrapper(test_utils.ConfigData.Provider.AnilistJwt),
logger: util.NewLogger(),
}
}
func (c *MockClientWrapper) BasicMediaByMalID(ctx context.Context, id *int, interceptors ...clientv2.RequestInterceptor) (*BasicMediaByMalID, error) {
file, err := os.Open(test_utils.GetTestDataPath("BasicMediaByMalID"))
defer file.Close()
if err != nil {
if os.IsNotExist(err) {
c.logger.Warn().Msgf("MockClientWrapper: CACHE MISS [BasicMediaByMalID]: %d", *id)
ret, err := c.realClientWrapper.BasicMediaByMalID(context.Background(), id)
if err != nil {
return nil, err
}
data, err := json.Marshal([]*BasicMediaByMalID{ret})
if err != nil {
log.Fatal(err)
}
err = os.WriteFile(test_utils.GetTestDataPath("BasicMediaByMalID"), data, 0644)
if err != nil {
log.Fatal(err)
}
return ret, nil
}
}
var media []*BasicMediaByMalID
err = json.NewDecoder(file).Decode(&media)
if err != nil {
log.Fatal(err)
}
var ret *BasicMediaByMalID
for _, m := range media {
if m.GetMedia().ID == *id {
ret = m
break
}
}
if ret == nil {
c.logger.Warn().Msgf("MockClientWrapper: CACHE MISS [BasicMediaByMalID]: %d", *id)
ret, err := c.realClientWrapper.BasicMediaByMalID(context.Background(), id)
if err != nil {
return nil, err
}
media = append(media, ret)
data, err := json.Marshal(media)
if err != nil {
log.Fatal(err)
}
err = os.WriteFile(test_utils.GetTestDataPath("BasicMediaByMalID"), data, 0644)
if err != nil {
log.Fatal(err)
}
return ret, nil
}
c.logger.Trace().Msgf("MockClientWrapper: CACHE HIT [BasicMediaByMalID]: %d", *id)
return ret, nil
}
func (c *MockClientWrapper) BasicMediaByID(ctx context.Context, id *int, interceptors ...clientv2.RequestInterceptor) (*BasicMediaByID, error) {
file, err := os.Open(test_utils.GetTestDataPath("BasicMediaByID"))
defer file.Close()
if err != nil {
if os.IsNotExist(err) {
c.logger.Warn().Msgf("MockClientWrapper: CACHE MISS [BasicMediaByID]: %d", *id)
ret, err := c.realClientWrapper.BasicMediaByID(context.Background(), id)
if err != nil {
return nil, err
}
data, err := json.Marshal([]*BasicMediaByID{ret})
if err != nil {
log.Fatal(err)
}
err = os.WriteFile(test_utils.GetTestDataPath("BasicMediaByID"), data, 0644)
if err != nil {
log.Fatal(err)
}
return ret, nil
}
}
var media []*BasicMediaByID
err = json.NewDecoder(file).Decode(&media)
if err != nil {
log.Fatal(err)
}
var ret *BasicMediaByID
for _, m := range media {
if m.GetMedia().ID == *id {
ret = m
break
}
}
if ret == nil {
c.logger.Warn().Msgf("MockClientWrapper: CACHE MISS [BasicMediaByID]: %d", *id)
ret, err := c.realClientWrapper.BasicMediaByID(context.Background(), id)
if err != nil {
return nil, err
}
media = append(media, ret)
data, err := json.Marshal(media)
if err != nil {
log.Fatal(err)
}
err = os.WriteFile(test_utils.GetTestDataPath("BasicMediaByID"), data, 0644)
if err != nil {
log.Fatal(err)
}
return ret, nil
}
c.logger.Trace().Msgf("MockClientWrapper: CACHE HIT [BasicMediaByID]: %d", *id)
return ret, nil
}
func (c *MockClientWrapper) BaseMediaByID(ctx context.Context, id *int, interceptors ...clientv2.RequestInterceptor) (*BaseMediaByID, error) {
file, err := os.Open(test_utils.GetTestDataPath("BaseMediaByID"))
defer file.Close()
if err != nil {
if os.IsNotExist(err) {
c.logger.Warn().Msgf("MockClientWrapper: CACHE MISS [BaseMediaByID]: %d", *id)
baseMedia, err := c.realClientWrapper.BaseMediaByID(context.Background(), id)
if err != nil {
return nil, err
}
data, err := json.Marshal([]*BaseMediaByID{baseMedia})
if err != nil {
log.Fatal(err)
}
err = os.WriteFile(test_utils.GetTestDataPath("BaseMediaByID"), data, 0644)
if err != nil {
log.Fatal(err)
}
return baseMedia, nil
}
}
var media []*BaseMediaByID
err = json.NewDecoder(file).Decode(&media)
if err != nil {
log.Fatal(err)
}
var baseMedia *BaseMediaByID
for _, m := range media {
if m.GetMedia().ID == *id {
baseMedia = m
break
}
}
if baseMedia == nil {
c.logger.Warn().Msgf("MockClientWrapper: CACHE MISS [BaseMediaByID]: %d", *id)
baseMedia, err := c.realClientWrapper.BaseMediaByID(context.Background(), id)
if err != nil {
return nil, err
}
media = append(media, baseMedia)
data, err := json.Marshal(media)
if err != nil {
log.Fatal(err)
}
err = os.WriteFile(test_utils.GetTestDataPath("BaseMediaByID"), data, 0644)
if err != nil {
log.Fatal(err)
}
return baseMedia, nil
}
c.logger.Trace().Msgf("MockClientWrapper: CACHE HIT [BaseMediaByID]: %d", *id)
return baseMedia, nil
}
func (c *MockClientWrapper) AnimeCollection(ctx context.Context, userName *string, interceptors ...clientv2.RequestInterceptor) (*AnimeCollection, error) {
file, err := os.Open(test_utils.GetTestDataPath("AnimeCollection"))
defer file.Close()
if err != nil {
if os.IsNotExist(err) {
c.logger.Warn().Msgf("MockClientWrapper: CACHE MISS [AnimeCollection]: %s", *userName)
ret, err := c.realClientWrapper.AnimeCollection(context.Background(), userName)
if err != nil {
return nil, err
}
data, err := json.Marshal(ret)
if err != nil {
log.Fatal(err)
}
err = os.WriteFile(test_utils.GetTestDataPath("AnimeCollection"), data, 0644)
if err != nil {
log.Fatal(err)
}
return ret, nil
}
}
var ret *AnimeCollection
err = json.NewDecoder(file).Decode(&ret)
if err != nil {
log.Fatal(err)
}
if ret == nil {
c.logger.Warn().Msgf("MockClientWrapper: CACHE MISS [AnimeCollection]: %s", *userName)
ret, err := c.realClientWrapper.AnimeCollection(context.Background(), userName)
if err != nil {
return nil, err
}
data, err := json.Marshal(ret)
if err != nil {
log.Fatal(err)
}
err = os.WriteFile(test_utils.GetTestDataPath("AnimeCollection"), data, 0644)
if err != nil {
log.Fatal(err)
}
return ret, nil
}
c.logger.Trace().Msgf("MockClientWrapper: CACHE HIT [AnimeCollection]: %s", *userName)
return ret, nil
}
type TestModifyAnimeCollectionEntryInput struct {
Status *MediaListStatus
Progress *int
Score *float64
Episodes *int
NextAiringEpisode *BaseMedia_NextAiringEpisode
}
// TestModifyAnimeCollectionEntry will modify an entry in the fetched anime collection.
// This is used to fine-tune the anime collection for testing purposes.
//
// Example: Setting a specific progress in case the origin anime collection has no progress
func TestModifyAnimeCollectionEntry(ac *AnimeCollection, mId int, input TestModifyAnimeCollectionEntryInput) *AnimeCollection {
if ac == nil {
panic("AnimeCollection is nil")
}
lists := ac.GetMediaListCollection().GetLists()
removedFromList := false
var rEntry *AnimeCollection_MediaListCollection_Lists_Entries
if input.Status != nil {
for _, list := range lists {
entries := list.GetEntries()
for idx, entry := range entries {
if entry.GetMedia().ID == mId {
if *list.Status != *input.Status {
removedFromList = true
entries = append(entries[:idx], entries[idx+1:]...)
rEntry = entry
break
}
}
}
}
if removedFromList {
for _, list := range lists {
if *list.Status == *input.Status {
list.Entries = append(list.Entries, rEntry)
}
}
}
}
out:
for _, list := range lists {
entries := list.GetEntries()
for _, entry := range entries {
if entry.GetMedia().ID == mId {
if input.Progress != nil {
entry.Progress = input.Progress
}
if input.Score != nil {
entry.Score = input.Score
}
if input.Episodes != nil {
entry.Media.Episodes = input.Episodes
}
if input.NextAiringEpisode != nil {
entry.Media.NextAiringEpisode = input.NextAiringEpisode
}
break out
}
}
}
return ac
}
//
// WILL NOT IMPLEMENT
//
func (c *MockClientWrapper) AddMediaToPlanning(mIds []int, rateLimiter *limiter.Limiter, logger *zerolog.Logger) error {
panic("not implemented")
}
func (c *MockClientWrapper) UpdateMediaListEntryProgress(ctx context.Context, mediaID *int, progress *int, totalEpisodes *int) error {
panic("not implemented")
}
func (c *MockClientWrapper) UpdateEntry(ctx context.Context, mediaID *int, status *MediaListStatus, score *float64, progress *int, repeat *int, private *bool, notes *string, hiddenFromStatusLists *bool, startedAt *FuzzyDateInput, completedAt *FuzzyDateInput, interceptors ...clientv2.RequestInterceptor) (*UpdateEntry, error) {
panic("not implemented")
}
func (c *MockClientWrapper) UpdateMediaListEntry(ctx context.Context, mediaID *int, status *MediaListStatus, scoreRaw *int, progress *int, startedAt *FuzzyDateInput, completedAt *FuzzyDateInput, interceptors ...clientv2.RequestInterceptor) (*UpdateMediaListEntry, error) {
panic("not implemented")
}
func (c *MockClientWrapper) UpdateMediaListEntryStatus(ctx context.Context, mediaID *int, progress *int, status *MediaListStatus, scoreRaw *int, interceptors ...clientv2.RequestInterceptor) (*UpdateMediaListEntryStatus, error) {
panic("not implemented")
}
func (c *MockClientWrapper) DeleteEntry(ctx context.Context, mediaListEntryID *int, interceptors ...clientv2.RequestInterceptor) (*DeleteEntry, error) {
panic("not implemented")
}
func (c *MockClientWrapper) SearchAnimeShortMedia(ctx context.Context, page *int, perPage *int, sort []*MediaSort, search *string, status []*MediaStatus, interceptors ...clientv2.RequestInterceptor) (*SearchAnimeShortMedia, error) {
panic("not implemented")
}
func (c *MockClientWrapper) MediaDetailsByID(ctx context.Context, id *int, interceptors ...clientv2.RequestInterceptor) (*MediaDetailsByID, error) {
panic("not implemented")
}
func (c *MockClientWrapper) CompleteMediaByID(ctx context.Context, id *int, interceptors ...clientv2.RequestInterceptor) (*CompleteMediaByID, error) {
panic("not implemented")
}
func (c *MockClientWrapper) ListMedia(ctx context.Context, page *int, search *string, perPage *int, sort []*MediaSort, status []*MediaStatus, genres []*string, averageScoreGreater *int, season *MediaSeason, seasonYear *int, format *MediaFormat, interceptors ...clientv2.RequestInterceptor) (*ListMedia, error) {
panic("not implemented")
}
func (c *MockClientWrapper) ListRecentMedia(ctx context.Context, page *int, perPage *int, airingAtGreater *int, airingAtLesser *int, interceptors ...clientv2.RequestInterceptor) (*ListRecentMedia, error) {
panic("not implemented")
}
func (c *MockClientWrapper) GetViewer(ctx context.Context, interceptors ...clientv2.RequestInterceptor) (*GetViewer, error) {
panic("not implemented")
}

View File

@@ -52,7 +52,7 @@ func TestSearchQuery(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
mediaRes, err := anilistClientWrapper.Client.BaseMediaByID(context.Background(), &test.mId)
mediaRes, err := anilistClientWrapper.BaseMediaByID(context.Background(), &test.mId)
if assert.NoError(t, err) {

View File

@@ -37,7 +37,7 @@ type (
TorrentClientRepository *torrent_client.Repository
Watcher *scanner.Watcher
AnizipCache *anizip.Cache // AnizipCache holds fetched AniZip media for 30 minutes. (used by route handlers)
AnilistClientWrapper *anilist.ClientWrapper
AnilistClientWrapper anilist.ClientWrapperInterface
NyaaSearchCache *nyaa.SearchCache
AnimeToshoSearchCache *animetosho.SearchCache
anilistCollection *anilist.AnimeCollection

View File

@@ -30,7 +30,7 @@ func (a *App) RefreshAnilistCollection() (*anilist.AnimeCollection, error) {
}
// Else, get the collection from Anilist
collection, err := a.AnilistClientWrapper.Client.AnimeCollection(context.Background(), &a.account.Username)
collection, err := a.AnilistClientWrapper.AnimeCollection(context.Background(), &a.account.Username)
if err != nil {
return nil, err
}

View File

@@ -1,48 +0,0 @@
package db
import (
"github.com/goccy/go-json"
"io"
"log"
"os"
"path/filepath"
)
func GetTestDatabaseInfo() *struct {
DataDir string `json:"dataDir"`
Name string `json:"name"`
} {
path, err := os.Getwd()
if err != nil {
log.Fatal(err)
}
// Open the JSON file
file, err := os.Open(filepath.Join(path, "../../test/db.json"))
if err != nil {
println("Error opening file:", err.Error())
log.Fatal(err)
return nil
}
defer file.Close()
jsonData, err := io.ReadAll(file)
if err != nil {
println("Error reading file:", err.Error())
log.Fatal(err)
return nil
}
var data *struct {
DataDir string `json:"dataDir"`
Name string `json:"name"`
}
if err := json.Unmarshal(jsonData, &data); err != nil {
println("Error unmarshaling JSON:", err.Error())
log.Fatal(err)
return nil
}
return data
}

View File

@@ -71,7 +71,7 @@ type (
AnilistCollection *anilist.AnimeCollection
LocalFiles []*LocalFile
AnizipCache *anizip.Cache
AnilistClientWrapper *anilist.ClientWrapper
AnilistClientWrapper anilist.ClientWrapperInterface
}
)
@@ -258,7 +258,7 @@ func (lc *LibraryCollection) hydrateContinueWatchingList(
localFiles []*LocalFile,
anilistCollection *anilist.AnimeCollection,
anizipCache *anizip.Cache,
anilistClientWrapper *anilist.ClientWrapper,
anilistClientWrapper anilist.ClientWrapperInterface,
) {
// Get currently watching list

View File

@@ -40,7 +40,7 @@ type (
LocalFiles []*LocalFile // All local files
AnizipCache *anizip.Cache
AnilistCollection *anilist.AnimeCollection
AnilistClientWrapper *anilist.ClientWrapper
AnilistClientWrapper anilist.ClientWrapperInterface
}
)
@@ -82,7 +82,7 @@ func NewMediaEntry(opts *NewMediaEntryOptions) (*MediaEntry, error) {
anilistEntry = &anilist.MediaListEntry{}
// Fetch the media
fetchedMedia, err := anilist.GetBaseMediaById(opts.AnilistClientWrapper.Client, opts.MediaId) // DEVNOTE: Maybe cache it?
fetchedMedia, err := anilist.GetBaseMediaById(opts.AnilistClientWrapper, opts.MediaId) // DEVNOTE: Maybe cache it?
if err != nil {
return nil, err
}

View File

@@ -0,0 +1,131 @@
package entities
import (
"context"
"github.com/seanime-app/seanime/internal/anilist"
"github.com/seanime-app/seanime/internal/anizip"
"github.com/seanime-app/seanime/internal/test_utils"
"github.com/stretchr/testify/assert"
"testing"
)
func TestNewMediaEntryDownloadInfo(t *testing.T) {
test_utils.InitTestProvider(t, test_utils.Anilist())
anilistClientWrapper := anilist.TestGetMockAnilistClientWrapper()
anilistCollection, err := anilistClientWrapper.AnimeCollection(context.Background(), &test_utils.ConfigData.Provider.AnilistUsername)
if err != nil {
t.Fatal(err)
}
tests := []struct {
name string
localFiles []*LocalFile
mediaId int
currentProgress int
status anilist.MediaListStatus
expectedEpisodeNumbersToDownload []struct {
episodeNumber int
aniDbEpisode string
}
}{
{
// AniList includes episode 0 as a main episode but AniDB lists it as a special S1
// So we should expect to see episode 0 (S1) in the list of episodes to download
name: "Mushoku Tensei: Jobless Reincarnation Season 2",
localFiles: nil,
mediaId: 146065,
currentProgress: 0,
status: anilist.MediaListStatusCurrent,
expectedEpisodeNumbersToDownload: []struct {
episodeNumber int
aniDbEpisode string
}{
{episodeNumber: 0, aniDbEpisode: "S1"},
{episodeNumber: 1, aniDbEpisode: "1"},
{episodeNumber: 2, aniDbEpisode: "2"},
{episodeNumber: 3, aniDbEpisode: "3"},
{episodeNumber: 4, aniDbEpisode: "4"},
{episodeNumber: 5, aniDbEpisode: "5"},
{episodeNumber: 6, aniDbEpisode: "6"},
{episodeNumber: 7, aniDbEpisode: "7"},
{episodeNumber: 8, aniDbEpisode: "8"},
{episodeNumber: 9, aniDbEpisode: "9"},
{episodeNumber: 10, aniDbEpisode: "10"},
{episodeNumber: 11, aniDbEpisode: "11"},
{episodeNumber: 12, aniDbEpisode: "12"},
},
},
{
// Same as above but progress of 1 should just eliminate episode 0 from the list and not episode 1
name: "Mushoku Tensei: Jobless Reincarnation Season 2 - 2",
localFiles: nil,
mediaId: 146065,
currentProgress: 1,
status: anilist.MediaListStatusCurrent,
expectedEpisodeNumbersToDownload: []struct {
episodeNumber int
aniDbEpisode string
}{
{episodeNumber: 1, aniDbEpisode: "1"},
{episodeNumber: 2, aniDbEpisode: "2"},
{episodeNumber: 3, aniDbEpisode: "3"},
{episodeNumber: 4, aniDbEpisode: "4"},
{episodeNumber: 5, aniDbEpisode: "5"},
{episodeNumber: 6, aniDbEpisode: "6"},
{episodeNumber: 7, aniDbEpisode: "7"},
{episodeNumber: 8, aniDbEpisode: "8"},
{episodeNumber: 9, aniDbEpisode: "9"},
{episodeNumber: 10, aniDbEpisode: "10"},
{episodeNumber: 11, aniDbEpisode: "11"},
{episodeNumber: 12, aniDbEpisode: "12"},
},
},
}
anizipCache := anizip.NewCache()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
anizipData, err := anizip.FetchAniZipMediaC("anilist", tt.mediaId, anizipCache)
if err != nil {
t.Fatal(err)
}
anilistEntry, _ := anilistCollection.GetListEntryFromMediaId(tt.mediaId)
info, err := NewMediaEntryDownloadInfo(&NewMediaEntryDownloadInfoOptions{
localFiles: tt.localFiles,
anizipMedia: anizipData,
progress: &tt.currentProgress,
status: &tt.status,
media: anilistEntry.Media,
})
if assert.NoError(t, err) && assert.NotNil(t, info) {
foundEpToDownload := make([]struct {
episodeNumber int
aniDbEpisode string
}, 0)
for _, ep := range info.EpisodesToDownload {
foundEpToDownload = append(foundEpToDownload, struct {
episodeNumber int
aniDbEpisode string
}{
episodeNumber: ep.EpisodeNumber,
aniDbEpisode: ep.AniDBEpisode,
})
}
assert.ElementsMatch(t, tt.expectedEpisodeNumbersToDownload, foundEpToDownload)
}
})
}
}

View File

@@ -20,12 +20,13 @@ type (
// NewMediaEntryLibraryData creates a new MediaEntryLibraryData based on the media id and a list of local files related to the media.
// It will return false if the list of local files is empty.
func NewMediaEntryLibraryData(opts *NewMediaEntryLibraryDataOptions) (*MediaEntryLibraryData, bool) {
if opts.entryLocalFiles == nil || len(opts.entryLocalFiles) == 0 {
return nil, false
}
sharedPath := strings.Replace(opts.entryLocalFiles[0].Path, opts.entryLocalFiles[0].Name, "", 1)
sharedPath, _ = strings.CutSuffix(sharedPath, "\\")
sharedPath, _ = strings.CutSuffix(sharedPath, "/")
sharedPath = strings.TrimSuffix(strings.TrimSuffix(sharedPath, "\\"), "/")
return &MediaEntryLibraryData{
AllFilesLocked: lo.EveryBy(opts.entryLocalFiles, func(item *LocalFile) bool { return item.Locked }),
SharedPath: sharedPath,

View File

@@ -31,7 +31,7 @@ type (
MediaId int
LocalFiles []*LocalFile // All local files
AnilistCollection *anilist.AnimeCollection
AnilistClientWrapper *anilist.ClientWrapper
AnilistClientWrapper anilist.ClientWrapperInterface
}
)
@@ -59,7 +59,7 @@ func NewSimpleMediaEntry(opts *NewSimpleMediaEntryOptions) (*SimpleMediaEntry, e
anilistEntry = &anilist.MediaListEntry{}
// Fetch the media
fetchedMedia, err := anilist.GetBaseMediaById(opts.AnilistClientWrapper.Client, opts.MediaId) // DEVNOTE: Maybe cache it?
fetchedMedia, err := anilist.GetBaseMediaById(opts.AnilistClientWrapper, opts.MediaId) // DEVNOTE: Maybe cache it?
if err != nil {
return nil, err
}

View File

@@ -1,214 +1,112 @@
package entities
import (
"github.com/goccy/go-json"
"context"
"github.com/samber/lo"
"github.com/seanime-app/seanime/internal/anilist"
"github.com/seanime-app/seanime/internal/anizip"
"github.com/seanime-app/seanime/internal/test_utils"
"github.com/stretchr/testify/assert"
"io"
"os"
"path/filepath"
"strconv"
"testing"
)
const mediaEntryMockDataFile = "./media_entry_test_mock_data.json"
// TestNewMediaEntry tests /library/entry endpoint.
// /!\ MAKE SURE TO HAVE THE MEDIA ADDED TO YOUR LIST TEST ACCOUNT LISTS
func TestNewMediaEntry(t *testing.T) {
test_utils.InitTestProvider(t, test_utils.Anilist())
var mediaId = 154587 // Sousou no Frieren
lfs, anilistCollection, err := getMediaEntryMockData(t, mediaId)
if assert.NoErrorf(t, err, "Failed to get mock data") &&
assert.NotNil(t, lfs) &&
assert.NotNil(t, anilistCollection) {
entry, err := NewMediaEntry(&NewMediaEntryOptions{
MediaId: mediaId,
LocalFiles: lfs,
AnizipCache: anizip.NewCache(),
AnilistCollection: anilistCollection,
AnilistClientWrapper: anilist.TestGetAnilistClientWrapper(),
})
if assert.NoError(t, err) {
assert.Equalf(t, len(lfs), len(entry.Episodes), "Number of episodes mismatch")
// Mock data progress is 4
if assert.NotNilf(t, entry.NextEpisode, "Next episode not found") {
assert.Equal(t, 5, entry.NextEpisode.EpisodeNumber, "Next episode mismatch")
}
t.Logf("Success, found %v episodes", len(entry.Episodes))
}
}
}
func TestNewMediaEntry2(t *testing.T) {
var mediaId = 146065 // Mushoku Tensei: Jobless Reincarnation Season 2
_, anilistCollection, err := getMediaEntryMockData(t, mediaId)
var lfs []*LocalFile
for idx, path := range []string{
"E:/Anime/Mushoku Tensei II Isekai Ittara Honki Dasu/[SubsPlease] Mushoku Tensei S2 - 00 (1080p) [9C362DC3].mkv",
"E:/Anime/Mushoku Tensei II Isekai Ittara Honki Dasu/[SubsPlease] Mushoku Tensei S2 - 01 (1080p) [EC64C8B1].mkv",
"E:/Anime/Mushoku Tensei II Isekai Ittara Honki Dasu/[SubsPlease] Mushoku Tensei S2 - 02 (1080p) [7EA9E789].mkv",
"E:/Anime/Mushoku Tensei II Isekai Ittara Honki Dasu/[SubsPlease] Mushoku Tensei S2 - 03 (1080p) [BEF3095D].mkv",
"E:/Anime/Mushoku Tensei II Isekai Ittara Honki Dasu/[SubsPlease] Mushoku Tensei S2 - 04 (1080p) [FD2285EB].mkv",
"E:/Anime/Mushoku Tensei II Isekai Ittara Honki Dasu/[SubsPlease] Mushoku Tensei S2 - 05 (1080p) [E691CDB3].mkv",
"E:/Anime/Mushoku Tensei II Isekai Ittara Honki Dasu/[SubsPlease] Mushoku Tensei S2 - 06 (1080p) [0438103E].mkv",
"E:/Anime/Mushoku Tensei II Isekai Ittara Honki Dasu/[SubsPlease] Mushoku Tensei S2 - 07 (1080p) [DA6366AD].mkv",
"E:/Anime/Mushoku Tensei II Isekai Ittara Honki Dasu/[SubsPlease] Mushoku Tensei S2 - 08 (1080p) [A761377D].mkv",
"E:/Anime/Mushoku Tensei II Isekai Ittara Honki Dasu/[SubsPlease] Mushoku Tensei S2 - 09 (1080p) [DFE9A041].mkv",
"E:/Anime/Mushoku Tensei II Isekai Ittara Honki Dasu/[SubsPlease] Mushoku Tensei S2 - 10 (1080p) [DFE1B93B].mkv",
"E:/Anime/Mushoku Tensei II Isekai Ittara Honki Dasu/[SubsPlease] Mushoku Tensei S2 - 11 (1080p) [F70DC34C].mkv",
"E:/Anime/Mushoku Tensei II Isekai Ittara Honki Dasu/[SubsPlease] Mushoku Tensei S2 - 12 (1080p) [BAA0EBAD].mkv",
} {
lf := NewLocalFile(path, "E:/Anime")
// Mock hydration
lf.MediaId = mediaId
lf.Metadata = &LocalFileMetadata{
Type: LocalFileTypeMain,
Episode: idx,
AniDBEpisode: strconv.Itoa(idx),
}
if idx == 0 {
lf.Metadata.AniDBEpisode = "S1"
}
lfs = append(lfs, lf)
tests := []struct {
name string
mediaId int
localFiles []*LocalFile
currentProgress int
expectedNextEpisodeNumber int
expectedNextEpisodeProgressNumber int
}{
{
name: "Sousou no Frieren",
mediaId: 154587,
localFiles: MockHydratedLocalFiles(
MockGenerateHydratedLocalFileGroupOptions("E:/Anime", "E:\\Anime\\Sousou no Frieren\\[SubsPlease] Sousou no Frieren - %ep (1080p) [F02B9CEE].mkv", 154587, []MockHydratedLocalFileWrapperOptionsMetadata{
{metadataEpisode: 1, metadataAniDbEpisode: "1", metadataType: LocalFileTypeMain},
{metadataEpisode: 2, metadataAniDbEpisode: "2", metadataType: LocalFileTypeMain},
{metadataEpisode: 3, metadataAniDbEpisode: "3", metadataType: LocalFileTypeMain},
{metadataEpisode: 4, metadataAniDbEpisode: "4", metadataType: LocalFileTypeMain},
{metadataEpisode: 5, metadataAniDbEpisode: "5", metadataType: LocalFileTypeMain},
}),
),
currentProgress: 4,
expectedNextEpisodeNumber: 5,
expectedNextEpisodeProgressNumber: 5,
},
{
name: "Mushoku Tensei II Isekai Ittara Honki Dasu",
mediaId: 146065,
localFiles: MockHydratedLocalFiles(
MockGenerateHydratedLocalFileGroupOptions("E:/Anime", "E:/Anime/Mushoku Tensei II Isekai Ittara Honki Dasu/[SubsPlease] Mushoku Tensei S2 - 00 (1080p) [9C362DC3].mkv", 146065, []MockHydratedLocalFileWrapperOptionsMetadata{
{metadataEpisode: 0, metadataAniDbEpisode: "S1", metadataType: LocalFileTypeMain}, // Special episode
{metadataEpisode: 1, metadataAniDbEpisode: "1", metadataType: LocalFileTypeMain},
{metadataEpisode: 2, metadataAniDbEpisode: "2", metadataType: LocalFileTypeMain},
{metadataEpisode: 3, metadataAniDbEpisode: "3", metadataType: LocalFileTypeMain},
{metadataEpisode: 4, metadataAniDbEpisode: "4", metadataType: LocalFileTypeMain},
{metadataEpisode: 5, metadataAniDbEpisode: "5", metadataType: LocalFileTypeMain},
{metadataEpisode: 6, metadataAniDbEpisode: "6", metadataType: LocalFileTypeMain},
{metadataEpisode: 7, metadataAniDbEpisode: "7", metadataType: LocalFileTypeMain},
{metadataEpisode: 8, metadataAniDbEpisode: "8", metadataType: LocalFileTypeMain},
{metadataEpisode: 9, metadataAniDbEpisode: "9", metadataType: LocalFileTypeMain},
{metadataEpisode: 10, metadataAniDbEpisode: "10", metadataType: LocalFileTypeMain},
{metadataEpisode: 11, metadataAniDbEpisode: "11", metadataType: LocalFileTypeMain},
{metadataEpisode: 12, metadataAniDbEpisode: "12", metadataType: LocalFileTypeMain},
}),
),
currentProgress: 0,
expectedNextEpisodeNumber: 0,
expectedNextEpisodeProgressNumber: 1,
},
}
if assert.NoErrorf(t, err, "Failed to get mock data") &&
assert.NotNil(t, lfs) &&
assert.NotNil(t, anilistCollection) {
entry, err := NewMediaEntry(&NewMediaEntryOptions{
MediaId: mediaId,
LocalFiles: lfs,
AnizipCache: anizip.NewCache(),
AnilistCollection: anilistCollection,
AnilistClientWrapper: anilist.TestGetAnilistClientWrapper(),
})
if assert.NoError(t, err) {
assert.Equalf(t, len(lfs), len(entry.Episodes), "Number of episodes mismatch")
// Mock data progress is 0 so the next episode should be "0" (S1) with progress number 1
if assert.NotNilf(t, entry.NextEpisode, "Next episode not found") {
assert.Equal(t, 0, entry.NextEpisode.EpisodeNumber, "Next episode number mismatch")
assert.Equal(t, 1, entry.NextEpisode.ProgressNumber, "Next episode progress number mismatch")
}
t.Logf("Success, found %v episodes", len(entry.Episodes))
}
}
}
func TestNewMissingEpisodes(t *testing.T) {
var mediaId = 154587 // Sousou no Frieren
lfs, anilistCollection, err := getMediaEntryMockData(t, mediaId)
if assert.NoError(t, err) {
missingData := NewMissingEpisodes(&NewMissingEpisodesOptions{
AnilistCollection: anilistCollection,
LocalFiles: lfs,
AnizipCache: anizip.NewCache(),
})
// Mock data has 5 files, Current number of episodes is 10, so 5 episodes are missing
assert.Equal(t, 5, len(missingData.Episodes))
}
}
// Fetching "Mushoku Tensei: Jobless Reincarnation Season 2" download info
// Anilist: 13 episodes, Anizip: 12 episodes + "S1"
// Info should include "S1" as episode 0
func TestNewMediaEntryDownloadInfo(t *testing.T) {
var mediaId = 146065 // Mushoku Tensei: Jobless Reincarnation Season 2
_, anilistCollection, err := getMediaEntryMockData(t, mediaId)
anilistClientWrapper := anilist.TestGetMockAnilistClientWrapper()
anilistCollection, err := anilistClientWrapper.AnimeCollection(context.Background(), &test_utils.ConfigData.Provider.AnilistUsername)
if err != nil {
t.Fatal(err)
}
anizipData, err := anizip.FetchAniZipMedia("anilist", mediaId)
if err != nil {
t.Fatal(err)
}
aniZipCache := anizip.NewCache()
anilistEntry, ok := anilistCollection.GetListEntryFromMediaId(mediaId)
for _, tt := range tests {
if assert.Truef(t, ok, "Could not find media entry for %d", mediaId) {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, 13, anilistEntry.Media.GetCurrentEpisodeCount(), "Number of episodes mismatch on Anilist")
assert.Equal(t, 12, anizipData.GetMainEpisodeCount(), "Number of episodes mismatch on Anizip")
info, err := NewMediaEntryDownloadInfo(&NewMediaEntryDownloadInfoOptions{
localFiles: nil,
anizipMedia: anizipData,
progress: lo.ToPtr(0),
status: lo.ToPtr(anilist.MediaListStatusCurrent),
media: anilistEntry.Media,
})
if assert.NoError(t, err) && assert.NotNil(t, info) {
_, found := lo.Find(info.EpisodesToDownload, func(i *MediaEntryDownloadEpisode) bool {
return i.EpisodeNumber == 0 && i.AniDBEpisode == "S1"
// && i.Episode.ProgressNumber == 1 DEVNOTE: Progress numbers are always 0 because we don't have local files
anilist.TestModifyAnimeCollectionEntry(anilistCollection, tt.mediaId, anilist.TestModifyAnimeCollectionEntryInput{
Progress: lo.ToPtr(tt.currentProgress), // Mock progress
})
assert.True(t, found)
entry, err := NewMediaEntry(&NewMediaEntryOptions{
MediaId: tt.mediaId,
LocalFiles: tt.localFiles,
AnizipCache: aniZipCache,
AnilistCollection: anilistCollection,
AnilistClientWrapper: anilistClientWrapper,
})
}
if assert.NoErrorf(t, err, "Failed to get mock data") {
if assert.NoError(t, err) {
// Mock progress is 4
nextEp, found := entry.FindNextEpisode()
if assert.True(t, found, "did not find next episode") {
assert.Equal(t, tt.expectedNextEpisodeNumber, nextEp.EpisodeNumber, "next episode number mismatch")
assert.Equal(t, tt.expectedNextEpisodeProgressNumber, nextEp.ProgressNumber, "next episode progress number mismatch")
}
t.Logf("Found %v episodes", len(entry.Episodes))
}
}
})
}
}
//----------------------------------------------------------------------------------------------------------------------
func getMediaEntryMockData(t *testing.T, mediaId int) ([]*LocalFile, *anilist.AnimeCollection, error) {
path, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
// Open the JSON file
file, err := os.Open(filepath.Join(path, mediaEntryMockDataFile))
if err != nil {
t.Fatal("Error opening file:", err.Error())
}
defer file.Close()
jsonData, err := io.ReadAll(file)
if err != nil {
t.Fatal("Error reading file:", err.Error())
}
var data map[string]struct {
LocalFiles []*LocalFile `json:"localFiles"`
AnilistCollection *anilist.AnimeCollection `json:"anilistCollection"`
}
if err := json.Unmarshal(jsonData, &data); err != nil {
t.Fatal("Error unmarshaling JSON:", err.Error())
}
ret, _ := data[strconv.Itoa(mediaId)]
return ret.LocalFiles, ret.AnilistCollection, nil
}

View File

@@ -0,0 +1,79 @@
package entities
import (
"context"
"github.com/samber/lo"
"github.com/seanime-app/seanime/internal/anilist"
"github.com/seanime-app/seanime/internal/anizip"
"github.com/seanime-app/seanime/internal/test_utils"
"github.com/stretchr/testify/assert"
"testing"
)
// Test to retrieve accurate missing episodes
func TestNewMissingEpisodes(t *testing.T) {
test_utils.InitTestProvider(t, test_utils.Anilist())
anilistClientWrapper := anilist.TestGetMockAnilistClientWrapper()
anilistCollection, err := anilistClientWrapper.AnimeCollection(context.Background(), &test_utils.ConfigData.Provider.AnilistUsername)
if err != nil {
t.Fatal(err)
}
tests := []struct {
name string
mediaId int
localFiles []*LocalFile
mediaAiredEpisodes int
currentProgress int
expectedMissingEpisodes int
}{
{
// Sousou no Frieren - 10 currently aired episodes
// User has 5 local files from ep 1 to 5, but only watched 4 episodes
// So we should expect to see 5 missing episodes
name: "Sousou no Frieren, missing 5 episodes",
mediaId: 154587,
localFiles: MockHydratedLocalFiles(
MockGenerateHydratedLocalFileGroupOptions("E:/Anime", "E:\\Anime\\Sousou no Frieren\\[SubsPlease] Sousou no Frieren - %ep (1080p) [F02B9CEE].mkv", 154587, []MockHydratedLocalFileWrapperOptionsMetadata{
{metadataEpisode: 1, metadataAniDbEpisode: "1", metadataType: LocalFileTypeMain},
{metadataEpisode: 2, metadataAniDbEpisode: "2", metadataType: LocalFileTypeMain},
{metadataEpisode: 3, metadataAniDbEpisode: "3", metadataType: LocalFileTypeMain},
{metadataEpisode: 4, metadataAniDbEpisode: "4", metadataType: LocalFileTypeMain},
{metadataEpisode: 5, metadataAniDbEpisode: "5", metadataType: LocalFileTypeMain},
}),
),
mediaAiredEpisodes: 10,
currentProgress: 4,
expectedMissingEpisodes: 5,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Mock Anilist collection
anilist.TestModifyAnimeCollectionEntry(anilistCollection, tt.mediaId, anilist.TestModifyAnimeCollectionEntryInput{
Progress: lo.ToPtr(tt.currentProgress), // Mock progress
Episodes: lo.ToPtr(tt.mediaAiredEpisodes), // Mock total episodes
NextAiringEpisode: &anilist.BaseMedia_NextAiringEpisode{
Episode: tt.mediaAiredEpisodes + 1,
},
})
})
if assert.NoError(t, err) {
missingData := NewMissingEpisodes(&NewMissingEpisodesOptions{
AnilistCollection: anilistCollection,
LocalFiles: tt.localFiles,
AnizipCache: anizip.NewCache(),
})
assert.Equal(t, tt.expectedMissingEpisodes, len(missingData.Episodes))
}
}
}

View File

@@ -36,7 +36,7 @@ func HandleEditAnilistListEntry(c *RouteCtx) error {
return c.RespondWithError(err)
}
ret, err := c.App.AnilistClientWrapper.Client.UpdateMediaListEntry(
ret, err := c.App.AnilistClientWrapper.UpdateMediaListEntry(
c.Fiber.Context(),
p.MediaId,
p.Status,
@@ -103,7 +103,7 @@ func HandleGetAnilistMediaDetails(c *RouteCtx) error {
if details, ok := detailsCache.Get(mId); ok {
return c.RespondWithData(details)
}
details, err := c.App.AnilistClientWrapper.Client.MediaDetailsByID(c.Fiber.Context(), &mId)
details, err := c.App.AnilistClientWrapper.MediaDetailsByID(c.Fiber.Context(), &mId)
if err != nil {
return c.RespondWithError(err)
}
@@ -137,7 +137,7 @@ func HandleDeleteAnilistListEntry(c *RouteCtx) error {
}
// Delete the list entry
ret, err := c.App.AnilistClientWrapper.Client.DeleteEntry(
ret, err := c.App.AnilistClientWrapper.DeleteEntry(
c.Fiber.Context(),
&listEntry.ID,
)

View File

@@ -31,7 +31,7 @@ func HandleLogin(c *RouteCtx) error {
c.App.UpdateAnilistClientToken(body.Token)
// Get viewer data from AniList
getViewer, err := c.App.AnilistClientWrapper.Client.GetViewer(context.Background())
getViewer, err := c.App.AnilistClientWrapper.GetViewer(context.Background())
if err != nil {
c.App.Logger.Error().Msg("Could not authenticate to AniList")
return c.RespondWithError(err)

View File

@@ -297,7 +297,7 @@ func HandleFindProspectiveMediaEntrySuggestions(c *RouteCtx) error {
return media
}
// Otherwise, fetch the media
mediaRes, err := c.App.AnilistClientWrapper.Client.BasicMediaByMalID(context.Background(), &s.ID)
mediaRes, err := c.App.AnilistClientWrapper.BasicMediaByMalID(context.Background(), &s.ID)
if err != nil {
return nil
}
@@ -365,7 +365,7 @@ func HandleMediaEntryManualMatch(c *RouteCtx) error {
})
// Get the media
mediaRes, err := c.App.AnilistClientWrapper.Client.BaseMediaByID(context.Background(), &b.MediaId)
mediaRes, err := c.App.AnilistClientWrapper.BaseMediaByID(context.Background(), &b.MediaId)
if err != nil {
return c.RespondWithError(err)
}
@@ -464,7 +464,7 @@ func HandleAddUnknownMedia(c *RouteCtx) error {
}
// Add non-added media entries to AniList collection
if err := c.App.AnilistClientWrapper.Client.AddMediaToPlanning(b.MediaIds, limiter.NewAnilistLimiter(), c.App.Logger); err != nil {
if err := c.App.AnilistClientWrapper.AddMediaToPlanning(b.MediaIds, limiter.NewAnilistLimiter(), c.App.Logger); err != nil {
return c.RespondWithError(errors.New("error: Anilist responded with an error, this is most likely a rate limit issue"))
}

View File

@@ -16,7 +16,7 @@ type (
AnimeEntriesMap map[int]*AnimeEntry
}
ProviderRepository struct { // Holds information used for making requests to the providers
AnilistClientWrapper *anilist.ClientWrapper
AnilistClientWrapper anilist.ClientWrapperInterface
MalWrapper *mal.Wrapper
Logger *zerolog.Logger
}
@@ -70,7 +70,7 @@ func (pr *ProviderRepository) AddAnime(to Source, entry *AnimeEntry) error {
status := ToAnilistListStatus(entry.Status)
score := entry.Score * 10
_, err = pr.AnilistClientWrapper.Client.UpdateMediaListEntryStatus(
_, err = pr.AnilistClientWrapper.UpdateMediaListEntryStatus(
context.Background(),
&anilistId,
&entry.Progress,
@@ -129,7 +129,7 @@ func (pr *ProviderRepository) UpdateAnime(to Source, entry *AnimeEntry) error {
status := ToAnilistListStatus(entry.Status)
score := entry.Score * 10
_, err = pr.AnilistClientWrapper.Client.UpdateMediaListEntryStatus(
_, err = pr.AnilistClientWrapper.UpdateMediaListEntryStatus(
context.Background(),
&anilistId,
&entry.Progress,
@@ -183,7 +183,7 @@ func (pr *ProviderRepository) DeleteAnime(from Source, entry *AnimeEntry) error
return errors.New("anilist id not found")
}
_, err = pr.AnilistClientWrapper.Client.DeleteEntry(
_, err = pr.AnilistClientWrapper.DeleteEntry(
context.Background(),
&anilistId,
)

View File

@@ -66,7 +66,7 @@ func BuildListSync(db *db.Database, logger *zerolog.Logger) (ls *ListSync, err e
// Get Anilist collection
anilistClientWrapper := anilist.NewClientWrapper(account.Token)
collection, err := anilistClientWrapper.Client.AnimeCollection(context.Background(), &account.Username)
collection, err := anilistClientWrapper.AnimeCollection(context.Background(), &account.Username)
if err != nil {
return nil, err
}

View File

@@ -59,7 +59,7 @@ func TestBuildSearchQuery(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
media, err := anilist.GetBaseMediaById(anilistClientWrapper.Client, tt.mediaId)
media, err := anilist.GetBaseMediaById(anilistClientWrapper, tt.mediaId)
if assert.NoError(t, err) &&
assert.NotNil(t, media) {

View File

@@ -28,7 +28,7 @@ type (
MediaPlayerRepository *mediaplayer.Repository // MediaPlayerRepository is used to control the media player
mediaPlayerRepoSubscriber *mediaplayer.RepositorySubscriber // Used to listen for media player events
wsEventManager events.IWSEventManager
anilistClientWrapper *anilist.ClientWrapper
anilistClientWrapper anilist.ClientWrapperInterface
anilistCollection *anilist.AnimeCollection
refreshAnilistCollectionFunc func() // This function is called to refresh the AniList collection
mu sync.Mutex
@@ -62,7 +62,7 @@ type (
NewProgressManagerOptions struct {
WSEventManager events.IWSEventManager
Logger *zerolog.Logger
AnilistClientWrapper *anilist.ClientWrapper
AnilistClientWrapper anilist.ClientWrapperInterface
AnilistCollection *anilist.AnimeCollection
Database *db.Database
RefreshAnilistCollectionFunc func() // This function is called to refresh the AniList collection
@@ -83,7 +83,7 @@ func New(opts *NewProgressManagerOptions) *PlaybackManager {
}
}
func (pm *PlaybackManager) SetAnilistClientWrapper(anilistClientWrapper *anilist.ClientWrapper) {
func (pm *PlaybackManager) SetAnilistClientWrapper(anilistClientWrapper anilist.ClientWrapperInterface) {
pm.anilistClientWrapper = anilistClientWrapper
}

View File

@@ -6,20 +6,27 @@ import (
"github.com/seanime-app/seanime/internal/db"
"github.com/seanime-app/seanime/internal/events"
"github.com/seanime-app/seanime/internal/playbackmanager"
"github.com/seanime-app/seanime/internal/test_utils"
"github.com/seanime-app/seanime/internal/util"
"testing"
)
func getPlaybackManager() (*playbackmanager.PlaybackManager, *anilist.ClientWrapper, *anilist.AnimeCollection, error) {
logger := util.NewLogger()
wsEventManager := events.NewMockWSEventManager(logger)
databaseInfo := db.GetTestDatabaseInfo()
database, err := db.NewDatabase(databaseInfo.DataDir, databaseInfo.Name, logger)
if err != nil {
return nil, nil, nil, err
}
anilistClientWrapper, data := anilist.TestGetAnilistClientWrapperAndInfo()
func getPlaybackManager(t *testing.T) (*playbackmanager.PlaybackManager, anilist.ClientWrapperInterface, *anilist.AnimeCollection, error) {
test_utils.InitTestProvider(t, test_utils.Anilist())
anilistCollection, err := anilistClientWrapper.Client.AnimeCollection(context.Background(), &data.AnilistUsername)
logger := util.NewLogger()
wsEventManager := events.NewMockWSEventManager(logger)
database, err := db.NewDatabase(test_utils.ConfigData.Path.DataDir, test_utils.ConfigData.Database.Name, logger)
if err != nil {
t.Fatalf("error while creating database, %v", err)
}
acw := anilist.TestGetAnilistClientWrapper()
anilistCollection, err := acw.AnimeCollection(context.Background(), &test_utils.ConfigData.Provider.AnilistUsername)
if err != nil {
return nil, nil, nil, err
}
@@ -27,11 +34,11 @@ func getPlaybackManager() (*playbackmanager.PlaybackManager, *anilist.ClientWrap
return playbackmanager.New(&playbackmanager.NewProgressManagerOptions{
Logger: logger,
WSEventManager: wsEventManager,
AnilistClientWrapper: anilistClientWrapper,
AnilistClientWrapper: acw,
Database: database,
AnilistCollection: anilistCollection,
RefreshAnilistCollectionFunc: func() {
// Do nothing
},
}), anilistClientWrapper, anilistCollection, nil
}), acw, anilistCollection, nil
}

View File

@@ -21,7 +21,7 @@ var localFilePaths = []string{
var mediaId = 153518
func TestPlaylists(t *testing.T) {
playbackManager, anilistClientWrapper, anilistCollection, err := getPlaybackManager()
playbackManager, anilistClientWrapper, anilistCollection, err := getPlaybackManager(t)
if err != nil {
t.Fatal(err)
}

View File

@@ -22,7 +22,7 @@ type (
scannedCh chan struct{}
waitTime time.Duration // Wait time to listen to additional changes before triggering a scan.
enabled bool
AnilistClientWrapper *anilist.ClientWrapper
AnilistClientWrapper anilist.ClientWrapperInterface
Logger *zerolog.Logger
WSEventManager events.IWSEventManager
Database *db.Database // Database instance is required to update the local files.
@@ -30,7 +30,7 @@ type (
}
NewAutoScannerOptions struct {
Database *db.Database
AnilistClientWrapper *anilist.ClientWrapper
AnilistClientWrapper anilist.ClientWrapperInterface
Logger *zerolog.Logger
WSEventManager events.IWSEventManager
Enabled bool

View File

@@ -3,17 +3,19 @@ package scanner
import (
"github.com/seanime-app/seanime/internal/anilist"
"github.com/seanime-app/seanime/internal/events"
"github.com/seanime-app/seanime/internal/test_utils"
"github.com/seanime-app/seanime/internal/util"
"testing"
"time"
)
func TestAutoScanner(t *testing.T) {
test_utils.InitTestProvider(t, test_utils.Anilist())
doneCh := make(chan struct{})
logger := util.NewLogger()
anilistClientWrapper, _ := anilist.TestGetAnilistClientWrapperAndInfo()
anilistClientWrapper := anilist.TestGetMockAnilistClientWrapper()
as := NewAutoScanner(&NewAutoScannerOptions{
Database: nil,

View File

@@ -24,7 +24,7 @@ type FileHydrator struct {
AllMedia []*entities.NormalizedMedia // All media used to hydrate local files
BaseMediaCache *anilist.BaseMediaCache
AnizipCache *anizip.Cache
AnilistClientWrapper *anilist.ClientWrapper
AnilistClientWrapper anilist.ClientWrapperInterface
AnilistRateLimiter *limiter.Limiter
Logger *zerolog.Logger
ScanLogger *ScanLogger // optional

View File

@@ -29,7 +29,7 @@ type MediaFetcher struct {
type MediaFetcherOptions struct {
Enhanced bool
Username string
AnilistClientWrapper *anilist.ClientWrapper
AnilistClientWrapper anilist.ClientWrapperInterface
LocalFiles []*entities.LocalFile
BaseMediaCache *anilist.BaseMediaCache
AnizipCache *anizip.Cache
@@ -75,7 +75,7 @@ func NewMediaFetcher(opts *MediaFetcherOptions) (ret *MediaFetcher, retErr error
// +---------------------+
// Fetch latest user's AniList collection
animeCollection, err := opts.AnilistClientWrapper.Client.AnimeCollection(context.Background(), &opts.Username)
animeCollection, err := opts.AnilistClientWrapper.AnimeCollection(context.Background(), &opts.Username)
if err != nil {
return nil, err
}
@@ -174,7 +174,7 @@ func NewMediaFetcher(opts *MediaFetcherOptions) (ret *MediaFetcher, retErr error
// It does not return an error if one of the steps fails.
// It returns the scanned media and a boolean indicating whether the process was successful.
func FetchMediaFromLocalFiles(
anilistClientWrapper *anilist.ClientWrapper,
anilistClientWrapper anilist.ClientWrapperInterface,
localFiles []*entities.LocalFile,
baseMediaCache *anilist.BaseMediaCache,
anizipCache *anizip.Cache,
@@ -261,7 +261,7 @@ func FetchMediaFromLocalFiles(
anilistMedia := make([]*anilist.BaseMedia, 0)
lop.ForEach(anilistIds, func(id int, index int) {
anilistRateLimiter.Wait()
media, err := anilist.GetBaseMediaById(anilistClientWrapper.Client, id)
media, err := anilist.GetBaseMediaById(anilistClientWrapper, id)
if err == nil {
anilistMedia = append(anilistMedia, media)
if scanLogger != nil {

View File

@@ -107,7 +107,7 @@ func TestNewMediaFetcher(t *testing.T) {
func TestNewEnhancedMediaFetcher(t *testing.T) {
anilistClientWrapper, _ := anilist.TestGetAnilistClientWrapperAndInfo()
acw := anilist.TestGetAnilistClientWrapper()
anizipCache := anizip.NewCache()
baseMediaCache := anilist.NewBaseMediaCache()
anilistRateLimiter := limiter.NewAnilistLimiter()
@@ -157,7 +157,7 @@ func TestNewEnhancedMediaFetcher(t *testing.T) {
mf, err := NewMediaFetcher(&MediaFetcherOptions{
Enhanced: tt.enhanced,
Username: "-",
AnilistClientWrapper: anilistClientWrapper,
AnilistClientWrapper: acw,
LocalFiles: lfs,
BaseMediaCache: baseMediaCache,
AnizipCache: anizipCache,
@@ -186,7 +186,7 @@ func TestNewEnhancedMediaFetcher(t *testing.T) {
func TestFetchMediaFromLocalFiles(t *testing.T) {
anilistClientWrapper := anilist.TestGetAnilistClientWrapper()
acw := anilist.TestGetAnilistClientWrapper()
anizipCache := anizip.NewCache()
baseMediaCache := anilist.NewBaseMediaCache()
anilistRateLimiter := limiter.NewAnilistLimiter()
@@ -234,7 +234,7 @@ func TestFetchMediaFromLocalFiles(t *testing.T) {
// +--------------------------+
media, ok := FetchMediaFromLocalFiles(
anilistClientWrapper,
acw,
lfs,
baseMediaCache,
anizipCache,

View File

@@ -112,7 +112,7 @@ func TestMediaTreeAnalysis2(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
media, err := anilistClientWrapper.Client.BaseMediaByID(context.Background(), &tt.mediaId)
media, err := anilistClientWrapper.BaseMediaByID(context.Background(), &tt.mediaId)
if err != nil {
t.Fatal("expected media, got error:", err.Error())
}

View File

@@ -19,7 +19,7 @@ type Scanner struct {
DirPath string
Username string
Enhanced bool
AnilistClientWrapper *anilist.ClientWrapper
AnilistClientWrapper anilist.ClientWrapperInterface
Logger *zerolog.Logger
WSEventManager events.IWSEventManager
ExistingLocalFiles []*entities.LocalFile
@@ -230,7 +230,7 @@ func (scn *Scanner) Scan() (lfs []*entities.LocalFile, err error) {
if len(mf.UnknownMediaIds) < 5 {
scn.WSEventManager.SendEvent(events.EventScanStatus, "Adding missing media to AniList...")
if err = scn.AnilistClientWrapper.Client.AddMediaToPlanning(mf.UnknownMediaIds, anilistRateLimiter, scn.Logger); err != nil {
if err = scn.AnilistClientWrapper.AddMediaToPlanning(mf.UnknownMediaIds, anilistRateLimiter, scn.Logger); err != nil {
scn.Logger.Warn().Msg("scanner: An error occurred while adding media to planning list: " + err.Error())
}
}

View File

@@ -5,6 +5,7 @@ import (
"github.com/seanime-app/seanime/internal/anilist"
"github.com/seanime-app/seanime/internal/entities"
"github.com/seanime-app/seanime/internal/events"
"github.com/seanime-app/seanime/internal/test_utils"
"github.com/seanime-app/seanime/internal/util"
"io"
"os"
@@ -15,8 +16,9 @@ import (
//----------------------------------------------------------------------------------------------------------------------
func TestScanner_Scan(t *testing.T) {
test_utils.InitTestProvider(t, test_utils.Anilist())
anilistClientWrapper, data := anilist.TestGetAnilistClientWrapperAndInfo()
acw := anilist.TestGetAnilistClientWrapper()
wsEventManager := events.NewMockWSEventManager(util.NewLogger())
dir := "E:/Anime"
@@ -51,9 +53,9 @@ func TestScanner_Scan(t *testing.T) {
scanner := &Scanner{
DirPath: dir,
Username: data.Username,
Username: test_utils.ConfigData.Provider.AnilistUsername,
Enhanced: false,
AnilistClientWrapper: anilistClientWrapper,
AnilistClientWrapper: acw,
Logger: util.NewLogger(),
WSEventManager: wsEventManager,
ExistingLocalFiles: existingLfs,

View File

@@ -0,0 +1,7 @@
package test_utils
import "fmt"
func GetTestDataPath(name string) string {
return fmt.Sprintf("%s/%s.json", TwoLevelDeepTestDataPath, name)
}

View File

@@ -9,6 +9,7 @@ import (
)
var ConfigData = &Config{}
var TwoLevelDeepTestDataPath = "../../test/testdata"
type (
Config struct {
@@ -65,7 +66,7 @@ func MyAnimeListMutation() FlagFunc {
}
}
// InitTestProvider initializes the ConfigData and skips the test if the given flags are not set
// InitTestProvider populates the ConfigData and skips the test if the given flags are not set
func InitTestProvider(t *testing.T, args ...FlagFunc) {
err := os.Setenv("TEST_CONFIG_PATH", "../../test")
ConfigData = getConfig()
@@ -80,10 +81,6 @@ func InitTestProvider(t *testing.T, args ...FlagFunc) {
}
}
func SetConfigPath(relPath string) {
os.Setenv("TEST_CONFIG_PATH", relPath)
}
func getConfig() *Config {
configPath, exists := os.LookupEnv("TEST_CONFIG_PATH")
if !exists {

View File

@@ -1,14 +1,14 @@
package test_utils
import (
"github.com/davecgh/go-spew/spew"
"github.com/stretchr/testify/assert"
"testing"
)
func TestGetConfig(t *testing.T) {
assert.Empty(t, ConfigData)
assert.NotNil(t, ConfigData)
InitTestProvider(t)
spew.Dump(ConfigData)
assert.NotEmpty(t, ConfigData)
}

View File

@@ -65,7 +65,7 @@ func TestSmartSelect(t *testing.T) {
t.Log(tt.name, hash)
// get media
media, err := anilist.GetBaseMediaById(anilistClientWrapper.Client, tt.mediaId)
media, err := anilist.GetBaseMediaById(anilistClientWrapper, tt.mediaId)
if err != nil {
t.Fatalf("error getting media: %s", err.Error())
}

16
test/config.example.toml Normal file
View File

@@ -0,0 +1,16 @@
[flags]
enable_anilist_tests = true
enable_anilist_mutation_tests = false
enable_mal_tests = true
enable_mal_mutation_tests = false
[provider]
anilist_jwt = ''
anilist_username = ''
mal_jwt = ''
[path]
dataDir = ''
[database]
name = 'seanime-test'

0
test/testdata/.gitkeep vendored Normal file
View File