Local Metadata Integration Part 1 (#817)
* Started with some basic plumbing with comic info parsing updating Series/Volume. * We can now get chapter title from comicInfo.xml * Hooked in the ability to store people into the chapter metadata. * Removed no longer used imports, fixed up some foreign key constraints on deleting series with person linked. * Refactored Summary out of the UI for Series into SeriesMetadata. Updated application to .net 6. There is a bug in metadata code for updating. * Removed the parallel.ForEach with a normal foreach which lets us use async. For I/O heavy code, shouldn't change much. * Refactored scan code to only check extensions with comic info, fixed a bug on scan events not using correct method name, removed summary field (still buggy) * Fixed a bug where on cancelling a metadata request in modal, underlying button would get stuck in a disabled state. * Changed how metadata selects the first volume to read summary info from. It will now select the first non-special volume rather than Volume 1. * More debugging and found more bugs to fix * Redid all the migrations as one single one. Fixed a bug with GetChapterInfo returning null when ChapterMetadata didn't exist for that Chapter. Fixed an issue with mapper failing on GetChapterMetadata. Started work on adding people and a design for people. * Fixed a bug where checking if file modified now takes into account if file has been processed at least once. Introduced a bug in saving people to series. * Just made code compilable again * Fixed up code. Now people for series and chapters add correctly without any db issues. * Things are working, but I'm not happy with how the management of Person is. I need to take into account that 1 person needs to map to an image and role is arbitrary. * Started adding UI code to showcase chapter metadata * Updated workflow to be .NET 6 * WIP of updating card detail to show the information more clearly and without so many if statements * Removed ChatperMetadata and store on the Chapter itself. Much easier to use and less joins. * Implemented Genre on SeriesMetadata level * Genres and People are now removed from Series level if they are no longer on comicInfo * PeopleHelper is done with unit tests. Everything is working. * Unit tests in place for Genre Helper * Starting on CacheHelper * Finished tests for ShouldUpdateCoverImage. Fixed and added tests in ArchiveService/ScannerService. * CacheHelper is fully tested * Some DI cleanup * Scanner Service now calls GetComicInfo for books. Added ability to update Series Sort name from metadata files (mainly epub as comicinfo doesn't have a field) * Forgot to move a line of code * SortName now populates from metadata (epub only, ComicInfo has no tags) * Cards now show the chapter title name if it's set on hover, else will default back to title. * Fixed a major issue with how MangaFiles were being updated with LastModified, which messed up our logic for avoiding refreshes. * Woohoo, more tests and some refactors to be able to test more services wtih mock filesystem. Fixed an issue where SortName was getting set as first chapter, but the Series was in a group. * Refactored the MangaFile creation code into the DbFactory where we also setup the first LastModified update. * Has file changed bug is now finally fixed * Remove dead genres, refactor genre to use title instead of name. * Refactored out a directory from ShouldUpdateCoverImage() to keep the code clean * Unit tests for ComicInfo on BookService. * Refactored series detail into it's own component * Series-detail now received refresh metadata events to refresh what's on screen * Removed references to Artist on PersonRole as it has no metadata mapping * Security audit * Fixed a benchmark * Updated JWT Token generator to use new methods in .NET 6 * Updated all the docker and build commands to use net6.0 * Commented out sonar scan since it's not setup for net6.0 yet.
This commit is contained in:
parent
10a6a3a544
commit
e7619e6b0a
140 changed files with 9315 additions and 1545 deletions
|
@ -1,15 +1,16 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="5.0.10" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
<PackageReference Include="NSubstitute" Version="4.2.2" />
|
||||
<PackageReference Include="System.IO.Abstractions.TestingHelpers" Version="14.0.3" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using API.Entities;
|
||||
using API.Entities.Metadata;
|
||||
using API.Extensions;
|
||||
using API.Parser;
|
||||
using Xunit;
|
||||
|
|
277
API.Tests/Helpers/CacheHelperTests.cs
Normal file
277
API.Tests/Helpers/CacheHelperTests.cs
Normal file
|
@ -0,0 +1,277 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Abstractions.TestingHelpers;
|
||||
using API.Entities;
|
||||
using API.Helpers;
|
||||
using API.Services;
|
||||
using Xunit;
|
||||
|
||||
namespace API.Tests.Helpers;
|
||||
|
||||
public class CacheHelperTests
|
||||
{
|
||||
private const string TestCoverImageDirectory = @"c:\";
|
||||
private const string TestCoverImageFile = "thumbnail.jpg";
|
||||
private readonly string _testCoverPath = Path.Join(TestCoverImageDirectory, TestCoverImageFile);
|
||||
private const string TestCoverArchive = @"file in folder.zip";
|
||||
private readonly ICacheHelper _cacheHelper;
|
||||
|
||||
public CacheHelperTests()
|
||||
{
|
||||
var file = new MockFileData("")
|
||||
{
|
||||
LastWriteTime = DateTimeOffset.Now.Subtract(TimeSpan.FromMinutes(1))
|
||||
};
|
||||
var fileSystem = new MockFileSystem(new Dictionary<string, MockFileData>
|
||||
{
|
||||
{ Path.Join(TestCoverImageDirectory, TestCoverArchive), file },
|
||||
{ Path.Join(TestCoverImageDirectory, TestCoverImageFile), file }
|
||||
});
|
||||
|
||||
var fileService = new FileService(fileSystem);
|
||||
_cacheHelper = new CacheHelper(fileService);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("", false)]
|
||||
[InlineData("C:/", false)]
|
||||
[InlineData(null, false)]
|
||||
public void CoverImageExists_DoesFileExist(string coverImage, bool exists)
|
||||
{
|
||||
Assert.Equal(exists, _cacheHelper.CoverImageExists(coverImage));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CoverImageExists_FileExists()
|
||||
{
|
||||
Assert.True(_cacheHelper.CoverImageExists(TestCoverArchive));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldUpdateCoverImage_OnFirstRun()
|
||||
{
|
||||
var file = new MangaFile()
|
||||
{
|
||||
FilePath = TestCoverArchive,
|
||||
LastModified = DateTime.Now
|
||||
};
|
||||
Assert.True(_cacheHelper.ShouldUpdateCoverImage(null, file, DateTime.Now.Subtract(TimeSpan.FromMinutes(1)),
|
||||
false, false));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldUpdateCoverImage_ShouldNotUpdateOnSecondRunWithCoverImageSetNotLocked()
|
||||
{
|
||||
// Represents first run
|
||||
var file = new MangaFile()
|
||||
{
|
||||
FilePath = TestCoverArchive,
|
||||
LastModified = DateTime.Now
|
||||
};
|
||||
Assert.False(_cacheHelper.ShouldUpdateCoverImage(_testCoverPath, file, DateTime.Now.Subtract(TimeSpan.FromMinutes(1)),
|
||||
false, false));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldUpdateCoverImage_ShouldNotUpdateOnSecondRunWithCoverImageSetLocked()
|
||||
{
|
||||
// Represents first run
|
||||
var file = new MangaFile()
|
||||
{
|
||||
FilePath = TestCoverArchive,
|
||||
LastModified = DateTime.Now
|
||||
};
|
||||
Assert.False(_cacheHelper.ShouldUpdateCoverImage(_testCoverPath, file, DateTime.Now.Subtract(TimeSpan.FromMinutes(1)),
|
||||
false, true));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldUpdateCoverImage_ShouldNotUpdateOnSecondRunWithCoverImageSetLocked_Modified()
|
||||
{
|
||||
// Represents first run
|
||||
var file = new MangaFile()
|
||||
{
|
||||
FilePath = TestCoverArchive,
|
||||
LastModified = DateTime.Now
|
||||
};
|
||||
Assert.False(_cacheHelper.ShouldUpdateCoverImage(_testCoverPath, file, DateTime.Now.Subtract(TimeSpan.FromMinutes(1)),
|
||||
false, true));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldUpdateCoverImage_CoverImageSetAndReplaced_Modified()
|
||||
{
|
||||
var filesystemFile = new MockFileData("")
|
||||
{
|
||||
LastWriteTime = DateTimeOffset.Now
|
||||
};
|
||||
var fileSystem = new MockFileSystem(new Dictionary<string, MockFileData>
|
||||
{
|
||||
{ Path.Join(TestCoverImageDirectory, TestCoverArchive), filesystemFile },
|
||||
{ Path.Join(TestCoverImageDirectory, TestCoverImageFile), filesystemFile }
|
||||
});
|
||||
|
||||
var fileService = new FileService(fileSystem);
|
||||
var cacheHelper = new CacheHelper(fileService);
|
||||
|
||||
var created = DateTime.Now.Subtract(TimeSpan.FromHours(1));
|
||||
var file = new MangaFile()
|
||||
{
|
||||
FilePath = TestCoverArchive,
|
||||
LastModified = DateTime.Now.Subtract(TimeSpan.FromMinutes(1))
|
||||
};
|
||||
Assert.True(cacheHelper.ShouldUpdateCoverImage(_testCoverPath, file, created,
|
||||
false, false));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasFileNotChangedSinceCreationOrLastScan_NotChangedSinceCreated()
|
||||
{
|
||||
var filesystemFile = new MockFileData("")
|
||||
{
|
||||
LastWriteTime = DateTimeOffset.Now
|
||||
};
|
||||
var fileSystem = new MockFileSystem(new Dictionary<string, MockFileData>
|
||||
{
|
||||
{ Path.Join(TestCoverImageDirectory, TestCoverArchive), filesystemFile },
|
||||
{ Path.Join(TestCoverImageDirectory, TestCoverImageFile), filesystemFile }
|
||||
});
|
||||
|
||||
var fileService = new FileService(fileSystem);
|
||||
var cacheHelper = new CacheHelper(fileService);
|
||||
|
||||
var chapter = new Chapter()
|
||||
{
|
||||
Created = filesystemFile.LastWriteTime.DateTime,
|
||||
LastModified = filesystemFile.LastWriteTime.DateTime
|
||||
};
|
||||
|
||||
var file = new MangaFile()
|
||||
{
|
||||
FilePath = TestCoverArchive,
|
||||
LastModified = filesystemFile.LastWriteTime.DateTime
|
||||
};
|
||||
Assert.True(cacheHelper.HasFileNotChangedSinceCreationOrLastScan(chapter, false, file));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasFileNotChangedSinceCreationOrLastScan_NotChangedSinceLastModified()
|
||||
{
|
||||
var filesystemFile = new MockFileData("")
|
||||
{
|
||||
LastWriteTime = DateTimeOffset.Now
|
||||
};
|
||||
var fileSystem = new MockFileSystem(new Dictionary<string, MockFileData>
|
||||
{
|
||||
{ Path.Join(TestCoverImageDirectory, TestCoverArchive), filesystemFile },
|
||||
{ Path.Join(TestCoverImageDirectory, TestCoverImageFile), filesystemFile }
|
||||
});
|
||||
|
||||
var fileService = new FileService(fileSystem);
|
||||
var cacheHelper = new CacheHelper(fileService);
|
||||
|
||||
var chapter = new Chapter()
|
||||
{
|
||||
Created = filesystemFile.LastWriteTime.DateTime,
|
||||
LastModified = filesystemFile.LastWriteTime.DateTime
|
||||
};
|
||||
|
||||
var file = new MangaFile()
|
||||
{
|
||||
FilePath = TestCoverArchive,
|
||||
LastModified = filesystemFile.LastWriteTime.DateTime
|
||||
};
|
||||
Assert.True(cacheHelper.HasFileNotChangedSinceCreationOrLastScan(chapter, false, file));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasFileNotChangedSinceCreationOrLastScan_NotChangedSinceLastModified_ForceUpdate()
|
||||
{
|
||||
var filesystemFile = new MockFileData("")
|
||||
{
|
||||
LastWriteTime = DateTimeOffset.Now
|
||||
};
|
||||
var fileSystem = new MockFileSystem(new Dictionary<string, MockFileData>
|
||||
{
|
||||
{ Path.Join(TestCoverImageDirectory, TestCoverArchive), filesystemFile },
|
||||
{ Path.Join(TestCoverImageDirectory, TestCoverImageFile), filesystemFile }
|
||||
});
|
||||
|
||||
var fileService = new FileService(fileSystem);
|
||||
var cacheHelper = new CacheHelper(fileService);
|
||||
|
||||
var chapter = new Chapter()
|
||||
{
|
||||
Created = filesystemFile.LastWriteTime.DateTime,
|
||||
LastModified = filesystemFile.LastWriteTime.DateTime
|
||||
};
|
||||
|
||||
var file = new MangaFile()
|
||||
{
|
||||
FilePath = TestCoverArchive,
|
||||
LastModified = filesystemFile.LastWriteTime.DateTime
|
||||
};
|
||||
Assert.False(cacheHelper.HasFileNotChangedSinceCreationOrLastScan(chapter, true, file));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasFileNotChangedSinceCreationOrLastScan_ModifiedSinceLastScan()
|
||||
{
|
||||
var filesystemFile = new MockFileData("")
|
||||
{
|
||||
LastWriteTime = DateTimeOffset.Now
|
||||
};
|
||||
var fileSystem = new MockFileSystem(new Dictionary<string, MockFileData>
|
||||
{
|
||||
{ Path.Join(TestCoverImageDirectory, TestCoverArchive), filesystemFile },
|
||||
{ Path.Join(TestCoverImageDirectory, TestCoverImageFile), filesystemFile }
|
||||
});
|
||||
|
||||
var fileService = new FileService(fileSystem);
|
||||
var cacheHelper = new CacheHelper(fileService);
|
||||
|
||||
var chapter = new Chapter()
|
||||
{
|
||||
Created = filesystemFile.LastWriteTime.DateTime.Subtract(TimeSpan.FromMinutes(10)),
|
||||
LastModified = filesystemFile.LastWriteTime.DateTime.Subtract(TimeSpan.FromMinutes(10))
|
||||
};
|
||||
|
||||
var file = new MangaFile()
|
||||
{
|
||||
FilePath = Path.Join(TestCoverImageDirectory, TestCoverArchive),
|
||||
LastModified = filesystemFile.LastWriteTime.DateTime
|
||||
};
|
||||
Assert.False(cacheHelper.HasFileNotChangedSinceCreationOrLastScan(chapter, false, file));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasFileNotChangedSinceCreationOrLastScan_ModifiedSinceLastScan_ButLastModifiedSame()
|
||||
{
|
||||
var filesystemFile = new MockFileData("")
|
||||
{
|
||||
LastWriteTime = DateTimeOffset.Now
|
||||
};
|
||||
var fileSystem = new MockFileSystem(new Dictionary<string, MockFileData>
|
||||
{
|
||||
{ Path.Join(TestCoverImageDirectory, TestCoverArchive), filesystemFile },
|
||||
{ Path.Join(TestCoverImageDirectory, TestCoverImageFile), filesystemFile }
|
||||
});
|
||||
|
||||
var fileService = new FileService(fileSystem);
|
||||
var cacheHelper = new CacheHelper(fileService);
|
||||
|
||||
var chapter = new Chapter()
|
||||
{
|
||||
Created = filesystemFile.LastWriteTime.DateTime.Subtract(TimeSpan.FromMinutes(10)),
|
||||
LastModified = filesystemFile.LastWriteTime.DateTime
|
||||
};
|
||||
|
||||
var file = new MangaFile()
|
||||
{
|
||||
FilePath = Path.Join(TestCoverImageDirectory, TestCoverArchive),
|
||||
LastModified = filesystemFile.LastWriteTime.DateTime
|
||||
};
|
||||
Assert.False(cacheHelper.HasFileNotChangedSinceCreationOrLastScan(chapter, false, file));
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
using API.Entities.Metadata;
|
||||
|
||||
namespace API.Tests.Helpers
|
||||
{
|
||||
|
|
110
API.Tests/Helpers/GenreHelperTests.cs
Normal file
110
API.Tests/Helpers/GenreHelperTests.cs
Normal file
|
@ -0,0 +1,110 @@
|
|||
using System.Collections.Generic;
|
||||
using API.Data;
|
||||
using API.Entities;
|
||||
using API.Helpers;
|
||||
using Xunit;
|
||||
|
||||
namespace API.Tests.Helpers;
|
||||
|
||||
public class GenreHelperTests
|
||||
{
|
||||
[Fact]
|
||||
public void UpdateGenre_ShouldAddNewGenre()
|
||||
{
|
||||
var allGenres = new List<Genre>
|
||||
{
|
||||
DbFactory.Genre("Action", false),
|
||||
DbFactory.Genre("action", false),
|
||||
DbFactory.Genre("Sci-fi", false),
|
||||
};
|
||||
var genreAdded = new List<Genre>();
|
||||
|
||||
GenreHelper.UpdateGenre(allGenres, new[] {"Action", "Adventure"}, false, genre =>
|
||||
{
|
||||
genreAdded.Add(genre);
|
||||
});
|
||||
|
||||
Assert.Equal(2, genreAdded.Count);
|
||||
Assert.Equal(4, allGenres.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UpdateGenre_ShouldNotAddDuplicateGenre()
|
||||
{
|
||||
var allGenres = new List<Genre>
|
||||
{
|
||||
DbFactory.Genre("Action", false),
|
||||
DbFactory.Genre("action", false),
|
||||
DbFactory.Genre("Sci-fi", false),
|
||||
|
||||
};
|
||||
var genreAdded = new List<Genre>();
|
||||
|
||||
GenreHelper.UpdateGenre(allGenres, new[] {"Action", "Scifi"}, false, genre =>
|
||||
{
|
||||
genreAdded.Add(genre);
|
||||
});
|
||||
|
||||
Assert.Equal(3, allGenres.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddGenre_ShouldAddOnlyNonExistingGenre()
|
||||
{
|
||||
var existingGenres = new List<Genre>
|
||||
{
|
||||
DbFactory.Genre("Action", false),
|
||||
DbFactory.Genre("action", false),
|
||||
DbFactory.Genre("Sci-fi", false),
|
||||
};
|
||||
|
||||
|
||||
GenreHelper.AddGenreIfNotExists(existingGenres, DbFactory.Genre("Action", false));
|
||||
Assert.Equal(3, existingGenres.Count);
|
||||
|
||||
GenreHelper.AddGenreIfNotExists(existingGenres, DbFactory.Genre("action", false));
|
||||
Assert.Equal(3, existingGenres.Count);
|
||||
|
||||
GenreHelper.AddGenreIfNotExists(existingGenres, DbFactory.Genre("Shonen", false));
|
||||
Assert.Equal(4, existingGenres.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddGenre_ShouldNotAddSameNameAndExternal()
|
||||
{
|
||||
var existingGenres = new List<Genre>
|
||||
{
|
||||
DbFactory.Genre("Action", false),
|
||||
DbFactory.Genre("action", false),
|
||||
DbFactory.Genre("Sci-fi", false),
|
||||
};
|
||||
|
||||
|
||||
GenreHelper.AddGenreIfNotExists(existingGenres, DbFactory.Genre("Action", true));
|
||||
Assert.Equal(3, existingGenres.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void KeepOnlySamePeopleBetweenLists()
|
||||
{
|
||||
var existingGenres = new List<Genre>
|
||||
{
|
||||
DbFactory.Genre("Action", false),
|
||||
DbFactory.Genre("Sci-fi", false),
|
||||
};
|
||||
|
||||
var peopleFromChapters = new List<Genre>
|
||||
{
|
||||
DbFactory.Genre("Action", false),
|
||||
};
|
||||
|
||||
var genreRemoved = new List<Genre>();
|
||||
GenreHelper.KeepOnlySameGenreBetweenLists(existingGenres,
|
||||
peopleFromChapters, genre =>
|
||||
{
|
||||
genreRemoved.Add(genre);
|
||||
});
|
||||
|
||||
Assert.Equal(1, genreRemoved.Count);
|
||||
}
|
||||
}
|
140
API.Tests/Helpers/PersonHelperTests.cs
Normal file
140
API.Tests/Helpers/PersonHelperTests.cs
Normal file
|
@ -0,0 +1,140 @@
|
|||
using System.Collections.Generic;
|
||||
using API.Data;
|
||||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
using API.Helpers;
|
||||
using Xunit;
|
||||
|
||||
namespace API.Tests.Helpers;
|
||||
|
||||
public class PersonHelperTests
|
||||
{
|
||||
[Fact]
|
||||
public void UpdatePeople_ShouldAddNewPeople()
|
||||
{
|
||||
var allPeople = new List<Person>
|
||||
{
|
||||
DbFactory.Person("Joe Shmo", PersonRole.CoverArtist),
|
||||
DbFactory.Person("Joe Shmo", PersonRole.Writer)
|
||||
};
|
||||
var peopleAdded = new List<Person>();
|
||||
|
||||
PersonHelper.UpdatePeople(allPeople, new[] {"Joseph Shmo", "Sally Ann"}, PersonRole.Writer, person =>
|
||||
{
|
||||
peopleAdded.Add(person);
|
||||
});
|
||||
|
||||
Assert.Equal(2, peopleAdded.Count);
|
||||
Assert.Equal(4, allPeople.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UpdatePeople_ShouldNotAddDuplicatePeople()
|
||||
{
|
||||
var allPeople = new List<Person>
|
||||
{
|
||||
DbFactory.Person("Joe Shmo", PersonRole.CoverArtist),
|
||||
DbFactory.Person("Joe Shmo", PersonRole.Writer),
|
||||
DbFactory.Person("Sally Ann", PersonRole.CoverArtist),
|
||||
|
||||
};
|
||||
var peopleAdded = new List<Person>();
|
||||
|
||||
PersonHelper.UpdatePeople(allPeople, new[] {"Joe Shmo", "Sally Ann"}, PersonRole.CoverArtist, person =>
|
||||
{
|
||||
peopleAdded.Add(person);
|
||||
});
|
||||
|
||||
Assert.Equal(3, allPeople.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemovePeople_ShouldRemovePeopleOfSameRole()
|
||||
{
|
||||
var existingPeople = new List<Person>
|
||||
{
|
||||
DbFactory.Person("Joe Shmo", PersonRole.CoverArtist),
|
||||
DbFactory.Person("Joe Shmo", PersonRole.Writer)
|
||||
};
|
||||
var peopleRemoved = new List<Person>();
|
||||
PersonHelper.RemovePeople(existingPeople, new[] {"Joe Shmo", "Sally Ann"}, PersonRole.Writer, person =>
|
||||
{
|
||||
peopleRemoved.Add(person);
|
||||
});
|
||||
|
||||
Assert.NotEqual(existingPeople, peopleRemoved);
|
||||
Assert.Equal(1, peopleRemoved.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemovePeople_ShouldRemovePeopleFromBothRoles()
|
||||
{
|
||||
var existingPeople = new List<Person>
|
||||
{
|
||||
DbFactory.Person("Joe Shmo", PersonRole.CoverArtist),
|
||||
DbFactory.Person("Joe Shmo", PersonRole.Writer)
|
||||
};
|
||||
var peopleRemoved = new List<Person>();
|
||||
PersonHelper.RemovePeople(existingPeople, new[] {"Joe Shmo", "Sally Ann"}, PersonRole.Writer, person =>
|
||||
{
|
||||
peopleRemoved.Add(person);
|
||||
});
|
||||
|
||||
Assert.NotEqual(existingPeople, peopleRemoved);
|
||||
Assert.Equal(1, peopleRemoved.Count);
|
||||
|
||||
PersonHelper.RemovePeople(existingPeople, new[] {"Joe Shmo"}, PersonRole.CoverArtist, person =>
|
||||
{
|
||||
peopleRemoved.Add(person);
|
||||
});
|
||||
|
||||
Assert.Equal(0, existingPeople.Count);
|
||||
Assert.Equal(2, peopleRemoved.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void KeepOnlySamePeopleBetweenLists()
|
||||
{
|
||||
var existingPeople = new List<Person>
|
||||
{
|
||||
DbFactory.Person("Joe Shmo", PersonRole.CoverArtist),
|
||||
DbFactory.Person("Joe Shmo", PersonRole.Writer),
|
||||
DbFactory.Person("Sally", PersonRole.Writer),
|
||||
};
|
||||
|
||||
var peopleFromChapters = new List<Person>
|
||||
{
|
||||
DbFactory.Person("Joe Shmo", PersonRole.CoverArtist),
|
||||
};
|
||||
|
||||
var peopleRemoved = new List<Person>();
|
||||
PersonHelper.KeepOnlySamePeopleBetweenLists(existingPeople,
|
||||
peopleFromChapters, person =>
|
||||
{
|
||||
peopleRemoved.Add(person);
|
||||
});
|
||||
|
||||
Assert.Equal(2, peopleRemoved.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddPeople_ShouldAddOnlyNonExistingPeople()
|
||||
{
|
||||
var existingPeople = new List<Person>
|
||||
{
|
||||
DbFactory.Person("Joe Shmo", PersonRole.CoverArtist),
|
||||
DbFactory.Person("Joe Shmo", PersonRole.Writer),
|
||||
DbFactory.Person("Sally", PersonRole.Writer),
|
||||
};
|
||||
|
||||
|
||||
PersonHelper.AddPersonIfNotExists(existingPeople, DbFactory.Person("Joe Shmo", PersonRole.CoverArtist));
|
||||
Assert.Equal(3, existingPeople.Count);
|
||||
|
||||
PersonHelper.AddPersonIfNotExists(existingPeople, DbFactory.Person("Joe Shmo", PersonRole.Writer));
|
||||
Assert.Equal(3, existingPeople.Count);
|
||||
|
||||
PersonHelper.AddPersonIfNotExists(existingPeople, DbFactory.Person("Joe Shmo Two", PersonRole.CoverArtist));
|
||||
Assert.Equal(4, existingPeople.Count);
|
||||
}
|
||||
}
|
125
API.Tests/Helpers/SeriesHelperTests.cs
Normal file
125
API.Tests/Helpers/SeriesHelperTests.cs
Normal file
|
@ -0,0 +1,125 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using API.Data;
|
||||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
using API.Helpers;
|
||||
using API.Services.Tasks.Scanner;
|
||||
using Xunit;
|
||||
|
||||
namespace API.Tests.Helpers;
|
||||
|
||||
public class SeriesHelperTests
|
||||
{
|
||||
#region FindSeries
|
||||
[Fact]
|
||||
public void FindSeries_ShouldFind_SameFormat()
|
||||
{
|
||||
var series = DbFactory.Series("Darker than Black");
|
||||
series.OriginalName = "Something Random";
|
||||
series.Format = MangaFormat.Archive;
|
||||
Assert.True(SeriesHelper.FindSeries(series, new ParsedSeries()
|
||||
{
|
||||
Format = MangaFormat.Archive,
|
||||
Name = "Darker than Black",
|
||||
NormalizedName = API.Parser.Parser.Normalize("Darker than Black")
|
||||
}));
|
||||
|
||||
Assert.True(SeriesHelper.FindSeries(series, new ParsedSeries()
|
||||
{
|
||||
Format = MangaFormat.Archive,
|
||||
Name = "Darker than Black".ToLower(),
|
||||
NormalizedName = API.Parser.Parser.Normalize("Darker than Black")
|
||||
}));
|
||||
|
||||
Assert.True(SeriesHelper.FindSeries(series, new ParsedSeries()
|
||||
{
|
||||
Format = MangaFormat.Archive,
|
||||
Name = "Darker than Black".ToUpper(),
|
||||
NormalizedName = API.Parser.Parser.Normalize("Darker than Black")
|
||||
}));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindSeries_ShouldNotFind_WrongFormat()
|
||||
{
|
||||
var series = DbFactory.Series("Darker than Black");
|
||||
series.OriginalName = "Something Random";
|
||||
series.Format = MangaFormat.Archive;
|
||||
Assert.False(SeriesHelper.FindSeries(series, new ParsedSeries()
|
||||
{
|
||||
Format = MangaFormat.Image,
|
||||
Name = "Darker than Black",
|
||||
NormalizedName = API.Parser.Parser.Normalize("Darker than Black")
|
||||
}));
|
||||
|
||||
Assert.False(SeriesHelper.FindSeries(series, new ParsedSeries()
|
||||
{
|
||||
Format = MangaFormat.Image,
|
||||
Name = "Darker than Black".ToLower(),
|
||||
NormalizedName = API.Parser.Parser.Normalize("Darker than Black")
|
||||
}));
|
||||
|
||||
Assert.False(SeriesHelper.FindSeries(series, new ParsedSeries()
|
||||
{
|
||||
Format = MangaFormat.Image,
|
||||
Name = "Darker than Black".ToUpper(),
|
||||
NormalizedName = API.Parser.Parser.Normalize("Darker than Black")
|
||||
}));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindSeries_ShouldFind_UsingOriginalName()
|
||||
{
|
||||
var series = DbFactory.Series("Darker than Black");
|
||||
series.OriginalName = "Something Random";
|
||||
series.Format = MangaFormat.Image;
|
||||
Assert.True(SeriesHelper.FindSeries(series, new ParsedSeries()
|
||||
{
|
||||
Format = MangaFormat.Image,
|
||||
Name = "Something Random",
|
||||
NormalizedName = API.Parser.Parser.Normalize("Something Random")
|
||||
}));
|
||||
|
||||
Assert.True(SeriesHelper.FindSeries(series, new ParsedSeries()
|
||||
{
|
||||
Format = MangaFormat.Image,
|
||||
Name = "Something Random".ToLower(),
|
||||
NormalizedName = API.Parser.Parser.Normalize("Something Random")
|
||||
}));
|
||||
|
||||
Assert.True(SeriesHelper.FindSeries(series, new ParsedSeries()
|
||||
{
|
||||
Format = MangaFormat.Image,
|
||||
Name = "Something Random".ToUpper(),
|
||||
NormalizedName = API.Parser.Parser.Normalize("Something Random")
|
||||
}));
|
||||
|
||||
Assert.True(SeriesHelper.FindSeries(series, new ParsedSeries()
|
||||
{
|
||||
Format = MangaFormat.Image,
|
||||
Name = "SomethingRandom".ToUpper(),
|
||||
NormalizedName = API.Parser.Parser.Normalize("SomethingRandom")
|
||||
}));
|
||||
}
|
||||
#endregion
|
||||
|
||||
[Fact]
|
||||
public void RemoveMissingSeries_Should_RemoveSeries()
|
||||
{
|
||||
var existingSeries = new List<Series>()
|
||||
{
|
||||
EntityFactory.CreateSeries("Darker than Black Vol 1"),
|
||||
EntityFactory.CreateSeries("Darker than Black"),
|
||||
EntityFactory.CreateSeries("Beastars"),
|
||||
};
|
||||
var missingSeries = new List<Series>()
|
||||
{
|
||||
EntityFactory.CreateSeries("Darker than Black Vol 1"),
|
||||
};
|
||||
existingSeries = SeriesHelper.RemoveMissingSeries(existingSeries, missingSeries, out var removeCount).ToList();
|
||||
|
||||
Assert.DoesNotContain(missingSeries[0].Name, existingSeries.Select(s => s.Name));
|
||||
Assert.Equal(missingSeries.Count, removeCount);
|
||||
}
|
||||
}
|
|
@ -139,6 +139,14 @@ namespace API.Tests.Parser
|
|||
Assert.Equal(expected, IsImage(filename));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Joe Smo", "Joe Smo")]
|
||||
[InlineData("Smo, Joe", "Joe Smo")]
|
||||
public void CleanAuthorTest(string author, string expected)
|
||||
{
|
||||
Assert.Equal(expected, CleanAuthor(expected));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("C:/", "C:/Love Hina/Love Hina - Special.cbz", "Love Hina")]
|
||||
[InlineData("C:/", "C:/Love Hina/Specials/Ani-Hina Art Collection.cbz", "Love Hina")]
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Abstractions.TestingHelpers;
|
||||
using System.IO.Compression;
|
||||
using API.Archive;
|
||||
using API.Data.Metadata;
|
||||
|
@ -19,7 +20,7 @@ namespace API.Tests.Services
|
|||
private readonly ArchiveService _archiveService;
|
||||
private readonly ILogger<ArchiveService> _logger = Substitute.For<ILogger<ArchiveService>>();
|
||||
private readonly ILogger<DirectoryService> _directoryServiceLogger = Substitute.For<ILogger<DirectoryService>>();
|
||||
private readonly IDirectoryService _directoryService = new DirectoryService(Substitute.For<ILogger<DirectoryService>>());
|
||||
private readonly IDirectoryService _directoryService = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), new MockFileSystem());
|
||||
|
||||
public ArchiveServiceTests(ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
|
@ -159,7 +160,7 @@ namespace API.Tests.Services
|
|||
[InlineData("sorting.zip", "sorting.expected.jpg")]
|
||||
public void GetCoverImage_Default_Test(string inputFile, string expectedOutputFile)
|
||||
{
|
||||
var archiveService = Substitute.For<ArchiveService>(_logger, new DirectoryService(_directoryServiceLogger));
|
||||
var archiveService = Substitute.For<ArchiveService>(_logger, new DirectoryService(_directoryServiceLogger, new MockFileSystem()));
|
||||
var testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/ArchiveService/CoverImages");
|
||||
var expectedBytes = File.ReadAllBytes(Path.Join(testDirectory, expectedOutputFile));
|
||||
archiveService.Configure().CanOpen(Path.Join(testDirectory, inputFile)).Returns(ArchiveLibrary.Default);
|
||||
|
@ -191,7 +192,7 @@ namespace API.Tests.Services
|
|||
[InlineData("sorting.zip", "sorting.expected.jpg")]
|
||||
public void GetCoverImage_SharpCompress_Test(string inputFile, string expectedOutputFile)
|
||||
{
|
||||
var archiveService = Substitute.For<ArchiveService>(_logger, new DirectoryService(_directoryServiceLogger));
|
||||
var archiveService = Substitute.For<ArchiveService>(_logger, new DirectoryService(_directoryServiceLogger, new MockFileSystem()));
|
||||
var testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/ArchiveService/CoverImages");
|
||||
var expectedBytes = File.ReadAllBytes(Path.Join(testDirectory, expectedOutputFile));
|
||||
|
||||
|
@ -215,10 +216,23 @@ namespace API.Tests.Services
|
|||
public void ShouldHaveComicInfo()
|
||||
{
|
||||
var testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/ArchiveService/ComicInfos");
|
||||
var archive = Path.Join(testDirectory, "file in folder.zip");
|
||||
var summaryInfo = "By all counts, Ryouta Sakamoto is a loser when he's not holed up in his room, bombing things into oblivion in his favorite online action RPG. But his very own uneventful life is blown to pieces when he's abducted and taken to an uninhabited island, where he soon learns the hard way that he's being pitted against others just like him in a explosives-riddled death match! How could this be happening? Who's putting them up to this? And why!? The name, not to mention the objective, of this very real survival game is eerily familiar to Ryouta, who has mastered its virtual counterpart-BTOOOM! Can Ryouta still come out on top when he's playing for his life!?";
|
||||
var archive = Path.Join(testDirectory, "ComicInfo.zip");
|
||||
const string summaryInfo = "By all counts, Ryouta Sakamoto is a loser when he's not holed up in his room, bombing things into oblivion in his favorite online action RPG. But his very own uneventful life is blown to pieces when he's abducted and taken to an uninhabited island, where he soon learns the hard way that he's being pitted against others just like him in a explosives-riddled death match! How could this be happening? Who's putting them up to this? And why!? The name, not to mention the objective, of this very real survival game is eerily familiar to Ryouta, who has mastered its virtual counterpart-BTOOOM! Can Ryouta still come out on top when he's playing for his life!?";
|
||||
|
||||
Assert.Equal(summaryInfo, _archiveService.GetComicInfo(archive).Summary);
|
||||
var comicInfo = _archiveService.GetComicInfo(archive);
|
||||
Assert.NotNull(comicInfo);
|
||||
Assert.Equal(summaryInfo, comicInfo.Summary);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldHaveComicInfo_WithAuthors()
|
||||
{
|
||||
var testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/ArchiveService/ComicInfos");
|
||||
var archive = Path.Join(testDirectory, "ComicInfo_authors.zip");
|
||||
|
||||
var comicInfo = _archiveService.GetComicInfo(archive);
|
||||
Assert.NotNull(comicInfo);
|
||||
Assert.Equal("Junya Inoue", comicInfo.Writer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
@ -27,5 +27,29 @@ namespace API.Tests.Services
|
|||
Assert.Equal(expectedPages, _bookService.GetNumberOfPages(Path.Join(testDirectory, filePath)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldHaveComicInfo()
|
||||
{
|
||||
var testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/BookService/EPUB");
|
||||
var archive = Path.Join(testDirectory, "The Golden Harpoon; Or, Lost Among the Floes A Story of the Whaling Grounds.epub");
|
||||
const string summaryInfo = "Book Description";
|
||||
|
||||
var comicInfo = _bookService.GetComicInfo(archive);
|
||||
Assert.NotNull(comicInfo);
|
||||
Assert.Equal(summaryInfo, comicInfo.Summary);
|
||||
Assert.Equal("genre1, genre2", comicInfo.Genre);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldHaveComicInfo_WithAuthors()
|
||||
{
|
||||
var testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/BookService/EPUB");
|
||||
var archive = Path.Join(testDirectory, "The Golden Harpoon; Or, Lost Among the Floes A Story of the Whaling Grounds.epub");
|
||||
|
||||
var comicInfo = _bookService.GetComicInfo(archive);
|
||||
Assert.NotNull(comicInfo);
|
||||
Assert.Equal("Roger Starbuck,Junya Inoue", comicInfo.Writer);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Abstractions.TestingHelpers;
|
||||
using System.Linq;
|
||||
using API.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
@ -17,7 +18,7 @@ namespace API.Tests.Services
|
|||
|
||||
public DirectoryServiceTests()
|
||||
{
|
||||
_directoryService = new DirectoryService(_logger);
|
||||
_directoryService = new DirectoryService(_logger, new MockFileSystem());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -26,7 +27,7 @@ namespace API.Tests.Services
|
|||
var testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/ScannerService/Manga");
|
||||
// ReSharper disable once CollectionNeverQueried.Local
|
||||
var files = new List<string>();
|
||||
var fileCount = DirectoryService.TraverseTreeParallelForEach(testDirectory, s => files.Add(s),
|
||||
var fileCount = _directoryService.TraverseTreeParallelForEach(testDirectory, s => files.Add(s),
|
||||
API.Parser.Parser.ArchiveFileExtensions, _logger);
|
||||
|
||||
Assert.Equal(28, fileCount);
|
||||
|
|
44
API.Tests/Services/FileSystemTests.cs
Normal file
44
API.Tests/Services/FileSystemTests.cs
Normal file
|
@ -0,0 +1,44 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO.Abstractions.TestingHelpers;
|
||||
using API.Services;
|
||||
using Xunit;
|
||||
|
||||
namespace API.Tests.Services;
|
||||
|
||||
public class FileSystemTests
|
||||
{
|
||||
[Fact]
|
||||
public void FileHasNotBeenModifiedSinceCreation()
|
||||
{
|
||||
var file = new MockFileData("Testing is meh.")
|
||||
{
|
||||
LastWriteTime = DateTimeOffset.Now.Subtract(TimeSpan.FromMinutes(1))
|
||||
};
|
||||
var fileSystem = new MockFileSystem(new Dictionary<string, MockFileData>
|
||||
{
|
||||
{ @"c:\myfile.txt", file }
|
||||
});
|
||||
|
||||
var fileService = new FileService(fileSystem);
|
||||
|
||||
Assert.False(fileService.HasFileBeenModifiedSince(@"c:\myfile.txt", DateTime.Now));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FileHasBeenModifiedSinceCreation()
|
||||
{
|
||||
var file = new MockFileData("Testing is meh.")
|
||||
{
|
||||
LastWriteTime = DateTimeOffset.Now
|
||||
};
|
||||
var fileSystem = new MockFileSystem(new Dictionary<string, MockFileData>
|
||||
{
|
||||
{ @"c:\myfile.txt", file }
|
||||
});
|
||||
|
||||
var fileService = new FileService(fileSystem);
|
||||
|
||||
Assert.True(fileService.HasFileBeenModifiedSince(@"c:\myfile.txt", DateTime.Now.Subtract(TimeSpan.FromMinutes(1))));
|
||||
}
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Abstractions.TestingHelpers;
|
||||
using API.Entities;
|
||||
using API.Helpers;
|
||||
using API.Services;
|
||||
using Xunit;
|
||||
|
||||
|
@ -10,6 +13,7 @@ namespace API.Tests.Services
|
|||
{
|
||||
private readonly string _testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/ArchiveService/Archives");
|
||||
private const string TestCoverImageFile = "thumbnail.jpg";
|
||||
private const string TestCoverArchive = @"c:\file in folder.zip";
|
||||
private readonly string _testCoverImageDirectory = Path.Join(Directory.GetCurrentDirectory(), @"../../../Services/Test Data/ArchiveService/CoverImages");
|
||||
//private readonly MetadataService _metadataService;
|
||||
// private readonly IUnitOfWork _unitOfWork = Substitute.For<IUnitOfWork>();
|
||||
|
@ -18,116 +22,23 @@ namespace API.Tests.Services
|
|||
// private readonly IArchiveService _archiveService = Substitute.For<IArchiveService>();
|
||||
// private readonly ILogger<MetadataService> _logger = Substitute.For<ILogger<MetadataService>>();
|
||||
// private readonly IHubContext<MessageHub> _messageHub = Substitute.For<IHubContext<MessageHub>>();
|
||||
private readonly ICacheHelper _cacheHelper;
|
||||
|
||||
|
||||
public MetadataServiceTests()
|
||||
{
|
||||
//_metadataService = new MetadataService(_unitOfWork, _logger, _archiveService, _bookService, _imageService, _messageHub);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldUpdateCoverImage_OnFirstRun()
|
||||
{
|
||||
// Represents first run
|
||||
Assert.True(MetadataService.ShouldUpdateCoverImage(null, new MangaFile()
|
||||
var file = new MockFileData("")
|
||||
{
|
||||
FilePath = Path.Join(_testDirectory, "file in folder.zip"),
|
||||
LastModified = DateTime.Now
|
||||
}, false, false));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldUpdateCoverImage_OnFirstRunSeries()
|
||||
{
|
||||
// Represents first run
|
||||
Assert.True(MetadataService.ShouldUpdateCoverImage(null,null, false, false));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldUpdateCoverImage_OnFirstRun_FileModified()
|
||||
{
|
||||
// Represents first run
|
||||
Assert.True(MetadataService.ShouldUpdateCoverImage(null, new MangaFile()
|
||||
LastWriteTime = DateTimeOffset.Now.Subtract(TimeSpan.FromMinutes(1))
|
||||
};
|
||||
var fileSystem = new MockFileSystem(new Dictionary<string, MockFileData>
|
||||
{
|
||||
FilePath = Path.Join(_testDirectory, "file in folder.zip"),
|
||||
LastModified = new FileInfo(Path.Join(_testDirectory, "file in folder.zip")).LastWriteTime.Subtract(TimeSpan.FromDays(1))
|
||||
}, false, false));
|
||||
}
|
||||
{ TestCoverArchive, file }
|
||||
});
|
||||
|
||||
[Fact]
|
||||
public void ShouldUpdateCoverImage_OnFirstRun_CoverImageLocked()
|
||||
{
|
||||
// Represents first run
|
||||
Assert.True(MetadataService.ShouldUpdateCoverImage(null, new MangaFile()
|
||||
{
|
||||
FilePath = Path.Join(_testDirectory, "file in folder.zip"),
|
||||
LastModified = new FileInfo(Path.Join(_testDirectory, "file in folder.zip")).LastWriteTime
|
||||
}, false, true));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldUpdateCoverImage_OnSecondRun_ForceUpdate()
|
||||
{
|
||||
// Represents first run
|
||||
Assert.True(MetadataService.ShouldUpdateCoverImage(null, new MangaFile()
|
||||
{
|
||||
FilePath = Path.Join(_testDirectory, "file in folder.zip"),
|
||||
LastModified = new FileInfo(Path.Join(_testDirectory, "file in folder.zip")).LastWriteTime
|
||||
}, true, false));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldUpdateCoverImage_OnSecondRun_NoFileChangeButNoCoverImage()
|
||||
{
|
||||
// Represents first run
|
||||
Assert.True(MetadataService.ShouldUpdateCoverImage(null, new MangaFile()
|
||||
{
|
||||
FilePath = Path.Join(_testDirectory, "file in folder.zip"),
|
||||
LastModified = new FileInfo(Path.Join(_testDirectory, "file in folder.zip")).LastWriteTime
|
||||
}, false, false));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldUpdateCoverImage_OnSecondRun_FileChangeButNoCoverImage()
|
||||
{
|
||||
// Represents first run
|
||||
Assert.True(MetadataService.ShouldUpdateCoverImage(null, new MangaFile()
|
||||
{
|
||||
FilePath = Path.Join(_testDirectory, "file in folder.zip"),
|
||||
LastModified = new FileInfo(Path.Join(_testDirectory, "file in folder.zip")).LastWriteTime + TimeSpan.FromDays(1)
|
||||
}, false, false));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldNotUpdateCoverImage_OnSecondRun_CoverImageSet()
|
||||
{
|
||||
// Represents first run
|
||||
Assert.False(MetadataService.ShouldUpdateCoverImage(TestCoverImageFile, new MangaFile()
|
||||
{
|
||||
FilePath = Path.Join(_testDirectory, "file in folder.zip"),
|
||||
LastModified = new FileInfo(Path.Join(_testDirectory, "file in folder.zip")).LastWriteTime
|
||||
}, false, false, _testCoverImageDirectory));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldNotUpdateCoverImage_OnSecondRun_HasCoverImage_NoForceUpdate_NoLock()
|
||||
{
|
||||
|
||||
Assert.False(MetadataService.ShouldUpdateCoverImage(TestCoverImageFile, new MangaFile()
|
||||
{
|
||||
FilePath = Path.Join(_testDirectory, "file in folder.zip"),
|
||||
LastModified = DateTime.Now
|
||||
}, false, false, _testCoverImageDirectory));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldUpdateCoverImage_OnSecondRun_HasCoverImage_NoForceUpdate_HasLock_CoverImageDoesntExist()
|
||||
{
|
||||
|
||||
Assert.True(MetadataService.ShouldUpdateCoverImage(@"doesn't_exist.jpg", new MangaFile()
|
||||
{
|
||||
FilePath = Path.Join(_testDirectory, "file in folder.zip"),
|
||||
LastModified = DateTime.Now
|
||||
}, false, true, _testCoverImageDirectory));
|
||||
var fileService = new FileService(fileSystem);
|
||||
_cacheHelper = new CacheHelper(fileService);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,11 +3,14 @@ using System.Collections.Concurrent;
|
|||
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.Entities;
|
||||
using API.Entities.Enums;
|
||||
using API.Entities.Metadata;
|
||||
using API.Helpers;
|
||||
using API.Interfaces;
|
||||
using API.Interfaces.Services;
|
||||
using API.Parser;
|
||||
|
@ -34,8 +37,9 @@ namespace API.Tests.Services
|
|||
private readonly IArchiveService _archiveService = Substitute.For<IArchiveService>();
|
||||
private readonly IBookService _bookService = Substitute.For<IBookService>();
|
||||
private readonly IImageService _imageService = Substitute.For<IImageService>();
|
||||
private readonly IDirectoryService _directoryService = Substitute.For<IDirectoryService>();
|
||||
private readonly ILogger<MetadataService> _metadataLogger = Substitute.For<ILogger<MetadataService>>();
|
||||
private readonly ICacheService _cacheService = Substitute.For<ICacheService>();
|
||||
private readonly ICacheService _cacheService;
|
||||
private readonly IHubContext<MessageHub> _messageHub = Substitute.For<IHubContext<MessageHub>>();
|
||||
|
||||
private readonly DbConnection _connection;
|
||||
|
@ -54,9 +58,26 @@ namespace API.Tests.Services
|
|||
|
||||
IUnitOfWork unitOfWork = new UnitOfWork(_context, Substitute.For<IMapper>(), null);
|
||||
|
||||
var file = new MockFileData("")
|
||||
{
|
||||
LastWriteTime = DateTimeOffset.Now.Subtract(TimeSpan.FromMinutes(1))
|
||||
};
|
||||
var fileSystem = new MockFileSystem(new Dictionary<string, MockFileData>
|
||||
{
|
||||
{ "/data/Darker than Black.zip", file },
|
||||
{ "/data/Cage of Eden - v10.cbz", file },
|
||||
{ "/data/Cage of Eden - v1.cbz", file },
|
||||
});
|
||||
|
||||
IMetadataService metadataService = Substitute.For<MetadataService>(unitOfWork, _metadataLogger, _archiveService, _bookService, _imageService, _messageHub);
|
||||
_scannerService = new ScannerService(unitOfWork, _logger, _archiveService, metadataService, _bookService, _cacheService, _messageHub);
|
||||
var fileService = new FileService(fileSystem);
|
||||
ICacheHelper cacheHelper = new CacheHelper(fileService);
|
||||
|
||||
|
||||
IMetadataService metadataService =
|
||||
Substitute.For<MetadataService>(unitOfWork, _metadataLogger, _archiveService,
|
||||
_bookService, _imageService, _messageHub, cacheHelper);
|
||||
_scannerService = new ScannerService(unitOfWork, _logger, _archiveService, metadataService, _bookService,
|
||||
_cacheService, _messageHub, fileService, _directoryService);
|
||||
}
|
||||
|
||||
private async Task<bool> SeedDb()
|
||||
|
@ -78,6 +99,13 @@ namespace API.Tests.Services
|
|||
return await _context.SaveChangesAsync() > 0;
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void AddOrUpdateFileForChapter()
|
||||
{
|
||||
// TODO: This can be tested, it has _filesystem mocked
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindSeriesNotOnDisk_Should_RemoveNothing_Test()
|
||||
{
|
||||
|
@ -138,24 +166,24 @@ namespace API.Tests.Services
|
|||
// Assert.Equal(expected, actualName);
|
||||
// }
|
||||
|
||||
[Fact]
|
||||
public void RemoveMissingSeries_Should_RemoveSeries()
|
||||
{
|
||||
var existingSeries = new List<Series>()
|
||||
{
|
||||
EntityFactory.CreateSeries("Darker than Black Vol 1"),
|
||||
EntityFactory.CreateSeries("Darker than Black"),
|
||||
EntityFactory.CreateSeries("Beastars"),
|
||||
};
|
||||
var missingSeries = new List<Series>()
|
||||
{
|
||||
EntityFactory.CreateSeries("Darker than Black Vol 1"),
|
||||
};
|
||||
existingSeries = ScannerService.RemoveMissingSeries(existingSeries, missingSeries, out var removeCount).ToList();
|
||||
|
||||
Assert.DoesNotContain(missingSeries[0].Name, existingSeries.Select(s => s.Name));
|
||||
Assert.Equal(missingSeries.Count, removeCount);
|
||||
}
|
||||
// [Fact]
|
||||
// public void RemoveMissingSeries_Should_RemoveSeries()
|
||||
// {
|
||||
// var existingSeries = new List<Series>()
|
||||
// {
|
||||
// EntityFactory.CreateSeries("Darker than Black Vol 1"),
|
||||
// EntityFactory.CreateSeries("Darker than Black"),
|
||||
// EntityFactory.CreateSeries("Beastars"),
|
||||
// };
|
||||
// var missingSeries = new List<Series>()
|
||||
// {
|
||||
// EntityFactory.CreateSeries("Darker than Black Vol 1"),
|
||||
// };
|
||||
// existingSeries = ScannerService.RemoveMissingSeries(existingSeries, missingSeries, out var removeCount).ToList();
|
||||
//
|
||||
// Assert.DoesNotContain(missingSeries[0].Name, existingSeries.Select(s => s.Name));
|
||||
// Assert.Equal(missingSeries.Count, removeCount);
|
||||
// }
|
||||
|
||||
private void AddToParsedInfo(IDictionary<ParsedSeries, List<ParserInfo>> collectedSeries, ParserInfo info)
|
||||
{
|
||||
|
|
Binary file not shown.
Binary file not shown.
81
API.Tests/Services/Test Data/BookService/EPUB/content.opf
Normal file
81
API.Tests/Services/Test Data/BookService/EPUB/content.opf
Normal file
|
@ -0,0 +1,81 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
|
||||
<package xmlns:opf="http://www.idpf.org/2007/opf" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.idpf.org/2007/opf" version="2.0" unique-identifier="id">
|
||||
<metadata>
|
||||
<dc:rights>Public domain in the USA.</dc:rights>
|
||||
<dc:identifier opf:scheme="URI" id="id">http://www.gutenberg.org/64999</dc:identifier>
|
||||
<dc:creator opf:file-as="Starbuck, Roger">Roger Starbuck</dc:creator>
|
||||
<dc:title>The Golden Harpoon / Lost Among the Floes</dc:title>
|
||||
<dc:language xsi:type="dcterms:RFC4646">en</dc:language>
|
||||
<dc:date opf:event="publication">2021-04-05</dc:date>
|
||||
<dc:date opf:event="conversion">2021-04-05T23:00:07.039989+00:00</dc:date>
|
||||
<dc:source>https://www.gutenberg.org/files/64999/64999-h/64999-h.htm</dc:source>
|
||||
<dc:description>Book Description</dc:description>
|
||||
<dc:subject>Genre1, Genre2</dc:subject>
|
||||
<dc:creator id="creator">Junya Inoue</dc:creator>
|
||||
<meta refines="#creator" property="file-as">Inoue, Junya</meta>
|
||||
<meta refines="#creator" property="role" scheme="marc:relators">aut</meta>
|
||||
<meta name="cover" content="item1"/>
|
||||
</metadata>
|
||||
<manifest>
|
||||
<!--Image: 1000 x 1572 size=145584 -->
|
||||
<item href="@public@vhost@g@gutenberg@html@files@64999@64999-h@images@cover.jpg" id="item1" media-type="image/jpeg"/>
|
||||
<item href="pgepub.css" id="item2" media-type="text/css"/>
|
||||
<item href="0.css" id="item3" media-type="text/css"/>
|
||||
<item href="1.css" id="item4" media-type="text/css"/>
|
||||
<!--Chunk: size=3477 Split on div.chapter-->
|
||||
<item href="@public@vhost@g@gutenberg@html@files@64999@64999-h@64999-h-0.htm.html" id="cover" media-type="application/xhtml+xml"/>
|
||||
<!--Chunk: size=3242 Split on div.chapter-->
|
||||
<item href="@public@vhost@g@gutenberg@html@files@64999@64999-h@64999-h-1.htm.html" id="item5" media-type="application/xhtml+xml"/>
|
||||
<!--Chunk: size=27802 Split on div.chapter-->
|
||||
<item href="@public@vhost@g@gutenberg@html@files@64999@64999-h@64999-h-2.htm.html" id="item6" media-type="application/xhtml+xml"/>
|
||||
<!--Chunk: size=13387 Split on div.chapter-->
|
||||
<item href="@public@vhost@g@gutenberg@html@files@64999@64999-h@64999-h-3.htm.html" id="item7" media-type="application/xhtml+xml"/>
|
||||
<!--Chunk: size=25774 Split on div.chapter-->
|
||||
<item href="@public@vhost@g@gutenberg@html@files@64999@64999-h@64999-h-4.htm.html" id="item8" media-type="application/xhtml+xml"/>
|
||||
<!--Chunk: size=17053 Split on div.chapter-->
|
||||
<item href="@public@vhost@g@gutenberg@html@files@64999@64999-h@64999-h-5.htm.html" id="item9" media-type="application/xhtml+xml"/>
|
||||
<!--Chunk: size=19590 Split on div.chapter-->
|
||||
<item href="@public@vhost@g@gutenberg@html@files@64999@64999-h@64999-h-6.htm.html" id="item10" media-type="application/xhtml+xml"/>
|
||||
<!--Chunk: size=16645 Split on div.chapter-->
|
||||
<item href="@public@vhost@g@gutenberg@html@files@64999@64999-h@64999-h-7.htm.html" id="item11" media-type="application/xhtml+xml"/>
|
||||
<!--Chunk: size=19768 Split on div.chapter-->
|
||||
<item href="@public@vhost@g@gutenberg@html@files@64999@64999-h@64999-h-8.htm.html" id="item12" media-type="application/xhtml+xml"/>
|
||||
<!--Chunk: size=30397 Split on div.chapter-->
|
||||
<item href="@public@vhost@g@gutenberg@html@files@64999@64999-h@64999-h-9.htm.html" id="item13" media-type="application/xhtml+xml"/>
|
||||
<!--Chunk: size=41064 Split on div.chapter-->
|
||||
<item href="@public@vhost@g@gutenberg@html@files@64999@64999-h@64999-h-10.htm.html" id="item14" media-type="application/xhtml+xml"/>
|
||||
<!--Chunk: size=35745 Split on div.chapter-->
|
||||
<item href="@public@vhost@g@gutenberg@html@files@64999@64999-h@64999-h-11.htm.html" id="item15" media-type="application/xhtml+xml"/>
|
||||
<!--Chunk: size=3277 Split on div.chapter-->
|
||||
<item href="@public@vhost@g@gutenberg@html@files@64999@64999-h@64999-h-12.htm.html" id="item16" media-type="application/xhtml+xml"/>
|
||||
<!--Chunk: size=5983 Split on div.chapter-->
|
||||
<item href="@public@vhost@g@gutenberg@html@files@64999@64999-h@64999-h-13.htm.html" id="item17" media-type="application/xhtml+xml"/>
|
||||
<!--Chunk: size=22066-->
|
||||
<item href="@public@vhost@g@gutenberg@html@files@64999@64999-h@64999-h-14.htm.html" id="item18" media-type="application/xhtml+xml"/>
|
||||
<item href="toc.ncx" id="ncx" media-type="application/x-dtbncx+xml"/>
|
||||
<item href="wrap0000.html" id="coverpage-wrapper" media-type="application/xhtml+xml"/>
|
||||
</manifest>
|
||||
<spine toc="ncx">
|
||||
<itemref idref="coverpage-wrapper" linear="yes"/>
|
||||
<itemref idref="cover" linear="yes"/>
|
||||
<itemref idref="item5" linear="yes"/>
|
||||
<itemref idref="item6" linear="yes"/>
|
||||
<itemref idref="item7" linear="yes"/>
|
||||
<itemref idref="item8" linear="yes"/>
|
||||
<itemref idref="item9" linear="yes"/>
|
||||
<itemref idref="item10" linear="yes"/>
|
||||
<itemref idref="item11" linear="yes"/>
|
||||
<itemref idref="item12" linear="yes"/>
|
||||
<itemref idref="item13" linear="yes"/>
|
||||
<itemref idref="item14" linear="yes"/>
|
||||
<itemref idref="item15" linear="yes"/>
|
||||
<itemref idref="item16" linear="yes"/>
|
||||
<itemref idref="item17" linear="yes"/>
|
||||
<itemref idref="item18" linear="yes"/>
|
||||
</spine>
|
||||
<guide>
|
||||
<reference type="toc" title="CONTENTS" href="@public@vhost@g@gutenberg@html@files@64999@64999-h@64999-h-1.htm.html#pgepubid00001"/>
|
||||
<reference type="cover" title="Cover" href="wrap0000.html"/>
|
||||
</guide>
|
||||
</package>
|
Loading…
Add table
Add a link
Reference in a new issue