New Scanner + People Pages (#3286)

Co-authored-by: Robbie Davis <robbie@therobbiedavis.com>
This commit is contained in:
Joe Milazzo 2024-10-23 15:11:18 -07:00 committed by GitHub
parent 1ed0eae22d
commit ba20ad4ecc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
142 changed files with 17529 additions and 3038 deletions

View file

@ -29,7 +29,6 @@
<ItemGroup>
<Folder Include="Services\Test Data\ArchiveService\ComicInfos" />
<Folder Include="Services\Test Data\ImageService\Covers\" />
<Folder Include="Services\Test Data\ScannerService\Manga" />
</ItemGroup>
<ItemGroup>

View file

@ -126,28 +126,45 @@ public class QueryableExtensionsTests
[InlineData(false, 1)]
public void RestrictAgainstAgeRestriction_Person_ShouldRestrictEverythingAboveTeen(bool includeUnknowns, int expectedCount)
{
var items = new List<Person>()
// Arrange
var items = new List<Person>
{
new PersonBuilder("Test", PersonRole.Character)
.WithSeriesMetadata(new SeriesMetadataBuilder().WithAgeRating(AgeRating.Teen).Build())
.Build(),
new PersonBuilder("Test", PersonRole.Character)
.WithSeriesMetadata(new SeriesMetadataBuilder().WithAgeRating(AgeRating.Unknown).Build())
.WithSeriesMetadata(new SeriesMetadataBuilder().WithAgeRating(AgeRating.Teen).Build())
.Build(),
new PersonBuilder("Test", PersonRole.Character)
.WithSeriesMetadata(new SeriesMetadataBuilder().WithAgeRating(AgeRating.X18Plus).Build())
.Build(),
CreatePersonWithSeriesMetadata("Test1", AgeRating.Teen),
CreatePersonWithSeriesMetadata("Test2", AgeRating.Unknown, AgeRating.Teen),
CreatePersonWithSeriesMetadata("Test3", AgeRating.X18Plus)
};
var filtered = items.AsQueryable().RestrictAgainstAgeRestriction(new AgeRestriction()
var ageRestriction = new AgeRestriction
{
AgeRating = AgeRating.Teen,
IncludeUnknowns = includeUnknowns
});
};
// Act
var filtered = items.AsQueryable().RestrictAgainstAgeRestriction(ageRestriction);
// Assert
Assert.Equal(expectedCount, filtered.Count());
}
private static Person CreatePersonWithSeriesMetadata(string name, params AgeRating[] ageRatings)
{
var person = new PersonBuilder(name).Build();
foreach (var ageRating in ageRatings)
{
var seriesMetadata = new SeriesMetadataBuilder().WithAgeRating(ageRating).Build();
person.SeriesMetadataPeople.Add(new SeriesMetadataPeople
{
SeriesMetadata = seriesMetadata,
Person = person,
Role = PersonRole.Character // Role is now part of the relationship
});
}
return person;
}
[Theory]
[InlineData(true, 2)]
[InlineData(false, 1)]

View file

