Release Polish (#1586)

* Fixed a scaling issue in the epub reader, where images could scale when they shouldn't.

* Removed some caching on library/ api and added more output for a foreign key constraint

* Hooked in Restricted Profile stat collection

* Added a new boolean on age restrictions to explicitly allow unknowns or not. Since unknown is the default state of metadata, if users are allowed access to Unknown, age restricted content could leak.

* Fixed a bug where sometimes series cover generation could fail under conditions where only specials existed.

* Fixed foreign constraint issue when cleaning up series not seen at end of scan loop

* Removed an additional epub parse when scanning and handled merging differently

* Code smell
This commit is contained in:
Joe Milazzo 2022-10-17 15:33:18 -07:00 committed by GitHub
parent 78762a5626
commit 9149c4cbca
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
46 changed files with 2504 additions and 145 deletions

View file

@ -1,4 +1,7 @@
using System.Linq;
using System.Collections.Generic;
using System.Linq;
using API.Data.Misc;
using API.Entities.Enums;
using API.Extensions;
using Xunit;
@ -132,4 +135,33 @@ public class EnumerableExtensionsTests
i++;
}
}
[Theory]
[InlineData(true, 2)]
[InlineData(false, 1)]
public void RestrictAgainstAgeRestriction_ShouldRestrictEverythingAboveTeen(bool includeUnknowns, int expectedCount)
{
var items = new List<RecentlyAddedSeries>()
{
new RecentlyAddedSeries()
{
AgeRating = AgeRating.Teen,
},
new RecentlyAddedSeries()
{
AgeRating = AgeRating.Unknown,
},
new RecentlyAddedSeries()
{
AgeRating = AgeRating.X18Plus,
},
};
var filtered = items.RestrictAgainstAgeRestriction(new AgeRestriction()
{
AgeRating = AgeRating.Teen,
IncludeUnknowns = includeUnknowns
});
Assert.Equal(expectedCount, filtered.Count());
}
}

View file

@ -0,0 +1,131 @@
using System.Collections.Generic;
using System.Linq;
using API.Data.Misc;
using API.Entities;
using API.Entities.Enums;
using API.Entities.Metadata;
using API.Extensions;
using Xunit;
namespace API.Tests.Extensions;
public class QueryableExtensionsTests
{
[Theory]
[InlineData(true, 2)]
[InlineData(false, 1)]
public void RestrictAgainstAgeRestriction_Series_ShouldRestrictEverythingAboveTeen(bool includeUnknowns, int expectedCount)
{
var items = new List<Series>()
{
new Series()
{
Metadata = new SeriesMetadata()
{
AgeRating = AgeRating.Teen,
}
},
new Series()
{
Metadata = new SeriesMetadata()
{
AgeRating = AgeRating.Unknown,
}
},
new Series()
{
Metadata = new SeriesMetadata()
{
AgeRating = AgeRating.X18Plus,
}
},
};
var filtered = items.AsQueryable().RestrictAgainstAgeRestriction(new AgeRestriction()
{
AgeRating = AgeRating.Teen,
IncludeUnknowns = includeUnknowns
});
Assert.Equal(expectedCount, filtered.Count());
}
[Theory]
[InlineData(true, 2)]
[InlineData(false, 1)]
public void RestrictAgainstAgeRestriction_CollectionTag_ShouldRestrictEverythingAboveTeen(bool includeUnknowns, int expectedCount)
{
var items = new List<CollectionTag>()
{
new CollectionTag()
{
SeriesMetadatas = new List<SeriesMetadata>()
{
new SeriesMetadata()
{
AgeRating = AgeRating.Teen,
}
}
},
new CollectionTag()
{
SeriesMetadatas = new List<SeriesMetadata>()
{
new SeriesMetadata()
{
AgeRating = AgeRating.Unknown,
},
new SeriesMetadata()
{
AgeRating = AgeRating.Teen,
}
}
},
new CollectionTag()
{
SeriesMetadatas = new List<SeriesMetadata>()
{
new SeriesMetadata()
{
AgeRating = AgeRating.X18Plus,
}
}
},
};
var filtered = items.AsQueryable().RestrictAgainstAgeRestriction(new AgeRestriction()
{
AgeRating = AgeRating.Teen,
IncludeUnknowns = includeUnknowns
});
Assert.Equal(expectedCount, filtered.Count());
}
[Theory]
[InlineData(true, 2)]
[InlineData(false, 1)]
public void RestrictAgainstAgeRestriction_ReadingList_ShouldRestrictEverythingAboveTeen(bool includeUnknowns, int expectedCount)
{
var items = new List<ReadingList>()
{
new ReadingList()
{
AgeRating = AgeRating.Teen,
},
new ReadingList()
{
AgeRating = AgeRating.Unknown,
},
new ReadingList()
{
AgeRating = AgeRating.X18Plus
},
};
var filtered = items.AsQueryable().RestrictAgainstAgeRestriction(new AgeRestriction()
{
AgeRating = AgeRating.Teen,
IncludeUnknowns = includeUnknowns
});
Assert.Equal(expectedCount, filtered.Count());
}
}

