mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-06-23 15:30:34 -04:00
* When account updates occur for a user, send an event to them to tell them to refresh their account information (if they are on the site at the time). This way if we revoke permissions, the site will reactively adapt. * Some cleanup on the user preferences to remove some calls we don't need anymore. * Removed old bulk cleanup bookmark code as it's no longer needed. * Tweaked the messaging for stat collection to reflect what we collect now versus when this was initially implemented. * Implemented the ability for users to configure their servers to save bookmarks as webP. Reorganized the tabs for Admin dashboard to account for upcoming features. * Implemented the ability to bulk convert bookmarks (as many times as the user wants). Added a display of Reoccurring Jobs to the Tasks admin tab. Currently it's just placeholder, but will be enhanced further later in the release. * Tweaked the wording around the convert switch. * Moved System actions to the task tab * Added a controller just for Tachiyomi so we can have dedicated APIs for that client. Deprecated an existing API on the Reader route. * Fixed the unit tests
407 lines
12 KiB
C#
407 lines
12 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Data.Common;
|
|
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.Reader;
|
|
using API.Entities;
|
|
using API.Entities.Enums;
|
|
using API.Services;
|
|
using API.SignalR;
|
|
using AutoMapper;
|
|
using Microsoft.Data.Sqlite;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
using Microsoft.Extensions.Logging;
|
|
using NSubstitute;
|
|
using Xunit;
|
|
|
|
namespace API.Tests.Services;
|
|
|
|
public class BookmarkServiceTests
|
|
{
|
|
private readonly IUnitOfWork _unitOfWork;
|
|
private readonly DbConnection _connection;
|
|
private readonly DataContext _context;
|
|
|
|
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 BookmarkDirectory = "C:/kavita/config/bookmarks/";
|
|
|
|
|
|
public BookmarkServiceTests()
|
|
{
|
|
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);
|
|
}
|
|
|
|
private BookmarkService Create(IDirectoryService ds)
|
|
{
|
|
return new BookmarkService(Substitute.For<ILogger<BookmarkService>>(), _unitOfWork, ds,
|
|
Substitute.For<IImageService>(), Substitute.For<IEventHub>());
|
|
}
|
|
|
|
#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 Library()
|
|
{
|
|
Name = "Manga",
|
|
Folders = new List<FolderPath>()
|
|
{
|
|
new FolderPath()
|
|
{
|
|
Path = "C:/data/"
|
|
}
|
|
}
|
|
});
|
|
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();
|
|
}
|
|
|
|
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(BookmarkDirectory);
|
|
fileSystem.AddDirectory("C:/data/");
|
|
|
|
return fileSystem;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region BookmarkPage
|
|
|
|
[Fact]
|
|
public async Task BookmarkPage_ShouldCopyTheFileAndUpdateDB()
|
|
{
|
|
var filesystem = CreateFileSystem();
|
|
var file = $"{CacheDirectory}1/0001.jpg";
|
|
filesystem.AddFile(file, new MockFileData("123"));
|
|
|
|
// Delete all Series to reset state
|
|
await ResetDB();
|
|
|
|
_context.Series.Add(new Series()
|
|
{
|
|
Name = "Test",
|
|
Library = new Library() {
|
|
Name = "Test LIb",
|
|
Type = LibraryType.Manga,
|
|
},
|
|
Volumes = new List<Volume>()
|
|
{
|
|
new Volume()
|
|
{
|
|
Chapters = new List<Chapter>()
|
|
{
|
|
new Chapter()
|
|
{
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
_context.AppUser.Add(new AppUser()
|
|
{
|
|
UserName = "Joe"
|
|
});
|
|
|
|
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 result = await bookmarkService.BookmarkPage(user, new BookmarkDto()
|
|
{
|
|
ChapterId = 1,
|
|
Page = 1,
|
|
SeriesId = 1,
|
|
VolumeId = 1
|
|
}, file);
|
|
|
|
|
|
Assert.True(result);
|
|
Assert.Equal(1, ds.GetFiles(BookmarkDirectory, searchOption:SearchOption.AllDirectories).Count());
|
|
Assert.NotNull(await _unitOfWork.UserRepository.GetBookmarkAsync(1));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task BookmarkPage_ShouldDeleteFileOnUnbookmark()
|
|
{
|
|
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();
|
|
|
|
_context.Series.Add(new Series()
|
|
{
|
|
Name = "Test",
|
|
Library = new Library() {
|
|
Name = "Test LIb",
|
|
Type = LibraryType.Manga,
|
|
},
|
|
Volumes = new List<Volume>()
|
|
{
|
|
new Volume()
|
|
{
|
|
Chapters = new List<Chapter>()
|
|
{
|
|
new Chapter()
|
|
{
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
|
|
_context.AppUser.Add(new AppUser()
|
|
{
|
|
UserName = "Joe",
|
|
Bookmarks = new List<AppUserBookmark>()
|
|
{
|
|
new AppUserBookmark()
|
|
{
|
|
Page = 1,
|
|
ChapterId = 1,
|
|
FileName = $"1/1/0001.jpg",
|
|
SeriesId = 1,
|
|
VolumeId = 1
|
|
}
|
|
}
|
|
});
|
|
|
|
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 result = await bookmarkService.RemoveBookmarkPage(user, new BookmarkDto()
|
|
{
|
|
ChapterId = 1,
|
|
Page = 1,
|
|
SeriesId = 1,
|
|
VolumeId = 1
|
|
});
|
|
|
|
|
|
Assert.True(result);
|
|
Assert.Equal(0, ds.GetFiles(BookmarkDirectory, searchOption:SearchOption.AllDirectories).Count());
|
|
Assert.Null(await _unitOfWork.UserRepository.GetBookmarkAsync(1));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region DeleteBookmarkFiles
|
|
|
|
[Fact]
|
|
public async Task DeleteBookmarkFiles_ShouldDeleteOnlyPassedFiles()
|
|
{
|
|
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();
|
|
|
|
_context.Series.Add(new Series()
|
|
{
|
|
Name = "Test",
|
|
Library = new Library() {
|
|
Name = "Test LIb",
|
|
Type = LibraryType.Manga,
|
|
},
|
|
Volumes = new List<Volume>()
|
|
{
|
|
new Volume()
|
|
{
|
|
Chapters = new List<Chapter>()
|
|
{
|
|
new Chapter()
|
|
{
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
|
|
_context.AppUser.Add(new AppUser()
|
|
{
|
|
UserName = "Joe",
|
|
Bookmarks = new List<AppUserBookmark>()
|
|
{
|
|
new AppUserBookmark()
|
|
{
|
|
Page = 1,
|
|
ChapterId = 1,
|
|
FileName = $"1/1/1/0001.jpg",
|
|
SeriesId = 1,
|
|
VolumeId = 1
|
|
},
|
|
new AppUserBookmark()
|
|
{
|
|
Page = 2,
|
|
ChapterId = 1,
|
|
FileName = $"1/2/1/0002.jpg",
|
|
SeriesId = 2,
|
|
VolumeId = 1
|
|
},
|
|
new AppUserBookmark()
|
|
{
|
|
Page = 1,
|
|
ChapterId = 2,
|
|
FileName = $"1/2/1/0001.jpg",
|
|
SeriesId = 2,
|
|
VolumeId = 1
|
|
}
|
|
}
|
|
});
|
|
|
|
await _context.SaveChangesAsync();
|
|
|
|
|
|
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
|
|
var bookmarkService = Create(ds);
|
|
|
|
await bookmarkService.DeleteBookmarkFiles(new [] {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());
|
|
Assert.False(ds.FileSystem.FileInfo.FromFileName(Path.Join(BookmarkDirectory, "1/1/1/0001.jpg")).Exists);
|
|
}
|
|
#endregion
|
|
|
|
#region GetBookmarkFilesById
|
|
|
|
[Fact]
|
|
public async Task GetBookmarkFilesById_ShouldMatchActualFiles()
|
|
{
|
|
var filesystem = CreateFileSystem();
|
|
filesystem.AddFile($"{CacheDirectory}1/0001.jpg", new MockFileData("123"));
|
|
|
|
// Delete all Series to reset state
|
|
await ResetDB();
|
|
|
|
_context.Series.Add(new Series()
|
|
{
|
|
Name = "Test",
|
|
Library = new Library() {
|
|
Name = "Test LIb",
|
|
Type = LibraryType.Manga,
|
|
},
|
|
Volumes = new List<Volume>()
|
|
{
|
|
new Volume()
|
|
{
|
|
Chapters = new List<Chapter>()
|
|
{
|
|
new Chapter()
|
|
{
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
_context.AppUser.Add(new AppUser()
|
|
{
|
|
UserName = "Joe"
|
|
});
|
|
|
|
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);
|
|
|
|
await bookmarkService.BookmarkPage(user, new BookmarkDto()
|
|
{
|
|
ChapterId = 1,
|
|
Page = 1,
|
|
SeriesId = 1,
|
|
VolumeId = 1
|
|
}, $"{CacheDirectory}1/0001.jpg");
|
|
|
|
var files = await bookmarkService.GetBookmarkFilesById(new[] {1});
|
|
var actualFiles = ds.GetFiles(BookmarkDirectory, searchOption: SearchOption.AllDirectories);
|
|
Assert.Equal(files.Select(API.Parser.Parser.NormalizePath).ToList(), actualFiles.Select(API.Parser.Parser.NormalizePath).ToList());
|
|
}
|
|
|
|
|
|
#endregion
|
|
}
|