@ -185,6 +185,35 @@ public class SeriesExtensionsTests
Assert.Equal("Volume 1 Chapter 1", series.GetCoverImage());
}
[Fact]
public void GetCoverImage_JustVolumes_ButVolume0()
{
var series = new SeriesBuilder("Test 1")
.WithFormat(MangaFormat.Archive)
.WithVolume(new VolumeBuilder("0")
.WithName("Volume 0")
.WithChapter(new ChapterBuilder(Parser.DefaultChapter)
.WithCoverImage("Volume 0")
.Build())
.Build())
.WithVolume(new VolumeBuilder("1")
.WithName("Volume 1")
.WithChapter(new ChapterBuilder(Parser.DefaultChapter)
.WithCoverImage("Volume 1")
.Build())
.Build())
.Build();
foreach (var vol in series.Volumes)
{
vol.CoverImage = vol.Chapters.MinBy(x => x.SortOrder, ChapterSortComparerDefaultFirst.Default)?.CoverImage;
}
Assert.Equal("Volume 1", series.GetCoverImage());
}
[Fact]
public void GetCoverImage_JustSpecials_WithDecimal()
{

View file

@ -1,128 +0,0 @@
using System.Collections.Generic;
using API.Data;
using API.Entities;
using API.Extensions;
using API.Helpers;
using API.Helpers.Builders;
using Xunit;
namespace API.Tests.Helpers;
public class GenreHelperTests
{
[Fact]
public void UpdateGenre_ShouldAddNewGenre()
{
var allGenres = new Dictionary<string, Genre>
{
{"Action".ToNormalized(), new GenreBuilder("Action").Build()},
{"Sci-fi".ToNormalized(), new GenreBuilder("Sci-fi").Build()}
};
var genreAdded = new List<Genre>();
var addedCount = 0;
GenreHelper.UpdateGenre(allGenres, new[] {"Action", "Adventure"}, (genre, isNew) =>
{
if (isNew)
{
addedCount++;
}
genreAdded.Add(genre);
});
Assert.Equal(2, genreAdded.Count);
Assert.Equal(1, addedCount);
Assert.Equal(3, allGenres.Count);
}
[Fact]
public void UpdateGenre_ShouldNotAddDuplicateGenre()
{
var allGenres = new Dictionary<string, Genre>
{
{"Action".ToNormalized(), new GenreBuilder("Action").Build()},
{"Sci-fi".ToNormalized(), new GenreBuilder("Sci-fi").Build()}
};
var genreAdded = new List<Genre>();
var addedCount = 0;
GenreHelper.UpdateGenre(allGenres, new[] {"Action", "Scifi"}, (genre, isNew) =>
{
if (isNew)
{
addedCount++;
}
genreAdded.Add(genre);
});
Assert.Equal(0, addedCount);
Assert.Equal(2, genreAdded.Count);
Assert.Equal(2, allGenres.Count);
}
[Fact]
public void AddGenre_ShouldAddOnlyNonExistingGenre()
{
var existingGenres = new List<Genre>
{
new GenreBuilder("Action").Build(),
new GenreBuilder("action").Build(),
new GenreBuilder("Sci-fi").Build(),
};
GenreHelper.AddGenreIfNotExists(existingGenres, new GenreBuilder("Action").Build());
Assert.Equal(3, existingGenres.Count);
GenreHelper.AddGenreIfNotExists(existingGenres, new GenreBuilder("action").Build());
Assert.Equal(3, existingGenres.Count);
GenreHelper.AddGenreIfNotExists(existingGenres, new GenreBuilder("Shonen").Build());
Assert.Equal(4, existingGenres.Count);
}
[Fact]
public void KeepOnlySamePeopleBetweenLists()
{
var existingGenres = new List<Genre>
{
new GenreBuilder("Action").Build(),
new GenreBuilder("Sci-fi").Build(),
};
var peopleFromChapters = new List<Genre>
{
new GenreBuilder("Action").Build(),
};
var genreRemoved = new List<Genre>();
GenreHelper.KeepOnlySameGenreBetweenLists(existingGenres,
peopleFromChapters, genre =>
{
genreRemoved.Add(genre);
});
Assert.Single(genreRemoved);
}
[Fact]
public void RemoveEveryoneIfNothingInRemoveAllExcept()
{
var existingGenres = new List<Genre>
{
new GenreBuilder("Action").Build(),
new GenreBuilder("Sci-fi").Build(),
};
var peopleFromChapters = new List<Genre>();
var genreRemoved = new List<Genre>();
GenreHelper.KeepOnlySameGenreBetweenLists(existingGenres,
peopleFromChapters, genre =>
{
genreRemoved.Add(genre);
});
Assert.Equal(2, genreRemoved.Count);
}
}

View file

@ -1,415 +1,143 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using API.Data;
using API.DTOs;
using API.Entities;
using API.Entities.Enums;
using API.Helpers;
using API.Helpers.Builders;
using API.Services.Tasks.Scanner.Parser;
using Xunit;
namespace API.Tests.Helpers;
public class PersonHelperTests
public class PersonHelperTests : AbstractDbTest
{
#region UpdatePeople
[Fact]
public void UpdatePeople_ShouldAddNewPeople()
protected override async Task ResetDb()
{
var allPeople = new List<Person>
{
new PersonBuilder("Joe Shmo", PersonRole.CoverArtist).Build(),
new PersonBuilder("Joe Shmo", PersonRole.Writer).Build(),
};
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);
_context.Series.RemoveRange(_context.Series.ToList());
await _context.SaveChangesAsync();
}
[Fact]
public void UpdatePeople_ShouldNotAddDuplicatePeople()
{
var allPeople = new List<Person>
{
new PersonBuilder("Joe Shmo", PersonRole.CoverArtist).Build(),
new PersonBuilder("Joe Shmo", PersonRole.Writer).Build(),
new PersonBuilder("Sally Ann", PersonRole.CoverArtist).Build(),
};
var peopleAdded = new List<Person>();
PersonHelper.UpdatePeople(allPeople, new[] {"Joe Shmo", "Sally Ann"}, PersonRole.CoverArtist, person =>
{
peopleAdded.Add(person);
});
Assert.Equal(3, allPeople.Count);
}
#endregion
#region UpdatePeopleList
[Fact]
public void UpdatePeopleList_NullTags_NoChanges()
{
// Arrange
ICollection<PersonDto> tags = null;
var series = new SeriesBuilder("Test Series").Build();
var allTags = new List<Person>();
var handleAddCalled = false;
var onModifiedCalled = false;
// Act
PersonHelper.UpdatePeopleList(PersonRole.Writer, tags, series, allTags, p => handleAddCalled = true, () => onModifiedCalled = true);
// Assert
Assert.False(handleAddCalled);
Assert.False(onModifiedCalled);
}
[Fact]
public void UpdatePeopleList_AddNewTag_TagAddedAndOnModifiedCalled()
{
// Arrange
const PersonRole role = PersonRole.Writer;
var tags = new List<PersonDto>
{
new PersonDto { Id = 1, Name = "John Doe", Role = role }
};
var series = new SeriesBuilder("Test Series").Build();
var allTags = new List<Person>();
var handleAddCalled = false;
var onModifiedCalled = false;
// Act
PersonHelper.UpdatePeopleList(role, tags, series, allTags, p =>
{
handleAddCalled = true;
series.Metadata.People.Add(p);
}, () => onModifiedCalled = true);
// Assert
Assert.True(handleAddCalled);
Assert.True(onModifiedCalled);
Assert.Single(series.Metadata.People);
Assert.Equal("John Doe", series.Metadata.People.First().Name);
}
[Fact]
public void UpdatePeopleList_RemoveExistingTag_TagRemovedAndOnModifiedCalled()
{
// Arrange
const PersonRole role = PersonRole.Writer;
var tags = new List<PersonDto>();
var series = new SeriesBuilder("Test Series").Build();
var person = new PersonBuilder("John Doe", role).Build();
person.Id = 1;
series.Metadata.People.Add(person);
var allTags = new List<Person>
{
person
};
var handleAddCalled = false;
var onModifiedCalled = false;
// Act
PersonHelper.UpdatePeopleList(role, tags, series, allTags, p =>
{
handleAddCalled = true;
series.Metadata.People.Add(p);
}, () => onModifiedCalled = true);
// Assert
Assert.False(handleAddCalled);
Assert.True(onModifiedCalled);
Assert.Empty(series.Metadata.People);
}
[Fact]
public void UpdatePeopleList_UpdateExistingTag_OnModifiedCalled()
{
// Arrange
const PersonRole role = PersonRole.Writer;
var tags = new List<PersonDto>
{
new PersonDto { Id = 1, Name = "John Doe", Role = role }
};
var series = new SeriesBuilder("Test Series").Build();
var person = new PersonBuilder("John Doe", role).Build();
person.Id = 1;
series.Metadata.People.Add(person);
var allTags = new List<Person>
{
person
};
var handleAddCalled = false;
var onModifiedCalled = false;
// Act
PersonHelper.UpdatePeopleList(role, tags, series, allTags, p =>
{
handleAddCalled = true;
series.Metadata.People.Add(p);
}, () => onModifiedCalled = true);
// Assert
Assert.False(handleAddCalled);
Assert.False(onModifiedCalled);
Assert.Single(series.Metadata.People);
Assert.Equal("John Doe", series.Metadata.People.First().Name);
}
[Fact]
public void UpdatePeopleList_NoChanges_HandleAddAndOnModifiedNotCalled()
{
// Arrange
const PersonRole role = PersonRole.Writer;
var tags = new List<PersonDto>
{
new PersonDto { Id = 1, Name = "John Doe", Role = role }
};
var series = new SeriesBuilder("Test Series").Build();
var person = new PersonBuilder("John Doe", role).Build();
person.Id = 1;
series.Metadata.People.Add(person);
var allTags = new List<Person>
{
new PersonBuilder("John Doe", role).Build()
};
var handleAddCalled = false;
var onModifiedCalled = false;
// Act
PersonHelper.UpdatePeopleList(role, tags, series, allTags, p =>
{
handleAddCalled = true;
series.Metadata.People.Add(p);
}, () => onModifiedCalled = true);
// Assert
Assert.False(handleAddCalled);
Assert.False(onModifiedCalled);
Assert.Single(series.Metadata.People);
Assert.Equal("John Doe", series.Metadata.People.First().Name);
}
#endregion
#region RemovePeople
[Fact]
public void RemovePeople_ShouldRemovePeopleOfSameRole()
{
var existingPeople = new List<Person>
{
new PersonBuilder("Joe Shmo", PersonRole.CoverArtist).Build(),
new PersonBuilder("Joe Shmo", PersonRole.Writer).Build(),
};
var peopleRemoved = new List<Person>();
PersonHelper.RemovePeople(existingPeople, new[] {"Joe Shmo", "Sally Ann"}, PersonRole.Writer, person =>
{
peopleRemoved.Add(person);
});
Assert.NotEqual(existingPeople, peopleRemoved);
Assert.Single(peopleRemoved);
}
[Fact]
public void RemovePeople_ShouldRemovePeopleFromBothRoles()
{
var existingPeople = new List<Person>
{
new PersonBuilder("Joe Shmo", PersonRole.CoverArtist).Build(),
new PersonBuilder("Joe Shmo", PersonRole.Writer).Build(),
};
var peopleRemoved = new List<Person>();
PersonHelper.RemovePeople(existingPeople, new[] {"Joe Shmo", "Sally Ann"}, PersonRole.Writer, person =>
{
peopleRemoved.Add(person);
});
Assert.NotEqual(existingPeople, peopleRemoved);
Assert.Single(peopleRemoved);
PersonHelper.RemovePeople(existingPeople, new[] {"Joe Shmo"}, PersonRole.CoverArtist, person =>
{
peopleRemoved.Add(person);
});
Assert.Empty(existingPeople);
Assert.Equal(2, peopleRemoved.Count);
}
[Fact]
public void RemovePeople_ShouldRemovePeopleOfSameRole_WhenNothingPassed()
{
var existingPeople = new List<Person>
{
new PersonBuilder("Joe Shmo", PersonRole.CoverArtist).Build(),
new PersonBuilder("Joe Shmo", PersonRole.Writer).Build(),
new PersonBuilder("Joe Shmo", PersonRole.Writer).Build(),
};
var peopleRemoved = new List<Person>();
PersonHelper.RemovePeople(existingPeople, new List<string>(), PersonRole.Writer, person =>
{
peopleRemoved.Add(person);
});
Assert.NotEqual(existingPeople, peopleRemoved);
Assert.Equal(2, peopleRemoved.Count);
}
#endregion
#region KeepOnlySamePeopleBetweenLists
[Fact]
public void KeepOnlySamePeopleBetweenLists()
{
var existingPeople = new List<Person>
{
new PersonBuilder("Joe Shmo", PersonRole.CoverArtist).Build(),
new PersonBuilder("Joe Shmo", PersonRole.Writer).Build(),
new PersonBuilder("Sally", PersonRole.Writer).Build(),
};
var peopleFromChapters = new List<Person>
{
new PersonBuilder("Joe Shmo", PersonRole.CoverArtist).Build(),
};
var peopleRemoved = new List<Person>();
PersonHelper.KeepOnlySamePeopleBetweenLists(existingPeople,
peopleFromChapters, person =>
{
peopleRemoved.Add(person);
});
Assert.Equal(2, peopleRemoved.Count);
}
#endregion
#region AddPeople
[Fact]
public void AddPersonIfNotExists_ShouldAddPerson_WhenPersonDoesNotExist()
{
// Arrange
var metadataPeople = new List<Person>();
var person = new PersonBuilder("John Smith", PersonRole.Character).Build();
// Act
PersonHelper.AddPersonIfNotExists(metadataPeople, person);
// Assert
Assert.Single(metadataPeople);
Assert.Contains(person, metadataPeople);
}
[Fact]
public void AddPersonIfNotExists_ShouldNotAddPerson_WhenPersonAlreadyExists()
{
// Arrange
var metadataPeople = new List<Person>
{
new PersonBuilder("John Smith", PersonRole.Character)
.WithId(1)
.Build()
};
var person = new PersonBuilder("John Smith", PersonRole.Character).Build();
// Act
PersonHelper.AddPersonIfNotExists(metadataPeople, person);
// Assert
Assert.Single(metadataPeople);
Assert.NotNull(metadataPeople.SingleOrDefault(p =>
p.Name.Equals(person.Name) && p.Role == person.Role && p.NormalizedName == person.NormalizedName));
Assert.Equal(1, metadataPeople.First().Id);
}
[Fact]
public void AddPersonIfNotExists_ShouldNotAddPerson_WhenPersonNameIsNullOrEmpty()
{
// Arrange
var metadataPeople = new List<Person>();
var person2 = new PersonBuilder(string.Empty, PersonRole.Character).Build();
// Act
PersonHelper.AddPersonIfNotExists(metadataPeople, person2);
// Assert
Assert.Empty(metadataPeople);
}
[Fact]
public void AddPersonIfNotExists_ShouldAddPerson_WhenPersonNameIsDifferentButRoleIsSame()
{
// Arrange
var metadataPeople = new List<Person>
{
new PersonBuilder("John Smith", PersonRole.Character).Build()
};
var person = new PersonBuilder("John Doe", PersonRole.Character).Build();
// Act
PersonHelper.AddPersonIfNotExists(metadataPeople, person);
// Assert
Assert.Equal(2, metadataPeople.Count);
Assert.Contains(person, metadataPeople);
}
[Fact]
public void AddPersonIfNotExists_ShouldAddPerson_WhenPersonNameIsSameButRoleIsDifferent()
{
// Arrange
var metadataPeople = new List<Person>
{
new PersonBuilder("John Doe", PersonRole.Writer).Build()
};
var person = new PersonBuilder("John Smith", PersonRole.Character).Build();
// Act
PersonHelper.AddPersonIfNotExists(metadataPeople, person);
// Assert
Assert.Equal(2, metadataPeople.Count);
Assert.Contains(person, metadataPeople);
}
[Fact]
public void AddPeople_ShouldAddOnlyNonExistingPeople()
{
var existingPeople = new List<Person>
{
new PersonBuilder("Joe Shmo", PersonRole.CoverArtist).Build(),
new PersonBuilder("Joe Shmo", PersonRole.Writer).Build(),
new PersonBuilder("Sally", PersonRole.Writer).Build(),
};
PersonHelper.AddPersonIfNotExists(existingPeople, new PersonBuilder("Joe Shmo", PersonRole.CoverArtist).Build());
Assert.Equal(3, existingPeople.Count);
PersonHelper.AddPersonIfNotExists(existingPeople, new PersonBuilder("Joe Shmo", PersonRole.Writer).Build());
Assert.Equal(3, existingPeople.Count);
PersonHelper.AddPersonIfNotExists(existingPeople, new PersonBuilder("Joe Shmo Two", PersonRole.CoverArtist).Build());
Assert.Equal(4, existingPeople.Count);
}
#endregion
//
// // 1. Test adding new people and keeping existing ones
// [Fact]
// public async Task UpdateChapterPeopleAsync_AddNewPeople_ExistingPersonRetained()
// {
// var existingPerson = new PersonBuilder("Joe Shmo").Build();
// var chapter = new ChapterBuilder("1").Build();
//
// // Create an existing person and assign them to the series with a role
// var series = new SeriesBuilder("Test 1")
// .WithFormat(MangaFormat.Archive)
// .WithMetadata(new SeriesMetadataBuilder()
// .WithPerson(existingPerson, PersonRole.Editor)
// .Build())
// .WithVolume(new VolumeBuilder("1").WithChapter(chapter).Build())
// .Build();
//
// _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);
//
// // Assert existing person retained and new person added
// var people = await _unitOfWork.PersonRepository.GetAllPeople();
// Assert.Contains(people, p => p.Name == "Joe Shmo");
// Assert.Contains(people, p => p.Name == "New Person");
//
// var chapterPeople = chapter.People.Select(cp => cp.Person.Name).ToList();
// Assert.Contains("Joe Shmo", chapterPeople);
// Assert.Contains("New Person", chapterPeople);
// }
//
// // 2. Test removing a person no longer in the list
// [Fact]
// public async Task UpdateChapterPeopleAsync_RemovePeople()
// {
// var existingPerson1 = new PersonBuilder("Joe Shmo").Build();
// var existingPerson2 = new PersonBuilder("Jane Doe").Build();
// var chapter = new ChapterBuilder("1").Build();
//
// var series = new SeriesBuilder("Test 1")
// .WithVolume(new VolumeBuilder("1")
// .WithChapter(new ChapterBuilder("1")
// .WithPerson(existingPerson1, PersonRole.Editor)
// .WithPerson(existingPerson2, PersonRole.Editor)
// .Build())
// .Build())
// .Build();
//
// _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);
//
// var people = await _unitOfWork.PersonRepository.GetAllPeople();
// Assert.DoesNotContain(people, p => p.Name == "Jane Doe");
//
// var chapterPeople = chapter.People.Select(cp => cp.Person.Name).ToList();
// Assert.Contains("Joe Shmo", chapterPeople);
// Assert.DoesNotContain("Jane Doe", chapterPeople);
// }
//
// // 3. Test no changes when the list of people is the same
// [Fact]
// public async Task UpdateChapterPeopleAsync_NoChanges()
// {
// var existingPerson = new PersonBuilder("Joe Shmo").Build();
// var chapter = new ChapterBuilder("1").Build();
//
// var series = new SeriesBuilder("Test 1")
// .WithVolume(new VolumeBuilder("1")
// .WithChapter(new ChapterBuilder("1")
// .WithPerson(existingPerson, PersonRole.Editor)
// .Build())
// .Build())
// .Build();
//
// _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);
//
// var people = await _unitOfWork.PersonRepository.GetAllPeople();
// Assert.Contains(people, p => p.Name == "Joe Shmo");
//
// var chapterPeople = chapter.People.Select(cp => cp.Person.Name).ToList();
// Assert.Contains("Joe Shmo", chapterPeople);
// Assert.Single(chapter.People); // No duplicate entries
// }
//
// // 4. Test multiple roles for a person
// [Fact]
// public async Task UpdateChapterPeopleAsync_MultipleRoles()
// {
// var person = new PersonBuilder("Joe Shmo").Build();
// var chapter = new ChapterBuilder("1").Build();
//
// var series = new SeriesBuilder("Test 1")
// .WithVolume(new VolumeBuilder("1")
// .WithChapter(new ChapterBuilder("1")
// .WithPerson(person, PersonRole.Writer) // Assign person as Writer
// .Build())
// .Build())
// .Build();
//
// _unitOfWork.SeriesRepository.Add(series);
// await _unitOfWork.CommitAsync();
//
// // Add same person as Editor
// 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.People.Where(cp => cp.Person.Name == "Joe Shmo").ToList();
// Assert.Equal(2, chapterPeople.Count); // One for each role
// Assert.Contains(chapterPeople, cp => cp.Role == PersonRole.Writer);
// Assert.Contains(chapterPeople, cp => cp.Role == PersonRole.Editor);
// }
}