View file

@ -1,4 +1,6 @@
using System.Linq;
using System.Collections.Generic;
using System.Linq;
using API.Comparators;
using API.Entities;
using API.Entities.Enums;
using API.Entities.Metadata;
@ -81,11 +83,286 @@ public class SeriesExtensionsTests
NormalizedName = seriesInput.Length == 4 ? seriesInput[3] : API.Services.Tasks.Scanner.Parser.Parser.Normalize(seriesInput[0]),
Metadata = new SeriesMetadata()
};
var info = new ParserInfo();
info.Series = parserSeries;
var info = new ParserInfo
{
Series = parserSeries
};
Assert.Equal(expected, series.NameInParserInfo(info));
}
[Fact]
public void GetCoverImage_MultipleSpecials_Comics()
{
var series = new Series()
{
Format = MangaFormat.Archive,
Volumes = new List<Volume>()
{
new Volume()
{
Number = 0,
Name = API.Services.Tasks.Scanner.Parser.Parser.DefaultVolume,
Chapters = new List<Chapter>()
{
new Chapter()
{
IsSpecial = true,
Number = API.Services.Tasks.Scanner.Parser.Parser.DefaultChapter,
CoverImage = "Special 1",
},
new Chapter()
{
IsSpecial = true,
Number = API.Services.Tasks.Scanner.Parser.Parser.DefaultChapter,
CoverImage = "Special 2",
}
},
}
}
};
Assert.Equal("Special 1", series.GetCoverImage());
}
[Fact]
public void GetCoverImage_MultipleSpecials_Books()
{
var series = new Series()
{
Format = MangaFormat.Epub,
Volumes = new List<Volume>()
{
new Volume()
{
Number = 0,
Name = API.Services.Tasks.Scanner.Parser.Parser.DefaultVolume,
Chapters = new List<Chapter>()
{
new Chapter()
{
IsSpecial = true,
Number = API.Services.Tasks.Scanner.Parser.Parser.DefaultChapter,
CoverImage = "Special 1",
},
new Chapter()
{
IsSpecial = true,
Number = API.Services.Tasks.Scanner.Parser.Parser.DefaultChapter,
CoverImage = "Special 2",
}
},
}
}
};
Assert.Equal("Special 1", series.GetCoverImage());
}
[Fact]
public void GetCoverImage_JustChapters_Comics()
{
var series = new Series()
{
Format = MangaFormat.Archive,
Volumes = new List<Volume>()
{
new Volume()
{
Number = 0,
Name = API.Services.Tasks.Scanner.Parser.Parser.DefaultVolume,
Chapters = new List<Chapter>()
{
new Chapter()
{
IsSpecial = false,
Number = "2.5",
CoverImage = "Special 1",
},
new Chapter()
{
IsSpecial = false,
Number = "2",
CoverImage = "Special 2",
}
},
}
}
};
foreach (var vol in series.Volumes)
{
vol.CoverImage = vol.Chapters.MinBy(x => double.Parse(x.Number), ChapterSortComparerZeroFirst.Default)?.CoverImage;
}
Assert.Equal("Special 2", series.GetCoverImage());
}
[Fact]
public void GetCoverImage_JustChaptersAndSpecials_Comics()
{
var series = new Series()
{
Format = MangaFormat.Archive,
Volumes = new List<Volume>()
{
new Volume()
{
Number = 0,
Name = API.Services.Tasks.Scanner.Parser.Parser.DefaultVolume,
Chapters = new List<Chapter>()
{
new Chapter()
{
IsSpecial = false,
Number = "2.5",
CoverImage = "Special 1",
},
new Chapter()
{
IsSpecial = false,
Number = "2",
CoverImage = "Special 2",
},
new Chapter()
{
IsSpecial = true,
Number = API.Services.Tasks.Scanner.Parser.Parser.DefaultChapter,
CoverImage = "Special 3",
}
},
}
}
};
foreach (var vol in series.Volumes)
{
vol.CoverImage = vol.Chapters.MinBy(x => double.Parse(x.Number), ChapterSortComparerZeroFirst.Default)?.CoverImage;
}
Assert.Equal("Special 2", series.GetCoverImage());
}
[Fact]
public void GetCoverImage_VolumesChapters_Comics()
{
var series = new Series()
{
Format = MangaFormat.Archive,
Volumes = new List<Volume>()
{
new Volume()
{
Number = 0,
Name = API.Services.Tasks.Scanner.Parser.Parser.DefaultVolume,
Chapters = new List<Chapter>()
{
new Chapter()
{
IsSpecial = false,
Number = "2.5",
CoverImage = "Special 1",
},
new Chapter()
{
IsSpecial = false,
Number = "2",
CoverImage = "Special 2",
},
new Chapter()
{
IsSpecial = true,
Number = API.Services.Tasks.Scanner.Parser.Parser.DefaultChapter,
CoverImage = "Special 3",
}
},
},
new Volume()
{
Number = 1,
Name = "1",
Chapters = new List<Chapter>()
{
new Chapter()
{
IsSpecial = false,
Number = "0",
CoverImage = "Volume 1",
},
},
}
}
};
foreach (var vol in series.Volumes)
{
vol.CoverImage = vol.Chapters.MinBy(x => double.Parse(x.Number), ChapterSortComparerZeroFirst.Default)?.CoverImage;
}
Assert.Equal("Volume 1", series.GetCoverImage());
}
[Fact]
public void GetCoverImage_VolumesChaptersAndSpecials_Comics()
{
var series = new Series()
{
Format = MangaFormat.Archive,
Volumes = new List<Volume>()
{
new Volume()
{
Number = 0,
Name = API.Services.Tasks.Scanner.Parser.Parser.DefaultVolume,
Chapters = new List<Chapter>()
{
new Chapter()
{
IsSpecial = false,
Number = "2.5",
CoverImage = "Special 1",
},
new Chapter()
{
IsSpecial = false,
Number = "2",
CoverImage = "Special 2",
},
new Chapter()
{
IsSpecial = true,
Number = API.Services.Tasks.Scanner.Parser.Parser.DefaultChapter,
CoverImage = "Special 3",
}
},
},
new Volume()
{
Number = 1,
Name = "1",
Chapters = new List<Chapter>()
{
new Chapter()
{
IsSpecial = false,
Number = "0",
CoverImage = "Volume 1",
},
},
}
}
};
foreach (var vol in series.Volumes)
{
vol.CoverImage = vol.Chapters.MinBy(x => double.Parse(x.Number), ChapterSortComparerZeroFirst.Default)?.CoverImage;
}
Assert.Equal("Volume 1", series.GetCoverImage());
}
}