Misc Polish and Fixes (#1542)

* Moved LibraryWatcher to utilize a queue for calculating the change event to ensure the Watcher doesn't get overwhelmed on large moves.

* Fixed a security vulnerability (https://huntr.dev/bounties/8a3e652f-d6bf-436e-877e-0eaf5c69ef95/). This will be disclosed in Stable release changelog.

* Tweaked the log message template

* Removed some dead code from Configuration json patcher

* Fixed a bug with the ComicInfo finding to properly handle root level.

Fixed a bug where sometimes scanner wouldn't choose the first file with ComicInfo for filling out information.

* Added new setting for managing how many logs files are allowed, just like how backups work.

* Added unit tests for new CleanupLogs code

* Fixed a bug where manga reader background color wasn't actually sending from the UI

* Added new stats for tracking to help understand usage in the app and what features are used or not.

* Fixed Stats url

* Fixed a bug where volumes that had larger than 1 difference wouldn't properly return next/prev chapter (for continuous reader)

* Remove a redundant test step in build pipeline, since it's already done at PR stage.

* Updated dockerfile to use the new Heath check endpoint

* Allow force to pass through to scan loop

* Removed some old config stuff from a safety check on config in entrypoint.sh

* Fixed broken unit tests due to new RBS check and how we setup mock data.
This commit is contained in:
Joseph Milazzo 2022-09-18 12:24:30 -05:00 committed by GitHub
parent c58c7deaf9
commit e89a06865c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 702 additions and 308 deletions

View file

@ -38,6 +38,7 @@ public class CleanupServiceTests
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 LogDirectory = "C:/kavita/config/logs/";
private const string BookmarkDirectory = "C:/kavita/config/bookmarks/";
@ -84,6 +85,9 @@ public class CleanupServiceTests
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.Value = "10";
_context.ServerSetting.Update(setting);
_context.Library.Add(new Library()
@ -412,6 +416,59 @@ public class CleanupServiceTests
#endregion
#region CleanupLogs
[Fact]
public async Task CleanupLogs_LeaveOneFile_SinceAllAreExpired()
{
var filesystem = CreateFileSystem();
foreach (var i in Enumerable.Range(1, 10))
{
var day = API.Services.Tasks.Scanner.Parser.Parser.PadZeros($"{i}");
filesystem.AddFile($"{LogDirectory}kavita202009{day}.log", new MockFileData("")
{
CreationTime = DateTimeOffset.Now.Subtract(TimeSpan.FromDays(31))
});
}
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
var cleanupService = new CleanupService(_logger, _unitOfWork, _messageHub,
ds);
await cleanupService.CleanupLogs();
Assert.Single(ds.GetFiles(LogDirectory, searchOption: SearchOption.AllDirectories));
}
[Fact]
public async Task CleanupLogs_LeaveLestExpired()
{
var filesystem = CreateFileSystem();
foreach (var i in Enumerable.Range(1, 9))
{
var day = API.Services.Tasks.Scanner.Parser.Parser.PadZeros($"{i}");
filesystem.AddFile($"{LogDirectory}kavita202009{day}.log", new MockFileData("")
{
CreationTime = DateTimeOffset.Now.Subtract(TimeSpan.FromDays(31 - i))
});
}
filesystem.AddFile($"{LogDirectory}kavita20200910.log", new MockFileData("")
{
CreationTime = DateTimeOffset.Now.Subtract(TimeSpan.FromDays(31 - 10))
});
filesystem.AddFile($"{LogDirectory}kavita20200911.log", new MockFileData("")
{
CreationTime = DateTimeOffset.Now.Subtract(TimeSpan.FromDays(31 - 11))
});
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
var cleanupService = new CleanupService(_logger, _unitOfWork, _messageHub,
ds);
await cleanupService.CleanupLogs();
Assert.True(filesystem.File.Exists($"{LogDirectory}kavita20200911.log"));
}
#endregion
// #region CleanupBookmarks
//
// [Fact]

View file

@ -895,6 +895,60 @@ public class ReaderServiceTests
Assert.Equal("1", actualChapter.Range);
}
[Fact]
public async Task GetPrevChapterIdAsync_ShouldGetPrevVolume_2()
{
await ResetDb();
_context.Series.Add(new Series()
{
Name = "Test",
Library = new Library() {
Name = "Test LIb",
Type = LibraryType.Manga,
},
Volumes = new List<Volume>()
{
EntityFactory.CreateVolume("0", new List<Chapter>()
{
EntityFactory.CreateChapter("40", false, new List<MangaFile>(), 1),
EntityFactory.CreateChapter("50", false, new List<MangaFile>(), 1),
EntityFactory.CreateChapter("60", false, new List<MangaFile>(), 1),
EntityFactory.CreateChapter("Some Special Title", true, new List<MangaFile>(), 1),
}),
EntityFactory.CreateVolume("1997", new List<Chapter>()
{
EntityFactory.CreateChapter("1", false, new List<MangaFile>(), 1),
}),
EntityFactory.CreateVolume("2001", new List<Chapter>()
{
EntityFactory.CreateChapter("21", false, new List<MangaFile>(), 1),
}),
EntityFactory.CreateVolume("2005", new List<Chapter>()
{
EntityFactory.CreateChapter("31", false, new List<MangaFile>(), 1),
}),
}
});
_context.AppUser.Add(new AppUser()
{
UserName = "majora2007"
});
await _context.SaveChangesAsync();
var readerService = new ReaderService(_unitOfWork, Substitute.For<ILogger<ReaderService>>(), Substitute.For<IEventHub>());
// prevChapter should be id from ch.21 from volume 2001
var prevChapter = await readerService.GetPrevChapterIdAsync(1, 4, 7, 1);
var actualChapter = await _unitOfWork.ChapterRepository.GetChapterAsync(prevChapter);
Assert.NotNull(actualChapter);
Assert.Equal("21", actualChapter.Range);
}
[Fact]
public async Task GetPrevChapterIdAsync_ShouldRollIntoPrevVolume()
{

View file

@ -85,19 +85,19 @@ public class SeriesServiceTests
_context.ServerSetting.Update(setting);
var lib = new Library()
{
Name = "Manga", Folders = new List<FolderPath>() {new FolderPath() {Path = "C:/data/"}}
};
_context.AppUser.Add(new AppUser()
{
UserName = "majora2007",
Libraries = new List<Library>()
{
lib
}
});
// var lib = new Library()
// {
// Name = "Manga", Folders = new List<FolderPath>() {new FolderPath() {Path = "C:/data/"}}
// };
//
// _context.AppUser.Add(new AppUser()
// {
// UserName = "majora2007",
// Libraries = new List<Library>()
// {
// lib
// }
// });
return await _context.SaveChangesAsync() > 0;
}
@ -109,6 +109,7 @@ public class SeriesServiceTests
_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();
}
@ -135,33 +136,45 @@ public class SeriesServiceTests
{
await ResetDb();
_context.Series.Add(new Series()
_context.Library.Add(new Library()
{
Name = "Test",
Library = new Library() {
Name = "Test LIb",
Type = LibraryType.Manga,
},
Volumes = new List<Volume>()
AppUsers = new List<AppUser>()
{
EntityFactory.CreateVolume("0", new List<Chapter>()
new AppUser()
{
EntityFactory.CreateChapter("Omake", true, new List<MangaFile>()),
EntityFactory.CreateChapter("Something SP02", true, new List<MangaFile>()),
}),
EntityFactory.CreateVolume("2", new List<Chapter>()
UserName = "majora2007"
}
},
Name = "Test LIb",
Type = LibraryType.Book,
Series = new List<Series>()
{
new Series()
{
EntityFactory.CreateChapter("21", false, new List<MangaFile>()),
EntityFactory.CreateChapter("22", false, new List<MangaFile>()),
}),
EntityFactory.CreateVolume("3", new List<Chapter>()
{
EntityFactory.CreateChapter("31", false, new List<MangaFile>()),
EntityFactory.CreateChapter("32", false, new List<MangaFile>()),
}),
Name = "Test",
Volumes = new List<Volume>()
{
EntityFactory.CreateVolume("0", new List<Chapter>()
{
EntityFactory.CreateChapter("Omake", true, new List<MangaFile>()),
EntityFactory.CreateChapter("Something SP02", true, new List<MangaFile>()),
}),
EntityFactory.CreateVolume("2", new List<Chapter>()
{
EntityFactory.CreateChapter("21", false, new List<MangaFile>()),
EntityFactory.CreateChapter("22", false, new List<MangaFile>()),
}),
EntityFactory.CreateVolume("3", new List<Chapter>()
{
EntityFactory.CreateChapter("31", false, new List<MangaFile>()),
EntityFactory.CreateChapter("32", false, new List<MangaFile>()),
}),
}
}
}
});
await _context.SaveChangesAsync();
var expectedRanges = new[] {"Omake", "Something SP02"};
@ -177,30 +190,41 @@ public class SeriesServiceTests
{
await ResetDb();
_context.Series.Add(new Series()
_context.Library.Add(new Library()
{
Name = "Test",
Library = new Library() {
Name = "Test LIb",
Type = LibraryType.Manga,
},
Volumes = new List<Volume>()
AppUsers = new List<AppUser>()
{
EntityFactory.CreateVolume("0", new List<Chapter>()
new AppUser()
{
EntityFactory.CreateChapter("1", false, new List<MangaFile>()),
EntityFactory.CreateChapter("2", false, new List<MangaFile>()),
}),
EntityFactory.CreateVolume("2", new List<Chapter>()
UserName = "majora2007"
}
},
Name = "Test LIb",
Type = LibraryType.Manga,
Series = new List<Series>()
{
new Series()
{
EntityFactory.CreateChapter("21", false, new List<MangaFile>()),
EntityFactory.CreateChapter("22", false, new List<MangaFile>()),
}),
EntityFactory.CreateVolume("3", new List<Chapter>()
{
EntityFactory.CreateChapter("31", false, new List<MangaFile>()),
EntityFactory.CreateChapter("32", false, new List<MangaFile>()),
}),
Name = "Test",
Volumes = new List<Volume>()
{
EntityFactory.CreateVolume("0", new List<Chapter>()
{
EntityFactory.CreateChapter("1", false, new List<MangaFile>()),
EntityFactory.CreateChapter("2", false, new List<MangaFile>()),
}),
EntityFactory.CreateVolume("2", new List<Chapter>()
{
EntityFactory.CreateChapter("21", false, new List<MangaFile>()),
EntityFactory.CreateChapter("22", false, new List<MangaFile>()),
}),
EntityFactory.CreateVolume("3", new List<Chapter>()
{
EntityFactory.CreateChapter("31", false, new List<MangaFile>()),
EntityFactory.CreateChapter("32", false, new List<MangaFile>()),
}),
}
}
}
});
@ -220,28 +244,39 @@ public class SeriesServiceTests
{
await ResetDb();
_context.Series.Add(new Series()
_context.Library.Add(new Library()
{
Name = "Test",
Library = new Library() {
Name = "Test LIb",
Type = LibraryType.Manga,
},
Volumes = new List<Volume>()
AppUsers = new List<AppUser>()
{
EntityFactory.CreateVolume("0", new List<Chapter>()
new AppUser()
{
EntityFactory.CreateChapter("1", false, new List<MangaFile>()),
EntityFactory.CreateChapter("2", false, new List<MangaFile>()),
}),
EntityFactory.CreateVolume("2", new List<Chapter>()
UserName = "majora2007"
}
},
Name = "Test LIb",
Type = LibraryType.Manga,
Series = new List<Series>()
{
new Series()
{
EntityFactory.CreateChapter("0", false, new List<MangaFile>()),
}),
EntityFactory.CreateVolume("3", new List<Chapter>()
{
EntityFactory.CreateChapter("31", false, new List<MangaFile>()),
}),
Name = "Test",
Volumes = new List<Volume>()
{
EntityFactory.CreateVolume("0", new List<Chapter>()
{
EntityFactory.CreateChapter("1", false, new List<MangaFile>()),
EntityFactory.CreateChapter("2", false, new List<MangaFile>()),
}),
EntityFactory.CreateVolume("2", new List<Chapter>()
{
EntityFactory.CreateChapter("0", false, new List<MangaFile>()),
}),
EntityFactory.CreateVolume("3", new List<Chapter>()
{
EntityFactory.CreateChapter("31", false, new List<MangaFile>()),
}),
}
}
}
});
@ -261,28 +296,39 @@ public class SeriesServiceTests
{
await ResetDb();
_context.Series.Add(new Series()
_context.Library.Add(new Library()
{
Name = "Test",
Library = new Library() {
Name = "Test LIb",
Type = LibraryType.Manga,
},
Volumes = new List<Volume>()
AppUsers = new List<AppUser>()
{
EntityFactory.CreateVolume("0", new List<Chapter>()
new AppUser()
{
EntityFactory.CreateChapter("1", false, new List<MangaFile>()),
EntityFactory.CreateChapter("2", false, new List<MangaFile>()),
}),
EntityFactory.CreateVolume("2", new List<Chapter>()
UserName = "majora2007"
}
},
Name = "Test LIb",
Type = LibraryType.Manga,
Series = new List<Series>()
{
new Series()
{
EntityFactory.CreateChapter("0", false, new List<MangaFile>()),
}),
EntityFactory.CreateVolume("3", new List<Chapter>()
{
EntityFactory.CreateChapter("31", false, new List<MangaFile>()),
}),
Name = "Test",
Volumes = new List<Volume>()
{
EntityFactory.CreateVolume("0", new List<Chapter>()
{
EntityFactory.CreateChapter("1", false, new List<MangaFile>()),
EntityFactory.CreateChapter("2", false, new List<MangaFile>()),
}),
EntityFactory.CreateVolume("2", new List<Chapter>()
{
EntityFactory.CreateChapter("0", false, new List<MangaFile>()),
}),
EntityFactory.CreateVolume("3", new List<Chapter>()
{
EntityFactory.CreateChapter("31", false, new List<MangaFile>()),
}),
}
}
}
});
@ -305,26 +351,38 @@ public class SeriesServiceTests
{
await ResetDb();
_context.Series.Add(new Series()
_context.Library.Add(new Library()
{
Name = "Test",
Library = new Library() {
Name = "Test LIb",
Type = LibraryType.Book,
},
Volumes = new List<Volume>()
AppUsers = new List<AppUser>()
{
EntityFactory.CreateVolume("2", new List<Chapter>()
new AppUser()
{
EntityFactory.CreateChapter("0", false, new List<MangaFile>()),
}),
EntityFactory.CreateVolume("3", new List<Chapter>()
UserName = "majora2007"
}
},
Name = "Test LIb",
Type = LibraryType.Book,
Series = new List<Series>()
{
new Series()
{
EntityFactory.CreateChapter("0", false, new List<MangaFile>()),
}),
Name = "Test",
Volumes = new List<Volume>()
{
EntityFactory.CreateVolume("2", new List<Chapter>()
{
EntityFactory.CreateChapter("0", false, new List<MangaFile>()),
}),
EntityFactory.CreateVolume("3", new List<Chapter>()
{
EntityFactory.CreateChapter("0", false, new List<MangaFile>()),
}),
}
}
}
});
await _context.SaveChangesAsync();
var detail = await _seriesService.GetSeriesDetail(1, 1);
@ -339,26 +397,39 @@ public class SeriesServiceTests
{
await ResetDb();
_context.Series.Add(new Series()
_context.Library.Add(new Library()
{
Name = "Test",
Library = new Library() {
Name = "Test LIb",
Type = LibraryType.Book,
},
Volumes = new List<Volume>()
AppUsers = new List<AppUser>()
{
EntityFactory.CreateVolume("0", new List<Chapter>()
new AppUser()
{
EntityFactory.CreateChapter("Ano Orokamono ni mo Kyakkou wo! - Volume 1.epub", true, new List<MangaFile>()),
}),
EntityFactory.CreateVolume("2", new List<Chapter>()
UserName = "majora2007"
}
},
Name = "Test LIb",
Type = LibraryType.Book,
Series = new List<Series>()
{
new Series()
{
EntityFactory.CreateChapter("Ano Orokamono ni mo Kyakkou wo! - Volume 2.epub", false, new List<MangaFile>()),
}),
Name = "Test",
Volumes = new List<Volume>()
{
EntityFactory.CreateVolume("0", new List<Chapter>()
{
EntityFactory.CreateChapter("Ano Orokamono ni mo Kyakkou wo! - Volume 1.epub", true, new List<MangaFile>()),
}),
EntityFactory.CreateVolume("2", new List<Chapter>()
{
EntityFactory.CreateChapter("Ano Orokamono ni mo Kyakkou wo! - Volume 2.epub", false, new List<MangaFile>()),
}),
}
}
}
});
await _context.SaveChangesAsync();
var detail = await _seriesService.GetSeriesDetail(1, 1);
@ -379,36 +450,48 @@ public class SeriesServiceTests
{
await ResetDb();
_context.Series.Add(new Series()
_context.Library.Add(new Library()
{
Name = "Test",
Library = new Library() {
Name = "Test LIb",
Type = LibraryType.Book,
},
Volumes = new List<Volume>()
AppUsers = new List<AppUser>()
{
EntityFactory.CreateVolume("2", new List<Chapter>()
new AppUser()
{
EntityFactory.CreateChapter("0", false, new List<MangaFile>()),
}),
EntityFactory.CreateVolume("1.2", new List<Chapter>()
UserName = "majora2007"
}
},
Name = "Test LIb",
Type = LibraryType.Manga,
Series = new List<Series>()
{
new Series()
{
EntityFactory.CreateChapter("0", false, new List<MangaFile>()),
}),
EntityFactory.CreateVolume("1", new List<Chapter>()
{
EntityFactory.CreateChapter("0", false, new List<MangaFile>()),
}),
Name = "Test",
Volumes = new List<Volume>()
{
EntityFactory.CreateVolume("2", new List<Chapter>()
{
EntityFactory.CreateChapter("0", false, new List<MangaFile>()),
}),
EntityFactory.CreateVolume("1.2", new List<Chapter>()
{
EntityFactory.CreateChapter("0", false, new List<MangaFile>()),
}),
EntityFactory.CreateVolume("1", new List<Chapter>()
{
EntityFactory.CreateChapter("0", false, new List<MangaFile>()),
}),
}
}
}
});
await _context.SaveChangesAsync();
var detail = await _seriesService.GetSeriesDetail(1, 1);
Assert.Equal("1", detail.Volumes.ElementAt(0).Name);
Assert.Equal("1.2", detail.Volumes.ElementAt(1).Name);
Assert.Equal("2", detail.Volumes.ElementAt(2).Name);
Assert.Equal("Volume 1", detail.Volumes.ElementAt(0).Name);
Assert.Equal("Volume 1.2", detail.Volumes.ElementAt(1).Name);
Assert.Equal("Volume 2", detail.Volumes.ElementAt(2).Name);
}
@ -422,28 +505,34 @@ public class SeriesServiceTests
{
await ResetDb();
_context.Series.Add(new Series()
_context.Library.Add(new Library()
{
Name = "Test",
Library = new Library() {
Name = "Test LIb",
Type = LibraryType.Manga,
},
Volumes = new List<Volume>()
AppUsers = new List<AppUser>()
{
new Volume()
new AppUser()
{
Chapters = new List<Chapter>()
UserName = "majora2007"
}
},
Name = "Test LIb",
Type = LibraryType.Manga,
Series = new List<Series>()
{
new Series()
{
Name = "Test",
Volumes = new List<Volume>()
{
new Chapter()
EntityFactory.CreateVolume("1", new List<Chapter>()
{
Pages = 1
}
EntityFactory.CreateChapter("1", false, new List<MangaFile>(), 1),
}),
}
}
}
});
await _context.SaveChangesAsync();
@ -470,23 +559,28 @@ public class SeriesServiceTests
{
await ResetDb();
_context.Series.Add(new Series()
_context.Library.Add(new Library()
{
Name = "Test",
Library = new Library() {
Name = "Test LIb",
Type = LibraryType.Manga,
},
Volumes = new List<Volume>()
AppUsers = new List<AppUser>()
{
new Volume()
new AppUser()
{
Chapters = new List<Chapter>()
UserName = "majora2007"
}
},
Name = "Test LIb",
Type = LibraryType.Manga,
Series = new List<Series>()
{
new Series()
{
Name = "Test",
Volumes = new List<Volume>()
{
new Chapter()
EntityFactory.CreateVolume("1", new List<Chapter>()
{
Pages = 1
}
EntityFactory.CreateChapter("1", false, new List<MangaFile>(), 1),
}),
}
}
}
@ -536,23 +630,28 @@ public class SeriesServiceTests
{
await ResetDb();
_context.Series.Add(new Series()
_context.Library.Add(new Library()
{
Name = "Test",
Library = new Library() {
Name = "Test LIb",
Type = LibraryType.Manga,
},
Volumes = new List<Volume>()
AppUsers = new List<AppUser>()
{
new Volume()
new AppUser()
{
Chapters = new List<Chapter>()
UserName = "majora2007"
}
},
Name = "Test LIb",
Type = LibraryType.Manga,
Series = new List<Series>()
{
new Series()
{
Name = "Test",
Volumes = new List<Volume>()
{
new Chapter()
EntityFactory.CreateVolume("1", new List<Chapter>()
{
Pages = 1
}
EntityFactory.CreateChapter("1", false, new List<MangaFile>(), 1),
}),
}
}
}
@ -583,23 +682,28 @@ public class SeriesServiceTests
{
await ResetDb();
_context.Series.Add(new Series()
_context.Library.Add(new Library()
{
Name = "Test",
Library = new Library() {
Name = "Test LIb",
Type = LibraryType.Manga,
},
Volumes = new List<Volume>()
AppUsers = new List<AppUser>()
{
new Volume()
new AppUser()
{
Chapters = new List<Chapter>()
UserName = "majora2007"
}
},
Name = "Test LIb",
Type = LibraryType.Manga,
Series = new List<Series>()
{
new Series()
{
Name = "Test",
Volumes = new List<Volume>()
{
new Chapter()
EntityFactory.CreateVolume("1", new List<Chapter>()
{
Pages = 1
}
EntityFactory.CreateChapter("1", false, new List<MangaFile>(), 1),
}),
}
}
}
@ -626,18 +730,6 @@ public class SeriesServiceTests
#region UpdateSeriesMetadata
private void SetupUpdateSeriesMetadataDb()
{
_context.Series.Add(new Series()
{
Name = "Test",
Library = new Library() {
Name = "Test LIb",
Type = LibraryType.Book,
}
});
}
[Fact]
public async Task UpdateSeriesMetadata_ShouldCreateEmptyMetadata_IfDoesntExist()
{