using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using API.Data; using API.Entities.Enums; using API.Helpers; using API.Helpers.Builders; using Polly; using Xunit; using Xunit.Abstractions; namespace API.Tests.Helpers; public class PersonHelperTests(ITestOutputHelper outputHelper): AbstractDbTest(outputHelper) { // 1. Test adding new people and keeping existing ones [Fact] public async Task UpdateChapterPeopleAsync_AddNewPeople_ExistingPersonRetained() { var (unitOfWork, context, mapper) = await CreateDatabase(); var library = new LibraryBuilder("My Library") .Build(); unitOfWork.LibraryRepository.Add(library); await unitOfWork.CommitAsync(); 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") .WithLibraryId(library.Id) .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 { "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 (unitOfWork, context, mapper) = await CreateDatabase(); var library = new LibraryBuilder("My Library") .Build(); unitOfWork.LibraryRepository.Add(library); await unitOfWork.CommitAsync(); var existingPerson1 = new PersonBuilder("Joe Shmo").Build(); var existingPerson2 = new PersonBuilder("Jane Doe").Build(); var chapter = new ChapterBuilder("1") .WithPerson(existingPerson1, PersonRole.Editor) .WithPerson(existingPerson2, PersonRole.Editor) .Build(); var series = new SeriesBuilder("Test 1") .WithLibraryId(library.Id) .WithVolume(new VolumeBuilder("1") .WithChapter(chapter) .Build()) .Build(); unitOfWork.SeriesRepository.Add(series); await unitOfWork.CommitAsync(); // Call UpdateChapterPeopleAsync with only one person await PersonHelper.UpdateChapterPeopleAsync(chapter, new List { "Joe Shmo" }, PersonRole.Editor, unitOfWork); // PersonHelper does not remove the Person from the global DbSet itself await unitOfWork.PersonRepository.RemoveAllPeopleNoLongerAssociated(); 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 (unitOfWork, context, mapper) = await CreateDatabase(); var library = new LibraryBuilder("My Library") .Build(); unitOfWork.LibraryRepository.Add(library); await unitOfWork.CommitAsync(); var existingPerson = new PersonBuilder("Joe Shmo").Build(); var chapter = new ChapterBuilder("1").WithPerson(existingPerson, PersonRole.Editor).Build(); var series = new SeriesBuilder("Test 1") .WithLibraryId(library.Id) .WithVolume(new VolumeBuilder("1") .WithChapter(chapter) .Build()) .Build(); unitOfWork.SeriesRepository.Add(series); await unitOfWork.CommitAsync(); // Call UpdateChapterPeopleAsync with the same list await PersonHelper.UpdateChapterPeopleAsync(chapter, new List { "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 (unitOfWork, context, mapper) = await CreateDatabase(); var library = new LibraryBuilder("My Library") .Build(); unitOfWork.LibraryRepository.Add(library); await unitOfWork.CommitAsync(); var person = new PersonBuilder("Joe Shmo").Build(); var chapter = new ChapterBuilder("1").WithPerson(person, PersonRole.Writer).Build(); var series = new SeriesBuilder("Test 1") .WithLibraryId(library.Id) .WithVolume(new VolumeBuilder("1") .WithChapter(chapter) .Build()) .Build(); unitOfWork.SeriesRepository.Add(series); await unitOfWork.CommitAsync(); // Add same person as Editor await PersonHelper.UpdateChapterPeopleAsync(chapter, new List { "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); } [Fact] public async Task UpdateChapterPeopleAsync_MatchOnAlias_NoChanges() { var (unitOfWork, context, mapper) = await CreateDatabase(); var library = new LibraryBuilder("My Library") .Build(); unitOfWork.LibraryRepository.Add(library); await unitOfWork.CommitAsync(); var person = new PersonBuilder("Joe Doe") .WithAlias("Jonny Doe") .Build(); var chapter = new ChapterBuilder("1") .WithPerson(person, PersonRole.Editor) .Build(); var series = new SeriesBuilder("Test 1") .WithLibraryId(library.Id) .WithVolume(new VolumeBuilder("1") .WithChapter(chapter) .Build()) .Build(); unitOfWork.SeriesRepository.Add(series); await unitOfWork.CommitAsync(); // Add on Name await PersonHelper.UpdateChapterPeopleAsync(chapter, new List { "Joe Doe" }, PersonRole.Editor, unitOfWork); await unitOfWork.CommitAsync(); var allPeople = await unitOfWork.PersonRepository.GetAllPeople(); Assert.Single(allPeople); // Add on alias await PersonHelper.UpdateChapterPeopleAsync(chapter, new List { "Jonny Doe" }, PersonRole.Editor, unitOfWork); await unitOfWork.CommitAsync(); allPeople = await unitOfWork.PersonRepository.GetAllPeople(); Assert.Single(allPeople); } // TODO: Unit tests for series }