[skip ci] Unit test rework (#3997)

This commit is contained in:
Fesaa 2025-08-20 16:51:36 +02:00 committed by GitHub
parent 2ad7249725
commit c42dfeda02
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 3674 additions and 3617 deletions

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Data.Common;
using System.Linq;
using System.Threading.Tasks;
@ -15,37 +15,36 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.Logging;
using NSubstitute;
using Polly;
using Xunit.Abstractions;
namespace API.Tests;
public abstract class AbstractDbTest : AbstractFsTest , IDisposable
public abstract class AbstractDbTest(ITestOutputHelper testOutputHelper): AbstractFsTest
{
protected readonly DataContext Context;
protected readonly IUnitOfWork UnitOfWork;
protected readonly IMapper Mapper;
private readonly DbConnection _connection;
private bool _disposed;
protected AbstractDbTest()
protected async Task<(IUnitOfWork, DataContext, IMapper)> CreateDatabase()
{
var contextOptions = new DbContextOptionsBuilder<DataContext>()
.UseSqlite(CreateInMemoryDatabase())
.EnableSensitiveDataLogging()
.Options;
_connection = RelationalOptionsExtension.Extract(contextOptions).Connection;
var connection = RelationalOptionsExtension.Extract(contextOptions).Connection;
var context = new DataContext(contextOptions);
Context = new DataContext(contextOptions);
await context.Database.EnsureCreatedAsync();
Context.Database.EnsureCreated(); // Ensure DB schema is created
await SeedDb(context);
Task.Run(SeedDb).GetAwaiter().GetResult();
var config = new MapperConfiguration(cfg => cfg.AddProfile<AutoMapperProfiles>());
Mapper = config.CreateMapper();
var mapper = config.CreateMapper();
GlobalConfiguration.Configuration.UseInMemoryStorage();
UnitOfWork = new UnitOfWork(Context, Mapper, null);
var unitOfWork = new UnitOfWork(context, mapper, null);
return (unitOfWork, context, mapper);
}
private static DbConnection CreateInMemoryDatabase()
@ -56,81 +55,61 @@ public abstract class AbstractDbTest : AbstractFsTest , IDisposable
return connection;
}
private async Task<bool> SeedDb()
private async Task<bool> SeedDb(DataContext context)
{
try
{
await Context.Database.EnsureCreatedAsync();
await context.Database.EnsureCreatedAsync();
var filesystem = CreateFileSystem();
await Seed.SeedSettings(Context, new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem));
await Seed.SeedSettings(context, new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem));
var setting = await Context.ServerSetting.Where(s => s.Key == ServerSettingKey.CacheDirectory).SingleAsync();
var setting = await context.ServerSetting.Where(s => s.Key == ServerSettingKey.CacheDirectory).SingleAsync();
setting.Value = CacheDirectory;
setting = await Context.ServerSetting.Where(s => s.Key == ServerSettingKey.BackupDirectory).SingleAsync();
setting = await context.ServerSetting.Where(s => s.Key == ServerSettingKey.BackupDirectory).SingleAsync();
setting.Value = BackupDirectory;
setting = await Context.ServerSetting.Where(s => s.Key == ServerSettingKey.BookmarkDirectory).SingleAsync();
setting = await context.ServerSetting.Where(s => s.Key == ServerSettingKey.BookmarkDirectory).SingleAsync();
setting.Value = BookmarkDirectory;
setting = await Context.ServerSetting.Where(s => s.Key == ServerSettingKey.TotalLogs).SingleAsync();
setting = await context.ServerSetting.Where(s => s.Key == ServerSettingKey.TotalLogs).SingleAsync();
setting.Value = "10";
Context.ServerSetting.Update(setting);
context.ServerSetting.Update(setting);
Context.Library.Add(new LibraryBuilder("Manga")
context.Library.Add(new LibraryBuilder("Manga")
.WithAllowMetadataMatching(true)
.WithFolderPath(new FolderPathBuilder(DataDirectory).Build())
.Build());
await Context.SaveChangesAsync();
await context.SaveChangesAsync();
await Seed.SeedMetadataSettings(Context);
await Seed.SeedMetadataSettings(context);
return true;
}
catch (Exception ex)
{
Console.WriteLine($"[SeedDb] Error: {ex.Message}");
testOutputHelper.WriteLine($"[SeedDb] Error: {ex.Message}");
return false;
}
}
protected abstract Task ResetDb();
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing)
{
Context?.Dispose();
_connection?.Dispose();
}
_disposed = true;
}
/// <summary>
/// Add a role to an existing User. Commits.
/// </summary>
/// <param name="userId"></param>
/// <param name="roleName"></param>
protected async Task AddUserWithRole(int userId, string roleName)
protected async Task AddUserWithRole(DataContext context, int userId, string roleName)
{
var role = new AppRole { Id = userId, Name = roleName, NormalizedName = roleName.ToUpper() };
await Context.Roles.AddAsync(role);
await Context.UserRoles.AddAsync(new AppUserRole { UserId = userId, RoleId = userId });
await context.Roles.AddAsync(role);
await context.UserRoles.AddAsync(new AppUserRole { UserId = userId, RoleId = userId });
await Context.SaveChangesAsync();
await context.SaveChangesAsync();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,35 +1,30 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using API.Data;
using API.Entities.Enums;
using API.Helpers;
using API.Helpers.Builders;
using Polly;
using Xunit;
using Xunit.Abstractions;
namespace API.Tests.Helpers;
public class PersonHelperTests : AbstractDbTest
public class PersonHelperTests(ITestOutputHelper outputHelper): AbstractDbTest(outputHelper)
{
protected override async Task ResetDb()
{
Context.Series.RemoveRange(Context.Series.ToList());
Context.Person.RemoveRange(Context.Person.ToList());
Context.Library.RemoveRange(Context.Library.ToList());
Context.Series.RemoveRange(Context.Series.ToList());
await Context.SaveChangesAsync();
}
// 1. Test adding new people and keeping existing ones
[Fact]
public async Task UpdateChapterPeopleAsync_AddNewPeople_ExistingPersonRetained()
{
await ResetDb();
var (unitOfWork, context, mapper) = await CreateDatabase();
var library = new LibraryBuilder("My Library")
.Build();
UnitOfWork.LibraryRepository.Add(library);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Add(library);
await unitOfWork.CommitAsync();
var existingPerson = new PersonBuilder("Joe Shmo").Build();
var chapter = new ChapterBuilder("1").Build();
@ -44,14 +39,14 @@ public class PersonHelperTests : AbstractDbTest
.WithVolume(new VolumeBuilder("1").WithChapter(chapter).Build())
.Build();
UnitOfWork.SeriesRepository.Add(series);
await UnitOfWork.CommitAsync();
unitOfWork.SeriesRepository.Add(series);
await unitOfWork.CommitAsync();
// Call UpdateChapterPeopleAsync with one existing and one new person
await PersonHelper.UpdateChapterPeopleAsync(chapter, new List<string> { "Joe Shmo", "New Person" }, PersonRole.Editor, UnitOfWork);
await PersonHelper.UpdateChapterPeopleAsync(chapter, new List<string> { "Joe Shmo", "New Person" }, PersonRole.Editor, unitOfWork);
// Assert existing person retained and new person added
var people = await UnitOfWork.PersonRepository.GetAllPeople();
var people = await unitOfWork.PersonRepository.GetAllPeople();
Assert.Contains(people, p => p.Name == "Joe Shmo");
Assert.Contains(people, p => p.Name == "New Person");
@ -64,13 +59,13 @@ public class PersonHelperTests : AbstractDbTest
[Fact]
public async Task UpdateChapterPeopleAsync_RemovePeople()
{
await ResetDb();
var (unitOfWork, context, mapper) = await CreateDatabase();
var library = new LibraryBuilder("My Library")
.Build();
UnitOfWork.LibraryRepository.Add(library);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Add(library);
await unitOfWork.CommitAsync();
var existingPerson1 = new PersonBuilder("Joe Shmo").Build();
var existingPerson2 = new PersonBuilder("Jane Doe").Build();
@ -86,16 +81,16 @@ public class PersonHelperTests : AbstractDbTest
.Build())
.Build();
UnitOfWork.SeriesRepository.Add(series);
await UnitOfWork.CommitAsync();
unitOfWork.SeriesRepository.Add(series);
await unitOfWork.CommitAsync();
// Call UpdateChapterPeopleAsync with only one person
await PersonHelper.UpdateChapterPeopleAsync(chapter, new List<string> { "Joe Shmo" }, PersonRole.Editor, UnitOfWork);
await PersonHelper.UpdateChapterPeopleAsync(chapter, new List<string> { "Joe Shmo" }, PersonRole.Editor, unitOfWork);
// PersonHelper does not remove the Person from the global DbSet itself
await UnitOfWork.PersonRepository.RemoveAllPeopleNoLongerAssociated();
await unitOfWork.PersonRepository.RemoveAllPeopleNoLongerAssociated();
var people = await UnitOfWork.PersonRepository.GetAllPeople();
var people = await unitOfWork.PersonRepository.GetAllPeople();
Assert.DoesNotContain(people, p => p.Name == "Jane Doe");
var chapterPeople = chapter.People.Select(cp => cp.Person.Name).ToList();
@ -107,13 +102,13 @@ public class PersonHelperTests : AbstractDbTest
[Fact]
public async Task UpdateChapterPeopleAsync_NoChanges()
{
await ResetDb();
var (unitOfWork, context, mapper) = await CreateDatabase();
var library = new LibraryBuilder("My Library")
.Build();
UnitOfWork.LibraryRepository.Add(library);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Add(library);
await unitOfWork.CommitAsync();
var existingPerson = new PersonBuilder("Joe Shmo").Build();
var chapter = new ChapterBuilder("1").WithPerson(existingPerson, PersonRole.Editor).Build();
@ -125,13 +120,13 @@ public class PersonHelperTests : AbstractDbTest
.Build())
.Build();
UnitOfWork.SeriesRepository.Add(series);
await UnitOfWork.CommitAsync();
unitOfWork.SeriesRepository.Add(series);
await unitOfWork.CommitAsync();
// Call UpdateChapterPeopleAsync with the same list
await PersonHelper.UpdateChapterPeopleAsync(chapter, new List<string> { "Joe Shmo" }, PersonRole.Editor, UnitOfWork);
await PersonHelper.UpdateChapterPeopleAsync(chapter, new List<string> { "Joe Shmo" }, PersonRole.Editor, unitOfWork);
var people = await UnitOfWork.PersonRepository.GetAllPeople();
var people = await unitOfWork.PersonRepository.GetAllPeople();
Assert.Contains(people, p => p.Name == "Joe Shmo");
var chapterPeople = chapter.People.Select(cp => cp.Person.Name).ToList();
@ -143,13 +138,13 @@ public class PersonHelperTests : AbstractDbTest
[Fact]
public async Task UpdateChapterPeopleAsync_MultipleRoles()
{
await ResetDb();
var (unitOfWork, context, mapper) = await CreateDatabase();
var library = new LibraryBuilder("My Library")
.Build();
UnitOfWork.LibraryRepository.Add(library);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Add(library);
await unitOfWork.CommitAsync();
var person = new PersonBuilder("Joe Shmo").Build();
var chapter = new ChapterBuilder("1").WithPerson(person, PersonRole.Writer).Build();
@ -161,11 +156,11 @@ public class PersonHelperTests : AbstractDbTest
.Build())
.Build();
UnitOfWork.SeriesRepository.Add(series);
await UnitOfWork.CommitAsync();
unitOfWork.SeriesRepository.Add(series);
await unitOfWork.CommitAsync();
// Add same person as Editor
await PersonHelper.UpdateChapterPeopleAsync(chapter, new List<string> { "Joe Shmo" }, PersonRole.Editor, UnitOfWork);
await PersonHelper.UpdateChapterPeopleAsync(chapter, new List<string> { "Joe Shmo" }, PersonRole.Editor, unitOfWork);
// Ensure that the same person is assigned with two roles
var chapterPeople = chapter
@ -181,13 +176,13 @@ public class PersonHelperTests : AbstractDbTest
[Fact]
public async Task UpdateChapterPeopleAsync_MatchOnAlias_NoChanges()
{
await ResetDb();
var (unitOfWork, context, mapper) = await CreateDatabase();
var library = new LibraryBuilder("My Library")
.Build();
UnitOfWork.LibraryRepository.Add(library);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Add(library);
await unitOfWork.CommitAsync();
var person = new PersonBuilder("Joe Doe")
.WithAlias("Jonny Doe")
@ -204,21 +199,21 @@ public class PersonHelperTests : AbstractDbTest
.Build())
.Build();
UnitOfWork.SeriesRepository.Add(series);
await UnitOfWork.CommitAsync();
unitOfWork.SeriesRepository.Add(series);
await unitOfWork.CommitAsync();
// Add on Name
await PersonHelper.UpdateChapterPeopleAsync(chapter, new List<string> { "Joe Doe" }, PersonRole.Editor, UnitOfWork);
await UnitOfWork.CommitAsync();
await PersonHelper.UpdateChapterPeopleAsync(chapter, new List<string> { "Joe Doe" }, PersonRole.Editor, unitOfWork);
await unitOfWork.CommitAsync();
var allPeople = await UnitOfWork.PersonRepository.GetAllPeople();
var allPeople = await unitOfWork.PersonRepository.GetAllPeople();
Assert.Single(allPeople);
// Add on alias
await PersonHelper.UpdateChapterPeopleAsync(chapter, new List<string> { "Jonny Doe" }, PersonRole.Editor, UnitOfWork);
await UnitOfWork.CommitAsync();
await PersonHelper.UpdateChapterPeopleAsync(chapter, new List<string> { "Jonny Doe" }, PersonRole.Editor, unitOfWork);
await unitOfWork.CommitAsync();
allPeople = await UnitOfWork.PersonRepository.GetAllPeople();
allPeople = await unitOfWork.PersonRepository.GetAllPeople();
Assert.Single(allPeople);
}

View File

@ -2,28 +2,21 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using API.Data;
using API.DTOs.Metadata.Browse;
using API.Entities;
using API.Entities.Enums;
using API.Entities.Metadata;
using API.Helpers;
using API.Helpers.Builders;
using Polly;
using Xunit;
using Xunit.Abstractions;
namespace API.Tests.Repository;
public class GenreRepositoryTests : AbstractDbTest
public class GenreRepositoryTests(ITestOutputHelper outputHelper): AbstractDbTest(outputHelper)
{
private AppUser _fullAccess;
private AppUser _restrictedAccess;
private AppUser _restrictedAgeAccess;
protected override async Task ResetDb()
{
Context.Genre.RemoveRange(Context.Genre);
Context.Library.RemoveRange(Context.Library);
await Context.SaveChangesAsync();
}
private TestGenreSet CreateTestGenres()
{
@ -42,36 +35,35 @@ public class GenreRepositoryTests : AbstractDbTest
};
}
private async Task SeedDbWithGenres(TestGenreSet genres)
private async Task<(AppUser, AppUser, AppUser)> Setup(DataContext context, TestGenreSet genres)
{
await CreateTestUsers();
await AddGenresToContext(genres);
await CreateLibrariesWithGenres(genres);
await AssignLibrariesToUsers();
var fullAccess = new AppUserBuilder("amelia", "amelia@example.com").Build();
var restrictedAccess = new AppUserBuilder("mila", "mila@example.com").Build();
var restrictedAgeAccess = new AppUserBuilder("eva", "eva@example.com").Build();
restrictedAgeAccess.AgeRestriction = AgeRating.Teen;
restrictedAgeAccess.AgeRestrictionIncludeUnknowns = true;
context.Users.Add(fullAccess);
context.Users.Add(restrictedAccess);
context.Users.Add(restrictedAgeAccess);
await context.SaveChangesAsync();
await AddGenresToContext(context, genres);
await CreateLibrariesWithGenres(context, genres);
await AssignLibrariesToUsers(context, fullAccess, restrictedAccess, restrictedAgeAccess);
return (fullAccess, restrictedAccess, restrictedAgeAccess);
}
private async Task CreateTestUsers()
{
_fullAccess = new AppUserBuilder("amelia", "amelia@example.com").Build();
_restrictedAccess = new AppUserBuilder("mila", "mila@example.com").Build();
_restrictedAgeAccess = new AppUserBuilder("eva", "eva@example.com").Build();
_restrictedAgeAccess.AgeRestriction = AgeRating.Teen;
_restrictedAgeAccess.AgeRestrictionIncludeUnknowns = true;
Context.Users.Add(_fullAccess);
Context.Users.Add(_restrictedAccess);
Context.Users.Add(_restrictedAgeAccess);
await Context.SaveChangesAsync();
}
private async Task AddGenresToContext(TestGenreSet genres)
private async Task AddGenresToContext(DataContext context, TestGenreSet genres)
{
var allGenres = genres.GetAllGenres();
Context.Genre.AddRange(allGenres);
await Context.SaveChangesAsync();
context.Genre.AddRange(allGenres);
await context.SaveChangesAsync();
}
private async Task CreateLibrariesWithGenres(TestGenreSet genres)
private async Task CreateLibrariesWithGenres(DataContext context, TestGenreSet genres)
{
var lib0 = new LibraryBuilder("lib0")
.WithSeries(new SeriesBuilder("lib0-s0")
@ -120,22 +112,22 @@ public class GenreRepositoryTests : AbstractDbTest
.Build())
.Build();
Context.Library.Add(lib0);
Context.Library.Add(lib1);
await Context.SaveChangesAsync();
context.Library.Add(lib0);
context.Library.Add(lib1);
await context.SaveChangesAsync();
}
private async Task AssignLibrariesToUsers()
private async Task AssignLibrariesToUsers(DataContext context, AppUser fullAccess, AppUser restrictedAccess, AppUser restrictedAgeAccess)
{
var lib0 = Context.Library.First(l => l.Name == "lib0");
var lib1 = Context.Library.First(l => l.Name == "lib1");
var lib0 = context.Library.First(l => l.Name == "lib0");
var lib1 = context.Library.First(l => l.Name == "lib1");
_fullAccess.Libraries.Add(lib0);
_fullAccess.Libraries.Add(lib1);
_restrictedAccess.Libraries.Add(lib1);
_restrictedAgeAccess.Libraries.Add(lib1);
fullAccess.Libraries.Add(lib0);
fullAccess.Libraries.Add(lib1);
restrictedAccess.Libraries.Add(lib1);
restrictedAgeAccess.Libraries.Add(lib1);
await Context.SaveChangesAsync();
await context.SaveChangesAsync();
}
private static Predicate<BrowseGenreDto> ContainsGenreCheck(Genre genre)
@ -159,15 +151,14 @@ public class GenreRepositoryTests : AbstractDbTest
}
[Fact]
public async Task GetBrowseableGenre_FullAccess_ReturnsAllGenresWithCorrectCounts()
public async Task GetBrowseableGenrefullAccess_ReturnsAllGenresWithCorrectCounts()
{
// Arrange
await ResetDb();
var genres = CreateTestGenres();
await SeedDbWithGenres(genres);
var (unitOfWork, context, mapper) = await CreateDatabase();
var (fullAccess, restrictedAccess, restrictedAgeAccess) = await Setup(context, genres);
// Act
var fullAccessGenres = await UnitOfWork.GenreRepository.GetBrowseableGenre(_fullAccess.Id, new UserParams());
var fullAccessGenres = await unitOfWork.GenreRepository.GetBrowseableGenre(fullAccess.Id, new UserParams());
// Assert
Assert.Equal(genres.GetAllGenres().Count, fullAccessGenres.TotalCount);
@ -186,13 +177,12 @@ public class GenreRepositoryTests : AbstractDbTest
[Fact]
public async Task GetBrowseableGenre_RestrictedAccess_ReturnsOnlyAccessibleGenres()
{
// Arrange
await ResetDb();
var genres = CreateTestGenres();
await SeedDbWithGenres(genres);
var (unitOfWork, context, mapper) = await CreateDatabase();
var (fullAccess, restrictedAccess, restrictedAgeAccess) = await Setup(context, genres);
// Act
var restrictedAccessGenres = await UnitOfWork.GenreRepository.GetBrowseableGenre(_restrictedAccess.Id, new UserParams());
var restrictedAccessGenres = await unitOfWork.GenreRepository.GetBrowseableGenre(restrictedAccess.Id, new UserParams());
// Assert - Should see: 3 shared + 4 library 1 specific = 7 genres
Assert.Equal(7, restrictedAccessGenres.TotalCount);
@ -222,13 +212,12 @@ public class GenreRepositoryTests : AbstractDbTest
[Fact]
public async Task GetBrowseableGenre_RestrictedAgeAccess_FiltersAgeRestrictedContent()
{
// Arrange
await ResetDb();
var genres = CreateTestGenres();
await SeedDbWithGenres(genres);
var (unitOfWork, context, mapper) = await CreateDatabase();
var (fullAccess, restrictedAccess, restrictedAgeAccess) = await Setup(context, genres);
// Act
var restrictedAgeAccessGenres = await UnitOfWork.GenreRepository.GetBrowseableGenre(_restrictedAgeAccess.Id, new UserParams());
var restrictedAgeAccessGenres = await unitOfWork.GenreRepository.GetBrowseableGenre(restrictedAgeAccess.Id, new UserParams());
// Assert - Should see: 3 shared + 3 lib1 specific = 6 genres (age-restricted genre filtered out)
Assert.Equal(6, restrictedAgeAccessGenres.TotalCount);

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using API.Data;
using API.DTOs.Metadata.Browse;
using API.DTOs.Metadata.Browse.Requests;
using API.Entities;
@ -9,51 +10,44 @@ using API.Entities.Enums;
using API.Entities.Person;
using API.Helpers;
using API.Helpers.Builders;
using Polly;
using Xunit;
using Xunit.Abstractions;
namespace API.Tests.Repository;
public class PersonRepositoryTests : AbstractDbTest
public class PersonRepositoryTests(ITestOutputHelper outputHelper): AbstractDbTest(outputHelper)
{
private AppUser _fullAccess;
private AppUser _restrictedAccess;
private AppUser _restrictedAgeAccess;
protected override async Task ResetDb()
private async Task<(AppUser, AppUser, AppUser)> Setup(DataContext context)
{
Context.Person.RemoveRange(Context.Person.ToList());
Context.Library.RemoveRange(Context.Library.ToList());
Context.AppUser.RemoveRange(Context.AppUser.ToList());
await UnitOfWork.CommitAsync();
}
var fullAccess = new AppUserBuilder("amelia", "amelia@example.com").Build();
var restrictedAccess = new AppUserBuilder("mila", "mila@example.com").Build();
var restrictedAgeAccess = new AppUserBuilder("eva", "eva@example.com").Build();
restrictedAgeAccess.AgeRestriction = AgeRating.Teen;
restrictedAgeAccess.AgeRestrictionIncludeUnknowns = true;
private async Task SeedDb()
{
_fullAccess = new AppUserBuilder("amelia", "amelia@example.com").Build();
_restrictedAccess = new AppUserBuilder("mila", "mila@example.com").Build();
_restrictedAgeAccess = new AppUserBuilder("eva", "eva@example.com").Build();
_restrictedAgeAccess.AgeRestriction = AgeRating.Teen;
_restrictedAgeAccess.AgeRestrictionIncludeUnknowns = true;
Context.AppUser.Add(_fullAccess);
Context.AppUser.Add(_restrictedAccess);
Context.AppUser.Add(_restrictedAgeAccess);
await Context.SaveChangesAsync();
context.AppUser.Add(fullAccess);
context.AppUser.Add(restrictedAccess);
context.AppUser.Add(restrictedAgeAccess);
await context.SaveChangesAsync();
var people = CreateTestPeople();
Context.Person.AddRange(people);
await Context.SaveChangesAsync();
context.Person.AddRange(people);
await context.SaveChangesAsync();
var libraries = CreateTestLibraries(people);
Context.Library.AddRange(libraries);
await Context.SaveChangesAsync();
var libraries = CreateTestLibraries(context, people);
context.Library.AddRange(libraries);
await context.SaveChangesAsync();
_fullAccess.Libraries.Add(libraries[0]); // lib0
_fullAccess.Libraries.Add(libraries[1]); // lib1
_restrictedAccess.Libraries.Add(libraries[1]); // lib1 only
_restrictedAgeAccess.Libraries.Add(libraries[1]); // lib1 only
fullAccess.Libraries.Add(libraries[0]); // lib0
fullAccess.Libraries.Add(libraries[1]); // lib1
restrictedAccess.Libraries.Add(libraries[1]); // lib1 only
restrictedAgeAccess.Libraries.Add(libraries[1]); // lib1 only
await Context.SaveChangesAsync();
await context.SaveChangesAsync();
return (fullAccess, restrictedAccess, restrictedAgeAccess);
}
private static List<Person> CreateTestPeople()
@ -73,7 +67,7 @@ public class PersonRepositoryTests : AbstractDbTest
};
}
private static List<Library> CreateTestLibraries(List<Person> people)
private static List<Library> CreateTestLibraries(DataContext context, List<Person> people)
{
var lib0 = new LibraryBuilder("lib0")
.WithSeries(new SeriesBuilder("lib0-s0")
@ -158,9 +152,9 @@ public class PersonRepositoryTests : AbstractDbTest
return people.First(p => p.Name == name);
}
private Person GetPersonByName(string name)
private Person GetPersonByName(DataContext context, string name)
{
return Context.Person.First(p => p.Name == name);
return context.Person.First(p => p.Name == name);
}
private static Predicate<BrowsePersonDto> ContainsPersonCheck(Person person)
@ -171,18 +165,18 @@ public class PersonRepositoryTests : AbstractDbTest
[Fact]
public async Task GetBrowsePersonDtos()
{
await ResetDb();
await SeedDb();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (fullAccess, restrictedAccess, restrictedAgeAccess) = await Setup(context);
// Get people from database for assertions
var sharedSeriesChaptersPerson = GetPersonByName("Shared Series Chapter Person");
var lib0SeriesPerson = GetPersonByName("Lib0 Series Person");
var lib1SeriesPerson = GetPersonByName("Lib1 Series Person");
var lib1ChapterAgePerson = GetPersonByName("Lib1 Chapter Age Person");
var allPeople = Context.Person.ToList();
var sharedSeriesChaptersPerson = GetPersonByName(context, "Shared Series Chapter Person");
var lib0SeriesPerson = GetPersonByName(context, "Lib0 Series Person");
var lib1SeriesPerson = GetPersonByName(context, "Lib1 Series Person");
var lib1ChapterAgePerson = GetPersonByName(context, "Lib1 Chapter Age Person");
var allPeople = context.Person.ToList();
var fullAccessPeople =
await UnitOfWork.PersonRepository.GetBrowsePersonDtos(_fullAccess.Id, new BrowsePersonFilterDto(),
await unitOfWork.PersonRepository.GetBrowsePersonDtos(fullAccess.Id, new BrowsePersonFilterDto(),
new UserParams());
Assert.Equal(allPeople.Count, fullAccessPeople.TotalCount);
@ -199,18 +193,18 @@ public class PersonRepositoryTests : AbstractDbTest
Assert.Equal(2, fullAccessPeople.First(dto => dto.Id == lib1SeriesPerson.Id).SeriesCount);
var restrictedAccessPeople =
await UnitOfWork.PersonRepository.GetBrowsePersonDtos(_restrictedAccess.Id, new BrowsePersonFilterDto(),
await unitOfWork.PersonRepository.GetBrowsePersonDtos(restrictedAccess.Id, new BrowsePersonFilterDto(),
new UserParams());
Assert.Equal(7, restrictedAccessPeople.TotalCount);
Assert.Contains(restrictedAccessPeople, ContainsPersonCheck(GetPersonByName("Shared Series Chapter Person")));
Assert.Contains(restrictedAccessPeople, ContainsPersonCheck(GetPersonByName("Shared Series Person")));
Assert.Contains(restrictedAccessPeople, ContainsPersonCheck(GetPersonByName("Shared Chapters Person")));
Assert.Contains(restrictedAccessPeople, ContainsPersonCheck(GetPersonByName("Lib1 Series Chapter Person")));
Assert.Contains(restrictedAccessPeople, ContainsPersonCheck(GetPersonByName("Lib1 Series Person")));
Assert.Contains(restrictedAccessPeople, ContainsPersonCheck(GetPersonByName("Lib1 Chapters Person")));
Assert.Contains(restrictedAccessPeople, ContainsPersonCheck(GetPersonByName("Lib1 Chapter Age Person")));
Assert.Contains(restrictedAccessPeople, ContainsPersonCheck(GetPersonByName(context, "Shared Series Chapter Person")));
Assert.Contains(restrictedAccessPeople, ContainsPersonCheck(GetPersonByName(context, "Shared Series Person")));
Assert.Contains(restrictedAccessPeople, ContainsPersonCheck(GetPersonByName(context, "Shared Chapters Person")));
Assert.Contains(restrictedAccessPeople, ContainsPersonCheck(GetPersonByName(context, "Lib1 Series Chapter Person")));
Assert.Contains(restrictedAccessPeople, ContainsPersonCheck(GetPersonByName(context, "Lib1 Series Person")));
Assert.Contains(restrictedAccessPeople, ContainsPersonCheck(GetPersonByName(context, "Lib1 Chapters Person")));
Assert.Contains(restrictedAccessPeople, ContainsPersonCheck(GetPersonByName(context, "Lib1 Chapter Age Person")));
// 2 series in lib1, no series in lib0
Assert.Equal(2, restrictedAccessPeople.First(dto => dto.Id == sharedSeriesChaptersPerson.Id).SeriesCount);
@ -219,7 +213,7 @@ public class PersonRepositoryTests : AbstractDbTest
// 2 series in lib1
Assert.Equal(2, restrictedAccessPeople.First(dto => dto.Id == lib1SeriesPerson.Id).SeriesCount);
var restrictedAgeAccessPeople = await UnitOfWork.PersonRepository.GetBrowsePersonDtos(_restrictedAgeAccess.Id,
var restrictedAgeAccessPeople = await unitOfWork.PersonRepository.GetBrowsePersonDtos(restrictedAgeAccess.Id,
new BrowsePersonFilterDto(), new UserParams());
// Note: There is a potential bug here where a person in a different chapter of an age restricted series will show up
@ -232,30 +226,30 @@ public class PersonRepositoryTests : AbstractDbTest
[Fact]
public async Task GetRolesForPersonByName()
{
await ResetDb();
await SeedDb();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (fullAccess, restrictedAccess, restrictedAgeAccess) = await Setup(context);
var sharedSeriesPerson = GetPersonByName("Shared Series Person");
var sharedChaptersPerson = GetPersonByName("Shared Chapters Person");
var lib1ChapterAgePerson = GetPersonByName("Lib1 Chapter Age Person");
var sharedSeriesPerson = GetPersonByName(context, "Shared Series Person");
var sharedChaptersPerson = GetPersonByName(context, "Shared Chapters Person");
var lib1ChapterAgePerson = GetPersonByName(context, "Lib1 Chapter Age Person");
var sharedSeriesRoles = await UnitOfWork.PersonRepository.GetRolesForPersonByName(sharedSeriesPerson.Id, _fullAccess.Id);
var chapterRoles = await UnitOfWork.PersonRepository.GetRolesForPersonByName(sharedChaptersPerson.Id, _fullAccess.Id);
var ageChapterRoles = await UnitOfWork.PersonRepository.GetRolesForPersonByName(lib1ChapterAgePerson.Id, _fullAccess.Id);
var sharedSeriesRoles = await unitOfWork.PersonRepository.GetRolesForPersonByName(sharedSeriesPerson.Id, fullAccess.Id);
var chapterRoles = await unitOfWork.PersonRepository.GetRolesForPersonByName(sharedChaptersPerson.Id, fullAccess.Id);
var ageChapterRoles = await unitOfWork.PersonRepository.GetRolesForPersonByName(lib1ChapterAgePerson.Id, fullAccess.Id);
Assert.Equal(3, sharedSeriesRoles.Count());
Assert.Equal(6, chapterRoles.Count());
Assert.Single(ageChapterRoles);
var restrictedRoles = await UnitOfWork.PersonRepository.GetRolesForPersonByName(sharedSeriesPerson.Id, _restrictedAccess.Id);
var restrictedChapterRoles = await UnitOfWork.PersonRepository.GetRolesForPersonByName(sharedChaptersPerson.Id, _restrictedAccess.Id);
var restrictedAgePersonChapterRoles = await UnitOfWork.PersonRepository.GetRolesForPersonByName(lib1ChapterAgePerson.Id, _restrictedAccess.Id);
var restrictedRoles = await unitOfWork.PersonRepository.GetRolesForPersonByName(sharedSeriesPerson.Id, restrictedAccess.Id);
var restrictedChapterRoles = await unitOfWork.PersonRepository.GetRolesForPersonByName(sharedChaptersPerson.Id, restrictedAccess.Id);
var restrictedAgePersonChapterRoles = await unitOfWork.PersonRepository.GetRolesForPersonByName(lib1ChapterAgePerson.Id, restrictedAccess.Id);
Assert.Equal(2, restrictedRoles.Count());
Assert.Equal(4, restrictedChapterRoles.Count());
Assert.Single(restrictedAgePersonChapterRoles);
var restrictedAgeRoles = await UnitOfWork.PersonRepository.GetRolesForPersonByName(sharedSeriesPerson.Id, _restrictedAgeAccess.Id);
var restrictedAgeChapterRoles = await UnitOfWork.PersonRepository.GetRolesForPersonByName(sharedChaptersPerson.Id, _restrictedAgeAccess.Id);
var restrictedAgeAgePersonChapterRoles = await UnitOfWork.PersonRepository.GetRolesForPersonByName(lib1ChapterAgePerson.Id, _restrictedAgeAccess.Id);
var restrictedAgeRoles = await unitOfWork.PersonRepository.GetRolesForPersonByName(sharedSeriesPerson.Id, restrictedAgeAccess.Id);
var restrictedAgeChapterRoles = await unitOfWork.PersonRepository.GetRolesForPersonByName(sharedChaptersPerson.Id, restrictedAgeAccess.Id);
var restrictedAgeAgePersonChapterRoles = await unitOfWork.PersonRepository.GetRolesForPersonByName(lib1ChapterAgePerson.Id, restrictedAgeAccess.Id);
Assert.Single(restrictedAgeRoles);
Assert.Equal(2, restrictedAgeChapterRoles.Count());
// Note: There is a potential bug here where a person in a different chapter of an age restricted series will show up
@ -265,76 +259,76 @@ public class PersonRepositoryTests : AbstractDbTest
[Fact]
public async Task GetPersonDtoByName()
{
await ResetDb();
await SeedDb();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (fullAccess, restrictedAccess, restrictedAgeAccess) = await Setup(context);
var allPeople = Context.Person.ToList();
var allPeople = context.Person.ToList();
foreach (var person in allPeople)
{
Assert.NotNull(await UnitOfWork.PersonRepository.GetPersonDtoByName(person.Name, _fullAccess.Id));
Assert.NotNull(await unitOfWork.PersonRepository.GetPersonDtoByName(person.Name, fullAccess.Id));
}
Assert.Null(await UnitOfWork.PersonRepository.GetPersonDtoByName("Lib0 Chapters Person", _restrictedAccess.Id));
Assert.NotNull(await UnitOfWork.PersonRepository.GetPersonDtoByName("Shared Series Person", _restrictedAccess.Id));
Assert.NotNull(await UnitOfWork.PersonRepository.GetPersonDtoByName("Lib1 Series Person", _restrictedAccess.Id));
Assert.Null(await unitOfWork.PersonRepository.GetPersonDtoByName("Lib0 Chapters Person", restrictedAccess.Id));
Assert.NotNull(await unitOfWork.PersonRepository.GetPersonDtoByName("Shared Series Person", restrictedAccess.Id));
Assert.NotNull(await unitOfWork.PersonRepository.GetPersonDtoByName("Lib1 Series Person", restrictedAccess.Id));
Assert.Null(await UnitOfWork.PersonRepository.GetPersonDtoByName("Lib0 Chapters Person", _restrictedAgeAccess.Id));
Assert.NotNull(await UnitOfWork.PersonRepository.GetPersonDtoByName("Lib1 Series Person", _restrictedAgeAccess.Id));
Assert.Null(await unitOfWork.PersonRepository.GetPersonDtoByName("Lib0 Chapters Person", restrictedAgeAccess.Id));
Assert.NotNull(await unitOfWork.PersonRepository.GetPersonDtoByName("Lib1 Series Person", restrictedAgeAccess.Id));
// Note: There is a potential bug here where a person in a different chapter of an age restricted series will show up
Assert.Null(await UnitOfWork.PersonRepository.GetPersonDtoByName("Lib1 Chapter Age Person", _restrictedAgeAccess.Id));
Assert.Null(await unitOfWork.PersonRepository.GetPersonDtoByName("Lib1 Chapter Age Person", restrictedAgeAccess.Id));
}
[Fact]
public async Task GetSeriesKnownFor()
{
await ResetDb();
await SeedDb();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (fullAccess, restrictedAccess, restrictedAgeAccess) = await Setup(context);
var sharedSeriesPerson = GetPersonByName("Shared Series Person");
var lib1SeriesPerson = GetPersonByName("Lib1 Series Person");
var sharedSeriesPerson = GetPersonByName(context, "Shared Series Person");
var lib1SeriesPerson = GetPersonByName(context, "Lib1 Series Person");
var series = await UnitOfWork.PersonRepository.GetSeriesKnownFor(sharedSeriesPerson.Id, _fullAccess.Id);
var series = await unitOfWork.PersonRepository.GetSeriesKnownFor(sharedSeriesPerson.Id, fullAccess.Id);
Assert.Equal(3, series.Count());
series = await UnitOfWork.PersonRepository.GetSeriesKnownFor(sharedSeriesPerson.Id, _restrictedAccess.Id);
series = await unitOfWork.PersonRepository.GetSeriesKnownFor(sharedSeriesPerson.Id, restrictedAccess.Id);
Assert.Equal(2, series.Count());
series = await UnitOfWork.PersonRepository.GetSeriesKnownFor(sharedSeriesPerson.Id, _restrictedAgeAccess.Id);
series = await unitOfWork.PersonRepository.GetSeriesKnownFor(sharedSeriesPerson.Id, restrictedAgeAccess.Id);
Assert.Single(series);
series = await UnitOfWork.PersonRepository.GetSeriesKnownFor(lib1SeriesPerson.Id, _restrictedAgeAccess.Id);
series = await unitOfWork.PersonRepository.GetSeriesKnownFor(lib1SeriesPerson.Id, restrictedAgeAccess.Id);
Assert.Single(series);
}
[Fact]
public async Task GetChaptersForPersonByRole()
{
await ResetDb();
await SeedDb();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (fullAccess, restrictedAccess, restrictedAgeAccess) = await Setup(context);
var sharedChaptersPerson = GetPersonByName("Shared Chapters Person");
var sharedChaptersPerson = GetPersonByName(context, "Shared Chapters Person");
// Lib0
var chapters = await UnitOfWork.PersonRepository.GetChaptersForPersonByRole(sharedChaptersPerson.Id, _fullAccess.Id, PersonRole.Colorist);
var restrictedChapters = await UnitOfWork.PersonRepository.GetChaptersForPersonByRole(sharedChaptersPerson.Id, _restrictedAccess.Id, PersonRole.Colorist);
var restrictedAgeChapters = await UnitOfWork.PersonRepository.GetChaptersForPersonByRole(sharedChaptersPerson.Id, _restrictedAgeAccess.Id, PersonRole.Colorist);
var chapters = await unitOfWork.PersonRepository.GetChaptersForPersonByRole(sharedChaptersPerson.Id, fullAccess.Id, PersonRole.Colorist);
var restrictedChapters = await unitOfWork.PersonRepository.GetChaptersForPersonByRole(sharedChaptersPerson.Id, restrictedAccess.Id, PersonRole.Colorist);
var restrictedAgeChapters = await unitOfWork.PersonRepository.GetChaptersForPersonByRole(sharedChaptersPerson.Id, restrictedAgeAccess.Id, PersonRole.Colorist);
Assert.Single(chapters);
Assert.Empty(restrictedChapters);
Assert.Empty(restrictedAgeChapters);
// Lib1 - age restricted series
chapters = await UnitOfWork.PersonRepository.GetChaptersForPersonByRole(sharedChaptersPerson.Id, _fullAccess.Id, PersonRole.Imprint);
restrictedChapters = await UnitOfWork.PersonRepository.GetChaptersForPersonByRole(sharedChaptersPerson.Id, _restrictedAccess.Id, PersonRole.Imprint);
restrictedAgeChapters = await UnitOfWork.PersonRepository.GetChaptersForPersonByRole(sharedChaptersPerson.Id, _restrictedAgeAccess.Id, PersonRole.Imprint);
chapters = await unitOfWork.PersonRepository.GetChaptersForPersonByRole(sharedChaptersPerson.Id, fullAccess.Id, PersonRole.Imprint);
restrictedChapters = await unitOfWork.PersonRepository.GetChaptersForPersonByRole(sharedChaptersPerson.Id, restrictedAccess.Id, PersonRole.Imprint);
restrictedAgeChapters = await unitOfWork.PersonRepository.GetChaptersForPersonByRole(sharedChaptersPerson.Id, restrictedAgeAccess.Id, PersonRole.Imprint);
Assert.Single(chapters);
Assert.Single(restrictedChapters);
Assert.Empty(restrictedAgeChapters);
// Lib1 - not age restricted series
chapters = await UnitOfWork.PersonRepository.GetChaptersForPersonByRole(sharedChaptersPerson.Id, _fullAccess.Id, PersonRole.Team);
restrictedChapters = await UnitOfWork.PersonRepository.GetChaptersForPersonByRole(sharedChaptersPerson.Id, _restrictedAccess.Id, PersonRole.Team);
restrictedAgeChapters = await UnitOfWork.PersonRepository.GetChaptersForPersonByRole(sharedChaptersPerson.Id, _restrictedAgeAccess.Id, PersonRole.Team);
chapters = await unitOfWork.PersonRepository.GetChaptersForPersonByRole(sharedChaptersPerson.Id, fullAccess.Id, PersonRole.Team);
restrictedChapters = await unitOfWork.PersonRepository.GetChaptersForPersonByRole(sharedChaptersPerson.Id, restrictedAccess.Id, PersonRole.Team);
restrictedAgeChapters = await unitOfWork.PersonRepository.GetChaptersForPersonByRole(sharedChaptersPerson.Id, restrictedAgeAccess.Id, PersonRole.Team);
Assert.Single(chapters);
Assert.Single(restrictedChapters);
Assert.Single(restrictedAgeChapters);

View File

@ -2,28 +2,21 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using API.Data;
using API.DTOs.Metadata.Browse;
using API.Entities;
using API.Entities.Enums;
using API.Entities.Metadata;
using API.Helpers;
using API.Helpers.Builders;
using Polly;
using Xunit;
using Xunit.Abstractions;
namespace API.Tests.Repository;
public class TagRepositoryTests : AbstractDbTest
public class TagRepositoryTests(ITestOutputHelper outputHelper): AbstractDbTest(outputHelper)
{
private AppUser _fullAccess;
private AppUser _restrictedAccess;
private AppUser _restrictedAgeAccess;
protected override async Task ResetDb()
{
Context.Tag.RemoveRange(Context.Tag);
Context.Library.RemoveRange(Context.Library);
await Context.SaveChangesAsync();
}
private TestTagSet CreateTestTags()
{
@ -42,36 +35,46 @@ public class TagRepositoryTests : AbstractDbTest
};
}
private async Task SeedDbWithTags(TestTagSet tags)
private async Task<(AppUser, AppUser, AppUser)> SeedDbWithTags(DataContext context, TestTagSet tags)
{
await CreateTestUsers();
await AddTagsToContext(tags);
await CreateLibrariesWithTags(tags);
await AssignLibrariesToUsers();
await AddTagsTocontext(context, tags);
await CreateLibrariesWithTags(context, tags);
return await CreateTestUsers(context);
}
private async Task CreateTestUsers()
private async Task<(AppUser, AppUser, AppUser)> CreateTestUsers(DataContext context)
{
_fullAccess = new AppUserBuilder("amelia", "amelia@example.com").Build();
_restrictedAccess = new AppUserBuilder("mila", "mila@example.com").Build();
_restrictedAgeAccess = new AppUserBuilder("eva", "eva@example.com").Build();
_restrictedAgeAccess.AgeRestriction = AgeRating.Teen;
_restrictedAgeAccess.AgeRestrictionIncludeUnknowns = true;
var fullAccess = new AppUserBuilder("amelia", "amelia@example.com").Build();
var restrictedAccess = new AppUserBuilder("mila", "mila@example.com").Build();
var restrictedAgeAccess = new AppUserBuilder("eva", "eva@example.com").Build();
restrictedAgeAccess.AgeRestriction = AgeRating.Teen;
restrictedAgeAccess.AgeRestrictionIncludeUnknowns = true;
Context.Users.Add(_fullAccess);
Context.Users.Add(_restrictedAccess);
Context.Users.Add(_restrictedAgeAccess);
await Context.SaveChangesAsync();
context.Users.Add(fullAccess);
context.Users.Add(restrictedAccess);
context.Users.Add(restrictedAgeAccess);
var lib0 = context.Library.First(l => l.Name == "lib0");
var lib1 = context.Library.First(l => l.Name == "lib1");
fullAccess.Libraries.Add(lib0);
fullAccess.Libraries.Add(lib1);
restrictedAccess.Libraries.Add(lib1);
restrictedAgeAccess.Libraries.Add(lib1);
await context.SaveChangesAsync();
return (fullAccess, restrictedAccess, restrictedAgeAccess);
}
private async Task AddTagsToContext(TestTagSet tags)
private async Task AddTagsTocontext(DataContext context, TestTagSet tags)
{
var allTags = tags.GetAllTags();
Context.Tag.AddRange(allTags);
await Context.SaveChangesAsync();
context.Tag.AddRange(allTags);
await context.SaveChangesAsync();
}
private async Task CreateLibrariesWithTags(TestTagSet tags)
private async Task CreateLibrariesWithTags(DataContext context, TestTagSet tags)
{
var lib0 = new LibraryBuilder("lib0")
.WithSeries(new SeriesBuilder("lib0-s0")
@ -122,22 +125,9 @@ public class TagRepositoryTests : AbstractDbTest
.Build())
.Build();
Context.Library.Add(lib0);
Context.Library.Add(lib1);
await Context.SaveChangesAsync();
}
private async Task AssignLibrariesToUsers()
{
var lib0 = Context.Library.First(l => l.Name == "lib0");
var lib1 = Context.Library.First(l => l.Name == "lib1");
_fullAccess.Libraries.Add(lib0);
_fullAccess.Libraries.Add(lib1);
_restrictedAccess.Libraries.Add(lib1);
_restrictedAgeAccess.Libraries.Add(lib1);
await Context.SaveChangesAsync();
context.Library.Add(lib0);
context.Library.Add(lib1);
await context.SaveChangesAsync();
}
private static Predicate<BrowseTagDto> ContainsTagCheck(Tag tag)
@ -163,13 +153,12 @@ public class TagRepositoryTests : AbstractDbTest
[Fact]
public async Task GetBrowseableTag_FullAccess_ReturnsAllTagsWithCorrectCounts()
{
// Arrange
await ResetDb();
var (unitOfWork, context, _) = await CreateDatabase();
var tags = CreateTestTags();
await SeedDbWithTags(tags);
var (fullAccess, _, _) = await SeedDbWithTags(context, tags);
// Act
var fullAccessTags = await UnitOfWork.TagRepository.GetBrowseableTag(_fullAccess.Id, new UserParams());
var fullAccessTags = await unitOfWork.TagRepository.GetBrowseableTag(fullAccess.Id, new UserParams());
// Assert
Assert.Equal(tags.GetAllTags().Count, fullAccessTags.TotalCount);
@ -188,13 +177,12 @@ public class TagRepositoryTests : AbstractDbTest
[Fact]
public async Task GetBrowseableTag_RestrictedAccess_ReturnsOnlyAccessibleTags()
{
// Arrange
await ResetDb();
var (unitOfWork, context, _) = await CreateDatabase();
var tags = CreateTestTags();
await SeedDbWithTags(tags);
var (_, restrictedAccess, _) = await SeedDbWithTags(context, tags);
// Act
var restrictedAccessTags = await UnitOfWork.TagRepository.GetBrowseableTag(_restrictedAccess.Id, new UserParams());
var restrictedAccessTags = await unitOfWork.TagRepository.GetBrowseableTag(restrictedAccess.Id, new UserParams());
// Assert - Should see: 3 shared + 4 library 1 specific = 7 tags
Assert.Equal(7, restrictedAccessTags.TotalCount);
@ -223,13 +211,12 @@ public class TagRepositoryTests : AbstractDbTest
[Fact]
public async Task GetBrowseableTag_RestrictedAgeAccess_FiltersAgeRestrictedContent()
{
// Arrange
await ResetDb();
var (unitOfWork, context, _) = await CreateDatabase();
var tags = CreateTestTags();
await SeedDbWithTags(tags);
var (_, _, restrictedAgeAccess) = await SeedDbWithTags(context, tags);
// Act
var restrictedAgeAccessTags = await UnitOfWork.TagRepository.GetBrowseableTag(_restrictedAgeAccess.Id, new UserParams());
var restrictedAgeAccessTags = await unitOfWork.TagRepository.GetBrowseableTag(restrictedAgeAccess.Id, new UserParams());
// Assert - Should see: 3 shared + 3 lib1 specific = 6 tags (age-restricted tag filtered out)
Assert.Equal(6, restrictedAgeAccessTags.TotalCount);

View File

@ -10,6 +10,7 @@ using API.Extensions;
using API.Helpers.Builders;
using API.Services;
using API.Services.Tasks.Scanner;
using AutoMapper;
using Kavita.Common;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
@ -17,10 +18,11 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NSubstitute;
using Xunit;
using Xunit.Abstractions;
namespace API.Tests.Services;
public class AccountServiceTests: AbstractDbTest
public class AccountServiceTests(ITestOutputHelper outputHelper): AbstractDbTest(outputHelper)
{
[Theory]
@ -30,8 +32,8 @@ public class AccountServiceTests: AbstractDbTest
[InlineData("Kraft Lawrance", false)]
public async Task ValidateUsername_Regex(string username, bool valid)
{
await ResetDb();
var (_, accountService, _, _) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (_, accountService, _, _) = await Setup(unitOfWork, context, mapper);
Assert.Equal(valid, !(await accountService.ValidateUsername(username)).Any());
}
@ -39,10 +41,10 @@ public class AccountServiceTests: AbstractDbTest
[Fact]
public async Task ChangeIdentityProvider_Throws_WhenDefaultAdminUser()
{
await ResetDb();
var (_, accountService, _, _) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (_, accountService, _, _) = await Setup(unitOfWork, context, mapper);
var defaultAdmin = await UnitOfWork.UserRepository.GetDefaultAdminUser();
var defaultAdmin = await unitOfWork.UserRepository.GetDefaultAdminUser();
await Assert.ThrowsAsync<KavitaException>(() =>
accountService.ChangeIdentityProvider(defaultAdmin.Id, defaultAdmin, IdentityProvider.Kavita));
@ -51,14 +53,14 @@ public class AccountServiceTests: AbstractDbTest
[Fact]
public async Task ChangeIdentityProvider_Succeeds_WhenSyncUserSettingsIsFalse()
{
await ResetDb();
var (user, accountService, _, _) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (user, accountService, _, _) = await Setup(unitOfWork, context, mapper);
var result = await accountService.ChangeIdentityProvider(user.Id, user, IdentityProvider.Kavita);
Assert.False(result);
var updated = await UnitOfWork.UserRepository.GetUserByIdAsync(user.Id);
var updated = await unitOfWork.UserRepository.GetUserByIdAsync(user.Id);
Assert.NotNull(updated);
Assert.Equal(IdentityProvider.Kavita, updated.IdentityProvider);
}
@ -66,13 +68,13 @@ public class AccountServiceTests: AbstractDbTest
[Fact]
public async Task ChangeIdentityProvider_Throws_WhenUserIsOidcManaged_AndNoChange()
{
await ResetDb();
var (user, accountService, _, settingsService) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (user, accountService, _, settingsService) = await Setup(unitOfWork, context, mapper);
user.IdentityProvider = IdentityProvider.OpenIdConnect;
await UnitOfWork.CommitAsync();
await unitOfWork.CommitAsync();
var settings = await UnitOfWork.SettingsRepository.GetSettingsDtoAsync();
var settings = await unitOfWork.SettingsRepository.GetSettingsDtoAsync();
settings.OidcConfig.SyncUserSettings = true;
await settingsService.UpdateSettings(settings);
@ -83,13 +85,13 @@ public class AccountServiceTests: AbstractDbTest
[Fact]
public async Task ChangeIdentityProvider_Succeeds_WhenSyncUserSettingsTrue_AndChangeIsAllowed()
{
await ResetDb();
var (user, accountService, _, settingsService) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (user, accountService, _, settingsService) = await Setup(unitOfWork, context, mapper);
user.IdentityProvider = IdentityProvider.OpenIdConnect;
await UnitOfWork.CommitAsync();
await unitOfWork.CommitAsync();
var settings = await UnitOfWork.SettingsRepository.GetSettingsDtoAsync();
var settings = await unitOfWork.SettingsRepository.GetSettingsDtoAsync();
settings.OidcConfig.SyncUserSettings = true;
await settingsService.UpdateSettings(settings);
@ -97,7 +99,7 @@ public class AccountServiceTests: AbstractDbTest
Assert.False(result);
var updated = await UnitOfWork.UserRepository.GetUserByIdAsync(user.Id);
var updated = await unitOfWork.UserRepository.GetUserByIdAsync(user.Id);
Assert.NotNull(updated);
Assert.Equal(IdentityProvider.Kavita, updated.IdentityProvider);
}
@ -105,13 +107,13 @@ public class AccountServiceTests: AbstractDbTest
[Fact]
public async Task ChangeIdentityProvider_ReturnsTrue_WhenChangedToOidc()
{
await ResetDb();
var (user, accountService, _, settingsService) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (user, accountService, _, settingsService) = await Setup(unitOfWork, context, mapper);
user.IdentityProvider = IdentityProvider.Kavita;
await UnitOfWork.CommitAsync();
await unitOfWork.CommitAsync();
var settings = await UnitOfWork.SettingsRepository.GetSettingsDtoAsync();
var settings = await unitOfWork.SettingsRepository.GetSettingsDtoAsync();
settings.OidcConfig.SyncUserSettings = true;
await settingsService.UpdateSettings(settings);
@ -119,7 +121,7 @@ public class AccountServiceTests: AbstractDbTest
Assert.True(result);
var updated = await UnitOfWork.UserRepository.GetUserByIdAsync(user.Id);
var updated = await unitOfWork.UserRepository.GetUserByIdAsync(user.Id);
Assert.NotNull(updated);
Assert.Equal(IdentityProvider.OpenIdConnect, updated.IdentityProvider);
}
@ -127,40 +129,43 @@ public class AccountServiceTests: AbstractDbTest
[Fact]
public async Task UpdateLibrariesForUser_GrantsAccessToAllLibraries_WhenAdmin()
{
await ResetDb();
var (user, accountService, _, _) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (user, accountService, _, _) = await Setup(unitOfWork, context, mapper);;
var mangaLib = new LibraryBuilder("Manga", LibraryType.Manga).Build();
var lightNovelsLib = new LibraryBuilder("Light Novels", LibraryType.LightNovel).Build();
UnitOfWork.LibraryRepository.Add(mangaLib);
UnitOfWork.LibraryRepository.Add(lightNovelsLib);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Add(mangaLib);
unitOfWork.LibraryRepository.Add(lightNovelsLib);
await unitOfWork.CommitAsync();
var allLibs = await unitOfWork.LibraryRepository.GetLibrariesAsync();
var maxCount = allLibs.Count();
await accountService.UpdateLibrariesForUser(user, new List<int>(), hasAdminRole: true);
await UnitOfWork.CommitAsync();
await unitOfWork.CommitAsync();
var userLibs = await UnitOfWork.LibraryRepository.GetLibrariesForUserIdAsync(user.Id);
Assert.Equal(2, userLibs.Count());
var userLibs = await unitOfWork.LibraryRepository.GetLibrariesForUserIdAsync(user.Id);
Assert.Equal(maxCount, userLibs.Count());
}
[Fact]
public async Task UpdateLibrariesForUser_GrantsAccessToSelectedLibraries_WhenNotAdmin()
{
await ResetDb();
var (user, accountService, _, _) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (user, accountService, _, _) = await Setup(unitOfWork, context, mapper);;
var mangaLib = new LibraryBuilder("Manga", LibraryType.Manga).Build();
var lightNovelsLib = new LibraryBuilder("Light Novels", LibraryType.LightNovel).Build();
UnitOfWork.LibraryRepository.Add(mangaLib);
UnitOfWork.LibraryRepository.Add(lightNovelsLib);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Add(mangaLib);
unitOfWork.LibraryRepository.Add(lightNovelsLib);
await unitOfWork.CommitAsync();
await accountService.UpdateLibrariesForUser(user, new List<int> { mangaLib.Id }, hasAdminRole: false);
await UnitOfWork.CommitAsync();
await unitOfWork.CommitAsync();
var userLibs = (await UnitOfWork.LibraryRepository.GetLibrariesForUserIdAsync(user.Id)).ToList();
var userLibs = (await unitOfWork.LibraryRepository.GetLibrariesForUserIdAsync(user.Id)).ToList();
Assert.Single(userLibs);
Assert.Equal(mangaLib.Id, userLibs.First().Id);
}
@ -168,28 +173,28 @@ public class AccountServiceTests: AbstractDbTest
[Fact]
public async Task UpdateLibrariesForUser_RemovesAccessFromUnselectedLibraries_WhenNotAdmin()
{
await ResetDb();
var (user, accountService, _, _) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (user, accountService, _, _) = await Setup(unitOfWork, context, mapper);;
var mangaLib = new LibraryBuilder("Manga", LibraryType.Manga).Build();
var lightNovelsLib = new LibraryBuilder("Light Novels", LibraryType.LightNovel).Build();
UnitOfWork.LibraryRepository.Add(mangaLib);
UnitOfWork.LibraryRepository.Add(lightNovelsLib);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Add(mangaLib);
unitOfWork.LibraryRepository.Add(lightNovelsLib);
await unitOfWork.CommitAsync();
// Grant access to both libraries
await accountService.UpdateLibrariesForUser(user, new List<int> { mangaLib.Id, lightNovelsLib.Id }, hasAdminRole: false);
await UnitOfWork.CommitAsync();
await unitOfWork.CommitAsync();
var userLibs = (await UnitOfWork.LibraryRepository.GetLibrariesForUserIdAsync(user.Id)).ToList();
var userLibs = (await unitOfWork.LibraryRepository.GetLibrariesForUserIdAsync(user.Id)).ToList();
Assert.Equal(2, userLibs.Count);
// Now restrict access to only light novels
await accountService.UpdateLibrariesForUser(user, new List<int> { lightNovelsLib.Id }, hasAdminRole: false);
await UnitOfWork.CommitAsync();
await unitOfWork.CommitAsync();
userLibs = (await UnitOfWork.LibraryRepository.GetLibrariesForUserIdAsync(user.Id)).ToList();
userLibs = (await unitOfWork.LibraryRepository.GetLibrariesForUserIdAsync(user.Id)).ToList();
Assert.Single(userLibs);
Assert.Equal(lightNovelsLib.Id, userLibs.First().Id);
}
@ -197,34 +202,34 @@ public class AccountServiceTests: AbstractDbTest
[Fact]
public async Task UpdateLibrariesForUser_GrantsNoLibraries_WhenNoneSelected_AndNotAdmin()
{
await ResetDb();
var (user, accountService, _, _) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (user, accountService, _, _) = await Setup(unitOfWork, context, mapper);;
var mangaLib = new LibraryBuilder("Manga", LibraryType.Manga).Build();
var lightNovelsLib = new LibraryBuilder("Light Novels", LibraryType.LightNovel).Build();
UnitOfWork.LibraryRepository.Add(mangaLib);
UnitOfWork.LibraryRepository.Add(lightNovelsLib);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Add(mangaLib);
unitOfWork.LibraryRepository.Add(lightNovelsLib);
await unitOfWork.CommitAsync();
// Initially grant access to both libraries
await accountService.UpdateLibrariesForUser(user, new List<int> { mangaLib.Id, lightNovelsLib.Id }, hasAdminRole: false);
await UnitOfWork.CommitAsync();
await unitOfWork.CommitAsync();
var userLibs = (await UnitOfWork.LibraryRepository.GetLibrariesForUserIdAsync(user.Id)).ToList();
var userLibs = (await unitOfWork.LibraryRepository.GetLibrariesForUserIdAsync(user.Id)).ToList();
Assert.Equal(2, userLibs.Count);
// Now revoke all access by passing empty list
await accountService.UpdateLibrariesForUser(user, new List<int>(), hasAdminRole: false);
await UnitOfWork.CommitAsync();
await unitOfWork.CommitAsync();
userLibs = (await UnitOfWork.LibraryRepository.GetLibrariesForUserIdAsync(user.Id)).ToList();
userLibs = (await unitOfWork.LibraryRepository.GetLibrariesForUserIdAsync(user.Id)).ToList();
Assert.Empty(userLibs);
}
private async Task<(AppUser, IAccountService, UserManager<AppUser>, SettingsService)> Setup()
private static async Task<(AppUser, IAccountService, UserManager<AppUser>, SettingsService)> Setup(IUnitOfWork unitOfWork, DataContext context, IMapper mapper)
{
var defaultAdmin = new AppUserBuilder("defaultAdmin", "defaultAdmin@localhost")
.WithRole(PolicyConstants.AdminRole)
@ -237,7 +242,7 @@ public class AccountServiceTests: AbstractDbTest
int,
IdentityUserRole<int>,
IdentityRoleClaim<int>
>(Context);
>(context);
var roleManager = new RoleManager<AppRole>(
roleStore,
@ -267,7 +272,7 @@ public class AccountServiceTests: AbstractDbTest
IdentityUserLogin<int>,
IdentityUserToken<int>,
IdentityRoleClaim<int>
>(Context);
>(context);
var userManager = new UserManager<AppUser>(userStore,
new OptionsWrapper<IdentityOptions>(new IdentityOptions()),
new PasswordHasher<AppUser>(),
@ -282,17 +287,10 @@ public class AccountServiceTests: AbstractDbTest
await userManager.CreateAsync(user);
await userManager.CreateAsync(defaultAdmin);
var accountService = new AccountService(userManager, Substitute.For<ILogger<AccountService>>(), UnitOfWork, Mapper, Substitute.For<ILocalizationService>());
var settingsService = new SettingsService(UnitOfWork, Substitute.For<IDirectoryService>(), Substitute.For<ILibraryWatcher>(), Substitute.For<ITaskScheduler>(), Substitute.For<ILogger<SettingsService>> (), Substitute.For<IOidcService>());
var accountService = new AccountService(userManager, Substitute.For<ILogger<AccountService>>(), unitOfWork, mapper, Substitute.For<ILocalizationService>());
var settingsService = new SettingsService(unitOfWork, Substitute.For<IDirectoryService>(), Substitute.For<ILibraryWatcher>(), Substitute.For<ITaskScheduler>(), Substitute.For<ILogger<SettingsService>> (), Substitute.For<IOidcService>());
user = await UnitOfWork.UserRepository.GetUserByIdAsync(user.Id, AppUserIncludes.SideNavStreams);
user = await unitOfWork.UserRepository.GetUserByIdAsync(user.Id, AppUserIncludes.SideNavStreams);
return (user, accountService, userManager, settingsService);
}
protected override async Task ResetDb()
{
Context.AppUser.RemoveRange(Context.AppUser);
Context.Library.RemoveRange(Context.Library);
await UnitOfWork.CommitAsync();
}
}

View File

@ -16,90 +16,29 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using NSubstitute;
using Xunit;
using Xunit.Abstractions;
namespace API.Tests.Services;
public class BackupServiceTests: AbstractFsTest
public class BackupServiceTests(ITestOutputHelper outputHelper): AbstractDbTest(outputHelper)
{
private readonly ILogger<BackupService> _logger = Substitute.For<ILogger<BackupService>>();
private readonly IUnitOfWork _unitOfWork;
private readonly IEventHub _messageHub = Substitute.For<IEventHub>();
private readonly IConfiguration _config;
private readonly DbConnection _connection;
private readonly DataContext _context;
public BackupServiceTests()
{
var contextOptions = new DbContextOptionsBuilder()
.UseSqlite(CreateInMemoryDatabase())
.Options;
_connection = RelationalOptionsExtension.Extract(contextOptions).Connection;
_context = new DataContext(contextOptions);
Task.Run(SeedDb).GetAwaiter().GetResult();
_unitOfWork = new UnitOfWork(_context, Substitute.For<IMapper>(), null);
_config = Substitute.For<IConfiguration>();
}
#region Setup
private static DbConnection CreateInMemoryDatabase()
{
var connection = new SqliteConnection("Filename=:memory:");
connection.Open();
return connection;
}
public void Dispose() => _connection.Dispose();
private async Task<bool> SeedDb()
{
await _context.Database.MigrateAsync();
var filesystem = CreateFileSystem();
await Seed.SeedSettings(_context, new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem));
var setting = await _context.ServerSetting.Where(s => s.Key == ServerSettingKey.CacheDirectory).SingleAsync();
setting.Value = CacheDirectory;
setting = await _context.ServerSetting.Where(s => s.Key == ServerSettingKey.BackupDirectory).SingleAsync();
setting.Value = BackupDirectory;
_context.ServerSetting.Update(setting);
_context.Library.Add(new LibraryBuilder("Manga")
.WithFolderPath(new FolderPathBuilder(Root + "data/").Build())
.Build());
return await _context.SaveChangesAsync() > 0;
}
private async Task ResetDB()
{
_context.Series.RemoveRange(_context.Series.ToList());
await _context.SaveChangesAsync();
}
#endregion
#region GetLogFiles
[Fact]
public void GetLogFiles_ExpectAllFiles_NoRollingFiles()
public async Task GetLogFiles_ExpectAllFiles_NoRollingFiles()
{
var (unitOfWork, context, _) = await CreateDatabase();
var filesystem = CreateFileSystem();
filesystem.AddFile($"{LogDirectory}kavita.log", new MockFileData(""));
filesystem.AddFile($"{LogDirectory}kavita1.log", new MockFileData(""));
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
var backupService = new BackupService(_logger, _unitOfWork, ds, _messageHub);
var backupService = new BackupService(_logger, unitOfWork, ds, _messageHub);
var backupLogFiles = backupService.GetLogFiles(false).ToList();
Assert.Single(backupLogFiles);
@ -107,17 +46,19 @@ public class BackupServiceTests: AbstractFsTest
}
[Fact]
public void GetLogFiles_ExpectAllFiles_WithRollingFiles()
public async Task GetLogFiles_ExpectAllFiles_WithRollingFiles()
{
var (unitOfWork, context, _) = await CreateDatabase();
var filesystem = CreateFileSystem();
filesystem.AddFile($"{LogDirectory}kavita.log", new MockFileData(""));
filesystem.AddFile($"{LogDirectory}kavita20200213.log", new MockFileData(""));
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
var backupService = new BackupService(_logger, _unitOfWork, ds, _messageHub);
var backupService = new BackupService(_logger, unitOfWork, ds, _messageHub);
var backupLogFiles = backupService.GetLogFiles().Select(API.Services.Tasks.Scanner.Parser.Parser.NormalizePath).ToList();
Assert.NotEmpty(backupLogFiles.Where(file => file.Equals(API.Services.Tasks.Scanner.Parser.Parser.NormalizePath($"{LogDirectory}kavita.log")) || file.Equals(API.Services.Tasks.Scanner.Parser.Parser.NormalizePath($"{LogDirectory}kavita1.log"))));
Assert.Contains(backupLogFiles, file => file.Equals(API.Services.Tasks.Scanner.Parser.Parser.NormalizePath($"{LogDirectory}kavita.log")) || file.Equals(API.Services.Tasks.Scanner.Parser.Parser.NormalizePath($"{LogDirectory}kavita1.log")));
}

View File

@ -19,95 +19,29 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.Logging;
using NSubstitute;
using Xunit;
using Xunit.Abstractions;
namespace API.Tests.Services;
public class BookmarkServiceTests: AbstractFsTest
public class BookmarkServiceTests(ITestOutputHelper outputHelper): AbstractDbTest(outputHelper)
{
private readonly IUnitOfWork _unitOfWork;
private readonly DbConnection _connection;
private readonly DataContext _context;
public BookmarkServiceTests()
private BookmarkService Create(IDirectoryService ds, IUnitOfWork unitOfWork)
{
var contextOptions = new DbContextOptionsBuilder()
.UseSqlite(CreateInMemoryDatabase())
.Options;
_connection = RelationalOptionsExtension.Extract(contextOptions).Connection;
_context = new DataContext(contextOptions);
Task.Run(SeedDb).GetAwaiter().GetResult();
var config = new MapperConfiguration(cfg => cfg.AddProfile<AutoMapperProfiles>());
var mapper = config.CreateMapper();
_unitOfWork = new UnitOfWork(_context, mapper, null);
}
private BookmarkService Create(IDirectoryService ds)
{
return new BookmarkService(Substitute.For<ILogger<BookmarkService>>(), _unitOfWork, ds,
return new BookmarkService(Substitute.For<ILogger<BookmarkService>>(), unitOfWork, ds,
Substitute.For<IMediaConversionService>());
}
#region Setup
private static DbConnection CreateInMemoryDatabase()
{
var connection = new SqliteConnection("Filename=:memory:");
connection.Open();
return connection;
}
private async Task<bool> SeedDb()
{
await _context.Database.MigrateAsync();
var filesystem = CreateFileSystem();
await Seed.SeedSettings(_context, new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem));
var setting = await _context.ServerSetting.Where(s => s.Key == ServerSettingKey.CacheDirectory).SingleAsync();
setting.Value = CacheDirectory;
setting = await _context.ServerSetting.Where(s => s.Key == ServerSettingKey.BackupDirectory).SingleAsync();
setting.Value = BackupDirectory;
setting = await _context.ServerSetting.Where(s => s.Key == ServerSettingKey.BookmarkDirectory).SingleAsync();
setting.Value = BookmarkDirectory;
_context.ServerSetting.Update(setting);
_context.Library.Add(new LibraryBuilder("Manga")
.WithFolderPath(new FolderPathBuilder(Root + "data/").Build())
.Build());
return await _context.SaveChangesAsync() > 0;
}
private async Task ResetDB()
{
_context.Series.RemoveRange(_context.Series.ToList());
_context.Users.RemoveRange(_context.Users.ToList());
_context.AppUserBookmark.RemoveRange(_context.AppUserBookmark.ToList());
await _context.SaveChangesAsync();
}
#endregion
#region BookmarkPage
[Fact]
public async Task BookmarkPage_ShouldCopyTheFileAndUpdateDB()
{
var (unitOfWork, context, _) = await CreateDatabase();
var filesystem = CreateFileSystem();
var file = $"{CacheDirectory}1/0001.jpg";
filesystem.AddFile(file, new MockFileData("123"));
// Delete all Series to reset state
await ResetDB();
var series = new SeriesBuilder("Test")
.WithFormat(MangaFormat.Epub)
.WithVolume(new VolumeBuilder(API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)
@ -116,20 +50,20 @@ Substitute.For<IMediaConversionService>());
.Build())
.Build();
series.Library = new LibraryBuilder("Test LIb").Build();
_context.Series.Add(series);
context.Series.Add(series);
_context.AppUser.Add(new AppUser()
context.AppUser.Add(new AppUser()
{
UserName = "Joe"
});
await _context.SaveChangesAsync();
await context.SaveChangesAsync();
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
var bookmarkService = Create(ds);
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Bookmarks);
var bookmarkService = Create(ds, unitOfWork);
var user = await unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Bookmarks);
var result = await bookmarkService.BookmarkPage(user, new BookmarkDto()
{
@ -142,19 +76,18 @@ Substitute.For<IMediaConversionService>());
Assert.True(result);
Assert.Single(ds.GetFiles(BookmarkDirectory, searchOption:SearchOption.AllDirectories));
Assert.NotNull(await _unitOfWork.UserRepository.GetBookmarkAsync(1));
Assert.NotNull(await unitOfWork.UserRepository.GetBookmarkAsync(1));
}
[Fact]
public async Task BookmarkPage_ShouldDeleteFileOnUnbookmark()
{
var (unitOfWork, context, _) = await CreateDatabase();
var filesystem = CreateFileSystem();
filesystem.AddFile($"{CacheDirectory}1/0001.jpg", new MockFileData("123"));
filesystem.AddFile($"{BookmarkDirectory}1/1/0001.jpg", new MockFileData("123"));
// Delete all Series to reset state
await ResetDB();
var series = new SeriesBuilder("Test")
.WithFormat(MangaFormat.Epub)
.WithVolume(new VolumeBuilder("1")
@ -165,10 +98,10 @@ Substitute.For<IMediaConversionService>());
.Build();
series.Library = new LibraryBuilder("Test LIb").Build();
_context.Series.Add(series);
context.Series.Add(series);
_context.AppUser.Add(new AppUser()
context.AppUser.Add(new AppUser()
{
UserName = "Joe",
Bookmarks = new List<AppUserBookmark>()
@ -184,12 +117,12 @@ Substitute.For<IMediaConversionService>());
}
});
await _context.SaveChangesAsync();
await context.SaveChangesAsync();
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
var bookmarkService = Create(ds);
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Bookmarks);
var bookmarkService = Create(ds, unitOfWork);
var user = await unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Bookmarks);
var result = await bookmarkService.RemoveBookmarkPage(user, new BookmarkDto()
{
@ -202,7 +135,7 @@ Substitute.For<IMediaConversionService>());
Assert.True(result);
Assert.Empty(ds.GetFiles(BookmarkDirectory, searchOption:SearchOption.AllDirectories));
Assert.Null(await _unitOfWork.UserRepository.GetBookmarkAsync(1));
Assert.Null(await unitOfWork.UserRepository.GetBookmarkAsync(1));
}
#endregion
@ -212,15 +145,14 @@ Substitute.For<IMediaConversionService>());
[Fact]
public async Task DeleteBookmarkFiles_ShouldDeleteOnlyPassedFiles()
{
var (unitOfWork, context, _) = await CreateDatabase();
var filesystem = CreateFileSystem();
filesystem.AddFile($"{CacheDirectory}1/0001.jpg", new MockFileData("123"));
filesystem.AddFile($"{BookmarkDirectory}1/1/1/0001.jpg", new MockFileData("123"));
filesystem.AddFile($"{BookmarkDirectory}1/2/1/0002.jpg", new MockFileData("123"));
filesystem.AddFile($"{BookmarkDirectory}1/2/1/0001.jpg", new MockFileData("123"));
// Delete all Series to reset state
await ResetDB();
var series = new SeriesBuilder("Test")
.WithFormat(MangaFormat.Epub)
.WithVolume(new VolumeBuilder("1")
@ -231,9 +163,9 @@ Substitute.For<IMediaConversionService>());
.Build();
series.Library = new LibraryBuilder("Test LIb").Build();
_context.Series.Add(series);
context.Series.Add(series);
_context.AppUser.Add(new AppUser()
context.AppUser.Add(new AppUser()
{
UserName = "Joe",
Bookmarks = new List<AppUserBookmark>()
@ -265,20 +197,22 @@ Substitute.For<IMediaConversionService>());
}
});
await _context.SaveChangesAsync();
await context.SaveChangesAsync();
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
var bookmarkService = Create(ds);
var bookmarkService = Create(ds, unitOfWork);
await bookmarkService.DeleteBookmarkFiles(new [] {new AppUserBookmark()
{
await bookmarkService.DeleteBookmarkFiles([
new AppUserBookmark
{
Page = 1,
ChapterId = 1,
FileName = $"1/1/1/0001.jpg",
SeriesId = 1,
VolumeId = 1
}});
}
]);
Assert.Equal(2, ds.GetFiles(BookmarkDirectory, searchOption:SearchOption.AllDirectories).Count());
@ -294,8 +228,7 @@ Substitute.For<IMediaConversionService>());
var filesystem = CreateFileSystem();
filesystem.AddFile($"{CacheDirectory}1/0001.jpg", new MockFileData("123"));
// Delete all Series to reset state
await ResetDB();
var (unitOfWork, context, _) = await CreateDatabase();
var series = new SeriesBuilder("Test")
.WithFormat(MangaFormat.Epub)
@ -307,20 +240,20 @@ Substitute.For<IMediaConversionService>());
.Build();
series.Library = new LibraryBuilder("Test LIb").Build();
_context.Series.Add(series);
context.Series.Add(series);
_context.AppUser.Add(new AppUser()
context.AppUser.Add(new AppUser()
{
UserName = "Joe"
});
await _context.SaveChangesAsync();
await context.SaveChangesAsync();
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
var bookmarkService = Create(ds);
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Bookmarks);
var bookmarkService = Create(ds, unitOfWork);
var user = await unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Bookmarks);
await bookmarkService.BookmarkPage(user, new BookmarkDto()
{
@ -347,8 +280,7 @@ Substitute.For<IMediaConversionService>());
filesystem.AddFile($"{CacheDirectory}1/0001.jpg", new MockFileData("123"));
filesystem.AddFile($"{BookmarkDirectory}1/1/0001.jpg", new MockFileData("123"));
// Delete all Series to reset state
await ResetDB();
var (unitOfWork, context, _) = await CreateDatabase();
var series = new SeriesBuilder("Test")
.WithFormat(MangaFormat.Epub)
@ -360,9 +292,9 @@ Substitute.For<IMediaConversionService>());
.Build();
series.Library = new LibraryBuilder("Test LIb").Build();
_context.Series.Add(series);
context.Series.Add(series);
_context.AppUser.Add(new AppUser()
context.AppUser.Add(new AppUser()
{
UserName = "Joe",
Bookmarks = new List<AppUserBookmark>()
@ -378,19 +310,19 @@ Substitute.For<IMediaConversionService>());
}
});
await _context.SaveChangesAsync();
await context.SaveChangesAsync();
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
var vol = await _unitOfWork.VolumeRepository.GetVolumeAsync(1);
var vol = await unitOfWork.VolumeRepository.GetVolumeAsync(1);
vol.Chapters = new List<Chapter>();
_unitOfWork.VolumeRepository.Update(vol);
await _unitOfWork.CommitAsync();
unitOfWork.VolumeRepository.Update(vol);
await unitOfWork.CommitAsync();
Assert.Single(ds.GetFiles(BookmarkDirectory, searchOption:SearchOption.AllDirectories));
Assert.NotNull(await _unitOfWork.UserRepository.GetBookmarkAsync(1));
Assert.NotNull(await unitOfWork.UserRepository.GetBookmarkAsync(1));
}
@ -401,8 +333,7 @@ Substitute.For<IMediaConversionService>());
filesystem.AddFile($"{CacheDirectory}1/0001.jpg", new MockFileData("123"));
filesystem.AddFile($"{BookmarkDirectory}1/1/0001.jpg", new MockFileData("123"));
// Delete all Series to reset state
await ResetDB();
var (unitOfWork, context, _) = await CreateDatabase();
var series = new SeriesBuilder("Test")
.WithFormat(MangaFormat.Epub)
.WithVolume(new VolumeBuilder("1")
@ -413,10 +344,10 @@ Substitute.For<IMediaConversionService>());
.Build();
series.Library = new LibraryBuilder("Test LIb").Build();
_context.Series.Add(series);
context.Series.Add(series);
_context.AppUser.Add(new AppUser()
context.AppUser.Add(new AppUser()
{
UserName = "Joe",
Bookmarks = new List<AppUserBookmark>()
@ -432,19 +363,19 @@ Substitute.For<IMediaConversionService>());
}
});
await _context.SaveChangesAsync();
await context.SaveChangesAsync();
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Bookmarks);
var user = await unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Bookmarks);
Assert.NotEmpty(user!.Bookmarks);
series.Volumes = new List<Volume>();
_unitOfWork.SeriesRepository.Update(series);
await _unitOfWork.CommitAsync();
unitOfWork.SeriesRepository.Update(series);
await unitOfWork.CommitAsync();
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
Assert.Single(ds.GetFiles(BookmarkDirectory, searchOption:SearchOption.AllDirectories));
Assert.NotNull(await _unitOfWork.UserRepository.GetBookmarkAsync(1));
Assert.NotNull(await unitOfWork.UserRepository.GetBookmarkAsync(1));
}
#endregion

View File

@ -18,6 +18,7 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.Logging;
using NSubstitute;
using Xunit;
using Xunit.Abstractions;
namespace API.Tests.Services;
@ -60,87 +61,27 @@ internal class MockReadingItemServiceForCacheService : IReadingItemService
throw new System.NotImplementedException();
}
}
public class CacheServiceTests: AbstractFsTest
public class CacheServiceTests(ITestOutputHelper outputHelper): AbstractDbTest(outputHelper)
{
private readonly ILogger<CacheService> _logger = Substitute.For<ILogger<CacheService>>();
private readonly IUnitOfWork _unitOfWork;
private readonly IHubContext<MessageHub> _messageHub = Substitute.For<IHubContext<MessageHub>>();
private readonly DbConnection _connection;
private readonly DataContext _context;
public CacheServiceTests()
{
var contextOptions = new DbContextOptionsBuilder()
.UseSqlite(CreateInMemoryDatabase())
.Options;
_connection = RelationalOptionsExtension.Extract(contextOptions).Connection;
_context = new DataContext(contextOptions);
Task.Run(SeedDb).GetAwaiter().GetResult();
_unitOfWork = new UnitOfWork(_context, Substitute.For<IMapper>(), null);
}
#region Setup
private static DbConnection CreateInMemoryDatabase()
{
var connection = new SqliteConnection("Filename=:memory:");
connection.Open();
return connection;
}
public void Dispose() => _connection.Dispose();
private async Task<bool> SeedDb()
{
await _context.Database.MigrateAsync();
var filesystem = CreateFileSystem();
await Seed.SeedSettings(_context, new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem));
var setting = await _context.ServerSetting.Where(s => s.Key == ServerSettingKey.CacheDirectory).SingleAsync();
setting.Value = CacheDirectory;
setting = await _context.ServerSetting.Where(s => s.Key == ServerSettingKey.BackupDirectory).SingleAsync();
setting.Value = BackupDirectory;
_context.ServerSetting.Update(setting);
_context.Library.Add(new LibraryBuilder("Manga")
.WithFolderPath(new FolderPathBuilder(Root + "data/").Build())
.Build());
return await _context.SaveChangesAsync() > 0;
}
private async Task ResetDB()
{
_context.Series.RemoveRange(_context.Series.ToList());
await _context.SaveChangesAsync();
}
#endregion
#region Ensure
[Fact]
public async Task Ensure_DirectoryAlreadyExists_DontExtractAnything()
{
var (unitOfWork, context, _) = await CreateDatabase();
var filesystem = CreateFileSystem();
filesystem.AddFile($"{DataDirectory}Test v1.zip", new MockFileData(""));
filesystem.AddDirectory($"{CacheDirectory}1/");
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
var cleanupService = new CacheService(_logger, _unitOfWork, ds,
var cleanupService = new CacheService(_logger, unitOfWork, ds,
new ReadingItemService(Substitute.For<IArchiveService>(),
Substitute.For<IBookService>(),
Substitute.For<IImageService>(), ds, Substitute.For<ILogger<ReadingItemService>>()),
Substitute.For<IBookmarkService>());
await ResetDB();
var s = new SeriesBuilder("Test").Build();
var v = new VolumeBuilder("1").Build();
var c = new ChapterBuilder("1")
@ -149,9 +90,9 @@ public class CacheServiceTests: AbstractFsTest
v.Chapters.Add(c);
s.Volumes.Add(v);
s.LibraryId = 1;
_context.Series.Add(s);
context.Series.Add(s);
await _context.SaveChangesAsync();
await context.SaveChangesAsync();
await cleanupService.Ensure(1);
Assert.Empty(ds.GetFiles(filesystem.Path.Join(CacheDirectory, "1"), searchOption:SearchOption.AllDirectories));
@ -203,15 +144,17 @@ public class CacheServiceTests: AbstractFsTest
#region CleanupChapters
[Fact]
public void CleanupChapters_AllFilesShouldBeDeleted()
public async Task CleanupChapters_AllFilesShouldBeDeleted()
{
var (unitOfWork, context, _) = await CreateDatabase();
var filesystem = CreateFileSystem();
filesystem.AddDirectory($"{CacheDirectory}1/");
filesystem.AddFile($"{CacheDirectory}1/001.jpg", new MockFileData(""));
filesystem.AddFile($"{CacheDirectory}1/002.jpg", new MockFileData(""));
filesystem.AddFile($"{CacheDirectory}3/003.jpg", new MockFileData(""));
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
var cleanupService = new CacheService(_logger, _unitOfWork, ds,
var cleanupService = new CacheService(_logger, unitOfWork, ds,
new ReadingItemService(Substitute.For<IArchiveService>(),
Substitute.For<IBookService>(), Substitute.For<IImageService>(), ds, Substitute.For<ILogger<ReadingItemService>>()),
Substitute.For<IBookmarkService>());
@ -226,14 +169,16 @@ public class CacheServiceTests: AbstractFsTest
#region GetCachedEpubFile
[Fact]
public void GetCachedEpubFile_ShouldReturnFirstEpub()
public async Task GetCachedEpubFile_ShouldReturnFirstEpub()
{
var (unitOfWork, context, _) = await CreateDatabase();
var filesystem = CreateFileSystem();
filesystem.AddDirectory($"{CacheDirectory}1/");
filesystem.AddFile($"{DataDirectory}1.epub", new MockFileData(""));
filesystem.AddFile($"{DataDirectory}2.epub", new MockFileData(""));
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
var cs = new CacheService(_logger, _unitOfWork, ds,
var cs = new CacheService(_logger, unitOfWork, ds,
new ReadingItemService(Substitute.For<IArchiveService>(),
Substitute.For<IBookService>(), Substitute.For<IImageService>(), ds, Substitute.For<ILogger<ReadingItemService>>()),
Substitute.For<IBookmarkService>());
@ -251,8 +196,10 @@ public class CacheServiceTests: AbstractFsTest
#region GetCachedPagePath
[Fact]
public void GetCachedPagePath_ReturnNullIfNoFiles()
public async Task GetCachedPagePath_ReturnNullIfNoFiles()
{
var (unitOfWork, context, _) = await CreateDatabase();
var filesystem = CreateFileSystem();
filesystem.AddDirectory($"{CacheDirectory}1/");
filesystem.AddFile($"{DataDirectory}1.zip", new MockFileData(""));
@ -274,7 +221,7 @@ public class CacheServiceTests: AbstractFsTest
}
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
var cs = new CacheService(_logger, _unitOfWork, ds,
var cs = new CacheService(_logger, unitOfWork, ds,
new ReadingItemService(Substitute.For<IArchiveService>(),
Substitute.For<IBookService>(), Substitute.For<IImageService>(), ds, Substitute.For<ILogger<ReadingItemService>>()),
Substitute.For<IBookmarkService>());
@ -287,8 +234,10 @@ public class CacheServiceTests: AbstractFsTest
}
[Fact]
public void GetCachedPagePath_GetFileFromFirstFile()
public async Task GetCachedPagePath_GetFileFromFirstFile()
{
var (unitOfWork, context, _) = await CreateDatabase();
var filesystem = CreateFileSystem();
filesystem.AddDirectory($"{CacheDirectory}1/");
filesystem.AddFile($"{DataDirectory}1.zip", new MockFileData(""));
@ -318,7 +267,7 @@ public class CacheServiceTests: AbstractFsTest
}
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
var cs = new CacheService(_logger, _unitOfWork, ds,
var cs = new CacheService(_logger, unitOfWork, ds,
new ReadingItemService(Substitute.For<IArchiveService>(),
Substitute.For<IBookService>(), Substitute.For<IImageService>(), ds, Substitute.For<ILogger<ReadingItemService>>()),
Substitute.For<IBookmarkService>());
@ -332,8 +281,10 @@ public class CacheServiceTests: AbstractFsTest
[Fact]
public void GetCachedPagePath_GetLastPageFromSingleFile()
public async Task GetCachedPagePath_GetLastPageFromSingleFile()
{
var (unitOfWork, context, _) = await CreateDatabase();
var filesystem = CreateFileSystem();
filesystem.AddDirectory($"{CacheDirectory}1/");
filesystem.AddFile($"{DataDirectory}1.zip", new MockFileData(""));
@ -359,7 +310,7 @@ public class CacheServiceTests: AbstractFsTest
}
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
var cs = new CacheService(_logger, _unitOfWork, ds,
var cs = new CacheService(_logger, unitOfWork, ds,
new ReadingItemService(Substitute.For<IArchiveService>(),
Substitute.For<IBookService>(), Substitute.For<IImageService>(), ds, Substitute.For<ILogger<ReadingItemService>>()),
Substitute.For<IBookmarkService>());
@ -373,8 +324,10 @@ public class CacheServiceTests: AbstractFsTest
}
[Fact]
public void GetCachedPagePath_GetFileFromSecondFile()
public async Task GetCachedPagePath_GetFileFromSecondFile()
{
var (unitOfWork, context, _) = await CreateDatabase();
var filesystem = CreateFileSystem();
filesystem.AddDirectory($"{CacheDirectory}1/");
filesystem.AddFile($"{DataDirectory}1.zip", new MockFileData(""));
@ -404,7 +357,7 @@ public class CacheServiceTests: AbstractFsTest
}
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
var cs = new CacheService(_logger, _unitOfWork, ds,
var cs = new CacheService(_logger, unitOfWork, ds,
new ReadingItemService(Substitute.For<IArchiveService>(),
Substitute.For<IBookService>(), Substitute.For<IImageService>(), ds, Substitute.For<ILogger<ReadingItemService>>()),
Substitute.For<IBookmarkService>());

View File

@ -4,6 +4,7 @@ using System.IO;
using System.IO.Abstractions.TestingHelpers;
using System.Linq;
using System.Threading.Tasks;
using API.Data;
using API.Data.Repositories;
using API.DTOs.Filtering;
using API.Entities;
@ -18,36 +19,28 @@ using API.SignalR;
using Microsoft.Extensions.Logging;
using NSubstitute;
using Xunit;
using Xunit.Abstractions;
namespace API.Tests.Services;
public class CleanupServiceTests : AbstractDbTest
public class CleanupServiceTests(ITestOutputHelper outputHelper): AbstractDbTest(outputHelper)
{
private readonly ILogger<CleanupService> _logger = Substitute.For<ILogger<CleanupService>>();
private readonly IEventHub _messageHub = Substitute.For<IEventHub>();
private readonly IReaderService _readerService;
public CleanupServiceTests() : base()
{
Context.Library.Add(new LibraryBuilder("Manga")
.WithFolderPath(new FolderPathBuilder(Root + "data/").Build())
.Build());
_readerService = new ReaderService(UnitOfWork, Substitute.For<ILogger<ReaderService>>(), Substitute.For<IEventHub>(),
Substitute.For<IImageService>(),
new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), new MockFileSystem()), Substitute.For<IScrobblingService>());
}
#region Setup
protected override async Task ResetDb()
private async Task<(ILogger<CleanupService>, IEventHub, IReaderService)> Setup(IUnitOfWork unitOfWork, DataContext context)
{
Context.Series.RemoveRange(Context.Series.ToList());
Context.Users.RemoveRange(Context.Users.ToList());
Context.AppUserBookmark.RemoveRange(Context.AppUserBookmark.ToList());
context.Library.Add(new LibraryBuilder("Manga")
.WithFolderPath(new FolderPathBuilder(Root + "data/").Build())
.Build());
await Context.SaveChangesAsync();
var logger = Substitute.For<ILogger<CleanupService>>();
var messageHub = Substitute.For<IEventHub>();
var readerService = new ReaderService(unitOfWork, Substitute.For<ILogger<ReaderService>>(), Substitute.For<IEventHub>(),
Substitute.For<IImageService>(),
new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), new MockFileSystem()), Substitute.For<IScrobblingService>());
return (logger, messageHub, readerService);
}
#endregion
@ -63,23 +56,24 @@ public class CleanupServiceTests : AbstractDbTest
filesystem.AddFile($"{CoverImageDirectory}{ImageService.GetSeriesFormat(1000)}.jpg", new MockFileData(""));
// Delete all Series to reset state
await ResetDb();
var (unitOfWork, context, _) = await CreateDatabase();
var (logger, messageHub, readerService) = await Setup(unitOfWork, context);
var s = new SeriesBuilder("Test 1").Build();
s.CoverImage = $"{ImageService.GetSeriesFormat(1)}.jpg";
s.LibraryId = 1;
Context.Series.Add(s);
context.Series.Add(s);
s = new SeriesBuilder("Test 2").Build();
s.CoverImage = $"{ImageService.GetSeriesFormat(3)}.jpg";
s.LibraryId = 1;
Context.Series.Add(s);
context.Series.Add(s);
s = new SeriesBuilder("Test 3").Build();
s.CoverImage = $"{ImageService.GetSeriesFormat(1000)}.jpg";
s.LibraryId = 1;
Context.Series.Add(s);
context.Series.Add(s);
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
var cleanupService = new CleanupService(_logger, UnitOfWork, _messageHub,
var cleanupService = new CleanupService(logger, unitOfWork, messageHub,
ds);
await cleanupService.DeleteSeriesCoverImages();
@ -96,22 +90,23 @@ public class CleanupServiceTests : AbstractDbTest
filesystem.AddFile($"{CoverImageDirectory}{ImageService.GetSeriesFormat(1000)}.jpg", new MockFileData(""));
// Delete all Series to reset state
await ResetDb();
var (unitOfWork, context, _) = await CreateDatabase();
var (logger, messageHub, readerService) = await Setup(unitOfWork, context);
// Add 2 series with cover images
var s = new SeriesBuilder("Test 1").Build();
s.CoverImage = $"{ImageService.GetSeriesFormat(1)}.jpg";
s.LibraryId = 1;
Context.Series.Add(s);
context.Series.Add(s);
s = new SeriesBuilder("Test 2").Build();
s.CoverImage = $"{ImageService.GetSeriesFormat(3)}.jpg";
s.LibraryId = 1;
Context.Series.Add(s);
context.Series.Add(s);
await Context.SaveChangesAsync();
await context.SaveChangesAsync();
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
var cleanupService = new CleanupService(_logger, UnitOfWork, _messageHub,
var cleanupService = new CleanupService(logger, unitOfWork, messageHub,
ds);
await cleanupService.DeleteSeriesCoverImages();
@ -130,10 +125,11 @@ public class CleanupServiceTests : AbstractDbTest
filesystem.AddFile($"{CoverImageDirectory}v01_c1000.jpg", new MockFileData(""));
// Delete all Series to reset state
await ResetDb();
var (unitOfWork, context, _) = await CreateDatabase();
var (logger, messageHub, readerService) = await Setup(unitOfWork, context);
// Add 2 series with cover images
Context.Series.Add(new SeriesBuilder("Test 1")
context.Series.Add(new SeriesBuilder("Test 1")
.WithVolume(new VolumeBuilder("1")
.WithChapter(new ChapterBuilder(API.Services.Tasks.Scanner.Parser.Parser.DefaultChapter).WithCoverImage("v01_c01.jpg").Build())
.WithCoverImage("v01_c01.jpg")
@ -142,7 +138,7 @@ public class CleanupServiceTests : AbstractDbTest
.WithLibraryId(1)
.Build());
Context.Series.Add(new SeriesBuilder("Test 2")
context.Series.Add(new SeriesBuilder("Test 2")
.WithVolume(new VolumeBuilder("1")
.WithChapter(new ChapterBuilder(API.Services.Tasks.Scanner.Parser.Parser.DefaultChapter).WithCoverImage("v01_c03.jpg").Build())
.WithCoverImage("v01_c03.jpg")
@ -152,9 +148,9 @@ public class CleanupServiceTests : AbstractDbTest
.Build());
await Context.SaveChangesAsync();
await context.SaveChangesAsync();
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
var cleanupService = new CleanupService(_logger, UnitOfWork, _messageHub,
var cleanupService = new CleanupService(logger, unitOfWork, messageHub,
ds);
await cleanupService.DeleteChapterCoverImages();
@ -174,7 +170,8 @@ public class CleanupServiceTests : AbstractDbTest
// filesystem.AddFile($"{CoverImageDirectory}{ImageService.GetCollectionTagFormat(1000)}.jpg", new MockFileData(""));
//
// // Delete all Series to reset state
// await ResetDb();
// var (unitOfWork, context, _) = await CreateDatabase();
// var (logger, messageHub, readerService) = await Setup(unitOfWork, context);
//
// // Add 2 series with cover images
//
@ -201,7 +198,7 @@ public class CleanupServiceTests : AbstractDbTest
//
// await _context.SaveChangesAsync();
// var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
// var cleanupService = new CleanupService(_logger, _unitOfWork, _messageHub,
// var cleanupService = new CleanupService(logger, _unitOfWork, messageHub,
// ds);
//
// await cleanupService.DeleteTagCoverImages();
@ -221,9 +218,10 @@ public class CleanupServiceTests : AbstractDbTest
filesystem.AddFile($"{CoverImageDirectory}{ImageService.GetReadingListFormat(3)}.jpg", new MockFileData(""));
// Delete all Series to reset state
await ResetDb();
var (unitOfWork, context, _) = await CreateDatabase();
var (logger, messageHub, readerService) = await Setup(unitOfWork, context);
Context.Users.Add(new AppUser()
context.Users.Add(new AppUser()
{
UserName = "Joe",
ReadingLists = new List<ReadingList>()
@ -239,10 +237,9 @@ public class CleanupServiceTests : AbstractDbTest
}
});
await Context.SaveChangesAsync();
await context.SaveChangesAsync();
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
var cleanupService = new CleanupService(_logger, UnitOfWork, _messageHub,
ds);
var cleanupService = new CleanupService(logger, unitOfWork, messageHub, ds);
await cleanupService.DeleteReadingListCoverImages();
@ -253,29 +250,33 @@ public class CleanupServiceTests : AbstractDbTest
#region CleanupCacheDirectory
[Fact]
public void CleanupCacheDirectory_ClearAllFiles()
public async Task CleanupCacheDirectory_ClearAllFiles()
{
var filesystem = CreateFileSystem();
filesystem.AddFile($"{CacheDirectory}01.jpg", new MockFileData(""));
filesystem.AddFile($"{CacheDirectory}02.jpg", new MockFileData(""));
var (unitOfWork, context, _) = await CreateDatabase();
var (logger, messageHub, readerService) = await Setup(unitOfWork, context);
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
var cleanupService = new CleanupService(_logger, UnitOfWork, _messageHub,
ds);
var cleanupService = new CleanupService(logger, unitOfWork, messageHub, ds);
cleanupService.CleanupCacheAndTempDirectories();
Assert.Empty(ds.GetFiles(CacheDirectory, searchOption: SearchOption.AllDirectories));
}
[Fact]
public void CleanupCacheDirectory_ClearAllFilesInSubDirectory()
public async Task CleanupCacheDirectory_ClearAllFilesInSubDirectory()
{
var filesystem = CreateFileSystem();
filesystem.AddFile($"{CacheDirectory}01.jpg", new MockFileData(""));
filesystem.AddFile($"{CacheDirectory}subdir/02.jpg", new MockFileData(""));
var (unitOfWork, context, _) = await CreateDatabase();
var (logger, messageHub, readerService) = await Setup(unitOfWork, context);
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
var cleanupService = new CleanupService(_logger, UnitOfWork, _messageHub,
ds);
var cleanupService = new CleanupService(logger, unitOfWork, messageHub, ds);
cleanupService.CleanupCacheAndTempDirectories();
Assert.Empty(ds.GetFiles(CacheDirectory, searchOption: SearchOption.AllDirectories));
}
@ -296,9 +297,11 @@ public class CleanupServiceTests : AbstractDbTest
filesystem.AddFile($"{BackupDirectory}kavita_backup_12_3_2021_9_27_58 AM.zip", filesystemFile);
filesystem.AddFile($"{BackupDirectory}randomfile.zip", filesystemFile);
var (unitOfWork, context, _) = await CreateDatabase();
var (logger, messageHub, readerService) = await Setup(unitOfWork, context);
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
var cleanupService = new CleanupService(_logger, UnitOfWork, _messageHub,
ds);
var cleanupService = new CleanupService(logger, unitOfWork, messageHub, ds);
await cleanupService.CleanupBackups();
Assert.Single(ds.GetFiles(BackupDirectory, searchOption: SearchOption.AllDirectories));
}
@ -318,9 +321,11 @@ public class CleanupServiceTests : AbstractDbTest
CreationTime = DateTimeOffset.Now.Subtract(TimeSpan.FromDays(14))
});
var (unitOfWork, context, _) = await CreateDatabase();
var (logger, messageHub, readerService) = await Setup(unitOfWork, context);
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
var cleanupService = new CleanupService(_logger, UnitOfWork, _messageHub,
ds);
var cleanupService = new CleanupService(logger, unitOfWork, messageHub, ds);
await cleanupService.CleanupBackups();
Assert.True(filesystem.File.Exists($"{BackupDirectory}randomfile.zip"));
}
@ -342,9 +347,11 @@ public class CleanupServiceTests : AbstractDbTest
});
}
var (unitOfWork, context, _) = await CreateDatabase();
var (logger, messageHub, readerService) = await Setup(unitOfWork, context);
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
var cleanupService = new CleanupService(_logger, UnitOfWork, _messageHub,
ds);
var cleanupService = new CleanupService(logger, unitOfWork, messageHub, ds);
await cleanupService.CleanupLogs();
Assert.Single(ds.GetFiles(LogDirectory, searchOption: SearchOption.AllDirectories));
}
@ -370,10 +377,12 @@ public class CleanupServiceTests : AbstractDbTest
CreationTime = DateTimeOffset.Now.Subtract(TimeSpan.FromDays(31 - 11))
});
var (unitOfWork, context, _) = await CreateDatabase();
var (logger, messageHub, readerService) = await Setup(unitOfWork, context);
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
var cleanupService = new CleanupService(_logger, UnitOfWork, _messageHub,
ds);
var cleanupService = new CleanupService(logger, unitOfWork, messageHub, ds);
await cleanupService.CleanupLogs();
Assert.True(filesystem.File.Exists($"{LogDirectory}kavita20200911.log"));
}
@ -385,6 +394,9 @@ public class CleanupServiceTests : AbstractDbTest
[Fact]
public async Task CleanupDbEntries_CleanupAbandonedChapters()
{
var (unitOfWork, context, _) = await CreateDatabase();
var (logger, messageHub, readerService) = await Setup(unitOfWork, context);
var c = new ChapterBuilder(API.Services.Tasks.Scanner.Parser.Parser.DefaultChapter)
.WithPages(1)
.Build();
@ -396,47 +408,50 @@ public class CleanupServiceTests : AbstractDbTest
.Build();
series.Library = new LibraryBuilder("Test LIb").Build();
Context.Series.Add(series);
context.Series.Add(series);
Context.AppUser.Add(new AppUser()
context.AppUser.Add(new AppUser()
{
UserName = "majora2007"
});
await Context.SaveChangesAsync();
await context.SaveChangesAsync();
var user = await UnitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Progress);
await _readerService.MarkChaptersUntilAsRead(user, 1, 5);
await Context.SaveChangesAsync();
var user = await unitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Progress);
await readerService.MarkChaptersUntilAsRead(user, 1, 5);
await context.SaveChangesAsync();
// Validate correct chapters have read status
Assert.Equal(1, (await UnitOfWork.AppUserProgressRepository.GetUserProgressAsync(1, 1)).PagesRead);
Assert.Equal(1, (await unitOfWork.AppUserProgressRepository.GetUserProgressAsync(1, 1)).PagesRead);
var cleanupService = new CleanupService(Substitute.For<ILogger<CleanupService>>(), UnitOfWork,
var cleanupService = new CleanupService(Substitute.For<ILogger<CleanupService>>(), unitOfWork,
Substitute.For<IEventHub>(),
new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), new MockFileSystem()));
// Delete the Chapter
Context.Chapter.Remove(c);
await UnitOfWork.CommitAsync();
Assert.Empty(await UnitOfWork.AppUserProgressRepository.GetUserProgressForSeriesAsync(1, 1));
context.Chapter.Remove(c);
await unitOfWork.CommitAsync();
Assert.Empty(await unitOfWork.AppUserProgressRepository.GetUserProgressForSeriesAsync(1, 1));
// NOTE: This may not be needed, the underlying DB structure seems fixed as of v0.7
await cleanupService.CleanupDbEntries();
Assert.Empty(await UnitOfWork.AppUserProgressRepository.GetUserProgressForSeriesAsync(1, 1));
Assert.Empty(await unitOfWork.AppUserProgressRepository.GetUserProgressForSeriesAsync(1, 1));
}
[Fact]
public async Task CleanupDbEntries_RemoveTagsWithoutSeries()
{
var (unitOfWork, context, _) = await CreateDatabase();
var (logger, messageHub, readerService) = await Setup(unitOfWork, context);
var s = new SeriesBuilder("Test")
.WithFormat(MangaFormat.Epub)
.WithMetadata(new SeriesMetadataBuilder().Build())
.Build();
s.Library = new LibraryBuilder("Test LIb").Build();
Context.Series.Add(s);
context.Series.Add(s);
var c = new AppUserCollection()
{
@ -446,24 +461,24 @@ public class CleanupServiceTests : AbstractDbTest
Items = new List<Series>() {s}
};
Context.AppUser.Add(new AppUser()
context.AppUser.Add(new AppUser()
{
UserName = "majora2007",
Collections = new List<AppUserCollection>() {c}
});
await Context.SaveChangesAsync();
await context.SaveChangesAsync();
var cleanupService = new CleanupService(Substitute.For<ILogger<CleanupService>>(), UnitOfWork,
var cleanupService = new CleanupService(Substitute.For<ILogger<CleanupService>>(), unitOfWork,
Substitute.For<IEventHub>(),
new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), new MockFileSystem()));
// Delete the Chapter
Context.Series.Remove(s);
await UnitOfWork.CommitAsync();
context.Series.Remove(s);
await unitOfWork.CommitAsync();
await cleanupService.CleanupDbEntries();
Assert.Empty(await UnitOfWork.CollectionTagRepository.GetAllCollectionsAsync());
Assert.Empty(await unitOfWork.CollectionTagRepository.GetAllCollectionsAsync());
}
#endregion
@ -473,22 +488,23 @@ public class CleanupServiceTests : AbstractDbTest
[Fact]
public async Task CleanupWantToRead_ShouldRemoveFullyReadSeries()
{
await ResetDb();
var (unitOfWork, context, _) = await CreateDatabase();
var (logger, messageHub, readerService) = await Setup(unitOfWork, context);
var s = new SeriesBuilder("Test CleanupWantToRead_ShouldRemoveFullyReadSeries")
.WithMetadata(new SeriesMetadataBuilder().WithPublicationStatus(PublicationStatus.Completed).Build())
.Build();
s.Library = new LibraryBuilder("Test LIb").Build();
Context.Series.Add(s);
context.Series.Add(s);
var user = new AppUser()
{
UserName = "CleanupWantToRead_ShouldRemoveFullyReadSeries",
};
Context.AppUser.Add(user);
context.AppUser.Add(user);
await UnitOfWork.CommitAsync();
await unitOfWork.CommitAsync();
// Add want to read
user.WantToRead = new List<AppUserWantToRead>()
@ -498,12 +514,12 @@ public class CleanupServiceTests : AbstractDbTest
SeriesId = s.Id
}
};
await UnitOfWork.CommitAsync();
await unitOfWork.CommitAsync();
await _readerService.MarkSeriesAsRead(user, s.Id);
await UnitOfWork.CommitAsync();
await readerService.MarkSeriesAsRead(user, s.Id);
await unitOfWork.CommitAsync();
var cleanupService = new CleanupService(Substitute.For<ILogger<CleanupService>>(), UnitOfWork,
var cleanupService = new CleanupService(Substitute.For<ILogger<CleanupService>>(), unitOfWork,
Substitute.For<IEventHub>(),
new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), new MockFileSystem()));
@ -511,7 +527,7 @@ public class CleanupServiceTests : AbstractDbTest
await cleanupService.CleanupWantToRead();
var wantToRead =
await UnitOfWork.SeriesRepository.GetWantToReadForUserAsync(user.Id, new UserParams(), new FilterDto());
await unitOfWork.SeriesRepository.GetWantToReadForUserAsync(user.Id, new UserParams(), new FilterDto());
Assert.Equal(0, wantToRead.TotalCount);
}
@ -522,7 +538,8 @@ public class CleanupServiceTests : AbstractDbTest
[Fact]
public async Task ConsolidateProgress_ShouldRemoveDuplicates()
{
await ResetDb();
var (unitOfWork, context, _) = await CreateDatabase();
var (logger, messageHub, readerService) = await Setup(unitOfWork, context);
var s = new SeriesBuilder("Test ConsolidateProgress_ShouldRemoveDuplicates")
.WithVolume(new VolumeBuilder("1")
@ -533,15 +550,15 @@ public class CleanupServiceTests : AbstractDbTest
.Build();
s.Library = new LibraryBuilder("Test Lib").Build();
Context.Series.Add(s);
context.Series.Add(s);
var user = new AppUser()
{
UserName = "ConsolidateProgress_ShouldRemoveDuplicates",
};
Context.AppUser.Add(user);
context.AppUser.Add(user);
await UnitOfWork.CommitAsync();
await unitOfWork.CommitAsync();
// Add 2 progress events
user.Progresses ??= [];
@ -553,7 +570,7 @@ public class CleanupServiceTests : AbstractDbTest
LibraryId = s.LibraryId,
PagesRead = 1,
});
await UnitOfWork.CommitAsync();
await unitOfWork.CommitAsync();
// Add a duplicate with higher page number
user.Progresses.Add(new AppUserProgress()
@ -564,18 +581,18 @@ public class CleanupServiceTests : AbstractDbTest
LibraryId = s.LibraryId,
PagesRead = 3,
});
await UnitOfWork.CommitAsync();
await unitOfWork.CommitAsync();
Assert.Equal(2, (await UnitOfWork.AppUserProgressRepository.GetAllProgress()).Count());
Assert.Equal(2, (await unitOfWork.AppUserProgressRepository.GetAllProgress()).Count());
var cleanupService = new CleanupService(Substitute.For<ILogger<CleanupService>>(), UnitOfWork,
var cleanupService = new CleanupService(Substitute.For<ILogger<CleanupService>>(), unitOfWork,
Substitute.For<IEventHub>(),
new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), new MockFileSystem()));
await cleanupService.ConsolidateProgress();
var progress = await UnitOfWork.AppUserProgressRepository.GetAllProgress();
var progress = await unitOfWork.AppUserProgressRepository.GetAllProgress();
Assert.Single(progress);
Assert.True(progress.First().PagesRead == 3);
@ -588,7 +605,8 @@ public class CleanupServiceTests : AbstractDbTest
[Fact]
public async Task EnsureChapterProgressIsCapped_ShouldNormalizeProgress()
{
await ResetDb();
var (unitOfWork, context, _) = await CreateDatabase();
var (logger, messageHub, readerService) = await Setup(unitOfWork, context);
var s = new SeriesBuilder("Test CleanupWantToRead_ShouldRemoveFullyReadSeries")
.WithMetadata(new SeriesMetadataBuilder().WithPublicationStatus(PublicationStatus.Completed).Build())
@ -601,50 +619,50 @@ public class CleanupServiceTests : AbstractDbTest
{
new VolumeBuilder(API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume).WithChapter(c).Build()
};
Context.Series.Add(s);
context.Series.Add(s);
var user = new AppUser()
{
UserName = "EnsureChapterProgressIsCapped",
Progresses = new List<AppUserProgress>()
};
Context.AppUser.Add(user);
context.AppUser.Add(user);
await UnitOfWork.CommitAsync();
await unitOfWork.CommitAsync();
await _readerService.MarkChaptersAsRead(user, s.Id, new List<Chapter>() {c});
await UnitOfWork.CommitAsync();
await readerService.MarkChaptersAsRead(user, s.Id, new List<Chapter>() {c});
await unitOfWork.CommitAsync();
var chapter = await UnitOfWork.ChapterRepository.GetChapterDtoAsync(c.Id);
await UnitOfWork.ChapterRepository.AddChapterModifiers(user.Id, chapter);
var chapter = await unitOfWork.ChapterRepository.GetChapterDtoAsync(c.Id);
await unitOfWork.ChapterRepository.AddChapterModifiers(user.Id, chapter);
Assert.NotNull(chapter);
Assert.Equal(2, chapter.PagesRead);
// Update chapter to have 1 page
c.Pages = 1;
UnitOfWork.ChapterRepository.Update(c);
await UnitOfWork.CommitAsync();
unitOfWork.ChapterRepository.Update(c);
await unitOfWork.CommitAsync();
chapter = await UnitOfWork.ChapterRepository.GetChapterDtoAsync(c.Id);
await UnitOfWork.ChapterRepository.AddChapterModifiers(user.Id, chapter);
chapter = await unitOfWork.ChapterRepository.GetChapterDtoAsync(c.Id);
await unitOfWork.ChapterRepository.AddChapterModifiers(user.Id, chapter);
Assert.NotNull(chapter);
Assert.Equal(2, chapter.PagesRead);
Assert.Equal(1, chapter.Pages);
var cleanupService = new CleanupService(Substitute.For<ILogger<CleanupService>>(), UnitOfWork,
var cleanupService = new CleanupService(Substitute.For<ILogger<CleanupService>>(), unitOfWork,
Substitute.For<IEventHub>(),
new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), new MockFileSystem()));
await cleanupService.EnsureChapterProgressIsCapped();
chapter = await UnitOfWork.ChapterRepository.GetChapterDtoAsync(c.Id);
await UnitOfWork.ChapterRepository.AddChapterModifiers(user.Id, chapter);
chapter = await unitOfWork.ChapterRepository.GetChapterDtoAsync(c.Id);
await unitOfWork.ChapterRepository.AddChapterModifiers(user.Id, chapter);
Assert.NotNull(chapter);
Assert.Equal(1, chapter.PagesRead);
Context.AppUser.Remove(user);
await UnitOfWork.CommitAsync();
context.AppUser.Remove(user);
await unitOfWork.CommitAsync();
}
#endregion
@ -658,7 +676,8 @@ public class CleanupServiceTests : AbstractDbTest
// filesystem.AddFile($"{BookmarkDirectory}1/1/1/0002.jpg", new MockFileData(""));
//
// // Delete all Series to reset state
// await ResetDb();
// var (unitOfWork, context, _) = await CreateDatabase();
// var (logger, messageHub, readerService) = await Setup(unitOfWork, context);
//
// _context.Series.Add(new Series()
// {
@ -713,7 +732,7 @@ public class CleanupServiceTests : AbstractDbTest
//
//
// var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
// var cleanupService = new CleanupService(_logger, _unitOfWork, _messageHub,
// var cleanupService = new CleanupService(logger, _unitOfWork, messageHub,
// ds);
//
// await cleanupService.CleanupBookmarks();
@ -730,7 +749,8 @@ public class CleanupServiceTests : AbstractDbTest
// filesystem.AddFile($"{BookmarkDirectory}1/1/2/0002.jpg", new MockFileData(""));
//
// // Delete all Series to reset state
// await ResetDb();
// var (unitOfWork, context, _) = await CreateDatabase();
// var (logger, messageHub, readerService) = await Setup(unitOfWork, context);
//
// _context.Series.Add(new Series()
// {
@ -776,7 +796,7 @@ public class CleanupServiceTests : AbstractDbTest
//
//
// var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
// var cleanupService = new CleanupService(_logger, _unitOfWork, _messageHub,
// var cleanupService = new CleanupService(logger, _unitOfWork, messageHub,
// ds);
//
// await cleanupService.CleanupBookmarks();