View file

@ -1,128 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using API.Data;
using API.Entities;
using API.Extensions;
using API.Helpers;
using API.Helpers.Builders;
using Xunit;
namespace API.Tests.Helpers;
public class TagHelperTests
{
[Fact]
public void UpdateTag_ShouldAddNewTag()
{
var allTags = new Dictionary<string, Tag>
{
{"Action".ToNormalized(), new TagBuilder("Action").Build()},
{"Sci-fi".ToNormalized(), new TagBuilder("Sci-fi").Build()}
};
var tagCalled = new List<Tag>();
var addedCount = 0;
TagHelper.UpdateTag(allTags, new[] {"Action", "Adventure"}, (tag, added) =>
{
if (added)
{
addedCount++;
}
tagCalled.Add(tag);
});
Assert.Equal(1, addedCount);
Assert.Equal(2, tagCalled.Count());
Assert.Equal(3, allTags.Count);
}
[Fact]
public void UpdateTag_ShouldNotAddDuplicateTag()
{
var allTags = new Dictionary<string, Tag>
{
{"Action".ToNormalized(), new TagBuilder("Action").Build()},
{"Sci-fi".ToNormalized(), new TagBuilder("Sci-fi").Build()}
};
var tagCalled = new List<Tag>();
var addedCount = 0;
TagHelper.UpdateTag(allTags, new[] {"Action", "Scifi"}, (tag, added) =>
{
if (added)
{
addedCount++;
}
tagCalled.Add(tag);
});
Assert.Equal(2, allTags.Count);
Assert.Equal(0, addedCount);
}
[Fact]
public void AddTag_ShouldAddOnlyNonExistingTag()
{
var existingTags = new List<Tag>
{
new TagBuilder("Action").Build(),
new TagBuilder("action").Build(),
new TagBuilder("Sci-fi").Build(),
};
TagHelper.AddTagIfNotExists(existingTags, new TagBuilder("Action").Build());
Assert.Equal(3, existingTags.Count);
TagHelper.AddTagIfNotExists(existingTags, new TagBuilder("action").Build());
Assert.Equal(3, existingTags.Count);
TagHelper.AddTagIfNotExists(existingTags, new TagBuilder("Shonen").Build());
Assert.Equal(4, existingTags.Count);
}
[Fact]
public void KeepOnlySamePeopleBetweenLists()
{
var existingTags = new List<Tag>
{
new TagBuilder("Action").Build(),
new TagBuilder("Sci-fi").Build(),
};
var peopleFromChapters = new List<Tag>
{
new TagBuilder("Action").Build(),
};
var tagRemoved = new List<Tag>();
TagHelper.KeepOnlySameTagBetweenLists(existingTags,
peopleFromChapters, tag =>
{
tagRemoved.Add(tag);
});
Assert.Single(tagRemoved);
}
[Fact]
public void RemoveEveryoneIfNothingInRemoveAllExcept()
{
var existingTags = new List<Tag>
{
new TagBuilder("Action").Build(),
new TagBuilder("Sci-fi").Build(),
};
var peopleFromChapters = new List<Tag>();
var tagRemoved = new List<Tag>();
TagHelper.KeepOnlySameTagBetweenLists(existingTags,
peopleFromChapters, tag =>
{
tagRemoved.Add(tag);
});
Assert.Equal(2, tagRemoved.Count);
}
}

