mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-08-30 23:00:06 -04:00
[skip ci] Unit test rework (#3997)
This commit is contained in:
parent
2ad7249725
commit
c42dfeda02
@ -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
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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")));
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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>());
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user