View File

@ -14,33 +14,26 @@ using API.Services.Plus;
using API.SignalR;
using Kavita.Common;
using NSubstitute;
using Polly;
using Xunit;
using Xunit.Abstractions;
namespace API.Tests.Services;
public class CollectionTagServiceTests : AbstractDbTest
public class CollectionTagServiceTests(ITestOutputHelper outputHelper): AbstractDbTest(outputHelper)
{
private readonly ICollectionTagService _service;
public CollectionTagServiceTests()
{
_service = new CollectionTagService(UnitOfWork, Substitute.For<IEventHub>());
}
protected override async Task ResetDb()
private static async Task<ICollectionTagService> Setup(IUnitOfWork unitOfWork, DataContext context)
{
Context.AppUserCollection.RemoveRange(Context.AppUserCollection.ToList());
Context.Library.RemoveRange(Context.Library.ToList());
await UnitOfWork.CommitAsync();
}
private async Task SeedSeries()
{
if (Context.AppUserCollection.Any()) return;
if (context.AppUserCollection.Any())
{
return new CollectionTagService(unitOfWork, Substitute.For<IEventHub>());
}
var s1 = new SeriesBuilder("Series 1").WithMetadata(new SeriesMetadataBuilder().WithAgeRating(AgeRating.Mature).Build()).Build();
var s2 = new SeriesBuilder("Series 2").WithMetadata(new SeriesMetadataBuilder().WithAgeRating(AgeRating.G).Build()).Build();
Context.Library.Add(new LibraryBuilder("Library 2", LibraryType.Manga)
context.Library.Add(new LibraryBuilder("Library 2", LibraryType.Manga)
.WithSeries(s1)
.WithSeries(s2)
.Build());
@ -51,9 +44,11 @@ public class CollectionTagServiceTests : AbstractDbTest
new AppUserCollectionBuilder("Tag 1").WithItems(new []{s1}).Build(),
new AppUserCollectionBuilder("Tag 2").WithItems(new []{s1, s2}).WithIsPromoted(true).Build()
};
UnitOfWork.UserRepository.Add(user);
unitOfWork.UserRepository.Add(user);
await UnitOfWork.CommitAsync();
await unitOfWork.CommitAsync();
return new CollectionTagService(unitOfWork, Substitute.For<IEventHub>());
}
#region DeleteTag
@ -61,18 +56,18 @@ public class CollectionTagServiceTests : AbstractDbTest
[Fact]
public async Task DeleteTag_ShouldDeleteTag_WhenTagExists()
{
// Arrange
await SeedSeries();
var (unitOfWork, context, _) = await CreateDatabase();
var service = await Setup(unitOfWork, context);
var user = await UnitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Collections);
var user = await unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Collections);
Assert.NotNull(user);
// Act
var result = await _service.DeleteTag(1, user);
var result = await service.DeleteTag(1, user);
// Assert
Assert.True(result);
var deletedTag = await UnitOfWork.CollectionTagRepository.GetCollectionAsync(1);
var deletedTag = await unitOfWork.CollectionTagRepository.GetCollectionAsync(1);
Assert.Null(deletedTag);
Assert.Single(user.Collections); // Only one collection should remain
}
@ -80,13 +75,14 @@ public class CollectionTagServiceTests : AbstractDbTest
[Fact]
public async Task DeleteTag_ShouldReturnTrue_WhenTagDoesNotExist()
{
// Arrange
await SeedSeries();
var user = await UnitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Collections);
var (unitOfWork, context, _) = await CreateDatabase();
var service = await Setup(unitOfWork, context);
var user = await unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Collections);
Assert.NotNull(user);
// Act - Try to delete a non-existent tag
var result = await _service.DeleteTag(999, user);
var result = await service.DeleteTag(999, user);
// Assert
Assert.True(result); // Should return true because the tag is already "deleted"
@ -96,17 +92,18 @@ public class CollectionTagServiceTests : AbstractDbTest
[Fact]
public async Task DeleteTag_ShouldNotAffectOtherTags()
{
// Arrange
await SeedSeries();
var user = await UnitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Collections);
var (unitOfWork, context, _) = await CreateDatabase();
var service = await Setup(unitOfWork, context);
var user = await unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Collections);
Assert.NotNull(user);
// Act
var result = await _service.DeleteTag(1, user);
var result = await service.DeleteTag(1, user);
// Assert
Assert.True(result);
var remainingTag = await UnitOfWork.CollectionTagRepository.GetCollectionAsync(2);
var remainingTag = await unitOfWork.CollectionTagRepository.GetCollectionAsync(2);
Assert.NotNull(remainingTag);
Assert.Equal("Tag 2", remainingTag.Title);
Assert.True(remainingTag.Promoted);
@ -119,16 +116,17 @@ public class CollectionTagServiceTests : AbstractDbTest
[Fact]
public async Task UpdateTag_ShouldUpdateFields()
{
await SeedSeries();
var (unitOfWork, context, _) = await CreateDatabase();
var service = await Setup(unitOfWork, context);
var user = await UnitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Collections);
var user = await unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Collections);
Assert.NotNull(user);
user.Collections.Add(new AppUserCollectionBuilder("UpdateTag_ShouldUpdateFields").WithIsPromoted(true).Build());
UnitOfWork.UserRepository.Update(user);
await UnitOfWork.CommitAsync();
unitOfWork.UserRepository.Update(user);
await unitOfWork.CommitAsync();
await _service.UpdateTag(new AppUserCollectionDto()
await service.UpdateTag(new AppUserCollectionDto()
{
Title = "UpdateTag_ShouldUpdateFields",
Id = 3,
@ -137,7 +135,7 @@ public class CollectionTagServiceTests : AbstractDbTest
AgeRating = AgeRating.Unknown
}, 1);
var tag = await UnitOfWork.CollectionTagRepository.GetCollectionAsync(3);
var tag = await unitOfWork.CollectionTagRepository.GetCollectionAsync(3);
Assert.NotNull(tag);
Assert.True(tag.Promoted);
Assert.False(string.IsNullOrEmpty(tag.Summary));
@ -149,16 +147,17 @@ public class CollectionTagServiceTests : AbstractDbTest
[Fact]
public async Task UpdateTag_ShouldNotChangeTitle_WhenNotKavitaSource()
{
await SeedSeries();
var (unitOfWork, context, _) = await CreateDatabase();
var service = await Setup(unitOfWork, context);
var user = await UnitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Collections);
var user = await unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Collections);
Assert.NotNull(user);
user.Collections.Add(new AppUserCollectionBuilder("UpdateTag_ShouldNotChangeTitle_WhenNotKavitaSource").WithSource(ScrobbleProvider.Mal).Build());
UnitOfWork.UserRepository.Update(user);
await UnitOfWork.CommitAsync();
unitOfWork.UserRepository.Update(user);
await unitOfWork.CommitAsync();
await _service.UpdateTag(new AppUserCollectionDto()
await service.UpdateTag(new AppUserCollectionDto()
{
Title = "New Title",
Id = 3,
@ -167,7 +166,7 @@ public class CollectionTagServiceTests : AbstractDbTest
AgeRating = AgeRating.Unknown
}, 1);
var tag = await UnitOfWork.CollectionTagRepository.GetCollectionAsync(3);
var tag = await unitOfWork.CollectionTagRepository.GetCollectionAsync(3);
Assert.NotNull(tag);
Assert.Equal("UpdateTag_ShouldNotChangeTitle_WhenNotKavitaSource", tag.Title);
Assert.False(string.IsNullOrEmpty(tag.Summary));
@ -177,10 +176,11 @@ public class CollectionTagServiceTests : AbstractDbTest
public async Task UpdateTag_ShouldThrowException_WhenTagDoesNotExist()
{
// Arrange
await SeedSeries();
var (unitOfWork, context, _) = await CreateDatabase();
var service = await Setup(unitOfWork, context);
// Act & Assert
var exception = await Assert.ThrowsAsync<KavitaException>(() => _service.UpdateTag(new AppUserCollectionDto()
var exception = await Assert.ThrowsAsync<KavitaException>(() => service.UpdateTag(new AppUserCollectionDto()
{
Title = "Non-existent Tag",
Id = 999, // Non-existent ID
@ -194,15 +194,16 @@ public class CollectionTagServiceTests : AbstractDbTest
public async Task UpdateTag_ShouldThrowException_WhenUserDoesNotOwnTag()
{
// Arrange
await SeedSeries();
var (unitOfWork, context, _) = await CreateDatabase();
var service = await Setup(unitOfWork, context);
// Create a second user
var user2 = new AppUserBuilder("user2", "user2", Seed.DefaultThemes.First()).Build();
UnitOfWork.UserRepository.Add(user2);
await UnitOfWork.CommitAsync();
unitOfWork.UserRepository.Add(user2);
await unitOfWork.CommitAsync();
// Act & Assert
var exception = await Assert.ThrowsAsync<KavitaException>(() => _service.UpdateTag(new AppUserCollectionDto()
var exception = await Assert.ThrowsAsync<KavitaException>(() => service.UpdateTag(new AppUserCollectionDto()
{
Title = "Tag 1",
Id = 1, // This belongs to user1
@ -216,10 +217,11 @@ public class CollectionTagServiceTests : AbstractDbTest
public async Task UpdateTag_ShouldThrowException_WhenTitleIsEmpty()
{
// Arrange
await SeedSeries();
var (unitOfWork, context, _) = await CreateDatabase();
var service = await Setup(unitOfWork, context);
// Act & Assert
var exception = await Assert.ThrowsAsync<KavitaException>(() => _service.UpdateTag(new AppUserCollectionDto()
var exception = await Assert.ThrowsAsync<KavitaException>(() => service.UpdateTag(new AppUserCollectionDto()
{
Title = " ", // Empty after trimming
Id = 1,
@ -233,10 +235,11 @@ public class CollectionTagServiceTests : AbstractDbTest
public async Task UpdateTag_ShouldThrowException_WhenTitleAlreadyExists()
{
// Arrange
await SeedSeries();
var (unitOfWork, context, _) = await CreateDatabase();
var service = await Setup(unitOfWork, context);
// Act & Assert
var exception = await Assert.ThrowsAsync<KavitaException>(() => _service.UpdateTag(new AppUserCollectionDto()
var exception = await Assert.ThrowsAsync<KavitaException>(() => service.UpdateTag(new AppUserCollectionDto()
{
Title = "Tag 2", // Already exists
Id = 1, // Trying to rename Tag 1 to Tag 2
@ -250,10 +253,11 @@ public class CollectionTagServiceTests : AbstractDbTest
public async Task UpdateTag_ShouldUpdateCoverImageSettings()
{
// Arrange
await SeedSeries();
var (unitOfWork, context, _) = await CreateDatabase();
var service = await Setup(unitOfWork, context);
// Act
await _service.UpdateTag(new AppUserCollectionDto()
await service.UpdateTag(new AppUserCollectionDto()
{
Title = "Tag 1",
Id = 1,
@ -261,19 +265,19 @@ public class CollectionTagServiceTests : AbstractDbTest
}, 1);
// Assert
var tag = await UnitOfWork.CollectionTagRepository.GetCollectionAsync(1);
var tag = await unitOfWork.CollectionTagRepository.GetCollectionAsync(1);
Assert.NotNull(tag);
Assert.True(tag.CoverImageLocked);
// Now test unlocking the cover image
await _service.UpdateTag(new AppUserCollectionDto()
await service.UpdateTag(new AppUserCollectionDto()
{
Title = "Tag 1",
Id = 1,
CoverImageLocked = false
}, 1);
tag = await UnitOfWork.CollectionTagRepository.GetCollectionAsync(1);
tag = await unitOfWork.CollectionTagRepository.GetCollectionAsync(1);
Assert.NotNull(tag);
Assert.False(tag.CoverImageLocked);
Assert.Equal(string.Empty, tag.CoverImage);
@ -283,16 +287,17 @@ public class CollectionTagServiceTests : AbstractDbTest
public async Task UpdateTag_ShouldAllowPromoteForAdminRole()
{
// Arrange
await SeedSeries();
var (unitOfWork, context, _) = await CreateDatabase();
var service = await Setup(unitOfWork, context);
// Setup a user with admin role
var user = await UnitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Collections);
var user = await unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Collections);
Assert.NotNull(user);
await AddUserWithRole(user.Id, PolicyConstants.AdminRole);
await AddUserWithRole(context, user.Id, PolicyConstants.AdminRole);
// Act - Try to promote a tag that wasn't previously promoted
await _service.UpdateTag(new AppUserCollectionDto()
await service.UpdateTag(new AppUserCollectionDto()
{
Title = "Tag 1",
Id = 1,
@ -300,7 +305,7 @@ public class CollectionTagServiceTests : AbstractDbTest
}, 1);
// Assert
var tag = await UnitOfWork.CollectionTagRepository.GetCollectionAsync(1);
var tag = await unitOfWork.CollectionTagRepository.GetCollectionAsync(1);
Assert.NotNull(tag);
Assert.True(tag.Promoted);
}
@ -309,17 +314,18 @@ public class CollectionTagServiceTests : AbstractDbTest
public async Task UpdateTag_ShouldAllowPromoteForPromoteRole()
{
// Arrange
await SeedSeries();
var (unitOfWork, context, _) = await CreateDatabase();
var service = await Setup(unitOfWork, context);
// Setup a user with promote role
var user = await UnitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Collections);
var user = await unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Collections);
Assert.NotNull(user);
// Mock to return promote role for the user
await AddUserWithRole(user.Id, PolicyConstants.PromoteRole);
await AddUserWithRole(context, user.Id, PolicyConstants.PromoteRole);
// Act - Try to promote a tag that wasn't previously promoted
await _service.UpdateTag(new AppUserCollectionDto()
await service.UpdateTag(new AppUserCollectionDto()
{
Title = "Tag 1",
Id = 1,
@ -327,7 +333,7 @@ public class CollectionTagServiceTests : AbstractDbTest
}, 1);
// Assert
var tag = await UnitOfWork.CollectionTagRepository.GetCollectionAsync(1);
var tag = await unitOfWork.CollectionTagRepository.GetCollectionAsync(1);
Assert.NotNull(tag);
Assert.True(tag.Promoted);
}
@ -336,14 +342,15 @@ public class CollectionTagServiceTests : AbstractDbTest
public async Task UpdateTag_ShouldNotChangePromotion_WhenUserHasNoPermission()
{
// Arrange
await SeedSeries();
var (unitOfWork, context, _) = await CreateDatabase();
var service = await Setup(unitOfWork, context);
// Setup a user with no special roles
var user = await UnitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Collections);
var user = await unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Collections);
Assert.NotNull(user);
// Act - Try to promote a tag without proper role
await _service.UpdateTag(new AppUserCollectionDto()
await service.UpdateTag(new AppUserCollectionDto()
{
Title = "Tag 1",
Id = 1,
@ -351,7 +358,7 @@ public class CollectionTagServiceTests : AbstractDbTest
}, 1);
// Assert
var tag = await UnitOfWork.CollectionTagRepository.GetCollectionAsync(1);
var tag = await unitOfWork.CollectionTagRepository.GetCollectionAsync(1);
Assert.NotNull(tag);
Assert.False(tag.Promoted); // Should remain unpromoted
}
@ -363,17 +370,18 @@ public class CollectionTagServiceTests : AbstractDbTest
[Fact]
public async Task RemoveTagFromSeries_RemoveSeriesFromTag()
{
await SeedSeries();
var (unitOfWork, context, _) = await CreateDatabase();
var service = await Setup(unitOfWork, context);
var user = await UnitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Collections);
var user = await unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Collections);
Assert.NotNull(user);
// Tag 2 has 2 series
var tag = await UnitOfWork.CollectionTagRepository.GetCollectionAsync(2);
var tag = await unitOfWork.CollectionTagRepository.GetCollectionAsync(2);
Assert.NotNull(tag);
await _service.RemoveTagFromSeries(tag, new[] {1});
var userCollections = await UnitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Collections);
await service.RemoveTagFromSeries(tag, new[] {1});
var userCollections = await unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Collections);
Assert.Equal(2, userCollections!.Collections.Count);
Assert.Single(tag.Items);
Assert.Equal(2, tag.Items.First().Id);
@ -385,16 +393,17 @@ public class CollectionTagServiceTests : AbstractDbTest
[Fact]
public async Task RemoveTagFromSeries_RemoveSeriesFromTag_UpdatesRating()
{
await SeedSeries();
var (unitOfWork, context, _) = await CreateDatabase();
var service = await Setup(unitOfWork, context);
var user = await UnitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Collections);
var user = await unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Collections);
Assert.NotNull(user);
// Tag 2 has 2 series
var tag = await UnitOfWork.CollectionTagRepository.GetCollectionAsync(2);
var tag = await unitOfWork.CollectionTagRepository.GetCollectionAsync(2);
Assert.NotNull(tag);
await _service.RemoveTagFromSeries(tag, new[] {1});
await service.RemoveTagFromSeries(tag, new[] {1});
Assert.Equal(AgeRating.G, tag.AgeRating);
}
@ -405,25 +414,28 @@ public class CollectionTagServiceTests : AbstractDbTest
[Fact]
public async Task RemoveTagFromSeries_RemoveSeriesFromTag_DeleteTagWhenNoSeriesLeft()
{
await SeedSeries();
var (unitOfWork, context, _) = await CreateDatabase();
var service = await Setup(unitOfWork, context);
var user = await UnitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Collections);
var user = await unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Collections);
Assert.NotNull(user);
// Tag 1 has 1 series
var tag = await UnitOfWork.CollectionTagRepository.GetCollectionAsync(1);
var tag = await unitOfWork.CollectionTagRepository.GetCollectionAsync(1);
Assert.NotNull(tag);
await _service.RemoveTagFromSeries(tag, new[] {1});
var tag2 = await UnitOfWork.CollectionTagRepository.GetCollectionAsync(1);
await service.RemoveTagFromSeries(tag, new[] {1});
var tag2 = await unitOfWork.CollectionTagRepository.GetCollectionAsync(1);
Assert.Null(tag2);
}
[Fact]
public async Task RemoveTagFromSeries_ShouldReturnFalse_WhenTagIsNull()
{
// Act
var result = await _service.RemoveTagFromSeries(null, [1]);
var (unitOfWork, context, _) = await CreateDatabase();
var service = await Setup(unitOfWork, context);
var result = await service.RemoveTagFromSeries(null, [1]);
// Assert
Assert.False(result);
@ -433,18 +445,19 @@ public class CollectionTagServiceTests : AbstractDbTest
public async Task RemoveTagFromSeries_ShouldHandleEmptySeriesIdsList()
{
// Arrange
await SeedSeries();
var (unitOfWork, context, _) = await CreateDatabase();
var service = await Setup(unitOfWork, context);
var tag = await UnitOfWork.CollectionTagRepository.GetCollectionAsync(1);
var tag = await unitOfWork.CollectionTagRepository.GetCollectionAsync(1);
Assert.NotNull(tag);
var initialItemCount = tag.Items.Count;
// Act
var result = await _service.RemoveTagFromSeries(tag, Array.Empty<int>());
var result = await service.RemoveTagFromSeries(tag, Array.Empty<int>());
// Assert
Assert.True(result);
tag = await UnitOfWork.CollectionTagRepository.GetCollectionAsync(1);
tag = await unitOfWork.CollectionTagRepository.GetCollectionAsync(1);
Assert.NotNull(tag);
Assert.Equal(initialItemCount, tag.Items.Count); // No items should be removed
}
@ -453,18 +466,19 @@ public class CollectionTagServiceTests : AbstractDbTest
public async Task RemoveTagFromSeries_ShouldHandleNonExistentSeriesIds()
{
// Arrange
await SeedSeries();
var (unitOfWork, context, _) = await CreateDatabase();
var service = await Setup(unitOfWork, context);
var tag = await UnitOfWork.CollectionTagRepository.GetCollectionAsync(1);
var tag = await unitOfWork.CollectionTagRepository.GetCollectionAsync(1);
Assert.NotNull(tag);
var initialItemCount = tag.Items.Count;
// Act - Try to remove a series that doesn't exist in the tag
var result = await _service.RemoveTagFromSeries(tag, [999]);
var result = await service.RemoveTagFromSeries(tag, [999]);
// Assert
Assert.True(result);
tag = await UnitOfWork.CollectionTagRepository.GetCollectionAsync(1);
tag = await unitOfWork.CollectionTagRepository.GetCollectionAsync(1);
Assert.NotNull(tag);
Assert.Equal(initialItemCount, tag.Items.Count); // No items should be removed
}
@ -473,23 +487,24 @@ public class CollectionTagServiceTests : AbstractDbTest
public async Task RemoveTagFromSeries_ShouldHandleNullItemsList()
{
// Arrange
await SeedSeries();
var (unitOfWork, context, _) = await CreateDatabase();
var service = await Setup(unitOfWork, context);
var tag = await UnitOfWork.CollectionTagRepository.GetCollectionAsync(1);
var tag = await unitOfWork.CollectionTagRepository.GetCollectionAsync(1);
Assert.NotNull(tag);
// Force null items list
tag.Items = null;
UnitOfWork.CollectionTagRepository.Update(tag);
await UnitOfWork.CommitAsync();
unitOfWork.CollectionTagRepository.Update(tag);
await unitOfWork.CommitAsync();
// Act
var result = await _service.RemoveTagFromSeries(tag, [1]);
var result = await service.RemoveTagFromSeries(tag, [1]);
// Assert
Assert.True(result);
// The tag should not be removed since the items list was null, not empty
var tagAfter = await UnitOfWork.CollectionTagRepository.GetCollectionAsync(1);
var tagAfter = await unitOfWork.CollectionTagRepository.GetCollectionAsync(1);
Assert.Null(tagAfter);
}
@ -497,25 +512,26 @@ public class CollectionTagServiceTests : AbstractDbTest
public async Task RemoveTagFromSeries_ShouldUpdateAgeRating_WhenMultipleSeriesRemain()
{
// Arrange
await SeedSeries();
var (unitOfWork, context, _) = await CreateDatabase();
var service = await Setup(unitOfWork, context);
// Add a third series with a different age rating
var s3 = new SeriesBuilder("Series 3").WithMetadata(new SeriesMetadataBuilder().WithAgeRating(AgeRating.PG).Build()).Build();
Context.Library.First().Series.Add(s3);
await UnitOfWork.CommitAsync();
context.Library.First().Series.Add(s3);
await unitOfWork.CommitAsync();
// Add series 3 to tag 2
var tag = await UnitOfWork.CollectionTagRepository.GetCollectionAsync(2);
var tag = await unitOfWork.CollectionTagRepository.GetCollectionAsync(2);
Assert.NotNull(tag);
tag.Items.Add(s3);
UnitOfWork.CollectionTagRepository.Update(tag);
await UnitOfWork.CommitAsync();
unitOfWork.CollectionTagRepository.Update(tag);
await unitOfWork.CommitAsync();
// Act - Remove the series with Mature rating
await _service.RemoveTagFromSeries(tag, new[] {1});
await service.RemoveTagFromSeries(tag, new[] {1});
// Assert
tag = await UnitOfWork.CollectionTagRepository.GetCollectionAsync(2);
tag = await unitOfWork.CollectionTagRepository.GetCollectionAsync(2);
Assert.NotNull(tag);
Assert.Equal(2, tag.Items.Count);

View File

@ -3,6 +3,7 @@ using System.IO.Abstractions;
using System.Reflection;
using System.Threading.Tasks;
using API.Constants;
using API.Data;
using API.Entities.Enums;
using API.Extensions;
using API.Services;
@ -14,14 +15,13 @@ using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using NSubstitute;
using Xunit;
using Xunit.Abstractions;
namespace API.Tests.Services;
public class CoverDbServiceTests : AbstractDbTest
public class CoverDbServiceTests(ITestOutputHelper outputHelper): AbstractDbTest(outputHelper)
{
private readonly DirectoryService _directoryService;
private readonly IEasyCachingProviderFactory _cacheFactory = Substitute.For<IEasyCachingProviderFactory>();
private readonly ICoverDbService _coverDbService;
private static readonly IEasyCachingProviderFactory CacheFactory = Substitute.For<IEasyCachingProviderFactory>();
private static readonly string FaviconPath = Path.Join(Directory.GetCurrentDirectory(),
"../../../Services/Test Data/CoverDbService/Favicons");
@ -31,57 +31,59 @@ public class CoverDbServiceTests : AbstractDbTest
private static readonly string TempPath = Path.Join(Directory.GetCurrentDirectory(),
"../../../Services/Test Data/CoverDbService/Temp");
public CoverDbServiceTests()
{
_directoryService = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), CreateFileSystem());
var imageService = new ImageService(Substitute.For<ILogger<ImageService>>(), _directoryService);
_coverDbService = new CoverDbService(Substitute.For<ILogger<CoverDbService>>(), _directoryService, _cacheFactory,
Substitute.For<IHostEnvironment>(), imageService, UnitOfWork, Substitute.For<IEventHub>());
}
protected override Task ResetDb()
private static async Task<(IDirectoryService, ICoverDbService)> Setup(IUnitOfWork unitOfWork)
{
throw new System.NotImplementedException();
var directoryService = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), CreateFileSystem());
var imageService = new ImageService(Substitute.For<ILogger<ImageService>>(), directoryService);
var coverDbService = new CoverDbService(Substitute.For<ILogger<CoverDbService>>(), directoryService, CacheFactory,
Substitute.For<IHostEnvironment>(), imageService, unitOfWork, Substitute.For<IEventHub>());
return (directoryService, coverDbService);
}
#region Download Favicon
/// <summary>
/// I cannot figure out how to test this code due to the reliance on the _directoryService.FaviconDirectory and not being
/// I cannot figure out how to test this code due to the reliance on the directoryService.FaviconDirectory and not being
/// able to redirect it to the real filesystem.
/// </summary>
public async Task DownloadFaviconAsync_ShouldDownloadAndMatchExpectedFavicon()
{
var (unitOfWork, context, _) = await CreateDatabase();
var (directoryService, coverDbService) = await Setup(unitOfWork);
// Arrange
var testUrl = "https://anilist.co/anime/6205/Kmpfer/";
var encodeFormat = EncodeFormat.WEBP;
var expectedFaviconPath = Path.Combine(FaviconPath, "anilist.co.webp");
// Ensure TempPath exists
_directoryService.ExistOrCreate(TempPath);
directoryService.ExistOrCreate(TempPath);
var baseUrl = "https://anilist.co";
// Ensure there is no cache result for this URL
var provider = Substitute.For<IEasyCachingProvider>();
provider.GetAsync<string>(baseUrl).Returns(new CacheValue<string>(null, false));
_cacheFactory.GetCachingProvider(EasyCacheProfiles.Favicon).Returns(provider);
CacheFactory.GetCachingProvider(EasyCacheProfiles.Favicon).Returns(provider);
// // Replace favicon directory with TempPath
// var directoryService = (DirectoryService)_directoryService;
// var directoryService = (DirectoryService)directoryService;
// directoryService.FaviconDirectory = TempPath;
// Hack: Swap FaviconDirectory with TempPath for ability to download real files
typeof(DirectoryService)
.GetField("FaviconDirectory", BindingFlags.NonPublic | BindingFlags.Instance)
?.SetValue(_directoryService, TempPath);
?.SetValue(directoryService, TempPath);
// Act
var resultFilename = await _coverDbService.DownloadFaviconAsync(testUrl, encodeFormat);
var resultFilename = await coverDbService.DownloadFaviconAsync(testUrl, encodeFormat);
var actualFaviconPath = Path.Combine(TempPath, resultFilename);
// Assert file exists
@ -96,6 +98,9 @@ public class CoverDbServiceTests : AbstractDbTest
[Fact]
public async Task DownloadFaviconAsync_ShouldThrowKavitaException_WhenPreviouslyFailedUrlExistsInCache()
{
var (unitOfWork, context, _) = await CreateDatabase();
var (directoryService, coverDbService) = await Setup(unitOfWork);
// Arrange
var testUrl = "https://example.com";
var encodeFormat = EncodeFormat.WEBP;
@ -104,11 +109,11 @@ public class CoverDbServiceTests : AbstractDbTest
provider.GetAsync<string>(Arg.Any<string>())
.Returns(new CacheValue<string>(string.Empty, true)); // Simulate previous failure
_cacheFactory.GetCachingProvider(EasyCacheProfiles.Favicon).Returns(provider);
CacheFactory.GetCachingProvider(EasyCacheProfiles.Favicon).Returns(provider);
// Act & Assert
await Assert.ThrowsAsync<KavitaException>(() =>
_coverDbService.DownloadFaviconAsync(testUrl, encodeFormat));
coverDbService.DownloadFaviconAsync(testUrl, encodeFormat));
}
#endregion

View File

@ -1,37 +1,33 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using API.Data;
using API.DTOs.Device;
using API.Entities;
using API.Entities.Enums.Device;
using API.Services;
using Microsoft.Extensions.Logging;
using NSubstitute;
using Polly;
using Xunit;
using Xunit.Abstractions;
namespace API.Tests.Services;
public class DeviceServiceDbTests : AbstractDbTest
public class DeviceServiceDbTests(ITestOutputHelper outputHelper): AbstractDbTest(outputHelper)
{
private readonly ILogger<DeviceService> _logger = Substitute.For<ILogger<DeviceService>>();
private readonly IDeviceService _deviceService;
public DeviceServiceDbTests() : base()
private async Task<IDeviceService> Setup(IUnitOfWork unitOfWork)
{
_deviceService = new DeviceService(UnitOfWork, _logger, Substitute.For<IEmailService>());
return new DeviceService(unitOfWork, _logger, Substitute.For<IEmailService>());
}
protected override async Task ResetDb()
{
Context.Users.RemoveRange(Context.Users.ToList());
await UnitOfWork.CommitAsync();
}
[Fact]
public async Task CreateDevice_Succeeds()
{
var (unitOfWork, context, _) = await CreateDatabase();
var deviceService = await Setup(unitOfWork);
var user = new AppUser()
{
@ -39,10 +35,10 @@ public class DeviceServiceDbTests : AbstractDbTest
Devices = new List<Device>()
};
Context.Users.Add(user);
await UnitOfWork.CommitAsync();
context.Users.Add(user);
await unitOfWork.CommitAsync();
var device = await _deviceService.Create(new CreateDeviceDto()
var device = await deviceService.Create(new CreateDeviceDto()
{
EmailAddress = "fake@kindle.com",
Name = "Test Kindle",
@ -55,6 +51,8 @@ public class DeviceServiceDbTests : AbstractDbTest
[Fact]
public async Task CreateDevice_ThrowsErrorWhenEmailDoesntMatchRules()
{
var (unitOfWork, context, _) = await CreateDatabase();
var deviceService = await Setup(unitOfWork);
var user = new AppUser()
{
@ -62,10 +60,10 @@ public class DeviceServiceDbTests : AbstractDbTest
Devices = new List<Device>()
};
Context.Users.Add(user);
await UnitOfWork.CommitAsync();
context.Users.Add(user);
await unitOfWork.CommitAsync();
var device = await _deviceService.Create(new CreateDeviceDto()
var device = await deviceService.Create(new CreateDeviceDto()
{
EmailAddress = "fake@gmail.com",
Name = "Test Kindle",

View File

@ -187,6 +187,12 @@ public class DirectoryServiceTests: AbstractFsTest
[Fact]
public void GetFiles_All_MixedPathSeparators()
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
_testOutputHelper.WriteLine("Skipping test on non Windows platform");
return;
}
const string testDirectory = "/manga/";
var fileSystem = new MockFileSystem();
for (var i = 0; i < 10; i++)
@ -810,6 +816,12 @@ public class DirectoryServiceTests: AbstractFsTest
[InlineData(@"M:\", @"M:\Toukyou Akazukin\Vol. 01 Ch. 005.cbz", @"Toukyou Akazukin")]
public void GetFoldersTillRoot_Test(string rootPath, string fullpath, string expectedArray)
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
_testOutputHelper.WriteLine("Skipping test on non Windows platform");
return;
}
var fileSystem = new MockFileSystem();
fileSystem.AddDirectory(rootPath);
fileSystem.AddFile(fullpath, new MockFileData(""));

File diff suppressed because it is too large Load Diff

View File

@ -10,23 +10,26 @@ using API.Entities;
using API.Entities.Enums;
using API.Helpers.Builders;
using API.Services;
using AutoMapper;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NSubstitute;
using Polly;
using Xunit;
using Xunit.Abstractions;
namespace API.Tests.Services;
public class OidcServiceTests: AbstractDbTest
public class OidcServiceTests(ITestOutputHelper outputHelper): AbstractDbTest(outputHelper)
{
[Fact]
public async Task UserSync_Username()
{
await ResetDb();
var (oidcService, _, _, userManager) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (oidcService, _, _, userManager) = await Setup(unitOfWork, context, mapper);
var user = new AppUserBuilder("holo", "holo@localhost")
.WithIdentityProvider(IdentityProvider.OpenIdConnect)
@ -50,7 +53,7 @@ public class OidcServiceTests: AbstractDbTest
// name is updated as the current username is not found, amelia is skipped as it is alredy in use
await oidcService.SyncUserSettings(null!, settings, principal, user);
var dbUser = await UnitOfWork.UserRepository.GetUserByIdAsync(user.Id);
var dbUser = await unitOfWork.UserRepository.GetUserByIdAsync(user.Id);
Assert.NotNull(dbUser);
Assert.Equal("Lawrence", user.UserName);
@ -65,7 +68,7 @@ public class OidcServiceTests: AbstractDbTest
// Ensure a name longer down the list isn't picked if the current username is found
await oidcService.SyncUserSettings(null!, settings, principal, user);
dbUser = await UnitOfWork.UserRepository.GetUserByIdAsync(user.Id);
dbUser = await unitOfWork.UserRepository.GetUserByIdAsync(user.Id);
Assert.NotNull(dbUser);
Assert.Equal("Lawrence", user.UserName);
}
@ -73,15 +76,15 @@ public class OidcServiceTests: AbstractDbTest
[Fact]
public async Task UserSync_CustomClaim()
{
await ResetDb();
var (oidcService, user, _, _) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (oidcService, user, _, _) = await Setup(unitOfWork, context, mapper);
var mangaLib = new LibraryBuilder("Manga", LibraryType.Manga).Build();
var lightNovelsLib = new LibraryBuilder("Light Novels", LibraryType.LightNovel).Build();
UnitOfWork.LibraryRepository.Add(mangaLib);
UnitOfWork.LibraryRepository.Add(lightNovelsLib);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Add(mangaLib);
unitOfWork.LibraryRepository.Add(lightNovelsLib);
await unitOfWork.CommitAsync();
const string claim = "groups";
var claims = new List<Claim>()
@ -105,19 +108,19 @@ public class OidcServiceTests: AbstractDbTest
await oidcService.SyncUserSettings(null!, settings, principal, user);
// Check correct roles assigned
var userRoles = await UnitOfWork.UserRepository.GetRoles(user.Id);
var userRoles = await unitOfWork.UserRepository.GetRoles(user.Id);
Assert.Contains(PolicyConstants.LoginRole, userRoles);
Assert.Contains(PolicyConstants.DownloadRole, userRoles);
Assert.DoesNotContain(PolicyConstants.PromoteRole, userRoles);
// Check correct libraries
var libraries = (await UnitOfWork.LibraryRepository.GetLibrariesForUserIdAsync(user.Id)).Select(l => l.Name).ToList();
var libraries = (await unitOfWork.LibraryRepository.GetLibrariesForUserIdAsync(user.Id)).Select(l => l.Name).ToList();
Assert.Single(libraries);
Assert.Contains(mangaLib.Name, libraries);
Assert.DoesNotContain(lightNovelsLib.Name, libraries);
// Check correct age restrictions
var dbUser = await UnitOfWork.UserRepository.GetUserByIdAsync(user.Id);
var dbUser = await unitOfWork.UserRepository.GetUserByIdAsync(user.Id);
Assert.NotNull(dbUser);
Assert.Equal(AgeRating.Mature, dbUser.AgeRestriction);
Assert.False(dbUser.AgeRestrictionIncludeUnknowns);
@ -126,15 +129,15 @@ public class OidcServiceTests: AbstractDbTest
[Fact]
public async Task UserSync_CustomPrefix()
{
await ResetDb();
var (oidcService, user, _, _) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (oidcService, user, _, _) = await Setup(unitOfWork, context, mapper);
var mangaLib = new LibraryBuilder("Manga", LibraryType.Manga).Build();
var lightNovelsLib = new LibraryBuilder("Light Novels", LibraryType.LightNovel).Build();
UnitOfWork.LibraryRepository.Add(mangaLib);
UnitOfWork.LibraryRepository.Add(lightNovelsLib);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Add(mangaLib);
unitOfWork.LibraryRepository.Add(lightNovelsLib);
await unitOfWork.CommitAsync();
const string prefix = "kavita-";
var claims = new List<Claim>()
@ -158,19 +161,19 @@ public class OidcServiceTests: AbstractDbTest
await oidcService.SyncUserSettings(null!, settings, principal, user);
// Check correct roles assigned
var userRoles = await UnitOfWork.UserRepository.GetRoles(user.Id);
var userRoles = await unitOfWork.UserRepository.GetRoles(user.Id);
Assert.Contains(PolicyConstants.LoginRole, userRoles);
Assert.Contains(PolicyConstants.DownloadRole, userRoles);
Assert.DoesNotContain(PolicyConstants.PromoteRole, userRoles);
// Check correct libraries
var libraries = (await UnitOfWork.LibraryRepository.GetLibrariesForUserIdAsync(user.Id)).Select(l => l.Name).ToList();
var libraries = (await unitOfWork.LibraryRepository.GetLibrariesForUserIdAsync(user.Id)).Select(l => l.Name).ToList();
Assert.Single(libraries);
Assert.Contains(mangaLib.Name, libraries);
Assert.DoesNotContain(lightNovelsLib.Name, libraries);
// Check correct age restrictions
var dbUser = await UnitOfWork.UserRepository.GetUserByIdAsync(user.Id);
var dbUser = await unitOfWork.UserRepository.GetUserByIdAsync(user.Id);
Assert.NotNull(dbUser);
Assert.Equal(AgeRating.Mature, dbUser.AgeRestriction);
Assert.False(dbUser.AgeRestrictionIncludeUnknowns);
@ -179,8 +182,8 @@ public class OidcServiceTests: AbstractDbTest
[Fact]
public async Task SyncRoles()
{
await ResetDb();
var (oidcService, user, _, _) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (oidcService, user, _, _) = await Setup(unitOfWork, context, mapper);
var claims = new List<Claim>()
{
@ -197,7 +200,7 @@ public class OidcServiceTests: AbstractDbTest
await oidcService.SyncUserSettings(null!, settings, principal, user);
var userRoles = await UnitOfWork.UserRepository.GetRoles(user.Id);
var userRoles = await unitOfWork.UserRepository.GetRoles(user.Id);
Assert.Contains(PolicyConstants.LoginRole, userRoles);
Assert.Contains(PolicyConstants.DownloadRole, userRoles);
@ -208,7 +211,7 @@ public class OidcServiceTests: AbstractDbTest
await oidcService.SyncUserSettings(null!, settings, principal, user);
userRoles = await UnitOfWork.UserRepository.GetRoles(user.Id);
userRoles = await unitOfWork.UserRepository.GetRoles(user.Id);
Assert.Contains(PolicyConstants.LoginRole, userRoles);
Assert.DoesNotContain(PolicyConstants.DownloadRole, userRoles);
}
@ -216,15 +219,15 @@ public class OidcServiceTests: AbstractDbTest
[Fact]
public async Task SyncLibraries()
{
await ResetDb();
var (oidcService, user, _, _) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (oidcService, user, _, _) = await Setup(unitOfWork, context, mapper);
var mangaLib = new LibraryBuilder("Manga", LibraryType.Manga).Build();
var lightNovelsLib = new LibraryBuilder("Light Novels", LibraryType.LightNovel).Build();
UnitOfWork.LibraryRepository.Add(mangaLib);
UnitOfWork.LibraryRepository.Add(lightNovelsLib);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Add(mangaLib);
unitOfWork.LibraryRepository.Add(lightNovelsLib);
await unitOfWork.CommitAsync();
var claims = new List<Claim>()
{
@ -240,7 +243,7 @@ public class OidcServiceTests: AbstractDbTest
await oidcService.SyncUserSettings(null!, settings, principal, user);
var libraries = (await UnitOfWork.LibraryRepository.GetLibrariesForUserIdAsync(user.Id)).Select(l => l.Name).ToList();
var libraries = (await unitOfWork.LibraryRepository.GetLibrariesForUserIdAsync(user.Id)).Select(l => l.Name).ToList();
Assert.Single(libraries);
Assert.Contains(mangaLib.Name, libraries);
Assert.DoesNotContain(lightNovelsLib.Name, libraries);
@ -253,7 +256,7 @@ public class OidcServiceTests: AbstractDbTest
await oidcService.SyncUserSettings(null!, settings, principal, user);
// Check access has swicthed
libraries = (await UnitOfWork.LibraryRepository.GetLibrariesForUserIdAsync(user.Id)).Select(l => l.Name).ToList();
libraries = (await unitOfWork.LibraryRepository.GetLibrariesForUserIdAsync(user.Id)).Select(l => l.Name).ToList();
Assert.Single(libraries);
Assert.Contains(lightNovelsLib.Name, libraries);
Assert.DoesNotContain(mangaLib.Name, libraries);
@ -262,8 +265,8 @@ public class OidcServiceTests: AbstractDbTest
[Fact]
public async Task SyncAgeRestrictions_NoRestrictions()
{
await ResetDb();
var (oidcService, user, _, _) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (oidcService, user, _, _) = await Setup(unitOfWork, context, mapper);
var claims = new List<Claim>()
{
@ -280,7 +283,7 @@ public class OidcServiceTests: AbstractDbTest
await oidcService.SyncUserSettings(null!, settings, principal, user);
var dbUser = await UnitOfWork.UserRepository.GetUserByIdAsync(user.Id);
var dbUser = await unitOfWork.UserRepository.GetUserByIdAsync(user.Id);
Assert.NotNull(dbUser);
Assert.Equal(AgeRating.NotApplicable, dbUser.AgeRestriction);
Assert.True(dbUser.AgeRestrictionIncludeUnknowns);
@ -289,8 +292,8 @@ public class OidcServiceTests: AbstractDbTest
[Fact]
public async Task SyncAgeRestrictions_IncludeUnknowns()
{
await ResetDb();
var (oidcService, user, _, _) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (oidcService, user, _, _) = await Setup(unitOfWork, context, mapper);
var claims = new List<Claim>()
{
@ -307,7 +310,7 @@ public class OidcServiceTests: AbstractDbTest
await oidcService.SyncUserSettings(null!, settings, principal, user);
var dbUser = await UnitOfWork.UserRepository.GetUserByIdAsync(user.Id);
var dbUser = await unitOfWork.UserRepository.GetUserByIdAsync(user.Id);
Assert.NotNull(dbUser);
Assert.Equal(AgeRating.Mature, dbUser.AgeRestriction);
Assert.True(dbUser.AgeRestrictionIncludeUnknowns);
@ -316,8 +319,8 @@ public class OidcServiceTests: AbstractDbTest
[Fact]
public async Task SyncAgeRestriction_AdminNone()
{
await ResetDb();
var (oidcService, user, _, _) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (oidcService, user, _, _) = await Setup(unitOfWork, context, mapper);
var claims = new List<Claim>()
{
@ -334,7 +337,7 @@ public class OidcServiceTests: AbstractDbTest
await oidcService.SyncUserSettings(null!, settings, principal, user);
var dbUser = await UnitOfWork.UserRepository.GetUserByIdAsync(user.Id);
var dbUser = await unitOfWork.UserRepository.GetUserByIdAsync(user.Id);
Assert.NotNull(dbUser);
Assert.Equal(AgeRating.NotApplicable, dbUser.AgeRestriction);
Assert.True(dbUser.AgeRestrictionIncludeUnknowns);
@ -343,8 +346,8 @@ public class OidcServiceTests: AbstractDbTest
[Fact]
public async Task SyncAgeRestriction_MultipleAgeRestrictionClaims()
{
await ResetDb();
var (oidcService, user, _, _) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (oidcService, user, _, _) = await Setup(unitOfWork, context, mapper);
var claims = new List<Claim>()
{
@ -362,7 +365,7 @@ public class OidcServiceTests: AbstractDbTest
await oidcService.SyncUserSettings(null!, settings, principal, user);
var dbUser = await UnitOfWork.UserRepository.GetUserByIdAsync(user.Id);
var dbUser = await unitOfWork.UserRepository.GetUserByIdAsync(user.Id);
Assert.NotNull(dbUser);
Assert.Equal(AgeRating.Mature, dbUser.AgeRestriction);
}
@ -370,8 +373,8 @@ public class OidcServiceTests: AbstractDbTest
[Fact]
public async Task SyncAgeRestriction_NoAgeRestrictionClaims()
{
await ResetDb();
var (oidcService, user, _, _) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (oidcService, user, _, _) = await Setup(unitOfWork, context, mapper);
var identity = new ClaimsIdentity([]);
var principal = new ClaimsPrincipal(identity);
@ -383,7 +386,7 @@ public class OidcServiceTests: AbstractDbTest
await oidcService.SyncUserSettings(null!, settings, principal, user);
var dbUser = await UnitOfWork.UserRepository.GetUserByIdAsync(user.Id);
var dbUser = await unitOfWork.UserRepository.GetUserByIdAsync(user.Id);
Assert.NotNull(dbUser);
Assert.Equal(AgeRating.NotApplicable, dbUser.AgeRestriction);
Assert.True(dbUser.AgeRestrictionIncludeUnknowns);
@ -394,7 +397,7 @@ public class OidcServiceTests: AbstractDbTest
await oidcService.SyncUserSettings(null!, settings, principal, user);
dbUser = await UnitOfWork.UserRepository.GetUserByIdAsync(user.Id);
dbUser = await unitOfWork.UserRepository.GetUserByIdAsync(user.Id);
Assert.NotNull(dbUser);
Assert.Equal(AgeRating.NotApplicable, dbUser.AgeRestriction);
Assert.True(dbUser.AgeRestrictionIncludeUnknowns);
@ -403,11 +406,11 @@ public class OidcServiceTests: AbstractDbTest
[Fact]
public async Task SyncUserSettings_DontChangeDefaultAdmin()
{
await ResetDb();
var (oidcService, _, _, userManager) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (oidcService, _, _, userManager) = await Setup(unitOfWork, context, mapper);
// Make user default user
var user = await UnitOfWork.UserRepository.GetDefaultAdminUser();
var user = await unitOfWork.UserRepository.GetDefaultAdminUser();
var settings = new OidcConfigDto
{
@ -424,7 +427,7 @@ public class OidcServiceTests: AbstractDbTest
await oidcService.SyncUserSettings(null!, settings, principal, user);
var userFromDb = await UnitOfWork.UserRepository.GetUserByIdAsync(user.Id);
var userFromDb = await unitOfWork.UserRepository.GetUserByIdAsync(user.Id);
Assert.NotNull(userFromDb);
Assert.NotEqual(AgeRating.Teen, userFromDb.AgeRestriction);
@ -436,7 +439,7 @@ public class OidcServiceTests: AbstractDbTest
Assert.True(res.Succeeded);
await oidcService.SyncUserSettings(null!, settings, principal, newUser);
userFromDb = await UnitOfWork.UserRepository.GetUserByIdAsync(newUser.Id);
userFromDb = await unitOfWork.UserRepository.GetUserByIdAsync(newUser.Id);
Assert.NotNull(userFromDb);
Assert.True(await userManager.IsInRoleAsync(newUser, PolicyConstants.ChangePasswordRole));
Assert.Equal(AgeRating.Teen, userFromDb.AgeRestriction);
@ -446,8 +449,8 @@ public class OidcServiceTests: AbstractDbTest
[Fact]
public async Task FindBestAvailableName_NoDuplicates()
{
await ResetDb();
var (oidcService, _, _, userManager) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (oidcService, _, _, userManager) = await Setup(unitOfWork, context, mapper);
const string preferredName = "PreferredName";
@ -514,8 +517,12 @@ public class OidcServiceTests: AbstractDbTest
Assert.Null(bestName);
}
private async Task<(OidcService, AppUser, IAccountService, UserManager<AppUser>)> Setup()
private async Task<(OidcService, AppUser, IAccountService, UserManager<AppUser>)> Setup(IUnitOfWork unitOfWork, DataContext context, IMapper mapper)
{
// Remove the default library created with the AbstractDbTest class
context.Library.RemoveRange(context.Library);
await context.SaveChangesAsync();
var defaultAdmin = new AppUserBuilder("defaultAdmin", "defaultAdmin@localhost")
.WithRole(PolicyConstants.AdminRole)
.Build();
@ -529,7 +536,7 @@ public class OidcServiceTests: AbstractDbTest
int,
IdentityUserRole<int>,
IdentityRoleClaim<int>
>(Context);
>(context);
var roleManager = new RoleManager<AppRole>(
roleStore,
@ -559,7 +566,7 @@ public class OidcServiceTests: AbstractDbTest
IdentityUserLogin<int>,
IdentityUserToken<int>,
IdentityRoleClaim<int>
>(Context);
>(context);
var userManager = new UserManager<AppUser>(userStore,
new OptionsWrapper<IdentityOptions>(new IdentityOptions()),
new PasswordHasher<AppUser>(),
@ -574,15 +581,8 @@ public class OidcServiceTests: AbstractDbTest
await userManager.CreateAsync(user);
await userManager.CreateAsync(defaultAdmin);
var accountService = new AccountService(userManager, Substitute.For<ILogger<AccountService>>(), UnitOfWork, Mapper, Substitute.For<ILocalizationService>());
var oidcService = new OidcService(Substitute.For<ILogger<OidcService>>(), userManager, UnitOfWork, accountService, Substitute.For<IEmailService>());
var accountService = new AccountService(userManager, Substitute.For<ILogger<AccountService>>(), unitOfWork, mapper, Substitute.For<ILocalizationService>());
var oidcService = new OidcService(Substitute.For<ILogger<OidcService>>(), userManager, unitOfWork, accountService, Substitute.For<IEmailService>());
return (oidcService, user, accountService, userManager);
}
protected override async Task ResetDb()
{
Context.AppUser.RemoveRange(Context.AppUser);
Context.Library.RemoveRange(Context.Library);
await UnitOfWork.CommitAsync();
}
}

View File

@ -5,6 +5,7 @@ using System.IO.Abstractions;
using System.IO.Abstractions.TestingHelpers;
using System.Linq;
using System.Threading.Tasks;
using API.Data;
using API.Data.Metadata;
using API.Data.Repositories;
using API.Entities.Enums;
@ -90,23 +91,21 @@ public class MockReadingItemService : IReadingItemService
}
}
public class ParseScannedFilesTests : AbstractDbTest
public class ParseScannedFilesTests: AbstractDbTest
{
private readonly ILogger<ParseScannedFiles> _logger = Substitute.For<ILogger<ParseScannedFiles>>();
private readonly ScannerHelper _scannerHelper;
private readonly ITestOutputHelper _outputHelper;
public ParseScannedFilesTests(ITestOutputHelper testOutputHelper)
public ParseScannedFilesTests(ITestOutputHelper testOutputHelper): base(testOutputHelper)
{
// Since ProcessFile relies on _readingItemService, we can implement our own versions of _readingItemService so we have control over how the calls work
GlobalConfiguration.Configuration.UseInMemoryStorage();
_scannerHelper = new ScannerHelper(UnitOfWork, testOutputHelper);
_outputHelper = testOutputHelper;
}
protected override async Task ResetDb()
private async Task<ScannerHelper> Setup(IUnitOfWork unitOfWork)
{
Context.Series.RemoveRange(Context.Series.ToList());
await Context.SaveChangesAsync();
return new ScannerHelper(unitOfWork, _outputHelper);
}
#region MergeName
@ -193,6 +192,9 @@ public class ParseScannedFilesTests : AbstractDbTest
[Fact]
public async Task ScanLibrariesForSeries_ShouldFindFiles()
{
var (unitOfWork, context, mapper) = await CreateDatabase();
var scannerHelper = await Setup(unitOfWork);
var fileSystem = new MockFileSystem();
fileSystem.AddDirectory(Root + "Data/");
fileSystem.AddFile(Root + "Data/Accel World v1.cbz", new MockFileData(string.Empty));
@ -206,20 +208,20 @@ public class ParseScannedFilesTests : AbstractDbTest
var library =
await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(1,
await unitOfWork.LibraryRepository.GetLibraryForIdAsync(1,
LibraryIncludes.Folders | LibraryIncludes.FileTypes);
Assert.NotNull(library);
library.Type = LibraryType.Manga;
var parsedSeries = await psf.ScanLibrariesForSeries(library, new List<string>() {Root + "Data/"}, false,
await UnitOfWork.SeriesRepository.GetFolderPathMap(1));
await unitOfWork.SeriesRepository.GetFolderPathMap(1));
// Assert.Equal(3, parsedSeries.Values.Count);
// Assert.NotEmpty(parsedSeries.Keys.Where(p => p.Format == MangaFormat.Archive && p.Name.Equals("Accel World")));
Assert.Equal(3, parsedSeries.Count);
Assert.NotEmpty(parsedSeries.Select(p => p.ParsedSeries).Where(p => p.Format == MangaFormat.Archive && p.Name.Equals("Accel World")));
Assert.Contains(parsedSeries.Select(p => p.ParsedSeries), p => p.Format == MangaFormat.Archive && p.Name.Equals("Accel World"));
}
#endregion
@ -245,15 +247,18 @@ public class ParseScannedFilesTests : AbstractDbTest
[Fact]
public async Task ProcessFiles_ForLibraryMode_OnlyCallsFolderActionForEachTopLevelFolder()
{
var (unitOfWork, context, mapper) = await CreateDatabase();
var scannerHelper = await Setup(unitOfWork);
var fileSystem = CreateTestFilesystem();
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), fileSystem);
var psf = new ParseScannedFiles(Substitute.For<ILogger<ParseScannedFiles>>(), ds,
new MockReadingItemService(ds, Substitute.For<IBookService>()), Substitute.For<IEventHub>());
var directoriesSeen = new HashSet<string>();
var library = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(1,
var library = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(1,
LibraryIncludes.Folders | LibraryIncludes.FileTypes);
var scanResults = await psf.ScanFiles("C:/Data/", true, await UnitOfWork.SeriesRepository.GetFolderPathMap(1), library);
var scanResults = await psf.ScanFiles("C:/Data/", true, await unitOfWork.SeriesRepository.GetFolderPathMap(1), library);
foreach (var scanResult in scanResults)
{
directoriesSeen.Add(scanResult.Folder);
@ -265,18 +270,21 @@ public class ParseScannedFilesTests : AbstractDbTest
[Fact]
public async Task ProcessFiles_ForNonLibraryMode_CallsFolderActionOnce()
{
var (unitOfWork, context, mapper) = await CreateDatabase();
var scannerHelper = await Setup(unitOfWork);
var fileSystem = CreateTestFilesystem();
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), fileSystem);
var psf = new ParseScannedFiles(Substitute.For<ILogger<ParseScannedFiles>>(), ds,
new MockReadingItemService(ds, Substitute.For<IBookService>()), Substitute.For<IEventHub>());
var library = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(1,
var library = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(1,
LibraryIncludes.Folders | LibraryIncludes.FileTypes);
Assert.NotNull(library);
var directoriesSeen = new HashSet<string>();
var scanResults = await psf.ScanFiles("C:/Data/", false,
await UnitOfWork.SeriesRepository.GetFolderPathMap(1), library);
await unitOfWork.SeriesRepository.GetFolderPathMap(1), library);
foreach (var scanResult in scanResults)
{
@ -291,6 +299,9 @@ public class ParseScannedFilesTests : AbstractDbTest
[Fact]
public async Task ProcessFiles_ShouldCallFolderActionTwice()
{
var (unitOfWork, context, mapper) = await CreateDatabase();
var scannerHelper = await Setup(unitOfWork);
var fileSystem = new MockFileSystem();
fileSystem.AddDirectory("C:/Data/");
fileSystem.AddDirectory("C:/Data/Accel World");
@ -305,10 +316,10 @@ public class ParseScannedFilesTests : AbstractDbTest
var psf = new ParseScannedFiles(Substitute.For<ILogger<ParseScannedFiles>>(), ds,
new MockReadingItemService(ds, Substitute.For<IBookService>()), Substitute.For<IEventHub>());
var library = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(1,
var library = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(1,
LibraryIncludes.Folders | LibraryIncludes.FileTypes);
Assert.NotNull(library);
var scanResults = await psf.ScanFiles("C:/Data", true, await UnitOfWork.SeriesRepository.GetFolderPathMap(1), library);
var scanResults = await psf.ScanFiles("C:/Data", true, await unitOfWork.SeriesRepository.GetFolderPathMap(1), library);
Assert.Equal(2, scanResults.Count);
}
@ -320,6 +331,9 @@ public class ParseScannedFilesTests : AbstractDbTest
[Fact]
public async Task ProcessFiles_ShouldCallFolderActionOnce()
{
var (unitOfWork, context, mapper) = await CreateDatabase();
var scannerHelper = await Setup(unitOfWork);
var fileSystem = new MockFileSystem();
fileSystem.AddDirectory("C:/Data/");
fileSystem.AddDirectory("C:/Data/Accel World");
@ -334,11 +348,11 @@ public class ParseScannedFilesTests : AbstractDbTest
var psf = new ParseScannedFiles(Substitute.For<ILogger<ParseScannedFiles>>(), ds,
new MockReadingItemService(ds, Substitute.For<IBookService>()), Substitute.For<IEventHub>());
var library = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(1,
var library = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(1,
LibraryIncludes.Folders | LibraryIncludes.FileTypes);
Assert.NotNull(library);
var scanResults = await psf.ScanFiles("C:/Data", false,
await UnitOfWork.SeriesRepository.GetFolderPathMap(1), library);
await unitOfWork.SeriesRepository.GetFolderPathMap(1), library);
Assert.Single(scanResults);
}
@ -352,23 +366,26 @@ public class ParseScannedFilesTests : AbstractDbTest
//[Fact]
public async Task HasSeriesFolderNotChangedSinceLastScan_AllSeriesFoldersHaveChanges()
{
var (unitOfWork, context, mapper) = await CreateDatabase();
var scannerHelper = await Setup(unitOfWork);
const string testcase = "Subfolders always scanning all series changes - Manga.json";
var infos = new Dictionary<string, ComicInfo>();
var library = await _scannerHelper.GenerateScannerData(testcase, infos);
var library = await scannerHelper.GenerateScannerData(testcase, infos);
var testDirectoryPath = library.Folders.First().Path;
UnitOfWork.LibraryRepository.Update(library);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Update(library);
await unitOfWork.CommitAsync();
var fs = new FileSystem();
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), fs);
var psf = new ParseScannedFiles(Substitute.For<ILogger<ParseScannedFiles>>(), ds,
new MockReadingItemService(ds, Substitute.For<IBookService>()), Substitute.For<IEventHub>());
var scanner = _scannerHelper.CreateServices(ds, fs);
var scanner = scannerHelper.CreateServices(ds, fs);
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Equal(4, postLib.Series.Count);
@ -391,7 +408,7 @@ public class ParseScannedFilesTests : AbstractDbTest
Path.Join(executionerCopyDir, "The Executioner and Her Way of Life Vol. 1 Ch. 0002.cbz"));
// 4 series, of which 2 have volumes as directories
var folderMap = await UnitOfWork.SeriesRepository.GetFolderPathMap(postLib.Id);
var folderMap = await unitOfWork.SeriesRepository.GetFolderPathMap(postLib.Id);
Assert.Equal(6, folderMap.Count);
var res = await psf.ScanFiles(testDirectoryPath, true, folderMap, postLib);
@ -404,23 +421,26 @@ public class ParseScannedFilesTests : AbstractDbTest
[Fact]
public async Task HasSeriesFolderNotChangedSinceLastScan_PublisherLayout()
{
var (unitOfWork, context, mapper) = await CreateDatabase();
var scannerHelper = await Setup(unitOfWork);
const string testcase = "Subfolder always scanning fix publisher layout - Comic.json";
var infos = new Dictionary<string, ComicInfo>();
var library = await _scannerHelper.GenerateScannerData(testcase, infos);
var library = await scannerHelper.GenerateScannerData(testcase, infos);
var testDirectoryPath = library.Folders.First().Path;
UnitOfWork.LibraryRepository.Update(library);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Update(library);
await unitOfWork.CommitAsync();
var fs = new FileSystem();
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), fs);
var psf = new ParseScannedFiles(Substitute.For<ILogger<ParseScannedFiles>>(), ds,
new MockReadingItemService(ds, Substitute.For<IBookService>()), Substitute.For<IEventHub>());
var scanner = _scannerHelper.CreateServices(ds, fs);
var scanner = scannerHelper.CreateServices(ds, fs);
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Equal(4, postLib.Series.Count);
@ -438,7 +458,7 @@ public class ParseScannedFilesTests : AbstractDbTest
Path.Join(executionerCopyDir, "The Executioner and Her Way of Life Vol. 2.cbz"));
var res = await psf.ScanFiles(testDirectoryPath, true,
await UnitOfWork.SeriesRepository.GetFolderPathMap(postLib.Id), postLib);
await unitOfWork.SeriesRepository.GetFolderPathMap(postLib.Id), postLib);
var changes = res.Count(sc => sc.HasChanged);
Assert.Equal(1, changes);
}
@ -447,23 +467,26 @@ public class ParseScannedFilesTests : AbstractDbTest
//[Fact]
public async Task SubFoldersNoSubFolders_SkipAll()
{
var (unitOfWork, context, mapper) = await CreateDatabase();
var scannerHelper = await Setup(unitOfWork);
const string testcase = "Subfolders and files at root - Manga.json";
var infos = new Dictionary<string, ComicInfo>();
var library = await _scannerHelper.GenerateScannerData(testcase, infos);
var library = await scannerHelper.GenerateScannerData(testcase, infos);
var testDirectoryPath = library.Folders.First().Path;
UnitOfWork.LibraryRepository.Update(library);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Update(library);
await unitOfWork.CommitAsync();
var fs = new FileSystem();
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), fs);
var psf = new ParseScannedFiles(Substitute.For<ILogger<ParseScannedFiles>>(), ds,
new MockReadingItemService(ds, Substitute.For<IBookService>()), Substitute.For<IEventHub>());
var scanner = _scannerHelper.CreateServices(ds, fs);
var scanner = scannerHelper.CreateServices(ds, fs);
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Single(postLib.Series);
@ -476,30 +499,33 @@ public class ParseScannedFilesTests : AbstractDbTest
await Task.Delay(1100); // Ensure at least one second has passed since library scan
var res = await psf.ScanFiles(testDirectoryPath, true,
await UnitOfWork.SeriesRepository.GetFolderPathMap(postLib.Id), postLib);
await unitOfWork.SeriesRepository.GetFolderPathMap(postLib.Id), postLib);
Assert.DoesNotContain(res, sc => sc.HasChanged);
}
[Fact]
public async Task SubFoldersNoSubFolders_ScanAllAfterAddInRoot()
{
var (unitOfWork, context, mapper) = await CreateDatabase();
var scannerHelper = await Setup(unitOfWork);
const string testcase = "Subfolders and files at root - Manga.json";
var infos = new Dictionary<string, ComicInfo>();
var library = await _scannerHelper.GenerateScannerData(testcase, infos);
var library = await scannerHelper.GenerateScannerData(testcase, infos);
var testDirectoryPath = library.Folders.First().Path;
UnitOfWork.LibraryRepository.Update(library);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Update(library);
await unitOfWork.CommitAsync();
var fs = new FileSystem();
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), fs);
var psf = new ParseScannedFiles(Substitute.For<ILogger<ParseScannedFiles>>(), ds,
new MockReadingItemService(ds, Substitute.For<IBookService>()), Substitute.For<IEventHub>());
var scanner = _scannerHelper.CreateServices(ds, fs);
var scanner = scannerHelper.CreateServices(ds, fs);
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Single(postLib.Series);
@ -508,8 +534,8 @@ public class ParseScannedFilesTests : AbstractDbTest
Assert.Equal(4, spiceAndWolf.Volumes.Sum(v => v.Chapters.Count));
spiceAndWolf.LastFolderScanned = DateTime.Now.Subtract(TimeSpan.FromMinutes(2));
Context.Series.Update(spiceAndWolf);
await Context.SaveChangesAsync();
context.Series.Update(spiceAndWolf);
await context.SaveChangesAsync();
// Add file at series root
var spiceAndWolfDir = Path.Join(testDirectoryPath, "Spice and Wolf");
@ -517,7 +543,7 @@ public class ParseScannedFilesTests : AbstractDbTest
Path.Join(spiceAndWolfDir, "Spice and Wolf Vol. 4.cbz"));
var res = await psf.ScanFiles(testDirectoryPath, true,
await UnitOfWork.SeriesRepository.GetFolderPathMap(postLib.Id), postLib);
await unitOfWork.SeriesRepository.GetFolderPathMap(postLib.Id), postLib);
var changes = res.Count(sc => sc.HasChanged);
Assert.Equal(2, changes);
}
@ -525,23 +551,26 @@ public class ParseScannedFilesTests : AbstractDbTest
[Fact]
public async Task SubFoldersNoSubFolders_ScanAllAfterAddInSubFolder()
{
var (unitOfWork, context, mapper) = await CreateDatabase();
var scannerHelper = await Setup(unitOfWork);
const string testcase = "Subfolders and files at root - Manga.json";
var infos = new Dictionary<string, ComicInfo>();
var library = await _scannerHelper.GenerateScannerData(testcase, infos);
var library = await scannerHelper.GenerateScannerData(testcase, infos);
var testDirectoryPath = library.Folders.First().Path;
UnitOfWork.LibraryRepository.Update(library);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Update(library);
await unitOfWork.CommitAsync();
var fs = new FileSystem();
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), fs);
var psf = new ParseScannedFiles(Substitute.For<ILogger<ParseScannedFiles>>(), ds,
new MockReadingItemService(ds, Substitute.For<IBookService>()), Substitute.For<IEventHub>());
var scanner = _scannerHelper.CreateServices(ds, fs);
var scanner = scannerHelper.CreateServices(ds, fs);
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Single(postLib.Series);
@ -550,8 +579,8 @@ public class ParseScannedFilesTests : AbstractDbTest
Assert.Equal(4, spiceAndWolf.Volumes.Sum(v => v.Chapters.Count));
spiceAndWolf.LastFolderScanned = DateTime.Now.Subtract(TimeSpan.FromMinutes(2));
Context.Series.Update(spiceAndWolf);
await Context.SaveChangesAsync();
context.Series.Update(spiceAndWolf);
await context.SaveChangesAsync();
// Add file in subfolder
var spiceAndWolfDir = Path.Join(Path.Join(testDirectoryPath, "Spice and Wolf"), "Spice and Wolf Vol. 3");
@ -559,7 +588,7 @@ public class ParseScannedFilesTests : AbstractDbTest
Path.Join(spiceAndWolfDir, "Spice and Wolf Vol. 3 Ch. 0013.cbz"));
var res = await psf.ScanFiles(testDirectoryPath, true,
await UnitOfWork.SeriesRepository.GetFolderPathMap(postLib.Id), postLib);
await unitOfWork.SeriesRepository.GetFolderPathMap(postLib.Id), postLib);
var changes = res.Count(sc => sc.HasChanged);
Assert.Equal(2, changes);
}

View File

@ -1,5 +1,6 @@
using System.Linq;
using System.Threading.Tasks;
using API.Data;
using API.Data.Repositories;
using API.Entities;
using API.Entities.Enums;
@ -7,17 +8,21 @@ using API.Entities.Person;
using API.Extensions;
using API.Helpers.Builders;
using API.Services;
using Polly;
using Xunit;
using Xunit.Abstractions;
namespace API.Tests.Services;
public class PersonServiceTests: AbstractDbTest
public class PersonServiceTests(ITestOutputHelper outputHelper): AbstractDbTest(outputHelper)
{
[Fact]
public async Task PersonMerge_KeepNonEmptyMetadata()
{
var ps = new PersonService(UnitOfWork);
var (unitOfWork, _, _) = await CreateDatabase();
var ps = new PersonService(unitOfWork);
var person1 = new Person
{
@ -36,13 +41,13 @@ public class PersonServiceTests: AbstractDbTest
AniListId = 27,
};
UnitOfWork.PersonRepository.Attach(person1);
UnitOfWork.PersonRepository.Attach(person2);
await UnitOfWork.CommitAsync();
unitOfWork.PersonRepository.Attach(person1);
unitOfWork.PersonRepository.Attach(person2);
await unitOfWork.CommitAsync();
await ps.MergePeopleAsync(person2, person1);
var allPeople = await UnitOfWork.PersonRepository.GetAllPeople();
var allPeople = await unitOfWork.PersonRepository.GetAllPeople();
Assert.Single(allPeople);
var person = allPeople[0];
@ -58,7 +63,9 @@ public class PersonServiceTests: AbstractDbTest
[Fact]
public async Task PersonMerge_MergedPersonDestruction()
{
var ps = new PersonService(UnitOfWork);
var (unitOfWork, _, _) = await CreateDatabase();
var ps = new PersonService(unitOfWork);
var person1 = new Person
{
@ -72,27 +79,29 @@ public class PersonServiceTests: AbstractDbTest
NormalizedName = "Delores Casey".ToNormalized(),
};
UnitOfWork.PersonRepository.Attach(person1);
UnitOfWork.PersonRepository.Attach(person2);
await UnitOfWork.CommitAsync();
unitOfWork.PersonRepository.Attach(person1);
unitOfWork.PersonRepository.Attach(person2);
await unitOfWork.CommitAsync();
await ps.MergePeopleAsync(person2, person1);
var allPeople = await UnitOfWork.PersonRepository.GetAllPeople();
var allPeople = await unitOfWork.PersonRepository.GetAllPeople();
Assert.Single(allPeople);
}
[Fact]
public async Task PersonMerge_RetentionChapters()
{
var ps = new PersonService(UnitOfWork);
var (unitOfWork, _, _) = await CreateDatabase();
var ps = new PersonService(unitOfWork);
var library = new LibraryBuilder("My Library").Build();
UnitOfWork.LibraryRepository.Add(library);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Add(library);
await unitOfWork.CommitAsync();
var user = new AppUserBuilder("Amelia", "amelia@localhost")
.WithLibrary(library).Build();
UnitOfWork.UserRepository.Add(user);
unitOfWork.UserRepository.Add(user);
var person = new PersonBuilder("Jillian Cowan").Build();
@ -120,26 +129,26 @@ public class PersonServiceTests: AbstractDbTest
.Build())
.Build();
UnitOfWork.SeriesRepository.Add(series);
UnitOfWork.SeriesRepository.Add(series2);
await UnitOfWork.CommitAsync();
unitOfWork.SeriesRepository.Add(series);
unitOfWork.SeriesRepository.Add(series2);
await unitOfWork.CommitAsync();
await ps.MergePeopleAsync(person2, person);
var allPeople = await UnitOfWork.PersonRepository.GetAllPeople();
var allPeople = await unitOfWork.PersonRepository.GetAllPeople();
Assert.Single(allPeople);
var mergedPerson = allPeople[0];
Assert.Equal("Jillian Cowan", mergedPerson.Name);
var chapters = await UnitOfWork.PersonRepository.GetChaptersForPersonByRole(1, 1, PersonRole.Editor);
var chapters = await unitOfWork.PersonRepository.GetChaptersForPersonByRole(1, 1, PersonRole.Editor);
Assert.Equal(2, chapters.Count());
chapter = await UnitOfWork.ChapterRepository.GetChapterAsync(1, ChapterIncludes.People);
chapter = await unitOfWork.ChapterRepository.GetChapterAsync(1, ChapterIncludes.People);
Assert.NotNull(chapter);
Assert.Single(chapter.People);
chapter2 = await UnitOfWork.ChapterRepository.GetChapterAsync(2, ChapterIncludes.People);
chapter2 = await unitOfWork.ChapterRepository.GetChapterAsync(2, ChapterIncludes.People);
Assert.NotNull(chapter2);
Assert.Single(chapter2.People);
@ -149,17 +158,17 @@ public class PersonServiceTests: AbstractDbTest
[Fact]
public async Task PersonMerge_NoDuplicateChaptersOrSeries()
{
await ResetDb();
var (unitOfWork, _, _) = await CreateDatabase();
var ps = new PersonService(UnitOfWork);
var ps = new PersonService(unitOfWork);
var library = new LibraryBuilder("My Library").Build();
UnitOfWork.LibraryRepository.Add(library);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Add(library);
await unitOfWork.CommitAsync();
var user = new AppUserBuilder("Amelia", "amelia@localhost")
.WithLibrary(library).Build();
UnitOfWork.UserRepository.Add(user);
unitOfWork.UserRepository.Add(user);
var person = new PersonBuilder("Jillian Cowan").Build();
@ -197,39 +206,39 @@ public class PersonServiceTests: AbstractDbTest
.Build())
.Build();
UnitOfWork.SeriesRepository.Add(series);
UnitOfWork.SeriesRepository.Add(series2);
await UnitOfWork.CommitAsync();
unitOfWork.SeriesRepository.Add(series);
unitOfWork.SeriesRepository.Add(series2);
await unitOfWork.CommitAsync();
await ps.MergePeopleAsync(person2, person);
var allPeople = await UnitOfWork.PersonRepository.GetAllPeople();
var allPeople = await unitOfWork.PersonRepository.GetAllPeople();
Assert.Single(allPeople);
var mergedPerson = await UnitOfWork.PersonRepository.GetPersonById(person.Id, PersonIncludes.All);
var mergedPerson = await unitOfWork.PersonRepository.GetPersonById(person.Id, PersonIncludes.All);
Assert.NotNull(mergedPerson);
Assert.Equal(3, mergedPerson.ChapterPeople.Count);
Assert.Equal(3, mergedPerson.SeriesMetadataPeople.Count);
chapter = await UnitOfWork.ChapterRepository.GetChapterAsync(chapter.Id, ChapterIncludes.People);
chapter = await unitOfWork.ChapterRepository.GetChapterAsync(chapter.Id, ChapterIncludes.People);
Assert.NotNull(chapter);
Assert.Equal(2, chapter.People.Count);
Assert.Single(chapter.People.Select(p => p.Person.Id).Distinct());
Assert.Contains(chapter.People, p => p.Role == PersonRole.Editor);
Assert.Contains(chapter.People, p => p.Role == PersonRole.Colorist);
chapter2 = await UnitOfWork.ChapterRepository.GetChapterAsync(chapter2.Id, ChapterIncludes.People);
chapter2 = await unitOfWork.ChapterRepository.GetChapterAsync(chapter2.Id, ChapterIncludes.People);
Assert.NotNull(chapter2);
Assert.Single(chapter2.People);
Assert.Contains(chapter2.People, p => p.Role == PersonRole.Editor);
Assert.DoesNotContain(chapter2.People, p => p.Role == PersonRole.Colorist);
series = await UnitOfWork.SeriesRepository.GetSeriesByIdAsync(series.Id, SeriesIncludes.Metadata);
series = await unitOfWork.SeriesRepository.GetSeriesByIdAsync(series.Id, SeriesIncludes.Metadata);
Assert.NotNull(series);
Assert.Single(series.Metadata.People);
Assert.Contains(series.Metadata.People, p => p.Role == PersonRole.Editor);
Assert.DoesNotContain(series.Metadata.People, p => p.Role == PersonRole.Colorist);
series2 = await UnitOfWork.SeriesRepository.GetSeriesByIdAsync(series2.Id, SeriesIncludes.Metadata);
series2 = await unitOfWork.SeriesRepository.GetSeriesByIdAsync(series2.Id, SeriesIncludes.Metadata);
Assert.NotNull(series2);
Assert.Equal(2, series2.Metadata.People.Count);
Assert.Contains(series2.Metadata.People, p => p.Role == PersonRole.Editor);
@ -241,16 +250,16 @@ public class PersonServiceTests: AbstractDbTest
[Fact]
public async Task PersonAddAlias_NoOverlap()
{
await ResetDb();
var (unitOfWork, _, _) = await CreateDatabase();
UnitOfWork.PersonRepository.Attach(new PersonBuilder("Jillian Cowan").Build());
UnitOfWork.PersonRepository.Attach(new PersonBuilder("Jilly Cowan").WithAlias("Jolly Cowan").Build());
await UnitOfWork.CommitAsync();
unitOfWork.PersonRepository.Attach(new PersonBuilder("Jillian Cowan").Build());
unitOfWork.PersonRepository.Attach(new PersonBuilder("Jilly Cowan").WithAlias("Jolly Cowan").Build());
await unitOfWork.CommitAsync();
var ps = new PersonService(UnitOfWork);
var ps = new PersonService(unitOfWork);
var person1 = await UnitOfWork.PersonRepository.GetPersonByNameOrAliasAsync("Jillian Cowan");
var person2 = await UnitOfWork.PersonRepository.GetPersonByNameOrAliasAsync("Jilly Cowan");
var person1 = await unitOfWork.PersonRepository.GetPersonByNameOrAliasAsync("Jillian Cowan");
var person2 = await unitOfWork.PersonRepository.GetPersonByNameOrAliasAsync("Jilly Cowan");
Assert.NotNull(person1);
Assert.NotNull(person2);
@ -276,11 +285,4 @@ public class PersonServiceTests: AbstractDbTest
Assert.Single(person2.Aliases);
}
protected override async Task ResetDb()
{
Context.Person.RemoveRange(Context.Person.ToList());
await Context.SaveChangesAsync();
}
}

View File

@ -1,5 +1,6 @@
using System.Linq;
using System.Threading.Tasks;
using API.Data;
using API.Data.Repositories;
using API.DTOs;
using API.Entities.Enums;
@ -10,25 +11,23 @@ using Hangfire;
using Hangfire.InMemory;
using Microsoft.Extensions.Logging;
using NSubstitute;
using Polly;
using Xunit;
using Xunit.Abstractions;
namespace API.Tests.Services;
public class RatingServiceTests: AbstractDbTest
public class RatingServiceTests(ITestOutputHelper outputHelper): AbstractDbTest(outputHelper)
{
private readonly RatingService _ratingService;
public RatingServiceTests()
{
_ratingService = new RatingService(UnitOfWork, Substitute.For<IScrobblingService>(), Substitute.For<ILogger<RatingService>>());
}
[Fact]
public async Task UpdateRating_ShouldSetRating()
{
await ResetDb();
var (unitOfWork, context, _) = await CreateDatabase();
var ratingService = new RatingService(unitOfWork, Substitute.For<IScrobblingService>(), Substitute.For<ILogger<RatingService>>());
Context.Library.Add(new LibraryBuilder("Test LIb")
context.Library.Add(new LibraryBuilder("Test LIb")
.WithAppUser(new AppUserBuilder("majora2007", string.Empty).Build())
.WithSeries(new SeriesBuilder("Test")
@ -39,13 +38,13 @@ public class RatingServiceTests: AbstractDbTest
.Build());
await Context.SaveChangesAsync();
await context.SaveChangesAsync();
var user = await UnitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Ratings);
var user = await unitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Ratings);
JobStorage.Current = new InMemoryStorage();
var result = await _ratingService.UpdateSeriesRating(user, new UpdateRatingDto
var result = await ratingService.UpdateSeriesRating(user, new UpdateRatingDto
{
SeriesId = 1,
UserRating = 3,
@ -53,7 +52,7 @@ public class RatingServiceTests: AbstractDbTest
Assert.True(result);
var ratings = (await UnitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Ratings))!
var ratings = (await unitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Ratings))!
.Ratings;
Assert.NotEmpty(ratings);
Assert.Equal(3, ratings.First().Rating);
@ -62,9 +61,10 @@ public class RatingServiceTests: AbstractDbTest
[Fact]
public async Task UpdateRating_ShouldUpdateExistingRating()
{
await ResetDb();
var (unitOfWork, context, _) = await CreateDatabase();
var ratingService = new RatingService(unitOfWork, Substitute.For<IScrobblingService>(), Substitute.For<ILogger<RatingService>>());
Context.Library.Add(new LibraryBuilder("Test LIb")
context.Library.Add(new LibraryBuilder("Test LIb")
.WithAppUser(new AppUserBuilder("majora2007", string.Empty).Build())
.WithSeries(new SeriesBuilder("Test")
@ -75,11 +75,11 @@ public class RatingServiceTests: AbstractDbTest
.Build());
await Context.SaveChangesAsync();
await context.SaveChangesAsync();
var user = await UnitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Ratings);
var user = await unitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Ratings);
var result = await _ratingService.UpdateSeriesRating(user, new UpdateRatingDto
var result = await ratingService.UpdateSeriesRating(user, new UpdateRatingDto
{
SeriesId = 1,
UserRating = 3,
@ -88,14 +88,14 @@ public class RatingServiceTests: AbstractDbTest
Assert.True(result);
JobStorage.Current = new InMemoryStorage();
var ratings = (await UnitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Ratings))
var ratings = (await unitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Ratings))
.Ratings;
Assert.NotEmpty(ratings);
Assert.Equal(3, ratings.First().Rating);
// Update the DB again
var result2 = await _ratingService.UpdateSeriesRating(user, new UpdateRatingDto
var result2 = await ratingService.UpdateSeriesRating(user, new UpdateRatingDto
{
SeriesId = 1,
UserRating = 5,
@ -103,7 +103,7 @@ public class RatingServiceTests: AbstractDbTest
Assert.True(result2);
var ratings2 = (await UnitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Ratings))
var ratings2 = (await unitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Ratings))
.Ratings;
Assert.NotEmpty(ratings2);
Assert.True(ratings2.Count == 1);
@ -113,9 +113,10 @@ public class RatingServiceTests: AbstractDbTest
[Fact]
public async Task UpdateRating_ShouldClampRatingAt5()
{
await ResetDb();
var (unitOfWork, context, _) = await CreateDatabase();
var ratingService = new RatingService(unitOfWork, Substitute.For<IScrobblingService>(), Substitute.For<ILogger<RatingService>>());
Context.Library.Add(new LibraryBuilder("Test LIb")
context.Library.Add(new LibraryBuilder("Test LIb")
.WithAppUser(new AppUserBuilder("majora2007", string.Empty).Build())
.WithSeries(new SeriesBuilder("Test")
@ -125,11 +126,11 @@ public class RatingServiceTests: AbstractDbTest
.Build())
.Build());
await Context.SaveChangesAsync();
await context.SaveChangesAsync();
var user = await UnitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Ratings);
var user = await unitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Ratings);
var result = await _ratingService.UpdateSeriesRating(user, new UpdateRatingDto
var result = await ratingService.UpdateSeriesRating(user, new UpdateRatingDto
{
SeriesId = 1,
UserRating = 10,
@ -138,7 +139,7 @@ public class RatingServiceTests: AbstractDbTest
Assert.True(result);
JobStorage.Current = new InMemoryStorage();
var ratings = (await UnitOfWork.UserRepository.GetUserByUsernameAsync("majora2007",
var ratings = (await unitOfWork.UserRepository.GetUserByUsernameAsync("majora2007",
AppUserIncludes.Ratings)!)
.Ratings;
Assert.NotEmpty(ratings);
@ -148,9 +149,10 @@ public class RatingServiceTests: AbstractDbTest
[Fact]
public async Task UpdateRating_ShouldReturnFalseWhenSeriesDoesntExist()
{
await ResetDb();
var (unitOfWork, context, _) = await CreateDatabase();
var ratingService = new RatingService(unitOfWork, Substitute.For<IScrobblingService>(), Substitute.For<ILogger<RatingService>>());
Context.Library.Add(new LibraryBuilder("Test LIb", LibraryType.Book)
context.Library.Add(new LibraryBuilder("Test LIb", LibraryType.Book)
.WithAppUser(new AppUserBuilder("majora2007", string.Empty).Build())
.WithSeries(new SeriesBuilder("Test")
@ -160,11 +162,11 @@ public class RatingServiceTests: AbstractDbTest
.Build())
.Build());
await Context.SaveChangesAsync();
await context.SaveChangesAsync();
var user = await UnitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Ratings);
var user = await unitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Ratings);
var result = await _ratingService.UpdateSeriesRating(user, new UpdateRatingDto
var result = await ratingService.UpdateSeriesRating(user, new UpdateRatingDto
{
SeriesId = 2,
UserRating = 5,
@ -175,15 +177,4 @@ public class RatingServiceTests: AbstractDbTest
var ratings = user.Ratings;
Assert.Empty(ratings);
}
protected override async Task ResetDb()
{
Context.Series.RemoveRange(Context.Series.ToList());
Context.AppUserRating.RemoveRange(Context.AppUserRating.ToList());
Context.Genre.RemoveRange(Context.Genre.ToList());
Context.CollectionTag.RemoveRange(Context.CollectionTag.ToList());
Context.Person.RemoveRange(Context.Person.ToList());
Context.Library.RemoveRange(Context.Library.ToList());
await Context.SaveChangesAsync();
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,6 @@
using System.Linq;
using System.Threading.Tasks;
using API.Data;
using API.Data.Repositories;
using API.DTOs;
using API.Entities;
@ -7,25 +8,28 @@ using API.Entities.Enums;
using API.Helpers.Builders;
using API.Services;
using API.Tests.Helpers;
using AutoMapper;
using Kavita.Common;
using Microsoft.EntityFrameworkCore;
using NSubstitute;
using Polly;
using Xunit;
using Xunit.Abstractions;
namespace API.Tests.Services;
public class ReadingProfileServiceTest: AbstractDbTest
public class ReadingProfileServiceTest(ITestOutputHelper outputHelper): AbstractDbTest(outputHelper)
{
/// <summary>
/// Does not add a default reading profile
/// </summary>
/// <returns></returns>
public async Task<(ReadingProfileService, AppUser, Library, Series)> Setup()
public async Task<(ReadingProfileService, AppUser, Library, Series)> Setup(IUnitOfWork unitOfWork, DataContext context, IMapper mapper)
{
var user = new AppUserBuilder("amelia", "amelia@localhost").Build();
Context.AppUser.Add(user);
await UnitOfWork.CommitAsync();
context.AppUser.Add(user);
await unitOfWork.CommitAsync();
var series = new SeriesBuilder("Spice and Wolf").Build();
@ -34,10 +38,10 @@ public class ReadingProfileServiceTest: AbstractDbTest
.Build();
user.Libraries.Add(library);
await UnitOfWork.CommitAsync();
await unitOfWork.CommitAsync();
var rps = new ReadingProfileService(UnitOfWork, Substitute.For<ILocalizationService>(), Mapper);
user = await UnitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.UserPreferences);
var rps = new ReadingProfileService(unitOfWork, Substitute.For<ILocalizationService>(), mapper);
user = await unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.UserPreferences);
return (rps, user, library, series);
}
@ -45,8 +49,8 @@ public class ReadingProfileServiceTest: AbstractDbTest
[Fact]
public async Task ImplicitProfileFirst()
{
await ResetDb();
var (rps, user, library, series) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (rps, user, library, series) = await Setup(unitOfWork, context, mapper);
var profile = new AppUserReadingProfileBuilder(user.Id)
.WithKind(ReadingProfileKind.Implicit)
@ -61,7 +65,7 @@ public class ReadingProfileServiceTest: AbstractDbTest
user.ReadingProfiles.Add(profile);
user.ReadingProfiles.Add(profile2);
await UnitOfWork.CommitAsync();
await unitOfWork.CommitAsync();
var seriesProfile = await rps.GetReadingProfileDtoForSeries(user.Id, series.Id);
Assert.NotNull(seriesProfile);
@ -76,14 +80,14 @@ public class ReadingProfileServiceTest: AbstractDbTest
[Fact]
public async Task CantDeleteDefaultReadingProfile()
{
await ResetDb();
var (rps, user, _, _) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (rps, user, _, _) = await Setup(unitOfWork, context, mapper);
var profile = new AppUserReadingProfileBuilder(user.Id)
.WithKind(ReadingProfileKind.Default)
.Build();
Context.AppUserReadingProfiles.Add(profile);
await UnitOfWork.CommitAsync();
context.AppUserReadingProfiles.Add(profile);
await unitOfWork.CommitAsync();
await Assert.ThrowsAsync<KavitaException>(async () =>
{
@ -91,21 +95,21 @@ public class ReadingProfileServiceTest: AbstractDbTest
});
var profile2 = new AppUserReadingProfileBuilder(user.Id).Build();
Context.AppUserReadingProfiles.Add(profile2);
await UnitOfWork.CommitAsync();
context.AppUserReadingProfiles.Add(profile2);
await unitOfWork.CommitAsync();
await rps.DeleteReadingProfile(user.Id, profile2.Id);
await UnitOfWork.CommitAsync();
await unitOfWork.CommitAsync();
var allProfiles = await Context.AppUserReadingProfiles.ToListAsync();
var allProfiles = await context.AppUserReadingProfiles.ToListAsync();
Assert.Single(allProfiles);
}
[Fact]
public async Task CreateImplicitSeriesReadingProfile()
{
await ResetDb();
var (rps, user, _, series) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (rps, user, _, series) = await Setup(unitOfWork, context, mapper);
var dto = new UserReadingProfileDto
{
@ -125,8 +129,8 @@ public class ReadingProfileServiceTest: AbstractDbTest
[Fact]
public async Task UpdateImplicitReadingProfile_DoesNotCreateNew()
{
await ResetDb();
var (rps, user, _, series) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (rps, user, _, series) = await Setup(unitOfWork, context, mapper);
var dto = new UserReadingProfileDto
{
@ -154,7 +158,7 @@ public class ReadingProfileServiceTest: AbstractDbTest
Assert.Equal(ReadingProfileKind.Implicit, profile.Kind);
Assert.Equal(ReaderMode.LeftRight, profile.ReaderMode);
var implicitCount = await Context.AppUserReadingProfiles
var implicitCount = await context.AppUserReadingProfiles
.Where(p => p.Kind == ReadingProfileKind.Implicit)
.CountAsync();
Assert.Equal(1, implicitCount);
@ -163,8 +167,8 @@ public class ReadingProfileServiceTest: AbstractDbTest
[Fact]
public async Task GetCorrectProfile()
{
await ResetDb();
var (rps, user, lib, series) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (rps, user, lib, series) = await Setup(unitOfWork, context, mapper);
var profile = new AppUserReadingProfileBuilder(user.Id)
.WithSeries(series)
@ -178,9 +182,9 @@ public class ReadingProfileServiceTest: AbstractDbTest
.WithKind(ReadingProfileKind.Default)
.WithName("Global")
.Build();
Context.AppUserReadingProfiles.Add(profile);
Context.AppUserReadingProfiles.Add(profile2);
Context.AppUserReadingProfiles.Add(profile3);
context.AppUserReadingProfiles.Add(profile);
context.AppUserReadingProfiles.Add(profile2);
context.AppUserReadingProfiles.Add(profile3);
var series2 = new SeriesBuilder("Rainbows After Storms").Build();
lib.Series.Add(series2);
@ -190,7 +194,7 @@ public class ReadingProfileServiceTest: AbstractDbTest
lib2.Series.Add(series3);
user.Libraries.Add(lib2);
await UnitOfWork.CommitAsync();
await unitOfWork.CommitAsync();
var p = await rps.GetReadingProfileDtoForSeries(user.Id, series.Id);
Assert.NotNull(p);
@ -208,8 +212,8 @@ public class ReadingProfileServiceTest: AbstractDbTest
[Fact]
public async Task ReplaceReadingProfile()
{
await ResetDb();
var (rps, user, lib, series) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (rps, user, lib, series) = await Setup(unitOfWork, context, mapper);
var profile1 = new AppUserReadingProfileBuilder(user.Id)
.WithSeries(series)
@ -220,9 +224,9 @@ public class ReadingProfileServiceTest: AbstractDbTest
.WithName("Profile 2")
.Build();
Context.AppUserReadingProfiles.Add(profile1);
Context.AppUserReadingProfiles.Add(profile2);
await UnitOfWork.CommitAsync();
context.AppUserReadingProfiles.Add(profile1);
context.AppUserReadingProfiles.Add(profile2);
await unitOfWork.CommitAsync();
var profile = await rps.GetReadingProfileDtoForSeries(user.Id, series.Id);
Assert.NotNull(profile);
@ -237,19 +241,19 @@ public class ReadingProfileServiceTest: AbstractDbTest
[Fact]
public async Task DeleteReadingProfile()
{
await ResetDb();
var (rps, user, lib, series) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (rps, user, lib, series) = await Setup(unitOfWork, context, mapper);
var profile1 = new AppUserReadingProfileBuilder(user.Id)
.WithSeries(series)
.WithName("Profile 1")
.Build();
Context.AppUserReadingProfiles.Add(profile1);
await UnitOfWork.CommitAsync();
context.AppUserReadingProfiles.Add(profile1);
await unitOfWork.CommitAsync();
await rps.ClearSeriesProfile(user.Id, series.Id);
var profiles = await UnitOfWork.AppUserReadingProfileRepository.GetProfilesForUser(user.Id);
var profiles = await unitOfWork.AppUserReadingProfileRepository.GetProfilesForUser(user.Id);
Assert.DoesNotContain(profiles, rp => rp.SeriesIds.Contains(series.Id));
}
@ -257,8 +261,8 @@ public class ReadingProfileServiceTest: AbstractDbTest
[Fact]
public async Task BulkAddReadingProfiles()
{
await ResetDb();
var (rps, user, lib, series) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (rps, user, lib, series) = await Setup(unitOfWork, context, mapper);
for (var i = 0; i < 10; i++)
{
@ -270,15 +274,15 @@ public class ReadingProfileServiceTest: AbstractDbTest
.WithSeries(series)
.WithName("Profile")
.Build();
Context.AppUserReadingProfiles.Add(profile);
context.AppUserReadingProfiles.Add(profile);
var profile2 = new AppUserReadingProfileBuilder(user.Id)
.WithSeries(series)
.WithName("Profile2")
.Build();
Context.AppUserReadingProfiles.Add(profile2);
context.AppUserReadingProfiles.Add(profile2);
await UnitOfWork.CommitAsync();
await unitOfWork.CommitAsync();
var someSeriesIds = lib.Series.Take(lib.Series.Count / 2).Select(s => s.Id).ToList();
await rps.BulkAddProfileToSeries(user.Id, profile.Id, someSeriesIds);
@ -306,23 +310,23 @@ public class ReadingProfileServiceTest: AbstractDbTest
[Fact]
public async Task BulkAssignDeletesImplicit()
{
await ResetDb();
var (rps, user, lib, series) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (rps, user, lib, series) = await Setup(unitOfWork, context, mapper);
var implicitProfile = Mapper.Map<UserReadingProfileDto>(new AppUserReadingProfileBuilder(user.Id)
var implicitProfile = mapper.Map<UserReadingProfileDto>(new AppUserReadingProfileBuilder(user.Id)
.Build());
var profile = new AppUserReadingProfileBuilder(user.Id)
.WithName("Profile 1")
.Build();
Context.AppUserReadingProfiles.Add(profile);
context.AppUserReadingProfiles.Add(profile);
for (var i = 0; i < 10; i++)
{
var generatedSeries = new SeriesBuilder($"Generated Series #{i}").Build();
lib.Series.Add(generatedSeries);
}
await UnitOfWork.CommitAsync();
await unitOfWork.CommitAsync();
var ids = lib.Series.Select(s => s.Id).ToList();
@ -343,7 +347,7 @@ public class ReadingProfileServiceTest: AbstractDbTest
Assert.Equal(ReadingProfileKind.User, seriesProfile.Kind);
}
var implicitCount = await Context.AppUserReadingProfiles
var implicitCount = await context.AppUserReadingProfiles
.Where(p => p.Kind == ReadingProfileKind.Implicit)
.CountAsync();
Assert.Equal(0, implicitCount);
@ -352,18 +356,18 @@ public class ReadingProfileServiceTest: AbstractDbTest
[Fact]
public async Task AddDeletesImplicit()
{
await ResetDb();
var (rps, user, lib, series) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (rps, user, lib, series) = await Setup(unitOfWork, context, mapper);
var implicitProfile = Mapper.Map<UserReadingProfileDto>(new AppUserReadingProfileBuilder(user.Id)
var implicitProfile = mapper.Map<UserReadingProfileDto>(new AppUserReadingProfileBuilder(user.Id)
.WithKind(ReadingProfileKind.Implicit)
.Build());
var profile = new AppUserReadingProfileBuilder(user.Id)
.WithName("Profile 1")
.Build();
Context.AppUserReadingProfiles.Add(profile);
await UnitOfWork.CommitAsync();
context.AppUserReadingProfiles.Add(profile);
await unitOfWork.CommitAsync();
await rps.UpdateImplicitReadingProfile(user.Id, series.Id, implicitProfile);
@ -377,7 +381,7 @@ public class ReadingProfileServiceTest: AbstractDbTest
Assert.NotNull(seriesProfile);
Assert.Equal(ReadingProfileKind.User, seriesProfile.Kind);
var implicitCount = await Context.AppUserReadingProfiles
var implicitCount = await context.AppUserReadingProfiles
.Where(p => p.Kind == ReadingProfileKind.Implicit)
.CountAsync();
Assert.Equal(0, implicitCount);
@ -386,8 +390,8 @@ public class ReadingProfileServiceTest: AbstractDbTest
[Fact]
public async Task CreateReadingProfile()
{
await ResetDb();
var (rps, user, lib, series) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (rps, user, lib, series) = await Setup(unitOfWork, context, mapper);
var dto = new UserReadingProfileDto
{
@ -419,15 +423,15 @@ public class ReadingProfileServiceTest: AbstractDbTest
await rps.CreateReadingProfile(user.Id, dto3);
});
var allProfiles = Context.AppUserReadingProfiles.ToList();
var allProfiles = context.AppUserReadingProfiles.ToList();
Assert.Equal(2, allProfiles.Count);
}
[Fact]
public async Task ClearSeriesProfile_RemovesImplicitAndUnlinksExplicit()
{
await ResetDb();
var (rps, user, _, series) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (rps, user, _, series) = await Setup(unitOfWork, context, mapper);
var implicitProfile = new AppUserReadingProfileBuilder(user.Id)
.WithSeries(series)
@ -440,40 +444,40 @@ public class ReadingProfileServiceTest: AbstractDbTest
.WithName("Explicit Profile")
.Build();
Context.AppUserReadingProfiles.Add(implicitProfile);
Context.AppUserReadingProfiles.Add(explicitProfile);
await UnitOfWork.CommitAsync();
context.AppUserReadingProfiles.Add(implicitProfile);
context.AppUserReadingProfiles.Add(explicitProfile);
await unitOfWork.CommitAsync();
var allBefore = await UnitOfWork.AppUserReadingProfileRepository.GetProfilesForUser(user.Id);
var allBefore = await unitOfWork.AppUserReadingProfileRepository.GetProfilesForUser(user.Id);
Assert.Equal(2, allBefore.Count(rp => rp.SeriesIds.Contains(series.Id)));
await rps.ClearSeriesProfile(user.Id, series.Id);
var remainingProfiles = await Context.AppUserReadingProfiles.ToListAsync();
var remainingProfiles = await context.AppUserReadingProfiles.ToListAsync();
Assert.Single(remainingProfiles);
Assert.Equal("Explicit Profile", remainingProfiles[0].Name);
Assert.Empty(remainingProfiles[0].SeriesIds);
var profilesForSeries = await UnitOfWork.AppUserReadingProfileRepository.GetProfilesForUser(user.Id);
var profilesForSeries = await unitOfWork.AppUserReadingProfileRepository.GetProfilesForUser(user.Id);
Assert.DoesNotContain(profilesForSeries, rp => rp.SeriesIds.Contains(series.Id));
}
[Fact]
public async Task AddProfileToLibrary_AddsAndOverridesExisting()
{
await ResetDb();
var (rps, user, lib, _) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (rps, user, lib, _) = await Setup(unitOfWork, context, mapper);
var profile = new AppUserReadingProfileBuilder(user.Id)
.WithName("Library Profile")
.Build();
Context.AppUserReadingProfiles.Add(profile);
await UnitOfWork.CommitAsync();
context.AppUserReadingProfiles.Add(profile);
await unitOfWork.CommitAsync();
await rps.AddProfileToLibrary(user.Id, profile.Id, lib.Id);
await UnitOfWork.CommitAsync();
await unitOfWork.CommitAsync();
var linkedProfile = (await UnitOfWork.AppUserReadingProfileRepository.GetProfilesForUser(user.Id))
var linkedProfile = (await unitOfWork.AppUserReadingProfileRepository.GetProfilesForUser(user.Id))
.FirstOrDefault(rp => rp.LibraryIds.Contains(lib.Id));
Assert.NotNull(linkedProfile);
Assert.Equal(profile.Id, linkedProfile.Id);
@ -481,13 +485,13 @@ public class ReadingProfileServiceTest: AbstractDbTest
var newProfile = new AppUserReadingProfileBuilder(user.Id)
.WithName("New Profile")
.Build();
Context.AppUserReadingProfiles.Add(newProfile);
await UnitOfWork.CommitAsync();
context.AppUserReadingProfiles.Add(newProfile);
await unitOfWork.CommitAsync();
await rps.AddProfileToLibrary(user.Id, newProfile.Id, lib.Id);
await UnitOfWork.CommitAsync();
await unitOfWork.CommitAsync();
linkedProfile = (await UnitOfWork.AppUserReadingProfileRepository.GetProfilesForUser(user.Id))
linkedProfile = (await unitOfWork.AppUserReadingProfileRepository.GetProfilesForUser(user.Id))
.FirstOrDefault(rp => rp.LibraryIds.Contains(lib.Id));
Assert.NotNull(linkedProfile);
Assert.Equal(newProfile.Id, linkedProfile.Id);
@ -496,33 +500,33 @@ public class ReadingProfileServiceTest: AbstractDbTest
[Fact]
public async Task ClearLibraryProfile_RemovesImplicitOrUnlinksExplicit()
{
await ResetDb();
var (rps, user, lib, _) = await Setup();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (rps, user, lib, _) = await Setup(unitOfWork, context, mapper);
var implicitProfile = new AppUserReadingProfileBuilder(user.Id)
.WithKind(ReadingProfileKind.Implicit)
.WithLibrary(lib)
.Build();
Context.AppUserReadingProfiles.Add(implicitProfile);
await UnitOfWork.CommitAsync();
context.AppUserReadingProfiles.Add(implicitProfile);
await unitOfWork.CommitAsync();
await rps.ClearLibraryProfile(user.Id, lib.Id);
var profile = (await UnitOfWork.AppUserReadingProfileRepository.GetProfilesForUser(user.Id))
var profile = (await unitOfWork.AppUserReadingProfileRepository.GetProfilesForUser(user.Id))
.FirstOrDefault(rp => rp.LibraryIds.Contains(lib.Id));
Assert.Null(profile);
var explicitProfile = new AppUserReadingProfileBuilder(user.Id)
.WithLibrary(lib)
.Build();
Context.AppUserReadingProfiles.Add(explicitProfile);
await UnitOfWork.CommitAsync();
context.AppUserReadingProfiles.Add(explicitProfile);
await unitOfWork.CommitAsync();
await rps.ClearLibraryProfile(user.Id, lib.Id);
profile = (await UnitOfWork.AppUserReadingProfileRepository.GetProfilesForUser(user.Id))
profile = (await unitOfWork.AppUserReadingProfileRepository.GetProfilesForUser(user.Id))
.FirstOrDefault(rp => rp.LibraryIds.Contains(lib.Id));
Assert.Null(profile);
var stillExists = await Context.AppUserReadingProfiles.FindAsync(explicitProfile.Id);
var stillExists = await context.AppUserReadingProfiles.FindAsync(explicitProfile.Id);
Assert.NotNull(stillExists);
}
@ -531,8 +535,10 @@ public class ReadingProfileServiceTest: AbstractDbTest
/// is worth having.
/// </summary>
[Fact]
public void UpdateFields_UpdatesAll()
public async Task UpdateFields_UpdatesAll()
{
var (_, _, mapper) = await CreateDatabase();
// Repeat to ensure booleans are flipped and actually tested
for (int i = 0; i < 10; i++)
{
@ -544,18 +550,11 @@ public class ReadingProfileServiceTest: AbstractDbTest
ReadingProfileService.UpdateReaderProfileFields(profile, dto);
var newDto = Mapper.Map<UserReadingProfileDto>(profile);
var newDto = mapper.Map<UserReadingProfileDto>(profile);
Assert.True(RandfHelper.AreSimpleFieldsEqual(dto, newDto,
["<Id>k__BackingField", "<UserId>k__BackingField"]));
}
}
protected override async Task ResetDb()
{
Context.AppUserReadingProfiles.RemoveRange(Context.AppUserReadingProfiles);
await UnitOfWork.CommitAsync();
}
}

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using API.Data;
using API.Data.Metadata;
using API.Data.Repositories;
using API.Entities;
@ -16,57 +17,52 @@ using Xunit.Abstractions;
namespace API.Tests.Services;
public class ScannerServiceTests : AbstractDbTest
public class ScannerServiceTests: AbstractDbTest
{
private readonly ITestOutputHelper _testOutputHelper;
private readonly ScannerHelper _scannerHelper;
private readonly string _testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/ScannerService/ScanTests");
public ScannerServiceTests(ITestOutputHelper testOutputHelper)
public ScannerServiceTests(ITestOutputHelper testOutputHelper): base(testOutputHelper)
{
_testOutputHelper = testOutputHelper;
// Set up Hangfire to use in-memory storage for testing
GlobalConfiguration.Configuration.UseInMemoryStorage();
_scannerHelper = new ScannerHelper(UnitOfWork, testOutputHelper);
}
protected override async Task ResetDb()
{
Context.Library.RemoveRange(Context.Library);
await Context.SaveChangesAsync();
}
protected async Task SetAllSeriesLastScannedInThePast(Library library, TimeSpan? duration = null)
protected async Task SetAllSeriesLastScannedInThePast(DataContext context, Library library, TimeSpan? duration = null)
{
foreach (var series in library.Series)
{
await SetLastScannedInThePast(series, duration, false);
await SetLastScannedInThePast(context, series, duration, false);
}
await Context.SaveChangesAsync();
await context.SaveChangesAsync();
}
protected async Task SetLastScannedInThePast(Series series, TimeSpan? duration = null, bool save = true)
protected async Task SetLastScannedInThePast(DataContext context, Series series, TimeSpan? duration = null, bool save = true)
{
duration ??= TimeSpan.FromMinutes(2);
series.LastFolderScanned = DateTime.Now.Subtract(duration.Value);
Context.Series.Update(series);
context.Series.Update(series);
if (save)
{
await Context.SaveChangesAsync();
await context.SaveChangesAsync();
}
}
[Fact]
public async Task ScanLibrary_ComicVine_PublisherFolder()
{
var (unitOfWork, _, _) = await CreateDatabase();
var scannerHelper = new ScannerHelper(unitOfWork, _testOutputHelper);
var testcase = "Publisher - ComicVine.json";
var library = await _scannerHelper.GenerateScannerData(testcase);
var scanner = _scannerHelper.CreateServices();
var library = await scannerHelper.GenerateScannerData(testcase);
var scanner = scannerHelper.CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Equal(4, postLib.Series.Count);
@ -75,11 +71,14 @@ public class ScannerServiceTests : AbstractDbTest
[Fact]
public async Task ScanLibrary_ShouldCombineNestedFolder()
{
var (unitOfWork, _, _) = await CreateDatabase();
var scannerHelper = new ScannerHelper(unitOfWork, _testOutputHelper);
var testcase = "Series and Series-Series Combined - Manga.json";
var library = await _scannerHelper.GenerateScannerData(testcase);
var scanner = _scannerHelper.CreateServices();
var library = await scannerHelper.GenerateScannerData(testcase);
var scanner = scannerHelper.CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Single(postLib.Series);
@ -90,11 +89,14 @@ public class ScannerServiceTests : AbstractDbTest
[Fact]
public async Task ScanLibrary_FlatSeries()
{
var (unitOfWork, _, _) = await CreateDatabase();
var scannerHelper = new ScannerHelper(unitOfWork, _testOutputHelper);
const string testcase = "Flat Series - Manga.json";
var library = await _scannerHelper.GenerateScannerData(testcase);
var scanner = _scannerHelper.CreateServices();
var library = await scannerHelper.GenerateScannerData(testcase);
var scanner = scannerHelper.CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Single(postLib.Series);
@ -106,11 +108,14 @@ public class ScannerServiceTests : AbstractDbTest
[Fact]
public async Task ScanLibrary_FlatSeriesWithSpecialFolder()
{
var (unitOfWork, _, _) = await CreateDatabase();
var scannerHelper = new ScannerHelper(unitOfWork, _testOutputHelper);
const string testcase = "Flat Series with Specials Folder Alt Naming - Manga.json";
var library = await _scannerHelper.GenerateScannerData(testcase);
var scanner = _scannerHelper.CreateServices();
var library = await scannerHelper.GenerateScannerData(testcase);
var scanner = scannerHelper.CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Single(postLib.Series);
@ -121,11 +126,14 @@ public class ScannerServiceTests : AbstractDbTest
[Fact]
public async Task ScanLibrary_FlatSeriesWithSpecialFolder_AlternativeNaming()
{
var (unitOfWork, _, _) = await CreateDatabase();
var scannerHelper = new ScannerHelper(unitOfWork, _testOutputHelper);
const string testcase = "Flat Series with Specials Folder Alt Naming - Manga.json";
var library = await _scannerHelper.GenerateScannerData(testcase);
var scanner = _scannerHelper.CreateServices();
var library = await scannerHelper.GenerateScannerData(testcase);
var scanner = scannerHelper.CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Single(postLib.Series);
@ -136,12 +144,15 @@ public class ScannerServiceTests : AbstractDbTest
[Fact]
public async Task ScanLibrary_FlatSeriesWithSpecial()
{
var (unitOfWork, _, _) = await CreateDatabase();
var scannerHelper = new ScannerHelper(unitOfWork, _testOutputHelper);
const string testcase = "Flat Special - Manga.json";
var library = await _scannerHelper.GenerateScannerData(testcase);
var scanner = _scannerHelper.CreateServices();
var library = await scannerHelper.GenerateScannerData(testcase);
var scanner = scannerHelper.CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Single(postLib.Series);
@ -152,12 +163,15 @@ public class ScannerServiceTests : AbstractDbTest
[Fact]
public async Task ScanLibrary_SeriesWithUnbalancedParenthesis()
{
var (unitOfWork, _, _) = await CreateDatabase();
var scannerHelper = new ScannerHelper(unitOfWork, _testOutputHelper);
const string testcase = "Scan Library Parses as ( - Manga.json";
var library = await _scannerHelper.GenerateScannerData(testcase);
var scanner = _scannerHelper.CreateServices();
var library = await scannerHelper.GenerateScannerData(testcase);
var scanner = scannerHelper.CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Single(postLib.Series);
@ -173,6 +187,9 @@ public class ScannerServiceTests : AbstractDbTest
[Fact]
public async Task ScanLibrary_LocalizedSeries()
{
var (unitOfWork, _, _) = await CreateDatabase();
var scannerHelper = new ScannerHelper(unitOfWork, _testOutputHelper);
const string testcase = "Series with Localized - Manga.json";
// Get the first file and generate a ComicInfo
@ -183,12 +200,12 @@ public class ScannerServiceTests : AbstractDbTest
LocalizedSeries = "Sono Bisque Doll wa Koi wo Suru"
});
var library = await _scannerHelper.GenerateScannerData(testcase, infos);
var library = await scannerHelper.GenerateScannerData(testcase, infos);
var scanner = _scannerHelper.CreateServices();
var scanner = scannerHelper.CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Single(postLib.Series);
@ -198,6 +215,9 @@ public class ScannerServiceTests : AbstractDbTest
[Fact]
public async Task ScanLibrary_LocalizedSeries2()
{
var (unitOfWork, _, _) = await CreateDatabase();
var scannerHelper = new ScannerHelper(unitOfWork, _testOutputHelper);
const string testcase = "Series with Localized 2 - Manga.json";
// Get the first file and generate a ComicInfo
@ -208,12 +228,12 @@ public class ScannerServiceTests : AbstractDbTest
LocalizedSeries = "Futoku no Guild" // Filename has a capital N and localizedSeries has lowercase
});
var library = await _scannerHelper.GenerateScannerData(testcase, infos);
var library = await scannerHelper.GenerateScannerData(testcase, infos);
var scanner = _scannerHelper.CreateServices();
var scanner = scannerHelper.CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Single(postLib.Series);
@ -230,6 +250,9 @@ public class ScannerServiceTests : AbstractDbTest
[Fact]
public async Task ScanLibrary_ExtraShouldNotAffect()
{
var (unitOfWork, _, _) = await CreateDatabase();
var scannerHelper = new ScannerHelper(unitOfWork, _testOutputHelper);
const string testcase = "Series with Extra - Manga.json";
// Get the first file and generate a ComicInfo
@ -239,12 +262,12 @@ public class ScannerServiceTests : AbstractDbTest
Series = "The Novel's Extra",
});
var library = await _scannerHelper.GenerateScannerData(testcase, infos);
var library = await scannerHelper.GenerateScannerData(testcase, infos);
var scanner = _scannerHelper.CreateServices();
var scanner = scannerHelper.CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Single(postLib.Series);
@ -261,14 +284,17 @@ public class ScannerServiceTests : AbstractDbTest
[Fact]
public async Task ScanLibrary_ImageSeries_SpecialGrouping()
{
var (unitOfWork, _, _) = await CreateDatabase();
var scannerHelper = new ScannerHelper(unitOfWork, _testOutputHelper);
const string testcase = "Image Series with SP Folder - Manga.json";
var library = await _scannerHelper.GenerateScannerData(testcase);
var library = await scannerHelper.GenerateScannerData(testcase);
var scanner = _scannerHelper.CreateServices();
var scanner = scannerHelper.CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Single(postLib.Series);
@ -281,14 +307,17 @@ public class ScannerServiceTests : AbstractDbTest
/// </summary>
public async Task ScanLibrary_ImageSeries_SpecialGrouping_NonEnglish()
{
var (unitOfWork, _, _) = await CreateDatabase();
var scannerHelper = new ScannerHelper(unitOfWork, _testOutputHelper);
const string testcase = "Image Series with SP Folder (Non English) - Image.json";
var library = await _scannerHelper.GenerateScannerData(testcase);
var library = await scannerHelper.GenerateScannerData(testcase);
var scanner = _scannerHelper.CreateServices();
var scanner = scannerHelper.CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Single(postLib.Series);
@ -305,6 +334,9 @@ public class ScannerServiceTests : AbstractDbTest
[Fact]
public async Task ScanLibrary_PublishersInheritFromChapters()
{
var (unitOfWork, _, _) = await CreateDatabase();
var scannerHelper = new ScannerHelper(unitOfWork, _testOutputHelper);
const string testcase = "Flat Special - Manga.json";
var infos = new Dictionary<string, ComicInfo>();
@ -321,12 +353,12 @@ public class ScannerServiceTests : AbstractDbTest
Publisher = "Chapter Publisher"
});
var library = await _scannerHelper.GenerateScannerData(testcase, infos);
var library = await scannerHelper.GenerateScannerData(testcase, infos);
var scanner = _scannerHelper.CreateServices();
var scanner = scannerHelper.CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Single(postLib.Series);
@ -343,14 +375,17 @@ public class ScannerServiceTests : AbstractDbTest
[Fact]
public async Task ScanLibrary_LooseChapters_Pdf()
{
var (unitOfWork, _, _) = await CreateDatabase();
var scannerHelper = new ScannerHelper(unitOfWork, _testOutputHelper);
const string testcase = "PDF Comic Chapters - Comic.json";
var library = await _scannerHelper.GenerateScannerData(testcase);
var library = await scannerHelper.GenerateScannerData(testcase);
var scanner = _scannerHelper.CreateServices();
var scanner = scannerHelper.CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Single(postLib.Series);
@ -362,14 +397,17 @@ public class ScannerServiceTests : AbstractDbTest
[Fact]
public async Task ScanLibrary_LooseChapters_Pdf_LN()
{
var (unitOfWork, _, _) = await CreateDatabase();
var scannerHelper = new ScannerHelper(unitOfWork, _testOutputHelper);
const string testcase = "PDF Comic Chapters - LightNovel.json";
var library = await _scannerHelper.GenerateScannerData(testcase);
var library = await scannerHelper.GenerateScannerData(testcase);
var scanner = _scannerHelper.CreateServices();
var scanner = scannerHelper.CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Single(postLib.Series);
@ -384,6 +422,9 @@ public class ScannerServiceTests : AbstractDbTest
[Fact]
public async Task ScanSeries_NewChapterInNestedFolder()
{
var (unitOfWork, _, _) = await CreateDatabase();
var scannerHelper = new ScannerHelper(unitOfWork, _testOutputHelper);
const string testcase = "Series with Localized - Manga.json";
// Get the first file and generate a ComicInfo
@ -394,12 +435,12 @@ public class ScannerServiceTests : AbstractDbTest
LocalizedSeries = "Sono Bisque Doll wa Koi wo Suru"
});
var library = await _scannerHelper.GenerateScannerData(testcase, infos);
var library = await scannerHelper.GenerateScannerData(testcase, infos);
var scanner = _scannerHelper.CreateServices();
var scanner = scannerHelper.CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Single(postLib.Series);
@ -409,7 +450,7 @@ public class ScannerServiceTests : AbstractDbTest
// Bootstrap a new file in the nested "Sono Bisque Doll wa Koi wo Suru" directory and perform a series scan
var testDirectory = Path.Combine(_testDirectory, Path.GetFileNameWithoutExtension(testcase));
await _scannerHelper.Scaffold(testDirectory, ["My Dress-Up Darling/Sono Bisque Doll wa Koi wo Suru ch 11.cbz"]);
await scannerHelper.Scaffold(testDirectory, ["My Dress-Up Darling/Sono Bisque Doll wa Koi wo Suru ch 11.cbz"]);
// Now that a new file exists in the subdirectory, scan again
await scanner.ScanSeries(series.Id);
@ -421,6 +462,9 @@ public class ScannerServiceTests : AbstractDbTest
[Fact]
public async Task ScanLibrary_LocalizedSeries_MatchesFilename()
{
var (unitOfWork, _, _) = await CreateDatabase();
var scannerHelper = new ScannerHelper(unitOfWork, _testOutputHelper);
const string testcase = "Localized Name matches Filename - Manga.json";
// Get the first file and generate a ComicInfo
@ -431,12 +475,12 @@ public class ScannerServiceTests : AbstractDbTest
LocalizedSeries = "Futoku no Guild"
});
var library = await _scannerHelper.GenerateScannerData(testcase, infos);
var library = await scannerHelper.GenerateScannerData(testcase, infos);
var scanner = _scannerHelper.CreateServices();
var scanner = scannerHelper.CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Single(postLib.Series);
@ -449,6 +493,9 @@ public class ScannerServiceTests : AbstractDbTest
[Fact]
public async Task ScanLibrary_LocalizedSeries_MatchesFilename_SameNames()
{
var (unitOfWork, _, _) = await CreateDatabase();
var scannerHelper = new ScannerHelper(unitOfWork, _testOutputHelper);
const string testcase = "Localized Name matches Filename - Manga.json";
// Get the first file and generate a ComicInfo
@ -459,12 +506,12 @@ public class ScannerServiceTests : AbstractDbTest
LocalizedSeries = "Futoku no Guild"
});
var library = await _scannerHelper.GenerateScannerData(testcase, infos);
var library = await scannerHelper.GenerateScannerData(testcase, infos);
var scanner = _scannerHelper.CreateServices();
var scanner = scannerHelper.CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Single(postLib.Series);
@ -477,20 +524,23 @@ public class ScannerServiceTests : AbstractDbTest
[Fact]
public async Task ScanLibrary_ExcludePattern_Works()
{
var (unitOfWork, _, _) = await CreateDatabase();
var scannerHelper = new ScannerHelper(unitOfWork, _testOutputHelper);
const string testcase = "Exclude Pattern 1 - Manga.json";
// Get the first file and generate a ComicInfo
var infos = new Dictionary<string, ComicInfo>();
var library = await _scannerHelper.GenerateScannerData(testcase, infos);
var library = await scannerHelper.GenerateScannerData(testcase, infos);
library.LibraryExcludePatterns = [new LibraryExcludePattern() { Pattern = "**/Extra/*" }];
UnitOfWork.LibraryRepository.Update(library);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Update(library);
await unitOfWork.CommitAsync();
var scanner = _scannerHelper.CreateServices();
var scanner = scannerHelper.CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Single(postLib.Series);
@ -501,20 +551,23 @@ public class ScannerServiceTests : AbstractDbTest
[Fact]
public async Task ScanLibrary_ExcludePattern_FlippedSlashes_Works()
{
var (unitOfWork, _, _) = await CreateDatabase();
var scannerHelper = new ScannerHelper(unitOfWork, _testOutputHelper);
const string testcase = "Exclude Pattern 1 - Manga.json";
// Get the first file and generate a ComicInfo
var infos = new Dictionary<string, ComicInfo>();
var library = await _scannerHelper.GenerateScannerData(testcase, infos);
var library = await scannerHelper.GenerateScannerData(testcase, infos);
library.LibraryExcludePatterns = [new LibraryExcludePattern() { Pattern = "**\\Extra\\*" }];
UnitOfWork.LibraryRepository.Update(library);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Update(library);
await unitOfWork.CommitAsync();
var scanner = _scannerHelper.CreateServices();
var scanner = scannerHelper.CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Single(postLib.Series);
@ -525,11 +578,14 @@ public class ScannerServiceTests : AbstractDbTest
[Fact]
public async Task ScanLibrary_MultipleRoots_MultipleScans_DataPersists_Forced()
{
var (unitOfWork, _, _) = await CreateDatabase();
var scannerHelper = new ScannerHelper(unitOfWork, _testOutputHelper);
const string testcase = "Multiple Roots - Manga.json";
// Get the first file and generate a ComicInfo
var infos = new Dictionary<string, ComicInfo>();
var library = await _scannerHelper.GenerateScannerData(testcase, infos);
var library = await scannerHelper.GenerateScannerData(testcase, infos);
var testDirectoryPath =
Path.Join(
@ -541,13 +597,13 @@ public class ScannerServiceTests : AbstractDbTest
new FolderPath() {Path = Path.Join(testDirectoryPath, "Root 2")}
];
UnitOfWork.LibraryRepository.Update(library);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Update(library);
await unitOfWork.CommitAsync();
var scanner = _scannerHelper.CreateServices();
var scanner = scannerHelper.CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Equal(2, postLib.Series.Count);
@ -563,7 +619,7 @@ public class ScannerServiceTests : AbstractDbTest
// Rescan to ensure nothing changes yet again
await scanner.ScanLibrary(library.Id, true);
postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.Equal(2, postLib.Series.Count);
s = postLib.Series.First(s => s.Name == "Plush");
Assert.Equal(3, s.Volumes.Count);
@ -578,11 +634,14 @@ public class ScannerServiceTests : AbstractDbTest
[Fact]
public async Task ScanLibrary_MultipleRoots_MultipleScans_DataPersists_NonForced()
{
var (unitOfWork, context, _) = await CreateDatabase();
var scannerHelper = new ScannerHelper(unitOfWork, _testOutputHelper);
const string testcase = "Multiple Roots - Manga.json";
// Get the first file and generate a ComicInfo
var infos = new Dictionary<string, ComicInfo>();
var library = await _scannerHelper.GenerateScannerData(testcase, infos);
var library = await scannerHelper.GenerateScannerData(testcase, infos);
var testDirectoryPath =
Path.Join(
@ -594,13 +653,13 @@ public class ScannerServiceTests : AbstractDbTest
new FolderPath() {Path = Path.Join(testDirectoryPath, "Root 2")}
];
UnitOfWork.LibraryRepository.Update(library);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Update(library);
await unitOfWork.CommitAsync();
var scanner = _scannerHelper.CreateServices();
var scanner = scannerHelper.CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Equal(2, postLib.Series.Count);
@ -614,16 +673,17 @@ public class ScannerServiceTests : AbstractDbTest
File.Copy(Path.Join(root1PlushFolder, "Plush v02.cbz"), Path.Join(root1PlushFolder, "Plush v03.cbz"));
// Emulate time passage by updating lastFolderScan to be a min in the past
await SetLastScannedInThePast(s);
await SetLastScannedInThePast(context, s);
// Rescan to ensure nothing changes yet again
await scanner.ScanLibrary(library.Id, false);
postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Equal(2, postLib.Series.Count);
s = postLib.Series.First(s => s.Name == "Plush");
s = postLib.Series.First(series => series.Name == "Plush");
Assert.Equal(3, s.Volumes.Count);
s2 = postLib.Series.First(s => s.Name == "Accel");
s2 = postLib.Series.First(series => series.Name == "Accel");
Assert.Single(s2.Volumes);
}
@ -631,11 +691,14 @@ public class ScannerServiceTests : AbstractDbTest
public async Task ScanLibrary_AlternatingRemoval_IssueReplication()
{
// https://github.com/Kareadita/Kavita/issues/3476#issuecomment-2661635558
var (unitOfWork, context, _) = await CreateDatabase();
var scannerHelper = new ScannerHelper(unitOfWork, _testOutputHelper);
const string testcase = "Alternating Removal - Manga.json";
// Setup: Generate test library
var infos = new Dictionary<string, ComicInfo>();
var library = await _scannerHelper.GenerateScannerData(testcase, infos);
var library = await scannerHelper.GenerateScannerData(testcase, infos);
var testDirectoryPath = Path.Combine(Directory.GetCurrentDirectory(),
"../../../Services/Test Data/ScannerService/ScanTests",
@ -647,14 +710,14 @@ public class ScannerServiceTests : AbstractDbTest
new FolderPath() { Path = Path.Combine(testDirectoryPath, "Root 2") }
];
UnitOfWork.LibraryRepository.Update(library);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Update(library);
await unitOfWork.CommitAsync();
var scanner = _scannerHelper.CreateServices();
var scanner = scannerHelper.CreateServices();
// First Scan: Everything should be added
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Contains(postLib.Series, s => s.Name == "Accel");
@ -662,19 +725,19 @@ public class ScannerServiceTests : AbstractDbTest
// Second Scan: Remove Root 2, expect Accel to be removed
library.Folders = [new FolderPath() { Path = Path.Combine(testDirectoryPath, "Root 1") }];
UnitOfWork.LibraryRepository.Update(library);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Update(library);
await unitOfWork.CommitAsync();
// Emulate time passage by updating lastFolderScan to be a min in the past
foreach (var s in postLib.Series)
{
s.LastFolderScanned = DateTime.Now.Subtract(TimeSpan.FromMinutes(1));
Context.Series.Update(s);
context.Series.Update(s);
}
await Context.SaveChangesAsync();
await context.SaveChangesAsync();
await scanner.ScanLibrary(library.Id);
postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.DoesNotContain(postLib.Series, s => s.Name == "Accel"); // Ensure Accel is gone
Assert.Contains(postLib.Series, s => s.Name == "Plush");
@ -685,30 +748,32 @@ public class ScannerServiceTests : AbstractDbTest
new FolderPath() { Path = Path.Combine(testDirectoryPath, "Root 1") },
new FolderPath() { Path = Path.Combine(testDirectoryPath, "Root 2") }
];
UnitOfWork.LibraryRepository.Update(library);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Update(library);
await unitOfWork.CommitAsync();
// Emulate time passage by updating lastFolderScan to be a min in the past
foreach (var s in postLib.Series)
{
s.LastFolderScanned = DateTime.Now.Subtract(TimeSpan.FromMinutes(1));
Context.Series.Update(s);
context.Series.Update(s);
}
await Context.SaveChangesAsync();
await context.SaveChangesAsync();
await scanner.ScanLibrary(library.Id);
postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Contains(postLib.Series, s => s.Name == "Accel"); // Accel should be back
Assert.Contains(postLib.Series, s => s.Name == "Plush");
// Emulate time passage by updating lastFolderScan to be a min in the past
await SetAllSeriesLastScannedInThePast(postLib);
await SetAllSeriesLastScannedInThePast(context, postLib);
// Fourth Scan: Run again to check stability (should not remove Accel)
await scanner.ScanLibrary(library.Id);
postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Contains(postLib.Series, s => s.Name == "Accel");
Assert.Contains(postLib.Series, s => s.Name == "Plush");
}
@ -716,11 +781,14 @@ public class ScannerServiceTests : AbstractDbTest
[Fact]
public async Task ScanLibrary_DeleteSeriesInUI_ComeBack()
{
var (unitOfWork, _, _) = await CreateDatabase();
var scannerHelper = new ScannerHelper(unitOfWork, _testOutputHelper);
const string testcase = "Delete Series In UI - Manga.json";
// Setup: Generate test library
var infos = new Dictionary<string, ComicInfo>();
var library = await _scannerHelper.GenerateScannerData(testcase, infos);
var library = await scannerHelper.GenerateScannerData(testcase, infos);
var testDirectoryPath = Path.Combine(Directory.GetCurrentDirectory(),
"../../../Services/Test Data/ScannerService/ScanTests",
@ -732,14 +800,14 @@ public class ScannerServiceTests : AbstractDbTest
new FolderPath() { Path = Path.Combine(testDirectoryPath, "Root 2") }
];
UnitOfWork.LibraryRepository.Update(library);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Update(library);
await unitOfWork.CommitAsync();
var scanner = _scannerHelper.CreateServices();
var scanner = scannerHelper.CreateServices();
// First Scan: Everything should be added
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Contains(postLib.Series, s => s.Name == "Accel");
@ -747,15 +815,16 @@ public class ScannerServiceTests : AbstractDbTest
// Second Scan: Delete the Series
library.Series = [];
await UnitOfWork.CommitAsync();
await unitOfWork.CommitAsync();
postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Empty(postLib.Series);
await scanner.ScanLibrary(library.Id);
postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Contains(postLib.Series, s => s.Name == "Accel"); // Ensure Accel is gone
Assert.Contains(postLib.Series, s => s.Name == "Plush");
}
@ -763,18 +832,21 @@ public class ScannerServiceTests : AbstractDbTest
[Fact]
public async Task SubFolders_NoRemovals_ChangesFound()
{
var (unitOfWork, context, _) = await CreateDatabase();
var scannerHelper = new ScannerHelper(unitOfWork, _testOutputHelper);
const string testcase = "Subfolders always scanning all series changes - Manga.json";
var infos = new Dictionary<string, ComicInfo>();
var library = await _scannerHelper.GenerateScannerData(testcase, infos);
var library = await scannerHelper.GenerateScannerData(testcase, infos);
var testDirectoryPath = library.Folders.First().Path;
UnitOfWork.LibraryRepository.Update(library);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Update(library);
await unitOfWork.CommitAsync();
var scanner = _scannerHelper.CreateServices();
var scanner = scannerHelper.CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Equal(4, postLib.Series.Count);
@ -790,7 +862,7 @@ public class ScannerServiceTests : AbstractDbTest
Assert.Equal(2, executionerAndHerWayOfLife.Volumes.Count);
Assert.Equal(2, executionerAndHerWayOfLife.Volumes.Sum(v => v.Chapters.Count));
await SetAllSeriesLastScannedInThePast(postLib);
await SetAllSeriesLastScannedInThePast(context, postLib);
// Add a new chapter to a volume of the series, and scan. Validate that no chapters were lost, and the new
// chapter was added
@ -800,9 +872,9 @@ public class ScannerServiceTests : AbstractDbTest
Path.Join(executionerCopyDir, "The Executioner and Her Way of Life Vol. 1 Ch. 0002.cbz"));
await scanner.ScanLibrary(library.Id);
await UnitOfWork.CommitAsync();
await unitOfWork.CommitAsync();
postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Equal(4, postLib.Series.Count);
@ -822,18 +894,21 @@ public class ScannerServiceTests : AbstractDbTest
[Fact]
public async Task RemovalPickedUp_NoOtherChanges()
{
var (unitOfWork, _, _) = await CreateDatabase();
var scannerHelper = new ScannerHelper(unitOfWork, _testOutputHelper);
const string testcase = "Series removed when no other changes are made - Manga.json";
var infos = new Dictionary<string, ComicInfo>();
var library = await _scannerHelper.GenerateScannerData(testcase, infos);
var library = await scannerHelper.GenerateScannerData(testcase, infos);
var testDirectoryPath = library.Folders.First().Path;
UnitOfWork.LibraryRepository.Update(library);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Update(library);
await unitOfWork.CommitAsync();
var scanner = _scannerHelper.CreateServices();
var scanner = scannerHelper.CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Equal(2, postLib.Series.Count);
@ -841,9 +916,9 @@ public class ScannerServiceTests : AbstractDbTest
Directory.Delete(executionerCopyDir, true);
await scanner.ScanLibrary(library.Id);
await UnitOfWork.CommitAsync();
await unitOfWork.CommitAsync();
postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Single(postLib.Series);
Assert.Single(postLib.Series, s => s.Name == "Spice and Wolf");
@ -854,18 +929,21 @@ public class ScannerServiceTests : AbstractDbTest
public async Task SubFoldersNoSubFolders_CorrectPickupAfterAdd()
{
// This test case is used in multiple tests and can result in conflict if not separated
var (unitOfWork, context, _) = await CreateDatabase();
var scannerHelper = new ScannerHelper(unitOfWork, _testOutputHelper);
const string testcase = "Subfolders and files at root (2) - Manga.json";
var infos = new Dictionary<string, ComicInfo>();
var library = await _scannerHelper.GenerateScannerData(testcase, infos);
var library = await scannerHelper.GenerateScannerData(testcase, infos);
var testDirectoryPath = library.Folders.First().Path;
UnitOfWork.LibraryRepository.Update(library);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Update(library);
await unitOfWork.CommitAsync();
var scanner = _scannerHelper.CreateServices();
var scanner = scannerHelper.CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Single(postLib.Series);
@ -873,7 +951,7 @@ public class ScannerServiceTests : AbstractDbTest
Assert.Equal(3, spiceAndWolf.Volumes.Count);
Assert.Equal(4, spiceAndWolf.Volumes.Sum(v => v.Chapters.Count));
await SetLastScannedInThePast(spiceAndWolf);
await SetLastScannedInThePast(context, spiceAndWolf);
// Add volume to Spice and Wolf series directory
var spiceAndWolfDir = Path.Join(testDirectoryPath, "Spice and Wolf");
@ -882,7 +960,7 @@ public class ScannerServiceTests : AbstractDbTest
await scanner.ScanLibrary(library.Id);
postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Single(postLib.Series);
@ -890,7 +968,7 @@ public class ScannerServiceTests : AbstractDbTest
Assert.Equal(4, spiceAndWolf.Volumes.Count);
Assert.Equal(5, spiceAndWolf.Volumes.Sum(v => v.Chapters.Count));
await SetLastScannedInThePast(spiceAndWolf);
await SetLastScannedInThePast(context, spiceAndWolf);
// Add file in subfolder
spiceAndWolfDir = Path.Join(spiceAndWolfDir, "Spice and Wolf Vol. 3");
@ -899,7 +977,7 @@ public class ScannerServiceTests : AbstractDbTest
await scanner.ScanLibrary(library.Id);
postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Single(postLib.Series);
@ -916,14 +994,17 @@ public class ScannerServiceTests : AbstractDbTest
[Fact]
public async Task ScanLibrary_SortOrderWorks()
{
var (unitOfWork, _, _) = await CreateDatabase();
var scannerHelper = new ScannerHelper(unitOfWork, _testOutputHelper);
const string testcase = "Sort Order - Manga.json";
var library = await _scannerHelper.GenerateScannerData(testcase);
var library = await scannerHelper.GenerateScannerData(testcase);
var scanner = _scannerHelper.CreateServices();
var scanner = scannerHelper.CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
// Get the loose leaf volume and confirm each chapter aligns with expectation of Sort Order
@ -943,6 +1024,9 @@ public class ScannerServiceTests : AbstractDbTest
[Fact]
public async Task ScanLibrary_MetadataDisabled_NoOverrides()
{
var (unitOfWork, _, _) = await CreateDatabase();
var scannerHelper = new ScannerHelper(unitOfWork, _testOutputHelper);
const string testcase = "Series with Localized No Metadata - Manga.json";
// Get the first file and generate a ComicInfo
@ -953,17 +1037,17 @@ public class ScannerServiceTests : AbstractDbTest
LocalizedSeries = "Futoku no Guild" // Filename has a capital N and localizedSeries has lowercase
});
var library = await _scannerHelper.GenerateScannerData(testcase, infos);
var library = await scannerHelper.GenerateScannerData(testcase, infos);
// Disable metadata
library.EnableMetadata = false;
UnitOfWork.LibraryRepository.Update(library);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Update(library);
await unitOfWork.CommitAsync();
var scanner = _scannerHelper.CreateServices();
var scanner = scannerHelper.CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
// Validate that there are 2 series
Assert.NotNull(postLib);
@ -976,21 +1060,24 @@ public class ScannerServiceTests : AbstractDbTest
[Fact]
public async Task ScanLibrary_SortName_NoPrefix()
{
var (unitOfWork, _, _) = await CreateDatabase();
var scannerHelper = new ScannerHelper(unitOfWork, _testOutputHelper);
const string testcase = "Series with Prefix - Book.json";
var library = await _scannerHelper.GenerateScannerData(testcase);
var library = await scannerHelper.GenerateScannerData(testcase);
library.RemovePrefixForSortName = true;
UnitOfWork.LibraryRepository.Update(library);
await UnitOfWork.CommitAsync();
unitOfWork.LibraryRepository.Update(library);
await unitOfWork.CommitAsync();
var scanner = _scannerHelper.CreateServices();
var scanner = scannerHelper.CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await UnitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
var postLib = await unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Equal(1, postLib.Series.Count);
Assert.Single(postLib.Series);
Assert.Equal("The Avengers", postLib.Series.First().Name);
Assert.Equal("Avengers", postLib.Series.First().SortName);

View File

@ -2,6 +2,7 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using API.Data;
using API.Data.Repositories;
using API.DTOs.Scrobbling;
using API.Entities;
@ -11,15 +12,18 @@ using API.Helpers.Builders;
using API.Services;
using API.Services.Plus;
using API.SignalR;
using Hangfire.Storage.SQLite.Entities;
using Kavita.Common;
using Microsoft.Extensions.Logging;
using NSubstitute;
using Polly;
using Xunit;
using Xunit.Abstractions;
namespace API.Tests.Services;
#nullable enable
public class ScrobblingServiceTests : AbstractDbTest
public class ScrobblingServiceTests(ITestOutputHelper outputHelper): AbstractDbTest(outputHelper)
{
private const int ChapterPages = 100;
@ -34,58 +38,43 @@ public class ScrobblingServiceTests : AbstractDbTest
private const string ValidJwtToken =
"eyJhbGciOiJIUzI1NiJ9.eyJJc3N1ZXIiOiJJc3N1ZXIiLCJleHAiOjcyNzI0NTAxMTcsImlhdCI6MTc1MDAyMTMxN30.zADmcGq_BfxbcV8vy4xw5Cbzn4COkmVINxgqpuL17Ng";
private readonly ScrobblingService _service;
private readonly ILicenseService _licenseService;
private readonly ILocalizationService _localizationService;
private readonly ILogger<ScrobblingService> _logger;
private readonly IEmailService _emailService;
private readonly IKavitaPlusApiService _kavitaPlusApiService;
/// <summary>
/// IReaderService, without the ScrobblingService injected
///
/// </summary>
private readonly IReaderService _readerService;
/// <summary>
/// IReaderService, with the _service injected
/// </summary>
private readonly IReaderService _hookedUpReaderService;
public ScrobblingServiceTests()
/// <param name="unitOfWork"></param>
/// <param name="context"></param>
/// <returns>First IReaderService is not hooked up to the scrobbleService, second one is</returns>
public async Task<(ScrobblingService, ILicenseService, IKavitaPlusApiService, IReaderService, IReaderService)> Setup(IUnitOfWork unitOfWork, DataContext context)
{
_licenseService = Substitute.For<ILicenseService>();
_localizationService = Substitute.For<ILocalizationService>();
_logger = Substitute.For<ILogger<ScrobblingService>>();
_emailService = Substitute.For<IEmailService>();
_kavitaPlusApiService = Substitute.For<IKavitaPlusApiService>();
var licenseService = Substitute.For<ILicenseService>();
var localizationService = Substitute.For<ILocalizationService>();
var logger = Substitute.For<ILogger<ScrobblingService>>();
var emailService = Substitute.For<IEmailService>();
var kavitaPlusApiService = Substitute.For<IKavitaPlusApiService>();
_service = new ScrobblingService(UnitOfWork, Substitute.For<IEventHub>(), _logger, _licenseService,
_localizationService, _emailService, _kavitaPlusApiService);
var service = new ScrobblingService(unitOfWork, Substitute.For<IEventHub>(), logger, licenseService,
localizationService, emailService, kavitaPlusApiService);
_readerService = new ReaderService(UnitOfWork,
var readerService = new ReaderService(unitOfWork,
Substitute.For<ILogger<ReaderService>>(),
Substitute.For<IEventHub>(),
Substitute.For<IImageService>(),
Substitute.For<IDirectoryService>(),
Substitute.For<IScrobblingService>()); // Do not use the actual one
_hookedUpReaderService = new ReaderService(UnitOfWork,
var hookedUpReaderService = new ReaderService(unitOfWork,
Substitute.For<ILogger<ReaderService>>(),
Substitute.For<IEventHub>(),
Substitute.For<IImageService>(),
Substitute.For<IDirectoryService>(),
_service);
service);
await SeedData(unitOfWork, context);
return (service, licenseService, kavitaPlusApiService, readerService, hookedUpReaderService);
}
protected override async Task ResetDb()
{
Context.ScrobbleEvent.RemoveRange(Context.ScrobbleEvent.ToList());
Context.Series.RemoveRange(Context.Series.ToList());
Context.Library.RemoveRange(Context.Library.ToList());
Context.AppUser.RemoveRange(Context.AppUser.ToList());
await UnitOfWork.CommitAsync();
}
private async Task SeedData()
private async Task SeedData(IUnitOfWork unitOfWork, DataContext context)
{
var series = new SeriesBuilder("Test Series")
.WithFormat(MangaFormat.Archive)
@ -122,7 +111,7 @@ public class ScrobblingServiceTests : AbstractDbTest
.Build();
Context.Library.Add(library);
context.Library.Add(library);
var user = new AppUserBuilder("testuser", "testuser")
//.WithPreferences(new UserPreferencesBuilder().WithAniListScrobblingEnabled(true).Build())
@ -130,13 +119,17 @@ public class ScrobblingServiceTests : AbstractDbTest
user.UserPreferences.AniListScrobblingEnabled = true;
UnitOfWork.UserRepository.Add(user);
unitOfWork.UserRepository.Add(user);
await UnitOfWork.CommitAsync();
await unitOfWork.CommitAsync();
}
private async Task<ScrobbleEvent> CreateScrobbleEvent(int? seriesId = null)
{
var (unitOfWork, context, _) = await CreateDatabase();
await Setup(unitOfWork, context);
var evt = new ScrobbleEvent
{
ScrobbleEventType = ScrobbleEventType.ChapterRead,
@ -148,7 +141,7 @@ public class ScrobblingServiceTests : AbstractDbTest
if (seriesId != null)
{
var series = await UnitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId.Value);
var series = await unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId.Value);
if (series != null) evt.Series = series;
}
@ -161,7 +154,10 @@ public class ScrobblingServiceTests : AbstractDbTest
[Fact]
public async Task PostScrobbleUpdate_AuthErrors()
{
_kavitaPlusApiService.PostScrobbleUpdate(null!, "")
var (unitOfWork, context, _) = await CreateDatabase();
var (service, _, kavitaPlusApiService, _, _) = await Setup(unitOfWork, context);
kavitaPlusApiService.PostScrobbleUpdate(null!, "")
.ReturnsForAnyArgs(new ScrobbleResponseDto()
{
ErrorMessage = "Unauthorized"
@ -170,7 +166,7 @@ public class ScrobblingServiceTests : AbstractDbTest
var evt = await CreateScrobbleEvent();
await Assert.ThrowsAsync<KavitaException>(async () =>
{
await _service.PostScrobbleUpdate(new ScrobbleDto(), "", evt);
await service.PostScrobbleUpdate(new ScrobbleDto(), "", evt);
});
Assert.True(evt.IsErrored);
Assert.Equal("Kavita+ subscription no longer active", evt.ErrorDetails);
@ -179,24 +175,26 @@ public class ScrobblingServiceTests : AbstractDbTest
[Fact]
public async Task PostScrobbleUpdate_UnknownSeriesLoggedAsError()
{
_kavitaPlusApiService.PostScrobbleUpdate(null!, "")
var (unitOfWork, context, _) = await CreateDatabase();
var (service, _, kavitaPlusApiService, _, _) = await Setup(unitOfWork, context);
kavitaPlusApiService.PostScrobbleUpdate(null!, "")
.ReturnsForAnyArgs(new ScrobbleResponseDto()
{
ErrorMessage = "Unknown Series"
});
await SeedData();
var evt = await CreateScrobbleEvent(1);
await _service.PostScrobbleUpdate(new ScrobbleDto(), "", evt);
await UnitOfWork.CommitAsync();
await service.PostScrobbleUpdate(new ScrobbleDto(), "", evt);
await unitOfWork.CommitAsync();
Assert.True(evt.IsErrored);
var series = await UnitOfWork.SeriesRepository.GetSeriesByIdAsync(1);
var series = await unitOfWork.SeriesRepository.GetSeriesByIdAsync(1);
Assert.NotNull(series);
Assert.True(series.IsBlacklisted);
var errors = await UnitOfWork.ScrobbleRepository.GetAllScrobbleErrorsForSeries(1);
var errors = await unitOfWork.ScrobbleRepository.GetAllScrobbleErrorsForSeries(1);
Assert.Single(errors);
Assert.Equal("Series cannot be matched for Scrobbling", errors.First().Comment);
Assert.Equal(series.Id, errors.First().SeriesId);
@ -205,7 +203,10 @@ public class ScrobblingServiceTests : AbstractDbTest
[Fact]
public async Task PostScrobbleUpdate_InvalidAccessToken()
{
_kavitaPlusApiService.PostScrobbleUpdate(null!, "")
var (unitOfWork, context, _) = await CreateDatabase();
var (service, _, kavitaPlusApiService, _, _) = await Setup(unitOfWork, context);
kavitaPlusApiService.PostScrobbleUpdate(null!, "")
.ReturnsForAnyArgs(new ScrobbleResponseDto()
{
ErrorMessage = "Access token is invalid"
@ -215,7 +216,7 @@ public class ScrobblingServiceTests : AbstractDbTest
await Assert.ThrowsAsync<KavitaException>(async () =>
{
await _service.PostScrobbleUpdate(new ScrobbleDto(), "", evt);
await service.PostScrobbleUpdate(new ScrobbleDto(), "", evt);
});
Assert.True(evt.IsErrored);
@ -229,76 +230,76 @@ public class ScrobblingServiceTests : AbstractDbTest
[Fact]
public async Task ProcessReadEvents_CreatesNoEventsWhenNoProgress()
{
await ResetDb();
await SeedData();
var (unitOfWork, context, _) = await CreateDatabase();
var (service, licenseService, kavitaPlusApiService, _, _) = await Setup(unitOfWork, context);
// Set Returns
_licenseService.HasActiveLicense().Returns(Task.FromResult(true));
_kavitaPlusApiService.GetRateLimit(Arg.Any<string>(), Arg.Any<string>())
licenseService.HasActiveLicense().Returns(Task.FromResult(true));
kavitaPlusApiService.GetRateLimit(Arg.Any<string>(), Arg.Any<string>())
.Returns(100);
var user = await UnitOfWork.UserRepository.GetUserByIdAsync(1);
var user = await unitOfWork.UserRepository.GetUserByIdAsync(1);
Assert.NotNull(user);
// Ensure CanProcessScrobbleEvent returns true
user.AniListAccessToken = ValidJwtToken;
UnitOfWork.UserRepository.Update(user);
await UnitOfWork.CommitAsync();
unitOfWork.UserRepository.Update(user);
await unitOfWork.CommitAsync();
var chapter = await UnitOfWork.ChapterRepository.GetChapterAsync(4);
var chapter = await unitOfWork.ChapterRepository.GetChapterAsync(4);
Assert.NotNull(chapter);
var volume = await UnitOfWork.VolumeRepository.GetVolumeAsync(1, VolumeIncludes.Chapters);
var volume = await unitOfWork.VolumeRepository.GetVolumeAsync(1, VolumeIncludes.Chapters);
Assert.NotNull(volume);
// Call Scrobble without having any progress
await _service.ScrobbleReadingUpdate(1, 1);
var events = await UnitOfWork.ScrobbleRepository.GetAllEventsForSeries(1);
await service.ScrobbleReadingUpdate(1, 1);
var events = await unitOfWork.ScrobbleRepository.GetAllEventsForSeries(1);
Assert.Empty(events);
}
[Fact]
public async Task ProcessReadEvents_UpdateVolumeAndChapterData()
{
await ResetDb();
await SeedData();
var (unitOfWork, context, _) = await CreateDatabase();
var (service, licenseService, kavitaPlusApiService, readerService, _) = await Setup(unitOfWork, context);
// Set Returns
_licenseService.HasActiveLicense().Returns(Task.FromResult(true));
_kavitaPlusApiService.GetRateLimit(Arg.Any<string>(), Arg.Any<string>())
licenseService.HasActiveLicense().Returns(Task.FromResult(true));
kavitaPlusApiService.GetRateLimit(Arg.Any<string>(), Arg.Any<string>())
.Returns(100);
var user = await UnitOfWork.UserRepository.GetUserByIdAsync(1);
var user = await unitOfWork.UserRepository.GetUserByIdAsync(1);
Assert.NotNull(user);
// Ensure CanProcessScrobbleEvent returns true
user.AniListAccessToken = ValidJwtToken;
UnitOfWork.UserRepository.Update(user);
await UnitOfWork.CommitAsync();
unitOfWork.UserRepository.Update(user);
await unitOfWork.CommitAsync();
var chapter = await UnitOfWork.ChapterRepository.GetChapterAsync(4);
var chapter = await unitOfWork.ChapterRepository.GetChapterAsync(4);
Assert.NotNull(chapter);
var volume = await UnitOfWork.VolumeRepository.GetVolumeAsync(1, VolumeIncludes.Chapters);
var volume = await unitOfWork.VolumeRepository.GetVolumeAsync(1, VolumeIncludes.Chapters);
Assert.NotNull(volume);
// Mark something as read to trigger event creation
await _readerService.MarkChaptersAsRead(user, 1, new List<Chapter>() {volume.Chapters[0]});
await UnitOfWork.CommitAsync();
await readerService.MarkChaptersAsRead(user, 1, new List<Chapter>() {volume.Chapters[0]});
await unitOfWork.CommitAsync();
// Call Scrobble while having some progress
await _service.ScrobbleReadingUpdate(user.Id, 1);
var events = await UnitOfWork.ScrobbleRepository.GetAllEventsForSeries(1);
await service.ScrobbleReadingUpdate(user.Id, 1);
var events = await unitOfWork.ScrobbleRepository.GetAllEventsForSeries(1);
Assert.Single(events);
// Give it some (more) read progress
await _readerService.MarkChaptersAsRead(user, 1, volume.Chapters);
await _readerService.MarkChaptersAsRead(user, 1, [chapter]);
await UnitOfWork.CommitAsync();
await readerService.MarkChaptersAsRead(user, 1, volume.Chapters);
await readerService.MarkChaptersAsRead(user, 1, [chapter]);
await unitOfWork.CommitAsync();
await _service.ProcessUpdatesSinceLastSync();
await service.ProcessUpdatesSinceLastSync();
await _kavitaPlusApiService.Received(1).PostScrobbleUpdate(
await kavitaPlusApiService.Received(1).PostScrobbleUpdate(
Arg.Is<ScrobbleDto>(data =>
data.ChapterNumber == (int)chapter.MaxNumber &&
data.VolumeNumber == (int)volume.MaxNumber
@ -313,84 +314,84 @@ public class ScrobblingServiceTests : AbstractDbTest
[Fact]
public async Task ScrobbleReadingUpdate_IgnoreNoLicense()
{
await ResetDb();
await SeedData();
var (unitOfWork, context, _) = await CreateDatabase();
var (service, licenseService, _, _, _) = await Setup(unitOfWork, context);
_licenseService.HasActiveLicense().Returns(false);
licenseService.HasActiveLicense().Returns(false);
await _service.ScrobbleReadingUpdate(1, 1);
var events = await UnitOfWork.ScrobbleRepository.GetAllEventsForSeries(1);
await service.ScrobbleReadingUpdate(1, 1);
var events = await unitOfWork.ScrobbleRepository.GetAllEventsForSeries(1);
Assert.Empty(events);
}
[Fact]
public async Task ScrobbleReadingUpdate_RemoveWhenNoProgress()
{
await ResetDb();
await SeedData();
var (unitOfWork, context, _) = await CreateDatabase();
var (service, licenseService, _, readerService, hookedUpReaderService) = await Setup(unitOfWork, context);
_licenseService.HasActiveLicense().Returns(true);
licenseService.HasActiveLicense().Returns(true);
var user = await UnitOfWork.UserRepository.GetUserByIdAsync(1);
var user = await unitOfWork.UserRepository.GetUserByIdAsync(1);
Assert.NotNull(user);
var volume = await UnitOfWork.VolumeRepository.GetVolumeAsync(1, VolumeIncludes.Chapters);
var volume = await unitOfWork.VolumeRepository.GetVolumeAsync(1, VolumeIncludes.Chapters);
Assert.NotNull(volume);
await _readerService.MarkChaptersAsRead(user, 1, new List<Chapter>() {volume.Chapters[0]});
await UnitOfWork.CommitAsync();
await readerService.MarkChaptersAsRead(user, 1, new List<Chapter>() {volume.Chapters[0]});
await unitOfWork.CommitAsync();
await _service.ScrobbleReadingUpdate(1, 1);
var events = await UnitOfWork.ScrobbleRepository.GetAllEventsForSeries(1);
await service.ScrobbleReadingUpdate(1, 1);
var events = await unitOfWork.ScrobbleRepository.GetAllEventsForSeries(1);
Assert.Single(events);
var readEvent = events.First();
Assert.False(readEvent.IsProcessed);
await _hookedUpReaderService.MarkSeriesAsUnread(user, 1);
await UnitOfWork.CommitAsync();
await hookedUpReaderService.MarkSeriesAsUnread(user, 1);
await unitOfWork.CommitAsync();
// Existing event is deleted
await _service.ScrobbleReadingUpdate(1, 1);
events = await UnitOfWork.ScrobbleRepository.GetAllEventsForSeries(1);
await service.ScrobbleReadingUpdate(1, 1);
events = await unitOfWork.ScrobbleRepository.GetAllEventsForSeries(1);
Assert.Empty(events);
await _hookedUpReaderService.MarkSeriesAsUnread(user, 1);
await UnitOfWork.CommitAsync();
await hookedUpReaderService.MarkSeriesAsUnread(user, 1);
await unitOfWork.CommitAsync();
// No new events are added
events = await UnitOfWork.ScrobbleRepository.GetAllEventsForSeries(1);
events = await unitOfWork.ScrobbleRepository.GetAllEventsForSeries(1);
Assert.Empty(events);
}
[Fact]
public async Task ScrobbleReadingUpdate_UpdateExistingNotIsProcessed()
{
await ResetDb();
await SeedData();
var (unitOfWork, context, _) = await CreateDatabase();
var (service, licenseService, _, readerService, _) = await Setup(unitOfWork, context);
var user = await UnitOfWork.UserRepository.GetUserByIdAsync(1);
var user = await unitOfWork.UserRepository.GetUserByIdAsync(1);
Assert.NotNull(user);
var chapter1 = await UnitOfWork.ChapterRepository.GetChapterAsync(1);
var chapter2 = await UnitOfWork.ChapterRepository.GetChapterAsync(2);
var chapter3 = await UnitOfWork.ChapterRepository.GetChapterAsync(3);
var chapter1 = await unitOfWork.ChapterRepository.GetChapterAsync(1);
var chapter2 = await unitOfWork.ChapterRepository.GetChapterAsync(2);
var chapter3 = await unitOfWork.ChapterRepository.GetChapterAsync(3);
Assert.NotNull(chapter1);
Assert.NotNull(chapter2);
Assert.NotNull(chapter3);
_licenseService.HasActiveLicense().Returns(true);
licenseService.HasActiveLicense().Returns(true);
var events = await UnitOfWork.ScrobbleRepository.GetAllEventsForSeries(1);
var events = await unitOfWork.ScrobbleRepository.GetAllEventsForSeries(1);
Assert.Empty(events);
await _readerService.MarkChaptersAsRead(user, 1, [chapter1]);
await UnitOfWork.CommitAsync();
await readerService.MarkChaptersAsRead(user, 1, [chapter1]);
await unitOfWork.CommitAsync();
// Scrobble update
await _service.ScrobbleReadingUpdate(1, 1);
events = await UnitOfWork.ScrobbleRepository.GetAllEventsForSeries(1);
await service.ScrobbleReadingUpdate(1, 1);
events = await unitOfWork.ScrobbleRepository.GetAllEventsForSeries(1);
Assert.Single(events);
var readEvent = events[0];
@ -399,25 +400,25 @@ public class ScrobblingServiceTests : AbstractDbTest
// Mark as processed
readEvent.IsProcessed = true;
await UnitOfWork.CommitAsync();
await unitOfWork.CommitAsync();
await _readerService.MarkChaptersAsRead(user, 1, [chapter2]);
await UnitOfWork.CommitAsync();
await readerService.MarkChaptersAsRead(user, 1, [chapter2]);
await unitOfWork.CommitAsync();
// Scrobble update
await _service.ScrobbleReadingUpdate(1, 1);
events = await UnitOfWork.ScrobbleRepository.GetAllEventsForSeries(1);
await service.ScrobbleReadingUpdate(1, 1);
events = await unitOfWork.ScrobbleRepository.GetAllEventsForSeries(1);
Assert.Equal(2, events.Count);
Assert.Single(events.Where(e => e.IsProcessed).ToList());
Assert.Single(events.Where(e => !e.IsProcessed).ToList());
// Should update the existing non processed event
await _readerService.MarkChaptersAsRead(user, 1, [chapter3]);
await UnitOfWork.CommitAsync();
await readerService.MarkChaptersAsRead(user, 1, [chapter3]);
await unitOfWork.CommitAsync();
// Scrobble update
await _service.ScrobbleReadingUpdate(1, 1);
events = await UnitOfWork.ScrobbleRepository.GetAllEventsForSeries(1);
await service.ScrobbleReadingUpdate(1, 1);
events = await unitOfWork.ScrobbleRepository.GetAllEventsForSeries(1);
Assert.Equal(2, events.Count);
Assert.Single(events.Where(e => e.IsProcessed).ToList());
Assert.Single(events.Where(e => !e.IsProcessed).ToList());
@ -430,18 +431,19 @@ public class ScrobblingServiceTests : AbstractDbTest
[Fact]
public async Task ScrobbleWantToReadUpdate_NoExistingEvents_WantToRead_ShouldCreateNewEvent()
{
// Arrange
await SeedData();
_licenseService.HasActiveLicense().Returns(Task.FromResult(true));
var (unitOfWork, context, _) = await CreateDatabase();
var (service, licenseService, _, _, _) = await Setup(unitOfWork, context);
licenseService.HasActiveLicense().Returns(Task.FromResult(true));
const int userId = 1;
const int seriesId = 1;
// Act
await _service.ScrobbleWantToReadUpdate(userId, seriesId, true);
await service.ScrobbleWantToReadUpdate(userId, seriesId, true);
// Assert
var events = await UnitOfWork.ScrobbleRepository.GetAllEventsForSeries(seriesId);
var events = await unitOfWork.ScrobbleRepository.GetAllEventsForSeries(seriesId);
Assert.Single(events);
Assert.Equal(ScrobbleEventType.AddWantToRead, events[0].ScrobbleEventType);
Assert.Equal(userId, events[0].AppUserId);
@ -450,18 +452,19 @@ public class ScrobblingServiceTests : AbstractDbTest
[Fact]
public async Task ScrobbleWantToReadUpdate_NoExistingEvents_RemoveWantToRead_ShouldCreateNewEvent()
{
// Arrange
await SeedData();
_licenseService.HasActiveLicense().Returns(Task.FromResult(true));
var (unitOfWork, context, _) = await CreateDatabase();
var (service, licenseService, _, _, _) = await Setup(unitOfWork, context);
licenseService.HasActiveLicense().Returns(Task.FromResult(true));
const int userId = 1;
const int seriesId = 1;
// Act
await _service.ScrobbleWantToReadUpdate(userId, seriesId, false);
await service.ScrobbleWantToReadUpdate(userId, seriesId, false);
// Assert
var events = await UnitOfWork.ScrobbleRepository.GetAllEventsForSeries(seriesId);
var events = await unitOfWork.ScrobbleRepository.GetAllEventsForSeries(seriesId);
Assert.Single(events);
Assert.Equal(ScrobbleEventType.RemoveWantToRead, events[0].ScrobbleEventType);
Assert.Equal(userId, events[0].AppUserId);
@ -470,21 +473,22 @@ public class ScrobblingServiceTests : AbstractDbTest
[Fact]
public async Task ScrobbleWantToReadUpdate_ExistingWantToReadEvent_WantToRead_ShouldNotCreateNewEvent()
{
// Arrange
await SeedData();
_licenseService.HasActiveLicense().Returns(Task.FromResult(true));
var (unitOfWork, context, _) = await CreateDatabase();
var (service, licenseService, _, _, _) = await Setup(unitOfWork, context);
licenseService.HasActiveLicense().Returns(Task.FromResult(true));
const int userId = 1;
const int seriesId = 1;
// First, let's create an event through the service
await _service.ScrobbleWantToReadUpdate(userId, seriesId, true);
await service.ScrobbleWantToReadUpdate(userId, seriesId, true);
// Act - Try to create the same event again
await _service.ScrobbleWantToReadUpdate(userId, seriesId, true);
await service.ScrobbleWantToReadUpdate(userId, seriesId, true);
// Assert
var events = await UnitOfWork.ScrobbleRepository.GetAllEventsForSeries(seriesId);
var events = await unitOfWork.ScrobbleRepository.GetAllEventsForSeries(seriesId);
Assert.Single(events);
Assert.All(events, e => Assert.Equal(ScrobbleEventType.AddWantToRead, e.ScrobbleEventType));
@ -493,21 +497,22 @@ public class ScrobblingServiceTests : AbstractDbTest
[Fact]
public async Task ScrobbleWantToReadUpdate_ExistingWantToReadEvent_RemoveWantToRead_ShouldAddRemoveEvent()
{
// Arrange
await SeedData();
_licenseService.HasActiveLicense().Returns(Task.FromResult(true));
var (unitOfWork, context, _) = await CreateDatabase();
var (service, licenseService, _, _, _) = await Setup(unitOfWork, context);
licenseService.HasActiveLicense().Returns(Task.FromResult(true));
const int userId = 1;
const int seriesId = 1;
// First, let's create a want-to-read event through the service
await _service.ScrobbleWantToReadUpdate(userId, seriesId, true);
await service.ScrobbleWantToReadUpdate(userId, seriesId, true);
// Act - Now remove from want-to-read
await _service.ScrobbleWantToReadUpdate(userId, seriesId, false);
await service.ScrobbleWantToReadUpdate(userId, seriesId, false);
// Assert
var events = await UnitOfWork.ScrobbleRepository.GetAllEventsForSeries(seriesId);
var events = await unitOfWork.ScrobbleRepository.GetAllEventsForSeries(seriesId);
Assert.Single(events);
Assert.Contains(events, e => e.ScrobbleEventType == ScrobbleEventType.RemoveWantToRead);
@ -516,21 +521,22 @@ public class ScrobblingServiceTests : AbstractDbTest
[Fact]
public async Task ScrobbleWantToReadUpdate_ExistingRemoveWantToReadEvent_RemoveWantToRead_ShouldNotCreateNewEvent()
{
// Arrange
await SeedData();
_licenseService.HasActiveLicense().Returns(Task.FromResult(true));
var (unitOfWork, context, _) = await CreateDatabase();
var (service, licenseService, _, _, _) = await Setup(unitOfWork, context);
licenseService.HasActiveLicense().Returns(Task.FromResult(true));
const int userId = 1;
const int seriesId = 1;
// First, let's create a remove-from-want-to-read event through the service
await _service.ScrobbleWantToReadUpdate(userId, seriesId, false);
await service.ScrobbleWantToReadUpdate(userId, seriesId, false);
// Act - Try to create the same event again
await _service.ScrobbleWantToReadUpdate(userId, seriesId, false);
await service.ScrobbleWantToReadUpdate(userId, seriesId, false);
// Assert
var events = await UnitOfWork.ScrobbleRepository.GetAllEventsForSeries(seriesId);
var events = await unitOfWork.ScrobbleRepository.GetAllEventsForSeries(seriesId);
Assert.Single(events);
Assert.All(events, e => Assert.Equal(ScrobbleEventType.RemoveWantToRead, e.ScrobbleEventType));
@ -539,21 +545,22 @@ public class ScrobblingServiceTests : AbstractDbTest
[Fact]
public async Task ScrobbleWantToReadUpdate_ExistingRemoveWantToReadEvent_WantToRead_ShouldAddWantToReadEvent()
{
// Arrange
await SeedData();
_licenseService.HasActiveLicense().Returns(Task.FromResult(true));
var (unitOfWork, context, _) = await CreateDatabase();
var (service, licenseService, _, _, _) = await Setup(unitOfWork, context);
licenseService.HasActiveLicense().Returns(Task.FromResult(true));
const int userId = 1;
const int seriesId = 1;
// First, let's create a remove-from-want-to-read event through the service
await _service.ScrobbleWantToReadUpdate(userId, seriesId, false);
await service.ScrobbleWantToReadUpdate(userId, seriesId, false);
// Act - Now add to want-to-read
await _service.ScrobbleWantToReadUpdate(userId, seriesId, true);
await service.ScrobbleWantToReadUpdate(userId, seriesId, true);
// Assert
var events = await UnitOfWork.ScrobbleRepository.GetAllEventsForSeries(seriesId);
var events = await unitOfWork.ScrobbleRepository.GetAllEventsForSeries(seriesId);
Assert.Single(events);
Assert.Contains(events, e => e.ScrobbleEventType == ScrobbleEventType.AddWantToRead);
@ -566,47 +573,47 @@ public class ScrobblingServiceTests : AbstractDbTest
[Fact]
public async Task ScrobbleRatingUpdate_IgnoreNoLicense()
{
await ResetDb();
await SeedData();
var (unitOfWork, context, _) = await CreateDatabase();
var (service, licenseService, _, _, _) = await Setup(unitOfWork, context);
_licenseService.HasActiveLicense().Returns(false);
licenseService.HasActiveLicense().Returns(false);
await _service.ScrobbleRatingUpdate(1, 1, 1);
var events = await UnitOfWork.ScrobbleRepository.GetAllEventsForSeries(1);
await service.ScrobbleRatingUpdate(1, 1, 1);
var events = await unitOfWork.ScrobbleRepository.GetAllEventsForSeries(1);
Assert.Empty(events);
}
[Fact]
public async Task ScrobbleRatingUpdate_UpdateExistingNotIsProcessed()
{
await ResetDb();
await SeedData();
var (unitOfWork, context, _) = await CreateDatabase();
var (service, licenseService, _, _, _) = await Setup(unitOfWork, context);
_licenseService.HasActiveLicense().Returns(true);
licenseService.HasActiveLicense().Returns(true);
var user = await UnitOfWork.UserRepository.GetUserByIdAsync(1);
var user = await unitOfWork.UserRepository.GetUserByIdAsync(1);
Assert.NotNull(user);
var series = await UnitOfWork.SeriesRepository.GetSeriesByIdAsync(1);
var series = await unitOfWork.SeriesRepository.GetSeriesByIdAsync(1);
Assert.NotNull(series);
await _service.ScrobbleRatingUpdate(user.Id, series.Id, 1);
var events = await UnitOfWork.ScrobbleRepository.GetAllEventsForSeries(1);
await service.ScrobbleRatingUpdate(user.Id, series.Id, 1);
var events = await unitOfWork.ScrobbleRepository.GetAllEventsForSeries(1);
Assert.Single(events);
Assert.Equal(1, events.First().Rating);
// Mark as processed
events.First().IsProcessed = true;
await UnitOfWork.CommitAsync();
await unitOfWork.CommitAsync();
await _service.ScrobbleRatingUpdate(user.Id, series.Id, 5);
events = await UnitOfWork.ScrobbleRepository.GetAllEventsForSeries(1);
await service.ScrobbleRatingUpdate(user.Id, series.Id, 5);
events = await unitOfWork.ScrobbleRepository.GetAllEventsForSeries(1);
Assert.Equal(2, events.Count);
Assert.Single(events, evt => evt.IsProcessed);
Assert.Single(events, evt => !evt.IsProcessed);
await _service.ScrobbleRatingUpdate(user.Id, series.Id, 5);
events = await UnitOfWork.ScrobbleRepository.GetAllEventsForSeries(1);
await service.ScrobbleRatingUpdate(user.Id, series.Id, 5);
events = await unitOfWork.ScrobbleRepository.GetAllEventsForSeries(1);
Assert.Single(events, evt => !evt.IsProcessed);
Assert.Equal(5, events.First(evt => !evt.IsProcessed).Rating);

File diff suppressed because it is too large Load Diff

View File

@ -18,37 +18,25 @@ using Xunit.Abstractions;
namespace API.Tests.Services;
public abstract class SiteThemeServiceTest : AbstractDbTest
public class SiteThemeServiceTest(ITestOutputHelper outputHelper): AbstractDbTest(outputHelper)
{
private readonly ITestOutputHelper _testOutputHelper;
private readonly ITestOutputHelper _testOutputHelper = outputHelper;
private readonly IEventHub _messageHub = Substitute.For<IEventHub>();
protected SiteThemeServiceTest(ITestOutputHelper testOutputHelper)
{
_testOutputHelper = testOutputHelper;
}
protected override async Task ResetDb()
{
Context.SiteTheme.RemoveRange(Context.SiteTheme);
await Context.SaveChangesAsync();
// Recreate defaults
await Seed.SeedThemes(Context);
}
[Fact]
public async Task UpdateDefault_ShouldThrowOnInvalidId()
{
await ResetDb();
_testOutputHelper.WriteLine($"[UpdateDefault_ShouldThrowOnInvalidId] All Themes: {(await UnitOfWork.SiteThemeRepository.GetThemes()).Count(t => t.IsDefault)}");
var (unitOfWork, context, mapper) = await CreateDatabase();
await Seed.SeedThemes(context);
_testOutputHelper.WriteLine($"[UpdateDefault_ShouldThrowOnInvalidId] All Themes: {(await unitOfWork.SiteThemeRepository.GetThemes()).Count(t => t.IsDefault)}");
var filesystem = CreateFileSystem();
filesystem.AddFile($"{SiteThemeDirectory}custom.css", new MockFileData("123"));
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
var siteThemeService = new ThemeService(ds, UnitOfWork, _messageHub, Substitute.For<IFileService>(),
var siteThemeService = new ThemeService(ds, unitOfWork, _messageHub, Substitute.For<IFileService>(),
Substitute.For<ILogger<ThemeService>>(), Substitute.For<IMemoryCache>());
Context.SiteTheme.Add(new SiteTheme()
context.SiteTheme.Add(new SiteTheme()
{
Name = "Custom",
NormalizedName = "Custom".ToNormalized(),
@ -56,10 +44,10 @@ public abstract class SiteThemeServiceTest : AbstractDbTest
FileName = "custom.css",
IsDefault = false
});
await Context.SaveChangesAsync();
await context.SaveChangesAsync();
var ex = await Assert.ThrowsAsync<KavitaException>(() => siteThemeService.UpdateDefault(10));
Assert.Equal("Theme file missing or invalid", ex.Message);
Assert.Equal("theme-doesnt-exist", ex.Message);
}
@ -67,15 +55,17 @@ public abstract class SiteThemeServiceTest : AbstractDbTest
[Fact]
public async Task GetContent_ShouldReturnContent()
{
await ResetDb();
_testOutputHelper.WriteLine($"[GetContent_ShouldReturnContent] All Themes: {(await UnitOfWork.SiteThemeRepository.GetThemes()).Count(t => t.IsDefault)}");
var (unitOfWork, context, mapper) = await CreateDatabase();
await Seed.SeedThemes(context);
_testOutputHelper.WriteLine($"[GetContent_ShouldReturnContent] All Themes: {(await unitOfWork.SiteThemeRepository.GetThemes()).Count(t => t.IsDefault)}");
var filesystem = CreateFileSystem();
filesystem.AddFile($"{SiteThemeDirectory}custom.css", new MockFileData("123"));
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
var siteThemeService = new ThemeService(ds, UnitOfWork, _messageHub, Substitute.For<IFileService>(),
var siteThemeService = new ThemeService(ds, unitOfWork, _messageHub, Substitute.For<IFileService>(),
Substitute.For<ILogger<ThemeService>>(), Substitute.For<IMemoryCache>());
Context.SiteTheme.Add(new SiteTheme()
context.SiteTheme.Add(new SiteTheme()
{
Name = "Custom",
NormalizedName = "Custom".ToNormalized(),
@ -83,9 +73,9 @@ public abstract class SiteThemeServiceTest : AbstractDbTest
FileName = "custom.css",
IsDefault = false
});
await Context.SaveChangesAsync();
await context.SaveChangesAsync();
var content = await siteThemeService.GetContent((await UnitOfWork.SiteThemeRepository.GetThemeDtoByName("Custom")).Id);
var content = await siteThemeService.GetContent((await unitOfWork.SiteThemeRepository.GetThemeDtoByName("Custom")).Id);
Assert.NotNull(content);
Assert.NotEmpty(content);
Assert.Equal("123", content);
@ -94,15 +84,17 @@ public abstract class SiteThemeServiceTest : AbstractDbTest
[Fact]
public async Task UpdateDefault_ShouldHaveOneDefault()
{
await ResetDb();
_testOutputHelper.WriteLine($"[UpdateDefault_ShouldHaveOneDefault] All Themes: {(await UnitOfWork.SiteThemeRepository.GetThemes()).Count(t => t.IsDefault)}");
var (unitOfWork, context, mapper) = await CreateDatabase();
await Seed.SeedThemes(context);
_testOutputHelper.WriteLine($"[UpdateDefault_ShouldHaveOneDefault] All Themes: {(await unitOfWork.SiteThemeRepository.GetThemes()).Count(t => t.IsDefault)}");
var filesystem = CreateFileSystem();
filesystem.AddFile($"{SiteThemeDirectory}custom.css", new MockFileData("123"));
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
var siteThemeService = new ThemeService(ds, UnitOfWork, _messageHub, Substitute.For<IFileService>(),
var siteThemeService = new ThemeService(ds, unitOfWork, _messageHub, Substitute.For<IFileService>(),
Substitute.For<ILogger<ThemeService>>(), Substitute.For<IMemoryCache>());
Context.SiteTheme.Add(new SiteTheme()
context.SiteTheme.Add(new SiteTheme()
{
Name = "Custom",
NormalizedName = "Custom".ToNormalized(),
@ -110,16 +102,16 @@ public abstract class SiteThemeServiceTest : AbstractDbTest
FileName = "custom.css",
IsDefault = false
});
await Context.SaveChangesAsync();
await context.SaveChangesAsync();
var customTheme = (await UnitOfWork.SiteThemeRepository.GetThemeDtoByName("Custom"));
var customTheme = (await unitOfWork.SiteThemeRepository.GetThemeDtoByName("Custom"));
Assert.NotNull(customTheme);
await siteThemeService.UpdateDefault(customTheme.Id);
Assert.Equal(customTheme.Id, (await UnitOfWork.SiteThemeRepository.GetDefaultTheme()).Id);
Assert.Equal(customTheme.Id, (await unitOfWork.SiteThemeRepository.GetDefaultTheme()).Id);
}
}

View File

@ -1,5 +1,6 @@
using API.Helpers.Builders;
using API.Services.Plus;
using Xunit.Abstractions;
namespace API.Tests.Services;
using System.Collections.Generic;
@ -21,105 +22,29 @@ using Microsoft.Extensions.Logging;
using NSubstitute;
using Xunit;
public class TachiyomiServiceTests
public class TachiyomiServiceTests(ITestOutputHelper outputHelper): AbstractDbTest(outputHelper)
{
private readonly IUnitOfWork _unitOfWork;
private readonly IMapper _mapper;
private readonly DataContext _context;
private readonly ReaderService _readerService;
private readonly TachiyomiService _tachiyomiService;
private const string CacheDirectory = "C:/kavita/config/cache/";
private const string CoverImageDirectory = "C:/kavita/config/covers/";
private const string BackupDirectory = "C:/kavita/config/backups/";
private const string DataDirectory = "C:/data/";
public TachiyomiServiceTests()
public (IReaderService, ITachiyomiService) Setup(IUnitOfWork unitOfWork, IMapper mapper)
{
var contextOptions = new DbContextOptionsBuilder().UseSqlite(CreateInMemoryDatabase()).Options;
_context = new DataContext(contextOptions);
Task.Run(SeedDb).GetAwaiter().GetResult();
var config = new MapperConfiguration(cfg => cfg.AddProfile<AutoMapperProfiles>());
_mapper = config.CreateMapper();
_unitOfWork = new UnitOfWork(_context, _mapper, null);
_readerService = new ReaderService(_unitOfWork, Substitute.For<ILogger<ReaderService>>(),
var readerService = new ReaderService(unitOfWork, Substitute.For<ILogger<ReaderService>>(),
Substitute.For<IEventHub>(), Substitute.For<IImageService>(),
new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), new MockFileSystem()),
Substitute.For<IScrobblingService>());
_tachiyomiService = new TachiyomiService(_unitOfWork, _mapper, Substitute.For<ILogger<TachiyomiService>>(), _readerService);
var tachiyomiService = new TachiyomiService(unitOfWork, mapper, Substitute.For<ILogger<TachiyomiService>>(), readerService);
return (readerService, tachiyomiService);
}
#region Setup
private static DbConnection CreateInMemoryDatabase()
{
var connection = new SqliteConnection("Filename=:memory:");
connection.Open();
return connection;
}
private async Task<bool> SeedDb()
{
await _context.Database.MigrateAsync();
var filesystem = CreateFileSystem();
await Seed.SeedSettings(_context,
new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem));
var setting = await _context.ServerSetting.Where(s => s.Key == ServerSettingKey.CacheDirectory).SingleAsync();
setting.Value = CacheDirectory;
setting = await _context.ServerSetting.Where(s => s.Key == ServerSettingKey.BackupDirectory).SingleAsync();
setting.Value = BackupDirectory;
_context.ServerSetting.Update(setting);
_context.Library.Add(
new LibraryBuilder("Manga")
.WithFolderPath(new FolderPathBuilder("C:/data/").Build())
.Build()
);
return await _context.SaveChangesAsync() > 0;
}
private async Task ResetDb()
{
_context.Series.RemoveRange(_context.Series.ToList());
await _context.SaveChangesAsync();
}
private static MockFileSystem CreateFileSystem()
{
var fileSystem = new MockFileSystem();
fileSystem.Directory.SetCurrentDirectory("C:/kavita/");
fileSystem.AddDirectory("C:/kavita/config/");
fileSystem.AddDirectory(CacheDirectory);
fileSystem.AddDirectory(CoverImageDirectory);
fileSystem.AddDirectory(BackupDirectory);
fileSystem.AddDirectory(DataDirectory);
return fileSystem;
}
#endregion
#region GetLatestChapter
[Fact]
public async Task GetLatestChapter_ShouldReturnChapter_NoProgress()
{
await ResetDb();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (readerService, tachiyomiService) = Setup(unitOfWork, mapper);
var series = new SeriesBuilder("Test")
.WithVolume(new VolumeBuilder(API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)
@ -145,7 +70,7 @@ public class TachiyomiServiceTests
.Build();
_context.AppUser.Add(new AppUser()
context.AppUser.Add(new AppUser()
{
UserName = "majora2007",
Libraries = new List<Library>()
@ -154,9 +79,9 @@ public class TachiyomiServiceTests
}
});
await _context.SaveChangesAsync();
await context.SaveChangesAsync();
var latestChapter = await _tachiyomiService.GetLatestChapter(1, 1);
var latestChapter = await tachiyomiService.GetLatestChapter(1, 1);
Assert.Null(latestChapter);
}
@ -164,7 +89,8 @@ public class TachiyomiServiceTests
[Fact]
public async Task GetLatestChapter_ShouldReturnMaxChapter_CompletelyRead()
{
await ResetDb();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (readerService, tachiyomiService) = Setup(unitOfWork, mapper);
var series = new SeriesBuilder("Test")
.WithVolume(new VolumeBuilder(API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)
@ -189,7 +115,7 @@ public class TachiyomiServiceTests
.WithSeries(series)
.Build();
_context.AppUser.Add(new AppUser()
context.AppUser.Add(new AppUser()
{
UserName = "majora2007",
Libraries = new List<Library>()
@ -198,16 +124,16 @@ public class TachiyomiServiceTests
}
});
await _context.SaveChangesAsync();
await context.SaveChangesAsync();
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Progress);
await _readerService.MarkSeriesAsRead(user,1);
var user = await unitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Progress);
await readerService.MarkSeriesAsRead(user,1);
await _context.SaveChangesAsync();
await context.SaveChangesAsync();
var latestChapter = await _tachiyomiService.GetLatestChapter(1, 1);
var latestChapter = await tachiyomiService.GetLatestChapter(1, 1);
Assert.Equal("96", latestChapter.Number);
}
@ -215,7 +141,8 @@ public class TachiyomiServiceTests
[Fact]
public async Task GetLatestChapter_ShouldReturnHighestChapter_Progress()
{
await ResetDb();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (readerService, tachiyomiService) = Setup(unitOfWork, mapper);
var series = new SeriesBuilder("Test")
.WithVolume(new VolumeBuilder(API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)
@ -240,7 +167,7 @@ public class TachiyomiServiceTests
.WithSeries(series)
.Build();
_context.AppUser.Add(new AppUser()
context.AppUser.Add(new AppUser()
{
UserName = "majora2007",
Libraries = new List<Library>()
@ -249,16 +176,16 @@ public class TachiyomiServiceTests
}
});
await _context.SaveChangesAsync();
await context.SaveChangesAsync();
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Progress);
await _tachiyomiService.MarkChaptersUntilAsRead(user,1,21);
var user = await unitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Progress);
await tachiyomiService.MarkChaptersUntilAsRead(user,1,21);
await _context.SaveChangesAsync();
await context.SaveChangesAsync();
var latestChapter = await _tachiyomiService.GetLatestChapter(1, 1);
var latestChapter = await tachiyomiService.GetLatestChapter(1, 1);
Assert.Equal("21", latestChapter.Number);
}
@ -266,7 +193,8 @@ public class TachiyomiServiceTests
[Fact]
public async Task GetLatestChapter_ShouldReturnEncodedVolume_Progress()
{
await ResetDb();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (readerService, tachiyomiService) = Setup(unitOfWork, mapper);
var series = new SeriesBuilder("Test")
.WithVolume(new VolumeBuilder(API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)
@ -291,7 +219,7 @@ public class TachiyomiServiceTests
.WithSeries(series)
.Build();
_context.AppUser.Add(new AppUser()
context.AppUser.Add(new AppUser()
{
UserName = "majora2007",
Libraries = new List<Library>()
@ -300,24 +228,25 @@ public class TachiyomiServiceTests
}
});
await _context.SaveChangesAsync();
await context.SaveChangesAsync();
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Progress);
var user = await unitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Progress);
await _tachiyomiService.MarkChaptersUntilAsRead(user,1,1/10_000F);
await tachiyomiService.MarkChaptersUntilAsRead(user,1,1/10_000F);
await _context.SaveChangesAsync();
await context.SaveChangesAsync();
var latestChapter = await _tachiyomiService.GetLatestChapter(1, 1);
var latestChapter = await tachiyomiService.GetLatestChapter(1, 1);
Assert.Equal("0.0001", latestChapter.Number);
}
[Fact]
public async Task GetLatestChapter_ShouldReturnEncodedVolume_Progress2()
{
await ResetDb();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (readerService, tachiyomiService) = Setup(unitOfWork, mapper);
var series = new SeriesBuilder("Test")
.WithVolume(new VolumeBuilder("1")
@ -339,7 +268,7 @@ public class TachiyomiServiceTests
.WithSeries(series)
.Build();
_context.AppUser.Add(new AppUser()
context.AppUser.Add(new AppUser()
{
UserName = "majora2007",
Libraries = new List<Library>()
@ -348,17 +277,17 @@ public class TachiyomiServiceTests
}
});
await _context.SaveChangesAsync();
await context.SaveChangesAsync();
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Progress);
var user = await unitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Progress);
await _readerService.MarkSeriesAsRead(user, 1);
await readerService.MarkSeriesAsRead(user, 1);
await _context.SaveChangesAsync();
await context.SaveChangesAsync();
var latestChapter = await _tachiyomiService.GetLatestChapter(1, 1);
var latestChapter = await tachiyomiService.GetLatestChapter(1, 1);
Assert.Equal("0.0003", latestChapter.Number);
}
@ -366,7 +295,8 @@ public class TachiyomiServiceTests
[Fact]
public async Task GetLatestChapter_ShouldReturnEncodedYearlyVolume_Progress()
{
await ResetDb();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (readerService, tachiyomiService) = Setup(unitOfWork, mapper);
var series = new SeriesBuilder("Test")
.WithVolume(new VolumeBuilder(API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)
@ -389,7 +319,7 @@ public class TachiyomiServiceTests
.WithSeries(series)
.Build();
_context.AppUser.Add(new AppUser()
context.AppUser.Add(new AppUser()
{
UserName = "majora2007",
Libraries = new List<Library>()
@ -398,16 +328,16 @@ public class TachiyomiServiceTests
}
});
await _context.SaveChangesAsync();
await context.SaveChangesAsync();
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Progress);
var user = await unitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Progress);
await _tachiyomiService.MarkChaptersUntilAsRead(user,1,2002/10_000F);
await tachiyomiService.MarkChaptersUntilAsRead(user,1,2002/10_000F);
await _context.SaveChangesAsync();
await context.SaveChangesAsync();
var latestChapter = await _tachiyomiService.GetLatestChapter(1, 1);
var latestChapter = await tachiyomiService.GetLatestChapter(1, 1);
Assert.Equal("0.2002", latestChapter.Number);
}
@ -419,7 +349,8 @@ public class TachiyomiServiceTests
[Fact]
public async Task MarkChaptersUntilAsRead_ShouldReturnChapter_NoProgress()
{
await ResetDb();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (readerService, tachiyomiService) = Setup(unitOfWork, mapper);
var series = new SeriesBuilder("Test")
.WithVolume(new VolumeBuilder(API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)
@ -444,7 +375,7 @@ public class TachiyomiServiceTests
.WithSeries(series)
.Build();
_context.AppUser.Add(new AppUser()
context.AppUser.Add(new AppUser()
{
UserName = "majora2007",
Libraries = new List<Library>()
@ -453,16 +384,17 @@ public class TachiyomiServiceTests
}
});
await _context.SaveChangesAsync();
await context.SaveChangesAsync();
var latestChapter = await _tachiyomiService.GetLatestChapter(1, 1);
var latestChapter = await tachiyomiService.GetLatestChapter(1, 1);
Assert.Null(latestChapter);
}
[Fact]
public async Task MarkChaptersUntilAsRead_ShouldReturnMaxChapter_CompletelyRead()
{
await ResetDb();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (readerService, tachiyomiService) = Setup(unitOfWork, mapper);
var series = new SeriesBuilder("Test")
.WithVolume(new VolumeBuilder(API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)
@ -487,7 +419,7 @@ public class TachiyomiServiceTests
.WithSeries(series)
.Build();
_context.AppUser.Add(new AppUser()
context.AppUser.Add(new AppUser()
{
UserName = "majora2007",
Libraries = new List<Library>()
@ -496,15 +428,15 @@ public class TachiyomiServiceTests
}
});
await _context.SaveChangesAsync();
await context.SaveChangesAsync();
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Progress);
await _readerService.MarkSeriesAsRead(user,1);
var user = await unitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Progress);
await readerService.MarkSeriesAsRead(user,1);
await _context.SaveChangesAsync();
await context.SaveChangesAsync();
var latestChapter = await _tachiyomiService.GetLatestChapter(1, 1);
var latestChapter = await tachiyomiService.GetLatestChapter(1, 1);
Assert.Equal("96", latestChapter.Number);
}
@ -512,7 +444,8 @@ public class TachiyomiServiceTests
[Fact]
public async Task MarkChaptersUntilAsRead_ShouldReturnHighestChapter_Progress()
{
await ResetDb();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (readerService, tachiyomiService) = Setup(unitOfWork, mapper);
var series = new SeriesBuilder("Test")
.WithVolume(new VolumeBuilder(API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)
@ -537,7 +470,7 @@ public class TachiyomiServiceTests
.WithSeries(series)
.Build();
_context.AppUser.Add(new AppUser()
context.AppUser.Add(new AppUser()
{
UserName = "majora2007",
Libraries = new List<Library>()
@ -546,22 +479,23 @@ public class TachiyomiServiceTests
}
});
await _context.SaveChangesAsync();
await context.SaveChangesAsync();
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Progress);
await _tachiyomiService.MarkChaptersUntilAsRead(user,1,21);
var user = await unitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Progress);
await tachiyomiService.MarkChaptersUntilAsRead(user,1,21);
await _context.SaveChangesAsync();
await context.SaveChangesAsync();
var latestChapter = await _tachiyomiService.GetLatestChapter(1, 1);
var latestChapter = await tachiyomiService.GetLatestChapter(1, 1);
Assert.Equal("21", latestChapter.Number);
}
[Fact]
public async Task MarkChaptersUntilAsRead_ShouldReturnEncodedVolume_Progress()
{
await ResetDb();
var (unitOfWork, context, mapper) = await CreateDatabase();
var (readerService, tachiyomiService) = Setup(unitOfWork, mapper);
var series = new SeriesBuilder("Test")
.WithVolume(new VolumeBuilder(API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)
.WithChapter(new ChapterBuilder("95").WithPages(1).Build())
@ -585,7 +519,7 @@ public class TachiyomiServiceTests
.WithSeries(series)
.Build();
_context.AppUser.Add(new AppUser()
context.AppUser.Add(new AppUser()
{
UserName = "majora2007",
Libraries = new List<Library>()
@ -594,16 +528,16 @@ public class TachiyomiServiceTests
}
});
await _context.SaveChangesAsync();
await context.SaveChangesAsync();
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Progress);
var user = await unitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Progress);
await _tachiyomiService.MarkChaptersUntilAsRead(user,1,1/10_000F);
await tachiyomiService.MarkChaptersUntilAsRead(user,1,1/10_000F);
await _context.SaveChangesAsync();
await context.SaveChangesAsync();
var latestChapter = await _tachiyomiService.GetLatestChapter(1, 1);
var latestChapter = await tachiyomiService.GetLatestChapter(1, 1);
Assert.Equal("0.0001", latestChapter.Number);
}

View File

@ -3,6 +3,7 @@ using System.IO;
using System.IO.Abstractions.TestingHelpers;
using System.Linq;
using System.Threading.Tasks;
using API.Data;
using API.Entities;
using API.Entities.Enums;
using API.Extensions;
@ -14,38 +15,34 @@ using API.Services.Tasks.Metadata;
using API.SignalR;
using Microsoft.Extensions.Logging;
using NSubstitute;
using Polly;
using Xunit;
using Xunit.Abstractions;
namespace API.Tests.Services;
public class WordCountAnalysisTests : AbstractDbTest
public class WordCountAnalysisTests(ITestOutputHelper outputHelper): AbstractDbTest(outputHelper)
{
private readonly IReaderService _readerService;
private readonly string _testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/BookService");
private const long WordCount = 33608; // 37417 if splitting on space, 33608 if just character count
private const long MinHoursToRead = 1;
private const float AvgHoursToRead = 1.66954792f;
private const long MaxHoursToRead = 3;
public WordCountAnalysisTests()
private IReaderService Setup(IUnitOfWork unitOfWork)
{
_readerService = new ReaderService(UnitOfWork, Substitute.For<ILogger<ReaderService>>(),
return new ReaderService(unitOfWork, Substitute.For<ILogger<ReaderService>>(),
Substitute.For<IEventHub>(), Substitute.For<IImageService>(),
new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), new MockFileSystem()),
Substitute.For<IScrobblingService>());
}
protected override async Task ResetDb()
{
Context.Series.RemoveRange(Context.Series.ToList());
await Context.SaveChangesAsync();
}
[Fact]
public async Task ReadingTimeShouldBeNonZero()
{
await ResetDb();
var (unitOfWork, context, mapper) = await CreateDatabase();
var readerService = Setup(unitOfWork);
var series = new SeriesBuilder("Test Series")
.WithFormat(MangaFormat.Epub)
.Build();
@ -57,7 +54,7 @@ public class WordCountAnalysisTests : AbstractDbTest
MangaFormat.Epub).Build())
.Build();
Context.Library.Add(new LibraryBuilder("Test LIb", LibraryType.Book)
context.Library.Add(new LibraryBuilder("Test LIb", LibraryType.Book)
.WithSeries(series)
.Build());
@ -68,12 +65,12 @@ public class WordCountAnalysisTests : AbstractDbTest
.Build(),
};
await Context.SaveChangesAsync();
await context.SaveChangesAsync();
var cacheService = new CacheHelper(new FileService());
var service = new WordCountAnalyzerService(Substitute.For<ILogger<WordCountAnalyzerService>>(), UnitOfWork,
Substitute.For<IEventHub>(), cacheService, _readerService, Substitute.For<IMediaErrorService>());
var service = new WordCountAnalyzerService(Substitute.For<ILogger<WordCountAnalyzerService>>(), unitOfWork,
Substitute.For<IEventHub>(), cacheService, readerService, Substitute.For<IMediaErrorService>());
await service.ScanSeries(1, 1);
@ -101,7 +98,9 @@ public class WordCountAnalysisTests : AbstractDbTest
[Fact]
public async Task ReadingTimeShouldIncreaseWhenNewBookAdded()
{
await ResetDb();
var (unitOfWork, context, mapper) = await CreateDatabase();
var readerService = Setup(unitOfWork);
var chapter = new ChapterBuilder("")
.WithFile(new MangaFileBuilder(
Path.Join(_testDirectory,
@ -115,17 +114,17 @@ public class WordCountAnalysisTests : AbstractDbTest
.Build())
.Build();
Context.Library.Add(new LibraryBuilder("Test", LibraryType.Book)
context.Library.Add(new LibraryBuilder("Test", LibraryType.Book)
.WithSeries(series)
.Build());
await Context.SaveChangesAsync();
await context.SaveChangesAsync();
var cacheService = new CacheHelper(new FileService());
var service = new WordCountAnalyzerService(Substitute.For<ILogger<WordCountAnalyzerService>>(), UnitOfWork,
Substitute.For<IEventHub>(), cacheService, _readerService, Substitute.For<IMediaErrorService>());
var service = new WordCountAnalyzerService(Substitute.For<ILogger<WordCountAnalyzerService>>(), unitOfWork,
Substitute.For<IEventHub>(), cacheService, readerService, Substitute.For<IMediaErrorService>());
await service.ScanSeries(1, 1);
var chapter2 = new ChapterBuilder("2")
@ -141,7 +140,7 @@ public class WordCountAnalysisTests : AbstractDbTest
.Build());
series.Volumes[0].Chapters.Add(chapter2);
await UnitOfWork.CommitAsync();
await unitOfWork.CommitAsync();
await service.ScanSeries(1, 1);