View file

@ -138,13 +138,31 @@ public class BasicParserTests
[Fact]
public void Parse_MangaLibrary_SpecialInFilename()
{
var actual = _parser.Parse("C:/Books/Summer Time Rendering/Specials/Volume Omake.cbr",
var actual = _parser.Parse("C:/Books/Summer Time Rendering/Volume SP01.cbr",
"C:/Books/Summer Time Rendering/",
RootDirectory, LibraryType.Manga, null);
Assert.NotNull(actual);
Assert.Equal("Summer Time Rendering", actual.Series);
Assert.Equal("Volume Omake", actual.Title);
Assert.Equal("Volume SP01", actual.Title);
Assert.Equal(Parser.SpecialVolume, actual.Volumes);
Assert.Equal(Parser.DefaultChapter, actual.Chapters);
Assert.True(actual.IsSpecial);
}
/// <summary>
/// Tests that when the filename parses as a speical, it appropriately parses
/// </summary>
[Fact]
public void Parse_MangaLibrary_SpecialInFilename2()
{
var actual = _parser.Parse("M:/Kimi wa Midara na Boku no Joou/Specials/[Renzokusei] Special 1 SP02.zip",
"M:/Kimi wa Midara na Boku no Joou/",
RootDirectory, LibraryType.Manga, null);
Assert.NotNull(actual);
Assert.Equal("Kimi wa Midara na Boku no Joou", actual.Series);
Assert.Equal("[Renzokusei] Special 1 SP02", actual.Title);
Assert.Equal(Parser.SpecialVolume, actual.Volumes);
Assert.Equal(Parser.DefaultChapter, actual.Chapters);
Assert.True(actual.IsSpecial);

View file

@ -408,7 +408,7 @@ public class DefaultParserTests
expected = new ParserInfo
{
Series = "Foo 50", Volumes = API.Services.Tasks.Scanner.Parser.Parser.SpecialVolume, IsSpecial = true,
Chapters = "50", Filename = "Foo 50 SP01.cbz", Format = MangaFormat.Archive,
Chapters = Parser.DefaultChapter, Filename = "Foo 50 SP01.cbz", Format = MangaFormat.Archive,
FullFilePath = filepath
};

View file

@ -21,24 +21,4 @@ public class BookParsingTests
{
Assert.Equal(expected, API.Services.Tasks.Scanner.Parser.Parser.ParseVolume(filename, LibraryType.Book));
}
// [Theory]
// [InlineData("@font-face{font-family:'syyskuu_repaleinen';src:url(data:font/opentype;base64,AAEAAAA", "@font-face{font-family:'syyskuu_repaleinen';src:url(data:font/opentype;base64,AAEAAAA")]
// [InlineData("@font-face{font-family:'syyskuu_repaleinen';src:url('fonts/font.css')", "@font-face{font-family:'syyskuu_repaleinen';src:url('TEST/fonts/font.css')")]
// public void ReplaceFontSrcUrl(string input, string expected)
// {
// var apiBase = "TEST/";
// var actual = API.Parser.Parser.FontSrcUrlRegex.Replace(input, "$1" + apiBase + "$2" + "$3");
// Assert.Equal(expected, actual);
// }
//
// [Theory]
// [InlineData("@import url('font.css');", "@import url('TEST/font.css');")]
// public void ReplaceImportSrcUrl(string input, string expected)
// {
// var apiBase = "TEST/";
// var actual = API.Parser.Parser.CssImportUrlRegex.Replace(input, "$1" + apiBase + "$2" + "$3");
// Assert.Equal(expected, actual);
// }
}

View file

@ -1,11 +1,6 @@
using System.IO.Abstractions.TestingHelpers;
using API.Entities.Enums;
using API.Services;
using API.Services.Tasks.Scanner.Parser;
using Microsoft.Extensions.Logging;
using NSubstitute;
using Xunit;
using Xunit.Abstractions;
namespace API.Tests.Parsing;
@ -73,41 +68,41 @@ public class ComicParsingTests
[InlineData("SKY WORLD สกายเวิลด์ เล่มที่ 1", "SKY WORLD สกายเวิลด์")]
public void ParseComicSeriesTest(string filename, string expected)
{
Assert.Equal(expected, API.Services.Tasks.Scanner.Parser.Parser.ParseComicSeries(filename));
Assert.Equal(expected, Parser.ParseComicSeries(filename));
}
[Theory]
[InlineData("01 Spider-Man & Wolverine 01.cbr", API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)]
[InlineData("04 - Asterix the Gladiator (1964) (Digital-Empire) (WebP by Doc MaKS)", API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)]
[InlineData("The First Asterix Frieze (WebP by Doc MaKS)", API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)]
[InlineData("Batman & Catwoman - Trail of the Gun 01", API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)]
[InlineData("Batman & Daredevil - King of New York", API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)]
[InlineData("Batman & Grendel (1996) 01 - Devil's Bones", API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)]
[InlineData("Batman & Robin the Teen Wonder #0", API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)]
[InlineData("Batman & Wildcat (1 of 3)", API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)]
[InlineData("Batman And Superman World's Finest #01", API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)]
[InlineData("Babe 01", API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)]
[InlineData("Scott Pilgrim 01 - Scott Pilgrim's Precious Little Life (2004)", API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)]
[InlineData("01 Spider-Man & Wolverine 01.cbr", Parser.LooseLeafVolume)]
[InlineData("04 - Asterix the Gladiator (1964) (Digital-Empire) (WebP by Doc MaKS)", Parser.LooseLeafVolume)]
[InlineData("The First Asterix Frieze (WebP by Doc MaKS)", Parser.LooseLeafVolume)]
[InlineData("Batman & Catwoman - Trail of the Gun 01", Parser.LooseLeafVolume)]
[InlineData("Batman & Daredevil - King of New York", Parser.LooseLeafVolume)]
[InlineData("Batman & Grendel (1996) 01 - Devil's Bones", Parser.LooseLeafVolume)]
[InlineData("Batman & Robin the Teen Wonder #0", Parser.LooseLeafVolume)]
[InlineData("Batman & Wildcat (1 of 3)", Parser.LooseLeafVolume)]
[InlineData("Batman And Superman World's Finest #01", Parser.LooseLeafVolume)]
[InlineData("Babe 01", Parser.LooseLeafVolume)]
[InlineData("Scott Pilgrim 01 - Scott Pilgrim's Precious Little Life (2004)", Parser.LooseLeafVolume)]
[InlineData("Teen Titans v1 001 (1966-02) (digital) (OkC.O.M.P.U.T.O.-Novus)", "1")]
[InlineData("Scott Pilgrim 02 - Scott Pilgrim vs. The World (2005)", API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)]
[InlineData("Scott Pilgrim 02 - Scott Pilgrim vs. The World (2005)", Parser.LooseLeafVolume)]
[InlineData("Superman v1 024 (09-10 1943)", "1")]
[InlineData("Superman v1.5 024 (09-10 1943)", "1.5")]
[InlineData("Amazing Man Comics chapter 25", API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)]
[InlineData("Invincible 033.5 - Marvel Team-Up 14 (2006) (digital) (Minutemen-Slayer)", API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)]
[InlineData("Cyberpunk 2077 - Trauma Team 04.cbz", API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)]
[InlineData("spawn-123", API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)]
[InlineData("spawn-chapter-123", API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)]
[InlineData("Spawn 062 (1997) (digital) (TLK-EMPIRE-HD).cbr", API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)]
[InlineData("Batman Beyond 04 (of 6) (1999)", API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)]
[InlineData("Batman Beyond 001 (2012)", API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)]
[InlineData("Batman Beyond 2.0 001 (2013)", API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)]
[InlineData("Batman - Catwoman 001 (2021) (Webrip) (The Last Kryptonian-DCP)", API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)]
[InlineData("Amazing Man Comics chapter 25", Parser.LooseLeafVolume)]
[InlineData("Invincible 033.5 - Marvel Team-Up 14 (2006) (digital) (Minutemen-Slayer)", Parser.LooseLeafVolume)]
[InlineData("Cyberpunk 2077 - Trauma Team 04.cbz", Parser.LooseLeafVolume)]
[InlineData("spawn-123", Parser.LooseLeafVolume)]
[InlineData("spawn-chapter-123", Parser.LooseLeafVolume)]
[InlineData("Spawn 062 (1997) (digital) (TLK-EMPIRE-HD).cbr", Parser.LooseLeafVolume)]
[InlineData("Batman Beyond 04 (of 6) (1999)", Parser.LooseLeafVolume)]
[InlineData("Batman Beyond 001 (2012)", Parser.LooseLeafVolume)]
[InlineData("Batman Beyond 2.0 001 (2013)", Parser.LooseLeafVolume)]
[InlineData("Batman - Catwoman 001 (2021) (Webrip) (The Last Kryptonian-DCP)", Parser.LooseLeafVolume)]
[InlineData("Chew v1 - Taster´s Choise (2012) (Digital) (1920) (Kingpin-Empire)", "1")]
[InlineData("Chew Script Book (2011) (digital-Empire) SP04", API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)]
[InlineData("Chew Script Book (2011) (digital-Empire) SP04", Parser.LooseLeafVolume)]
[InlineData("Batgirl Vol.2000 #57 (December, 2004)", "2000")]
[InlineData("Batgirl V2000 #57", "2000")]
[InlineData("Fables 021 (2004) (Digital) (Nahga-Empire).cbr", API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)]
[InlineData("2000 AD 0366 [1984-04-28] (flopbie)", API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)]
[InlineData("Fables 021 (2004) (Digital) (Nahga-Empire).cbr", Parser.LooseLeafVolume)]
[InlineData("2000 AD 0366 [1984-04-28] (flopbie)", Parser.LooseLeafVolume)]
[InlineData("Daredevil - v6 - 10 - (2019)", "6")]
[InlineData("Daredevil - v6.5", "6.5")]
// Tome Tests
@ -117,25 +112,25 @@ public class ComicParsingTests
[InlineData("Conquistador_Tome_2", "2")]
[InlineData("Max_l_explorateur-_Tome_0", "0")]
[InlineData("Chevaliers d'Héliopolis T3 - Rubedo, l'oeuvre au rouge (Jodorowsky & Jérémy)", "3")]
[InlineData("Adventure Time (2012)/Adventure Time #1 (2012)", API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)]
[InlineData("Adventure Time (2012)/Adventure Time #1 (2012)", Parser.LooseLeafVolume)]
[InlineData("Adventure Time TPB (2012)/Adventure Time v01 (2012).cbz", "1")]
// Russian Tests
[InlineData("Kebab Том 1 Глава 3", "1")]
[InlineData("Манга Глава 2", API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)]
[InlineData("Манга Глава 2", Parser.LooseLeafVolume)]
[InlineData("ย้อนเวลากลับมาร้าย เล่ม 1", "1")]
[InlineData("เด็กคนนี้ขอลาออกจากการเป็นเจ้าของปราสาท เล่ม 1 ตอนที่ 3", "1")]
[InlineData("วิวาห์รัก เดิมพันชีวิต ตอนที่ 2", API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume)]
[InlineData("วิวาห์รัก เดิมพันชีวิต ตอนที่ 2", Parser.LooseLeafVolume)]
public void ParseComicVolumeTest(string filename, string expected)
{
Assert.Equal(expected, API.Services.Tasks.Scanner.Parser.Parser.ParseComicVolume(filename));
Assert.Equal(expected, Parser.ParseComicVolume(filename));
}
[Theory]
[InlineData("01 Spider-Man & Wolverine 01.cbr", "1")]
[InlineData("04 - Asterix the Gladiator (1964) (Digital-Empire) (WebP by Doc MaKS)", API.Services.Tasks.Scanner.Parser.Parser.DefaultChapter)]
[InlineData("The First Asterix Frieze (WebP by Doc MaKS)", API.Services.Tasks.Scanner.Parser.Parser.DefaultChapter)]
[InlineData("04 - Asterix the Gladiator (1964) (Digital-Empire) (WebP by Doc MaKS)", Parser.DefaultChapter)]
[InlineData("The First Asterix Frieze (WebP by Doc MaKS)", Parser.DefaultChapter)]
[InlineData("Batman & Catwoman - Trail of the Gun 01", "1")]
[InlineData("Batman & Daredevil - King of New York", API.Services.Tasks.Scanner.Parser.Parser.DefaultChapter)]
[InlineData("Batman & Daredevil - King of New York", Parser.DefaultChapter)]
[InlineData("Batman & Grendel (1996) 01 - Devil's Bones", "1")]
[InlineData("Batman & Robin the Teen Wonder #0", "0")]
[InlineData("Batman & Wildcat (1 of 3)", "1")]
@ -159,8 +154,8 @@ public class ComicParsingTests
[InlineData("Batman Beyond 001 (2012)", "1")]
[InlineData("Batman Beyond 2.0 001 (2013)", "1")]
[InlineData("Batman - Catwoman 001 (2021) (Webrip) (The Last Kryptonian-DCP)", "1")]
[InlineData("Chew v1 - Taster´s Choise (2012) (Digital) (1920) (Kingpin-Empire)", API.Services.Tasks.Scanner.Parser.Parser.DefaultChapter)]
[InlineData("Chew Script Book (2011) (digital-Empire) SP04", API.Services.Tasks.Scanner.Parser.Parser.DefaultChapter)]
[InlineData("Chew v1 - Taster´s Choise (2012) (Digital) (1920) (Kingpin-Empire)", Parser.DefaultChapter)]
[InlineData("Chew Script Book (2011) (digital-Empire) SP04", Parser.DefaultChapter)]
[InlineData("Batgirl Vol.2000 #57 (December, 2004)", "57")]
[InlineData("Batgirl V2000 #57", "57")]
[InlineData("Fables 021 (2004) (Digital) (Nahga-Empire).cbr", "21")]
@ -169,7 +164,7 @@ public class ComicParsingTests
[InlineData("Daredevil - v6 - 10 - (2019)", "10")]
[InlineData("Batman Beyond 2016 - Chapter 001.cbz", "1")]
[InlineData("Adventure Time (2012)/Adventure Time #1 (2012)", "1")]
[InlineData("Adventure Time TPB (2012)/Adventure Time v01 (2012).cbz", API.Services.Tasks.Scanner.Parser.Parser.DefaultChapter)]
[InlineData("Adventure Time TPB (2012)/Adventure Time v01 (2012).cbz", Parser.DefaultChapter)]
[InlineData("Kebab Том 1 Глава 3", "3")]
[InlineData("Манга Глава 2", "2")]
[InlineData("Манга 2 Глава", "2")]
@ -179,35 +174,35 @@ public class ComicParsingTests
[InlineData("หนึ่งความคิด นิจนิรันดร์ บทที่ 112", "112")]
public void ParseComicChapterTest(string filename, string expected)
{
Assert.Equal(expected, API.Services.Tasks.Scanner.Parser.Parser.ParseChapter(filename, LibraryType.Comic));
Assert.Equal(expected, Parser.ParseChapter(filename, LibraryType.Comic));
}
[Theory]
[InlineData("Batman - Detective Comics - Rebirth Deluxe Edition Book 02 (2018) (digital) (Son of Ultron-Empire)", true)]
[InlineData("Zombie Tramp vs. Vampblade TPB (2016) (Digital) (TheArchivist-Empire)", true)]
[InlineData("Batman - Detective Comics - Rebirth Deluxe Edition Book 02 (2018) (digital) (Son of Ultron-Empire)", false)]
[InlineData("Zombie Tramp vs. Vampblade TPB (2016) (Digital) (TheArchivist-Empire)", false)]
[InlineData("Baldwin the Brave & Other Tales Special SP1.cbr", true)]
[InlineData("Mouse Guard Specials - Spring 1153 - Fraggle Rock FCBD 2010", true)]
[InlineData("Boule et Bill - THS -Bill à disparu", true)]
[InlineData("Asterix - HS - Les 12 travaux d'Astérix", true)]
[InlineData("Sillage Hors Série - Le Collectionneur - Concordance-DKFR", true)]
[InlineData("Mouse Guard Specials - Spring 1153 - Fraggle Rock FCBD 2010", false)]
[InlineData("Boule et Bill - THS -Bill à disparu", false)]
[InlineData("Asterix - HS - Les 12 travaux d'Astérix", false)]
[InlineData("Sillage Hors Série - Le Collectionneur - Concordance-DKFR", false)]
[InlineData("laughs", false)]
[InlineData("Annual Days of Summer", true)]
[InlineData("Adventure Time 2013 Annual #001 (2013)", true)]
[InlineData("Adventure Time 2013_Annual_#001 (2013)", true)]
[InlineData("Adventure Time 2013_-_Annual #001 (2013)", true)]
[InlineData("Annual Days of Summer", false)]
[InlineData("Adventure Time 2013 Annual #001 (2013)", false)]
[InlineData("Adventure Time 2013_Annual_#001 (2013)", false)]
[InlineData("Adventure Time 2013_-_Annual #001 (2013)", false)]
[InlineData("G.I. Joe - A Real American Hero Yearbook 004 Reprint (2021)", false)]
[InlineData("Mazebook 001", false)]
[InlineData("X-23 One Shot (2010)", true)]
[InlineData("Casus Belli v1 Hors-Série 21 - Mousquetaires et Sorcellerie", true)]
[InlineData("Batman Beyond Annual", true)]
[InlineData("Batman Beyond Bonus", true)]
[InlineData("Batman Beyond OneShot", true)]
[InlineData("Batman Beyond Specials", true)]
[InlineData("Batman Beyond Omnibus (1999)", true)]
[InlineData("Batman Beyond Omnibus", true)]
[InlineData("01 Annual Batman Beyond", true)]
[InlineData("Blood Syndicate Annual #001", true)]
[InlineData("X-23 One Shot (2010)", false)]
[InlineData("Casus Belli v1 Hors-Série 21 - Mousquetaires et Sorcellerie", false)]
[InlineData("Batman Beyond Annual", false)]
[InlineData("Batman Beyond Bonus", false)]
[InlineData("Batman Beyond OneShot", false)]
[InlineData("Batman Beyond Specials", false)]
[InlineData("Batman Beyond Omnibus (1999)", false)]
[InlineData("Batman Beyond Omnibus", false)]
[InlineData("01 Annual Batman Beyond", false)]
[InlineData("Blood Syndicate Annual #001", false)]
public void IsComicSpecialTest(string input, bool expected)
{
Assert.Equal(expected, Parser.IsSpecial(input, LibraryType.Comic));

View file

@ -326,18 +326,18 @@ public class MangaParsingTests
Assert.Equal(expected, API.Services.Tasks.Scanner.Parser.Parser.ParseEdition(input));
}
[Theory]
[InlineData("Beelzebub Special OneShot - Minna no Kochikame x Beelzebub (2016) [Mangastream].cbz", true)]
[InlineData("Beelzebub_Omake_June_2012_RHS", true)]
[InlineData("Beelzebub Special OneShot - Minna no Kochikame x Beelzebub (2016) [Mangastream].cbz", false)]
[InlineData("Beelzebub_Omake_June_2012_RHS", false)]
[InlineData("Beelzebub_Side_Story_02_RHS.zip", false)]
[InlineData("Darker than Black Shikkoku no Hana Special [Simple Scans].zip", true)]
[InlineData("Darker than Black Shikkoku no Hana Fanbook Extra [Simple Scans].zip", true)]
[InlineData("Corpse Party -The Anthology- Sachikos game of love Hysteric Birthday 2U Extra Chapter", true)]
[InlineData("Ani-Hina Art Collection.cbz", true)]
[InlineData("Gifting The Wonderful World With Blessings! - 3 Side Stories [yuNS][Unknown]", true)]
[InlineData("A Town Where You Live - Bonus Chapter.zip", true)]
[InlineData("Darker than Black Shikkoku no Hana Special [Simple Scans].zip", false)]
[InlineData("Darker than Black Shikkoku no Hana Fanbook Extra [Simple Scans].zip", false)]
[InlineData("Corpse Party -The Anthology- Sachikos game of love Hysteric Birthday 2U Extra Chapter", false)]
[InlineData("Ani-Hina Art Collection.cbz", false)]
[InlineData("Gifting The Wonderful World With Blessings! - 3 Side Stories [yuNS][Unknown]", false)]
[InlineData("A Town Where You Live - Bonus Chapter.zip", false)]
[InlineData("Yuki Merry - 4-Komga Anthology", false)]
[InlineData("Beastars - SP01", false)]
[InlineData("Beastars SP01", false)]
[InlineData("Beastars - SP01", true)]
[InlineData("Beastars SP01", true)]
[InlineData("The League of Extraordinary Gentlemen", false)]
[InlineData("The League of Extra-ordinary Gentlemen", false)]
[InlineData("Dr. Ramune - Mysterious Disease Specialist v01 (2020) (Digital) (danke-Empire)", false)]

View file

@ -6,6 +6,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using API.Services;
using Kavita.Common.Helpers;
using Microsoft.Extensions.Logging;
using NSubstitute;
using Xunit;
@ -745,6 +746,12 @@ public class DirectoryServiceTests
[InlineData(new [] {"/manga"},
new [] {"/manga/Love Hina/Vol. 01.cbz", "/manga/Love Hina/Specials/Sp01.cbz"},
"/manga/Love Hina")]
[InlineData(new [] {"/manga"},
new [] {"/manga/Love Hina/Hina/Vol. 01.cbz", "/manga/Love Hina/Specials/Sp01.cbz"},
"/manga/Love Hina")]
[InlineData(new [] {"/manga"},
new [] {"/manga/Dress Up Darling/Dress Up Darling Ch 01.cbz", "/manga/Dress Up Darling/Dress Up Darling/Dress Up Darling Vol 01.cbz"},
"/manga/Dress Up Darling")]
public void FindLowestDirectoriesFromFilesTest(string[] rootDirectories, string[] files, string expectedDirectory)
{
var fileSystem = new MockFileSystem();
@ -920,8 +927,9 @@ public class DirectoryServiceTests
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), fileSystem);
var allFiles = ds.ScanFiles("C:/Data/", API.Services.Tasks.Scanner.Parser.Parser.SupportedExtensions);
var globMatcher = new GlobMatcher();
globMatcher.AddExclude("*.*");
var allFiles = ds.ScanFiles("C:/Data/", API.Services.Tasks.Scanner.Parser.Parser.SupportedExtensions, globMatcher);
Assert.Empty(allFiles);
@ -945,7 +953,9 @@ public class DirectoryServiceTests
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), fileSystem);
var allFiles = ds.ScanFiles("C:/Data/", API.Services.Tasks.Scanner.Parser.Parser.SupportedExtensions);
var globMatcher = new GlobMatcher();
globMatcher.AddExclude("**/Accel World/*");
var allFiles = ds.ScanFiles("C:/Data/", API.Services.Tasks.Scanner.Parser.Parser.SupportedExtensions, globMatcher);
Assert.Single(allFiles); // Ignore files are not counted in files, only valid extensions
@ -974,7 +984,10 @@ public class DirectoryServiceTests
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), fileSystem);
var allFiles = ds.ScanFiles("C:/Data/", API.Services.Tasks.Scanner.Parser.Parser.SupportedExtensions);
var globMatcher = new GlobMatcher();
globMatcher.AddExclude("**/Accel World/*");
globMatcher.AddExclude("**/ArtBooks/*");
var allFiles = ds.ScanFiles("C:/Data/", API.Services.Tasks.Scanner.Parser.Parser.SupportedExtensions, globMatcher);
Assert.Equal(2, allFiles.Count); // Ignore files are not counted in files, only valid extensions

View file

@ -206,24 +206,6 @@ public class ParseScannedFilesTests : AbstractDbTest
var psf = new ParseScannedFiles(Substitute.For<ILogger<ParseScannedFiles>>(), ds,
new MockReadingItemService(ds, Substitute.For<IBookService>()), Substitute.For<IEventHub>());
// var parsedSeries = new Dictionary<ParsedSeries, IList<ParserInfo>>();
//
// Task TrackFiles(Tuple<bool, IList<ParserInfo>> parsedInfo)
// {
// var skippedScan = parsedInfo.Item1;
// var parsedFiles = parsedInfo.Item2;
// if (parsedFiles.Count == 0) return Task.CompletedTask;
//
// var foundParsedSeries = new ParsedSeries()
// {
// Name = parsedFiles.First().Series,
// NormalizedName = parsedFiles.First().Series.ToNormalized(),
// Format = parsedFiles.First().Format
// };
//
// parsedSeries.Add(foundParsedSeries, parsedFiles);
// return Task.CompletedTask;
// }
var library =
await _unitOfWork.LibraryRepository.GetLibraryForIdAsync(1,
@ -273,7 +255,7 @@ public class ParseScannedFilesTests : AbstractDbTest
var directoriesSeen = new HashSet<string>();
var library = await _unitOfWork.LibraryRepository.GetLibraryForIdAsync(1,
LibraryIncludes.Folders | LibraryIncludes.FileTypes);
var scanResults = await psf.ProcessFiles("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);
@ -295,7 +277,7 @@ public class ParseScannedFilesTests : AbstractDbTest
Assert.NotNull(library);
var directoriesSeen = new HashSet<string>();
var scanResults = await psf.ProcessFiles("C:/Data/", false,
var scanResults = await psf.ScanFiles("C:/Data/", false,
await _unitOfWork.SeriesRepository.GetFolderPathMap(1), library);
foreach (var scanResult in scanResults)
@ -328,7 +310,7 @@ public class ParseScannedFilesTests : AbstractDbTest
var library = await _unitOfWork.LibraryRepository.GetLibraryForIdAsync(1,
LibraryIncludes.Folders | LibraryIncludes.FileTypes);
Assert.NotNull(library);
var scanResults = await psf.ProcessFiles("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);
}
@ -357,7 +339,7 @@ public class ParseScannedFilesTests : AbstractDbTest
var library = await _unitOfWork.LibraryRepository.GetLibraryForIdAsync(1,
LibraryIncludes.Folders | LibraryIncludes.FileTypes);
Assert.NotNull(library);
var scanResults = await psf.ProcessFiles("C:/Data", false,
var scanResults = await psf.ScanFiles("C:/Data", false,
await _unitOfWork.SeriesRepository.GetFolderPathMap(1), library);
Assert.Single(scanResults);

View file

@ -50,65 +50,14 @@ public class ScannerServiceTests : AbstractDbTest
await _context.SaveChangesAsync();
}
[Fact]
public void FindSeriesNotOnDisk_Should_Remove1()
{
var infos = new Dictionary<ParsedSeries, IList<ParserInfo>>();
ParserInfoFactory.AddToParsedInfo(infos, new ParserInfo() {Series = "Darker than Black", Volumes = "1", Format = MangaFormat.Archive});
//AddToParsedInfo(infos, new ParserInfo() {Series = "Darker than Black", Volumes = "1", Format = MangaFormat.Epub});
var existingSeries = new List<Series>
{
new SeriesBuilder("Darker Than Black")
.WithFormat(MangaFormat.Epub)
.WithVolume(new VolumeBuilder("1")
.WithName("1")
.Build())
.WithLocalizedName("Darker Than Black")
.Build()
};
Assert.Single(ScannerService.FindSeriesNotOnDisk(existingSeries, infos));
}
[Fact]
public void FindSeriesNotOnDisk_Should_RemoveNothing_Test()
{
var infos = new Dictionary<ParsedSeries, IList<ParserInfo>>();
ParserInfoFactory.AddToParsedInfo(infos, new ParserInfo() {Series = "Darker than Black", Format = MangaFormat.Archive});
ParserInfoFactory.AddToParsedInfo(infos, new ParserInfo() {Series = "Cage of Eden", Volumes = "1", Format = MangaFormat.Archive});
ParserInfoFactory.AddToParsedInfo(infos, new ParserInfo() {Series = "Cage of Eden", Volumes = "10", Format = MangaFormat.Archive});
var existingSeries = new List<Series>
{
new SeriesBuilder("Cage of Eden")
.WithFormat(MangaFormat.Archive)
.WithVolume(new VolumeBuilder("1")
.WithName("1")
.Build())
.WithLocalizedName("Darker Than Black")
.Build(),
new SeriesBuilder("Darker Than Black")
.WithFormat(MangaFormat.Archive)
.WithVolume(new VolumeBuilder("1")
.WithName("1")
.Build())
.WithLocalizedName("Darker Than Black")
.Build(),
};
Assert.Empty(ScannerService.FindSeriesNotOnDisk(existingSeries, infos));
}
[Fact]
public async Task ScanLibrary_ComicVine_PublisherFolder()
{
var testcase = "Publisher - ComicVine.json";
var postLib = await GenerateScannerData(testcase);
var library = await GenerateScannerData(testcase);
var scanner = CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await _unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Equal(4, postLib.Series.Count);
@ -118,18 +67,67 @@ public class ScannerServiceTests : AbstractDbTest
public async Task ScanLibrary_ShouldCombineNestedFolder()
{
var testcase = "Series and Series-Series Combined - Manga.json";
var postLib = await GenerateScannerData(testcase);
var library = await GenerateScannerData(testcase);
var scanner = CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await _unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Single(postLib.Series);
Assert.Single(postLib.Series);
Assert.Equal(2, postLib.Series.First().Volumes.Count);
}
[Fact]
public async Task ScanLibrary_FlatSeries()
{
var testcase = "Flat Series - Manga.json";
var library = await GenerateScannerData(testcase);
var scanner = CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await _unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Single(postLib.Series);
Assert.Equal(3, postLib.Series.First().Volumes.Count);
// TODO: Trigger a deletion of ch 10
}
[Fact]
public async Task ScanLibrary_FlatSeriesWithSpecialFolder()
{
var testcase = "Flat Series with Specials Folder - Manga.json";
var library = await GenerateScannerData(testcase);
var scanner = CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await _unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Single(postLib.Series);
Assert.Equal(4, postLib.Series.First().Volumes.Count);
Assert.NotNull(postLib.Series.First().Volumes.FirstOrDefault(v => v.Chapters.FirstOrDefault(c => c.IsSpecial) != null));
}
[Fact]
public async Task ScanLibrary_FlatSeriesWithSpecial()
{
const string testcase = "Flat Special - Manga.json";
var library = await GenerateScannerData(testcase);
var scanner = CreateServices();
await scanner.ScanLibrary(library.Id);
var postLib = await _unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
Assert.NotNull(postLib);
Assert.Single(postLib.Series);
Assert.Equal(3, postLib.Series.First().Volumes.Count);
Assert.NotNull(postLib.Series.First().Volumes.FirstOrDefault(v => v.Chapters.FirstOrDefault(c => c.IsSpecial) != null));
}
private async Task<Library> GenerateScannerData(string testcase)
{
var testDirectoryPath = await GenerateTestDirectory(Path.Join(_testcasesDirectory, testcase));
_testOutputHelper.WriteLine($"Test Directory Path: {testDirectoryPath}");
var (publisher, type) = SplitPublisherAndLibraryType(Path.GetFileNameWithoutExtension(testcase));
@ -145,25 +143,26 @@ public class ScannerServiceTests : AbstractDbTest
_unitOfWork.LibraryRepository.Add(library);
await _unitOfWork.CommitAsync();
return library;
}
private ScannerService CreateServices()
{
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), new FileSystem());
var mockReadingService = new MockReadingItemService(ds, Substitute.For<IBookService>());
var processSeries = new ProcessSeries(_unitOfWork, Substitute.For<ILogger<ProcessSeries>>(),
Substitute.For<IEventHub>(),
ds, Substitute.For<ICacheHelper>(), mockReadingService, Substitute.For<IFileService>(),
Substitute.For<IMetadataService>(),
Substitute.For<IWordCountAnalyzerService>(), Substitute.For<ICollectionTagService>(),
Substitute.For<IWordCountAnalyzerService>(),
Substitute.For<IReadingListService>(),
Substitute.For<IExternalMetadataService>(), new TagManagerService(_unitOfWork, Substitute.For<ILogger<TagManagerService>>()));
Substitute.For<IExternalMetadataService>());
var scanner = new ScannerService(_unitOfWork, Substitute.For<ILogger<ScannerService>>(),
Substitute.For<IMetadataService>(),
Substitute.For<ICacheService>(), Substitute.For<IEventHub>(), ds,
mockReadingService, processSeries, Substitute.For<IWordCountAnalyzerService>());
await scanner.ScanLibrary(library.Id);
var postLib = await _unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
return postLib;
return scanner;
}
private static (string Publisher, LibraryType Type) SplitPublisherAndLibraryType(string input)
@ -209,6 +208,8 @@ public class ScannerServiceTests : AbstractDbTest
// Generate the files and folders
await Scaffold(testDirectory, filePaths);
_testOutputHelper.WriteLine($"Test Directory Path: {testDirectory}");
return testDirectory;
}

View file

@ -817,12 +817,17 @@ public class SeriesServiceTests : AbstractDbTest
public async Task UpdateSeriesMetadata_ShouldAddNewPerson_NoExistingPeople()
{
await ResetDb();
var g = new PersonBuilder("Existing Person").Build();
await _context.SaveChangesAsync();
var s = new SeriesBuilder("Test")
.WithMetadata(new SeriesMetadataBuilder().Build())
.WithMetadata(new SeriesMetadataBuilder()
.WithPerson(g, PersonRole.Publisher)
.Build())
.Build();
s.Library = new LibraryBuilder("Test LIb", LibraryType.Book).Build();
var g = new PersonBuilder("Existing Person", PersonRole.Publisher).Build();
_context.Series.Add(s);
_context.Person.Add(g);
@ -833,7 +838,7 @@ public class SeriesServiceTests : AbstractDbTest
SeriesMetadata = new SeriesMetadataDto
{
SeriesId = 1,
Publishers = new List<PersonDto> {new () {Id = 0, Name = "Existing Person", Role = PersonRole.Publisher}},
Publishers = new List<PersonDto> {new () {Id = 0, Name = "Existing Person"}},
},
});
@ -842,7 +847,7 @@ public class SeriesServiceTests : AbstractDbTest
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(1);
Assert.NotNull(series.Metadata);
Assert.True(series.Metadata.People.Select(g => g.Name).All(g => g == "Existing Person"));
Assert.True(series.Metadata.People.Select(g => g.Person.Name).All(personName => personName == "Existing Person"));
Assert.False(series.Metadata.PublisherLocked); // PublisherLocked is false unless the UI Explicitly says it should be locked
}
@ -854,10 +859,14 @@ public class SeriesServiceTests : AbstractDbTest
.WithMetadata(new SeriesMetadataBuilder().Build())
.Build();
s.Library = new LibraryBuilder("Test LIb", LibraryType.Book).Build();
var g = new PersonBuilder("Existing Person", PersonRole.Publisher).Build();
s.Metadata.People = new List<Person>
{new PersonBuilder("Existing Writer", PersonRole.Writer).Build(),
new PersonBuilder("Existing Translator", PersonRole.Translator).Build(), new PersonBuilder("Existing Publisher 2", PersonRole.Publisher).Build()};
var g = new PersonBuilder("Existing Person").Build();
s.Metadata.People = new List<SeriesMetadataPeople>
{
new SeriesMetadataPeople() {Person = new PersonBuilder("Existing Writer").Build(), Role = PersonRole.Writer},
new SeriesMetadataPeople() {Person = new PersonBuilder("Existing Translator").Build(), Role = PersonRole.Translator},
new SeriesMetadataPeople() {Person = new PersonBuilder("Existing Publisher 2").Build(), Role = PersonRole.Publisher}
};
_context.Series.Add(s);
_context.Person.Add(g);
@ -868,7 +877,7 @@ public class SeriesServiceTests : AbstractDbTest
SeriesMetadata = new SeriesMetadataDto
{
SeriesId = 1,
Publishers = new List<PersonDto> {new () {Id = 0, Name = "Existing Person", Role = PersonRole.Publisher}},
Publishers = new List<PersonDto> {new () {Id = 0, Name = "Existing Person"}},
PublisherLocked = true
},
@ -878,7 +887,7 @@ public class SeriesServiceTests : AbstractDbTest
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(1);
Assert.NotNull(series.Metadata);
Assert.True(series.Metadata.People.Select(g => g.Name).All(g => g == "Existing Person"));
Assert.True(series.Metadata.People.Select(g => g.Person.Name).All(personName => personName == "Existing Person"));
Assert.True(series.Metadata.PublisherLocked);
}
@ -891,7 +900,7 @@ public class SeriesServiceTests : AbstractDbTest
.WithMetadata(new SeriesMetadataBuilder().Build())
.Build();
s.Library = new LibraryBuilder("Test LIb", LibraryType.Book).Build();
var g = new PersonBuilder("Existing Person", PersonRole.Publisher).Build();
var g = new PersonBuilder("Existing Person").Build();
_context.Series.Add(s);
_context.Person.Add(g);

View file

@ -0,0 +1,5 @@
[
"My Dress-Up Darling/My Dress-Up Darling v01.cbz",
"My Dress-Up Darling/My Dress-Up Darling v02.cbz",
"My Dress-Up Darling/My Dress-Up Darling ch 10.cbz"
]

View file

@ -0,0 +1,6 @@
[
"My Dress-Up Darling/My Dress-Up Darling v01.cbz",
"My Dress-Up Darling/My Dress-Up Darling v02.cbz",
"My Dress-Up Darling/My Dress-Up Darling ch 10.cbz",
"My Dress-Up Darling/Specials/Official Anime Fanbook SP05 (2024) (Digital).cbz"
]

View file

@ -0,0 +1,5 @@
[
"Uzaki-chan Wants to Hang Out!\\Uzaki-chan Wants to Hang Out! - 2022 New Years Special SP01.cbz",
"Uzaki-chan Wants to Hang Out!\\Uzaki-chan Wants to Hang Out! - Ch. 103 - Kouhai and Control.cbz",
"Uzaki-chan Wants to Hang Out!\\Uzaki-chan Wants to Hang Out! v01 (2019) (Digital) (danke-Empire).cbz"
]

View file

@ -0,0 +1,4 @@
[
"My Dress-Up Darling/Chapter 1/01.cbz",
"My Dress-Up Darling/Chapter 2/02.cbz"
]