Version Update Modal Rework + A few bugfixes (#3664)
This commit is contained in:
parent
9fb3bdd548
commit
43d0d1277f
65 changed files with 1963 additions and 805 deletions
|
@ -6,11 +6,11 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
|
||||
<PackageReference Include="NSubstitute" Version="5.3.0" />
|
||||
<PackageReference Include="System.IO.Abstractions.TestingHelpers" Version="22.0.11" />
|
||||
<PackageReference Include="TestableIO.System.IO.Abstractions.Wrappers" Version="22.0.11" />
|
||||
<PackageReference Include="System.IO.Abstractions.TestingHelpers" Version="22.0.12" />
|
||||
<PackageReference Include="TestableIO.System.IO.Abstractions.Wrappers" Version="22.0.12" />
|
||||
<PackageReference Include="xunit" Version="2.9.3" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.2">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
|
|
@ -108,4 +108,19 @@ public abstract class AbstractDbTest : AbstractFsTest , IDisposable
|
|||
_context.Dispose();
|
||||
_connection.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a role to an existing User. Commits.
|
||||
/// </summary>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="roleName"></param>
|
||||
protected async Task AddUserWithRole(int userId, string roleName)
|
||||
{
|
||||
var role = new AppRole { Id = userId, Name = roleName, NormalizedName = roleName.ToUpper() };
|
||||
|
||||
await _context.Roles.AddAsync(role);
|
||||
await _context.UserRoles.AddAsync(new AppUserRole { UserId = userId, RoleId = userId });
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
|
31
API.Tests/Extensions/EncodeFormatExtensionsTests.cs
Normal file
31
API.Tests/Extensions/EncodeFormatExtensionsTests.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using API.Entities.Enums;
|
||||
using API.Extensions;
|
||||
using Xunit;
|
||||
|
||||
namespace API.Tests.Extensions;
|
||||
|
||||
public class EncodeFormatExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void GetExtension_ShouldReturnCorrectExtensionForAllValues()
|
||||
{
|
||||
// Arrange
|
||||
var expectedExtensions = new Dictionary<EncodeFormat, string>
|
||||
{
|
||||
{ EncodeFormat.PNG, ".png" },
|
||||
{ EncodeFormat.WEBP, ".webp" },
|
||||
{ EncodeFormat.AVIF, ".avif" }
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
foreach (var format in Enum.GetValues(typeof(EncodeFormat)).Cast<EncodeFormat>())
|
||||
{
|
||||
var extension = format.GetExtension();
|
||||
Assert.Equal(expectedExtensions[format], extension);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
81
API.Tests/Extensions/VersionExtensionTests.cs
Normal file
81
API.Tests/Extensions/VersionExtensionTests.cs
Normal file
|
@ -0,0 +1,81 @@
|
|||
using System;
|
||||
using API.Extensions;
|
||||
using Xunit;
|
||||
|
||||
namespace API.Tests.Extensions;
|
||||
|
||||
public class VersionHelperTests
|
||||
{
|
||||
[Fact]
|
||||
public void CompareWithoutRevision_ShouldReturnTrue_WhenMajorMinorBuildMatch()
|
||||
{
|
||||
// Arrange
|
||||
var v1 = new Version(1, 2, 3, 4);
|
||||
var v2 = new Version(1, 2, 3, 5);
|
||||
|
||||
// Act
|
||||
var result = v1.CompareWithoutRevision(v2);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompareWithoutRevision_ShouldHandleBuildlessVersions()
|
||||
{
|
||||
// Arrange
|
||||
var v1 = new Version(1, 2);
|
||||
var v2 = new Version(1, 2);
|
||||
|
||||
// Act
|
||||
var result = v1.CompareWithoutRevision(v2);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(1, 2, 3, 1, 2, 4)]
|
||||
[InlineData(1, 2, 3, 1, 2, 0)]
|
||||
public void CompareWithoutRevision_ShouldReturnFalse_WhenBuildDiffers(
|
||||
int major1, int minor1, int build1,
|
||||
int major2, int minor2, int build2)
|
||||
{
|
||||
var v1 = new Version(major1, minor1, build1);
|
||||
var v2 = new Version(major2, minor2, build2);
|
||||
|
||||
var result = v1.CompareWithoutRevision(v2);
|
||||
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(1, 2, 3, 1, 3, 3)]
|
||||
[InlineData(1, 2, 3, 1, 0, 3)]
|
||||
public void CompareWithoutRevision_ShouldReturnFalse_WhenMinorDiffers(
|
||||
int major1, int minor1, int build1,
|
||||
int major2, int minor2, int build2)
|
||||
{
|
||||
var v1 = new Version(major1, minor1, build1);
|
||||
var v2 = new Version(major2, minor2, build2);
|
||||
|
||||
var result = v1.CompareWithoutRevision(v2);
|
||||
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(1, 2, 3, 2, 2, 3)]
|
||||
[InlineData(1, 2, 3, 0, 2, 3)]
|
||||
public void CompareWithoutRevision_ShouldReturnFalse_WhenMajorDiffers(
|
||||
int major1, int minor1, int build1,
|
||||
int major2, int minor2, int build2)
|
||||
{
|
||||
var v1 = new Version(major1, minor1, build1);
|
||||
var v2 = new Version(major2, minor2, build2);
|
||||
|
||||
var result = v1.CompareWithoutRevision(v2);
|
||||
|
||||
Assert.False(result);
|
||||
}
|
||||
}
|
258
API.Tests/Helpers/ReviewHelperTests.cs
Normal file
258
API.Tests/Helpers/ReviewHelperTests.cs
Normal file
|
@ -0,0 +1,258 @@
|
|||
using API.Helpers;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
using API.DTOs.SeriesDetail;
|
||||
|
||||
namespace API.Tests.Helpers;
|
||||
|
||||
public class ReviewHelperTests
|
||||
{
|
||||
#region SelectSpectrumOfReviews Tests
|
||||
|
||||
[Fact]
|
||||
public void SelectSpectrumOfReviews_WhenLessThan10Reviews_ReturnsAllReviews()
|
||||
{
|
||||
// Arrange
|
||||
var reviews = CreateReviewList(8);
|
||||
|
||||
// Act
|
||||
var result = ReviewHelper.SelectSpectrumOfReviews(reviews).ToList();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(8, result.Count);
|
||||
Assert.Equal(reviews, result.OrderByDescending(r => r.Score));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SelectSpectrumOfReviews_WhenMoreThan10Reviews_Returns10Reviews()
|
||||
{
|
||||
// Arrange
|
||||
var reviews = CreateReviewList(20);
|
||||
|
||||
// Act
|
||||
var result = ReviewHelper.SelectSpectrumOfReviews(reviews).ToList();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(10, result.Count);
|
||||
Assert.Equal(reviews[0], result.First());
|
||||
Assert.Equal(reviews[19], result.Last());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SelectSpectrumOfReviews_WithExactly10Reviews_ReturnsAllReviews()
|
||||
{
|
||||
// Arrange
|
||||
var reviews = CreateReviewList(10);
|
||||
|
||||
// Act
|
||||
var result = ReviewHelper.SelectSpectrumOfReviews(reviews).ToList();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(10, result.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SelectSpectrumOfReviews_WithLargeNumberOfReviews_ReturnsCorrectSpectrum()
|
||||
{
|
||||
// Arrange
|
||||
var reviews = CreateReviewList(100);
|
||||
|
||||
// Act
|
||||
var result = ReviewHelper.SelectSpectrumOfReviews(reviews).ToList();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(10, result.Count);
|
||||
Assert.Contains(reviews[0], result);
|
||||
Assert.Contains(reviews[1], result);
|
||||
Assert.Contains(reviews[98], result);
|
||||
Assert.Contains(reviews[99], result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SelectSpectrumOfReviews_WithEmptyList_ReturnsEmptyList()
|
||||
{
|
||||
// Arrange
|
||||
var reviews = new List<UserReviewDto>();
|
||||
|
||||
// Act
|
||||
var result = ReviewHelper.SelectSpectrumOfReviews(reviews).ToList();
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SelectSpectrumOfReviews_ResultsOrderedByScoreDescending()
|
||||
{
|
||||
// Arrange
|
||||
var reviews = new List<UserReviewDto>
|
||||
{
|
||||
new UserReviewDto { Tagline = "1", Score = 3 },
|
||||
new UserReviewDto { Tagline = "2", Score = 5 },
|
||||
new UserReviewDto { Tagline = "3", Score = 1 },
|
||||
new UserReviewDto { Tagline = "4", Score = 4 },
|
||||
new UserReviewDto { Tagline = "5", Score = 2 }
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = ReviewHelper.SelectSpectrumOfReviews(reviews).ToList();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(5, result.Count);
|
||||
Assert.Equal(5, result[0].Score);
|
||||
Assert.Equal(4, result[1].Score);
|
||||
Assert.Equal(3, result[2].Score);
|
||||
Assert.Equal(2, result[3].Score);
|
||||
Assert.Equal(1, result[4].Score);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetCharacters Tests
|
||||
|
||||
[Fact]
|
||||
public void GetCharacters_WithNullBody_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
string body = null;
|
||||
|
||||
// Act
|
||||
var result = ReviewHelper.GetCharacters(body);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetCharacters_WithEmptyBody_ReturnsEmptyString()
|
||||
{
|
||||
// Arrange
|
||||
var body = string.Empty;
|
||||
|
||||
// Act
|
||||
var result = ReviewHelper.GetCharacters(body);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(string.Empty, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetCharacters_WithNoTextNodes_ReturnsEmptyString()
|
||||
{
|
||||
// Arrange
|
||||
const string body = "<div></div>";
|
||||
|
||||
// Act
|
||||
var result = ReviewHelper.GetCharacters(body);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(string.Empty, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetCharacters_WithLessCharactersThanLimit_ReturnsFullText()
|
||||
{
|
||||
// Arrange
|
||||
var body = "<p>This is a short review.</p>";
|
||||
|
||||
// Act
|
||||
var result = ReviewHelper.GetCharacters(body);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("This is a short review.…", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetCharacters_WithMoreCharactersThanLimit_TruncatesText()
|
||||
{
|
||||
// Arrange
|
||||
var body = "<p>" + new string('a', 200) + "</p>";
|
||||
|
||||
// Act
|
||||
var result = ReviewHelper.GetCharacters(body);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new string('a', 175) + "…", result);
|
||||
Assert.Equal(176, result.Length); // 175 characters + ellipsis
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetCharacters_IgnoresScriptTags()
|
||||
{
|
||||
// Arrange
|
||||
const string body = "<p>Visible text</p><script>console.log('hidden');</script>";
|
||||
|
||||
// Act
|
||||
var result = ReviewHelper.GetCharacters(body);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("Visible text…", result);
|
||||
Assert.DoesNotContain("hidden", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetCharacters_RemovesMarkdownSymbols()
|
||||
{
|
||||
// Arrange
|
||||
const string body = "<p>This is **bold** and _italic_ text with [link](url).</p>";
|
||||
|
||||
// Act
|
||||
var result = ReviewHelper.GetCharacters(body);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("This is bold and italic text with link.…", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetCharacters_HandlesComplexMarkdownAndHtml()
|
||||
{
|
||||
// Arrange
|
||||
const string body = """
|
||||
|
||||
<div>
|
||||
<h1># Header</h1>
|
||||
<p>This is ~~strikethrough~~ and __underlined__ text</p>
|
||||
<p>~~~code block~~~</p>
|
||||
<p>+++highlighted+++</p>
|
||||
<p>img123(image.jpg)</p>
|
||||
</div>
|
||||
""";
|
||||
|
||||
// Act
|
||||
var result = ReviewHelper.GetCharacters(body);
|
||||
|
||||
// Assert
|
||||
Assert.DoesNotContain("~~", result);
|
||||
Assert.DoesNotContain("__", result);
|
||||
Assert.DoesNotContain("~~~", result);
|
||||
Assert.DoesNotContain("+++", result);
|
||||
Assert.DoesNotContain("img123(", result);
|
||||
Assert.Contains("Header", result);
|
||||
Assert.Contains("strikethrough", result);
|
||||
Assert.Contains("underlined", result);
|
||||
Assert.Contains("code block", result);
|
||||
Assert.Contains("highlighted", result);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helper Methods
|
||||
|
||||
private static List<UserReviewDto> CreateReviewList(int count)
|
||||
{
|
||||
var reviews = new List<UserReviewDto>();
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
reviews.Add(new UserReviewDto
|
||||
{
|
||||
Tagline = $"{i + 1}",
|
||||
Score = count - i // This makes them ordered by score descending initially
|
||||
});
|
||||
}
|
||||
return reviews;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
using System.IO.Abstractions.TestingHelpers;
|
||||
using System.IO;
|
||||
using System.IO.Abstractions.TestingHelpers;
|
||||
using API.Entities.Enums;
|
||||
using API.Services;
|
||||
using API.Services.Tasks.Scanner.Parser;
|
||||
|
@ -8,59 +9,54 @@ using Xunit;
|
|||
|
||||
namespace API.Tests.Parsers;
|
||||
|
||||
public class BasicParserTests
|
||||
public class BasicParserTests : AbstractFsTest
|
||||
{
|
||||
private readonly BasicParser _parser;
|
||||
private readonly ILogger<DirectoryService> _dsLogger = Substitute.For<ILogger<DirectoryService>>();
|
||||
private const string RootDirectory = "C:/Books/";
|
||||
private readonly string _rootDirectory;
|
||||
|
||||
public BasicParserTests()
|
||||
{
|
||||
var fileSystem = new MockFileSystem();
|
||||
fileSystem.AddDirectory("C:/Books/");
|
||||
fileSystem.AddFile("C:/Books/Harry Potter/Harry Potter - Vol 1.epub", new MockFileData(""));
|
||||
var fileSystem = CreateFileSystem();
|
||||
_rootDirectory = Path.Join(DataDirectory, "Books/");
|
||||
fileSystem.AddDirectory(_rootDirectory);
|
||||
fileSystem.AddFile($"{_rootDirectory}Harry Potter/Harry Potter - Vol 1.epub", new MockFileData(""));
|
||||
|
||||
fileSystem.AddFile("C:/Books/Accel World/Accel World - Volume 1.cbz", new MockFileData(""));
|
||||
fileSystem.AddFile("C:/Books/Accel World/Accel World - Volume 1 Chapter 2.cbz", new MockFileData(""));
|
||||
fileSystem.AddFile("C:/Books/Accel World/Accel World - Chapter 3.cbz", new MockFileData(""));
|
||||
fileSystem.AddFile("C:/Books/Accel World/Accel World Gaiden SP01.cbz", new MockFileData(""));
|
||||
fileSystem.AddFile($"{_rootDirectory}Accel World/Accel World - Volume 1.cbz", new MockFileData(""));
|
||||
fileSystem.AddFile($"{_rootDirectory}Accel World/Accel World - Volume 1 Chapter 2.cbz", new MockFileData(""));
|
||||
fileSystem.AddFile($"{_rootDirectory}Accel World/Accel World - Chapter 3.cbz", new MockFileData(""));
|
||||
fileSystem.AddFile("$\"{RootDirectory}Accel World/Accel World Gaiden SP01.cbz", new MockFileData(""));
|
||||
|
||||
|
||||
fileSystem.AddFile("C:/Books/Accel World/cover.png", new MockFileData(""));
|
||||
fileSystem.AddFile($"{_rootDirectory}Accel World/cover.png", new MockFileData(""));
|
||||
|
||||
fileSystem.AddFile("C:/Books/Batman/Batman #1.cbz", new MockFileData(""));
|
||||
fileSystem.AddFile($"{_rootDirectory}Batman/Batman #1.cbz", new MockFileData(""));
|
||||
|
||||
var ds = new DirectoryService(_dsLogger, fileSystem);
|
||||
_parser = new BasicParser(ds, new ImageParser(ds));
|
||||
}
|
||||
|
||||
#region Parse_Books
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Parse_Manga
|
||||
|
||||
/// <summary>
|
||||
/// Tests that when there is a loose leaf cover in the manga library, that it is ignored
|
||||
/// Tests that when there is a loose-leaf cover in the manga library, that it is ignored
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Parse_MangaLibrary_JustCover_ShouldReturnNull()
|
||||
{
|
||||
var actual = _parser.Parse(@"C:/Books/Accel World/cover.png", "C:/Books/Accel World/",
|
||||
RootDirectory, LibraryType.Manga, null);
|
||||
var actual = _parser.Parse($"{_rootDirectory}Accel World/cover.png", $"{_rootDirectory}Accel World/",
|
||||
_rootDirectory, LibraryType.Manga);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that when there is a loose leaf cover in the manga library, that it is ignored
|
||||
/// Tests that when there is a loose-leaf cover in the manga library, that it is ignored
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Parse_MangaLibrary_OtherImage_ShouldReturnNull()
|
||||
{
|
||||
var actual = _parser.Parse(@"C:/Books/Accel World/page 01.png", "C:/Books/Accel World/",
|
||||
RootDirectory, LibraryType.Manga, null);
|
||||
var actual = _parser.Parse($"{_rootDirectory}Accel World/page 01.png", $"{_rootDirectory}Accel World/",
|
||||
_rootDirectory, LibraryType.Manga);
|
||||
Assert.NotNull(actual);
|
||||
}
|
||||
|
||||
|
@ -70,8 +66,8 @@ public class BasicParserTests
|
|||
[Fact]
|
||||
public void Parse_MangaLibrary_VolumeAndChapterInFilename()
|
||||
{
|
||||
var actual = _parser.Parse("C:/Books/Mujaki no Rakuen/Mujaki no Rakuen Vol12 ch76.cbz", "C:/Books/Mujaki no Rakuen/",
|
||||
RootDirectory, LibraryType.Manga, null);
|
||||
var actual = _parser.Parse($"{_rootDirectory}Mujaki no Rakuen/Mujaki no Rakuen Vol12 ch76.cbz", $"{_rootDirectory}Mujaki no Rakuen/",
|
||||
_rootDirectory, LibraryType.Manga);
|
||||
Assert.NotNull(actual);
|
||||
|
||||
Assert.Equal("Mujaki no Rakuen", actual.Series);
|
||||
|
@ -86,9 +82,9 @@ public class BasicParserTests
|
|||
[Fact]
|
||||
public void Parse_MangaLibrary_JustVolumeInFilename()
|
||||
{
|
||||
var actual = _parser.Parse("C:/Books/Shimoneta to Iu Gainen ga Sonzai Shinai Taikutsu na Sekai Man-hen/Vol 1.cbz",
|
||||
"C:/Books/Shimoneta to Iu Gainen ga Sonzai Shinai Taikutsu na Sekai Man-hen/",
|
||||
RootDirectory, LibraryType.Manga, null);
|
||||
var actual = _parser.Parse($"{_rootDirectory}Shimoneta to Iu Gainen ga Sonzai Shinai Taikutsu na Sekai Man-hen/Vol 1.cbz",
|
||||
$"{_rootDirectory}Shimoneta to Iu Gainen ga Sonzai Shinai Taikutsu na Sekai Man-hen/",
|
||||
_rootDirectory, LibraryType.Manga);
|
||||
Assert.NotNull(actual);
|
||||
|
||||
Assert.Equal("Shimoneta to Iu Gainen ga Sonzai Shinai Taikutsu na Sekai Man-hen", actual.Series);
|
||||
|
@ -103,9 +99,9 @@ public class BasicParserTests
|
|||
[Fact]
|
||||
public void Parse_MangaLibrary_JustChapterInFilename()
|
||||
{
|
||||
var actual = _parser.Parse("C:/Books/Beelzebub/Beelzebub_01_[Noodles].zip",
|
||||
"C:/Books/Beelzebub/",
|
||||
RootDirectory, LibraryType.Manga, null);
|
||||
var actual = _parser.Parse($"{_rootDirectory}Beelzebub/Beelzebub_01_[Noodles].zip",
|
||||
$"{_rootDirectory}Beelzebub/",
|
||||
_rootDirectory, LibraryType.Manga);
|
||||
Assert.NotNull(actual);
|
||||
|
||||
Assert.Equal("Beelzebub", actual.Series);
|
||||
|
@ -120,9 +116,9 @@ public class BasicParserTests
|
|||
[Fact]
|
||||
public void Parse_MangaLibrary_SpecialMarkerInFilename()
|
||||
{
|
||||
var actual = _parser.Parse("C:/Books/Summer Time Rendering/Specials/Record 014 (between chapter 083 and ch084) SP11.cbr",
|
||||
"C:/Books/Summer Time Rendering/",
|
||||
RootDirectory, LibraryType.Manga, null);
|
||||
var actual = _parser.Parse($"{_rootDirectory}Summer Time Rendering/Specials/Record 014 (between chapter 083 and ch084) SP11.cbr",
|
||||
$"{_rootDirectory}Summer Time Rendering/",
|
||||
_rootDirectory, LibraryType.Manga);
|
||||
Assert.NotNull(actual);
|
||||
|
||||
Assert.Equal("Summer Time Rendering", actual.Series);
|
||||
|
@ -133,36 +129,54 @@ public class BasicParserTests
|
|||
|
||||
|
||||
/// <summary>
|
||||
/// Tests that when the filename parses as a speical, it appropriately parses
|
||||
/// Tests that when the filename parses as a special, it appropriately parses
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Parse_MangaLibrary_SpecialInFilename()
|
||||
{
|
||||
var actual = _parser.Parse("C:/Books/Summer Time Rendering/Volume SP01.cbr",
|
||||
"C:/Books/Summer Time Rendering/",
|
||||
RootDirectory, LibraryType.Manga, null);
|
||||
var actual = _parser.Parse($"{_rootDirectory}Summer Time Rendering/Volume SP01.cbr",
|
||||
$"{_rootDirectory}Summer Time Rendering/",
|
||||
_rootDirectory, LibraryType.Manga);
|
||||
Assert.NotNull(actual);
|
||||
|
||||
Assert.Equal("Summer Time Rendering", actual.Series);
|
||||
Assert.Equal("Volume SP01", actual.Title);
|
||||
Assert.Equal("Volume", 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
|
||||
/// Tests that when the filename parses as a special, 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);
|
||||
_rootDirectory, LibraryType.Manga);
|
||||
Assert.NotNull(actual);
|
||||
|
||||
Assert.Equal("Kimi wa Midara na Boku no Joou", actual.Series);
|
||||
Assert.Equal("[Renzokusei] Special 1 SP02", actual.Title);
|
||||
Assert.Equal("[Renzokusei] Special 1", 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 special, it appropriately parses
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Parse_MangaLibrary_SpecialInFilename_StrangeNaming()
|
||||
{
|
||||
var actual = _parser.Parse($"{_rootDirectory}My Dress-Up Darling/SP01 1. Special Name.cbz",
|
||||
_rootDirectory,
|
||||
_rootDirectory, LibraryType.Manga);
|
||||
Assert.NotNull(actual);
|
||||
|
||||
Assert.Equal("My Dress-Up Darling", actual.Series);
|
||||
Assert.Equal("1. Special Name", actual.Title);
|
||||
Assert.Equal(Parser.SpecialVolume, actual.Volumes);
|
||||
Assert.Equal(Parser.DefaultChapter, actual.Chapters);
|
||||
Assert.True(actual.IsSpecial);
|
||||
|
@ -174,9 +188,9 @@ public class BasicParserTests
|
|||
[Fact]
|
||||
public void Parse_MangaLibrary_EditionInFilename()
|
||||
{
|
||||
var actual = _parser.Parse("C:/Books/Air Gear/Air Gear Omnibus v01 (2016) (Digital) (Shadowcat-Empire).cbz",
|
||||
"C:/Books/Air Gear/",
|
||||
RootDirectory, LibraryType.Manga, null);
|
||||
var actual = _parser.Parse($"{_rootDirectory}Air Gear/Air Gear Omnibus v01 (2016) (Digital) (Shadowcat-Empire).cbz",
|
||||
$"{_rootDirectory}Air Gear/",
|
||||
_rootDirectory, LibraryType.Manga);
|
||||
Assert.NotNull(actual);
|
||||
|
||||
Assert.Equal("Air Gear", actual.Series);
|
||||
|
@ -195,9 +209,9 @@ public class BasicParserTests
|
|||
[Fact]
|
||||
public void Parse_MangaBooks_JustVolumeInFilename()
|
||||
{
|
||||
var actual = _parser.Parse("C:/Books/Epubs/Harrison, Kim - The Good, The Bad, and the Undead - Hollows Vol 2.5.epub",
|
||||
"C:/Books/Epubs/",
|
||||
RootDirectory, LibraryType.Manga, null);
|
||||
var actual = _parser.Parse($"{_rootDirectory}Epubs/Harrison, Kim - The Good, The Bad, and the Undead - Hollows Vol 2.5.epub",
|
||||
$"{_rootDirectory}Epubs/",
|
||||
_rootDirectory, LibraryType.Manga);
|
||||
Assert.NotNull(actual);
|
||||
|
||||
Assert.Equal("Harrison, Kim - The Good, The Bad, and the Undead - Hollows", actual.Series);
|
||||
|
|
|
@ -11,14 +11,14 @@ public class ParserInfoTests
|
|||
{
|
||||
var p1 = new ParserInfo()
|
||||
{
|
||||
Chapters = API.Services.Tasks.Scanner.Parser.Parser.DefaultChapter,
|
||||
Chapters = Parser.DefaultChapter,
|
||||
Edition = "",
|
||||
Format = MangaFormat.Archive,
|
||||
FullFilePath = "/manga/darker than black.cbz",
|
||||
IsSpecial = false,
|
||||
Series = "darker than black",
|
||||
Title = "darker than black",
|
||||
Volumes = API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume
|
||||
Volumes = Parser.LooseLeafVolume
|
||||
};
|
||||
|
||||
var p2 = new ParserInfo()
|
||||
|
@ -30,7 +30,7 @@ public class ParserInfoTests
|
|||
IsSpecial = false,
|
||||
Series = "darker than black",
|
||||
Title = "Darker Than Black",
|
||||
Volumes = API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume
|
||||
Volumes = Parser.LooseLeafVolume
|
||||
};
|
||||
|
||||
var expected = new ParserInfo()
|
||||
|
@ -42,7 +42,7 @@ public class ParserInfoTests
|
|||
IsSpecial = false,
|
||||
Series = "darker than black",
|
||||
Title = "darker than black",
|
||||
Volumes = API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume
|
||||
Volumes = Parser.LooseLeafVolume
|
||||
};
|
||||
p1.Merge(p2);
|
||||
|
||||
|
@ -62,12 +62,12 @@ public class ParserInfoTests
|
|||
IsSpecial = true,
|
||||
Series = "darker than black",
|
||||
Title = "darker than black",
|
||||
Volumes = API.Services.Tasks.Scanner.Parser.Parser.LooseLeafVolume
|
||||
Volumes = Parser.LooseLeafVolume
|
||||
};
|
||||
|
||||
var p2 = new ParserInfo()
|
||||
{
|
||||
Chapters = API.Services.Tasks.Scanner.Parser.Parser.DefaultChapter,
|
||||
Chapters = Parser.DefaultChapter,
|
||||
Edition = "",
|
||||
Format = MangaFormat.Archive,
|
||||
FullFilePath = "/manga/darker than black.cbz",
|
||||
|
|
|
@ -44,6 +44,7 @@ public class ParsingTests
|
|||
[InlineData("DEAD Tube Prologue", "DEAD Tube Prologue")]
|
||||
[InlineData("DEAD Tube Prologue SP01", "DEAD Tube Prologue")]
|
||||
[InlineData("DEAD_Tube_Prologue SP01", "DEAD Tube Prologue")]
|
||||
[InlineData("SP01 1. DEAD Tube Prologue", "1. DEAD Tube Prologue")]
|
||||
public void CleanSpecialTitleTest(string input, string expected)
|
||||
{
|
||||
Assert.Equal(expected, CleanSpecialTitle(input));
|
||||
|
@ -251,6 +252,7 @@ public class ParsingTests
|
|||
[InlineData("ch1/backcover.png", false)]
|
||||
[InlineData("backcover.png", false)]
|
||||
[InlineData("back_cover.png", false)]
|
||||
[InlineData("LD Blacklands #1 35 (back cover).png", false)]
|
||||
public void IsCoverImageTest(string inputPath, bool expected)
|
||||
{
|
||||
Assert.Equal(expected, IsCoverImage(inputPath));
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Constants;
|
||||
using API.Data;
|
||||
using API.Data.Repositories;
|
||||
using API.DTOs.Collection;
|
||||
|
@ -10,6 +12,8 @@ using API.Helpers.Builders;
|
|||
using API.Services;
|
||||
using API.Services.Plus;
|
||||
using API.SignalR;
|
||||
using Kavita.Common;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
|
@ -53,6 +57,64 @@ public class CollectionTagServiceTests : AbstractDbTest
|
|||
await _unitOfWork.CommitAsync();
|
||||
}
|
||||
|
||||
#region DeleteTag
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteTag_ShouldDeleteTag_WhenTagExists()
|
||||
{
|
||||
// Arrange
|
||||
await SeedSeries();
|
||||
|
||||
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Collections);
|
||||
Assert.NotNull(user);
|
||||
|
||||
// Act
|
||||
var result = await _service.DeleteTag(1, user);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
var deletedTag = await _unitOfWork.CollectionTagRepository.GetCollectionAsync(1);
|
||||
Assert.Null(deletedTag);
|
||||
Assert.Single(user.Collections); // Only one collection should remain
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteTag_ShouldReturnTrue_WhenTagDoesNotExist()
|
||||
{
|
||||
// Arrange
|
||||
await SeedSeries();
|
||||
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Collections);
|
||||
Assert.NotNull(user);
|
||||
|
||||
// Act - Try to delete a non-existent tag
|
||||
var result = await _service.DeleteTag(999, user);
|
||||
|
||||
// Assert
|
||||
Assert.True(result); // Should return true because the tag is already "deleted"
|
||||
Assert.Equal(2, user.Collections.Count); // Both collections should remain
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteTag_ShouldNotAffectOtherTags()
|
||||
{
|
||||
// Arrange
|
||||
await SeedSeries();
|
||||
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Collections);
|
||||
Assert.NotNull(user);
|
||||
|
||||
// Act
|
||||
var result = await _service.DeleteTag(1, user);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
var remainingTag = await _unitOfWork.CollectionTagRepository.GetCollectionAsync(2);
|
||||
Assert.NotNull(remainingTag);
|
||||
Assert.Equal("Tag 2", remainingTag.Title);
|
||||
Assert.True(remainingTag.Promoted);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region UpdateTag
|
||||
|
||||
[Fact]
|
||||
|
@ -111,6 +173,189 @@ public class CollectionTagServiceTests : AbstractDbTest
|
|||
Assert.Equal("UpdateTag_ShouldNotChangeTitle_WhenNotKavitaSource", tag.Title);
|
||||
Assert.False(string.IsNullOrEmpty(tag.Summary));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateTag_ShouldThrowException_WhenTagDoesNotExist()
|
||||
{
|
||||
// Arrange
|
||||
await SeedSeries();
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<KavitaException>(() => _service.UpdateTag(new AppUserCollectionDto()
|
||||
{
|
||||
Title = "Non-existent Tag",
|
||||
Id = 999, // Non-existent ID
|
||||
Promoted = false
|
||||
}, 1));
|
||||
|
||||
Assert.Equal("collection-doesnt-exist", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateTag_ShouldThrowException_WhenUserDoesNotOwnTag()
|
||||
{
|
||||
// Arrange
|
||||
await SeedSeries();
|
||||
|
||||
// Create a second user
|
||||
var user2 = new AppUserBuilder("user2", "user2", Seed.DefaultThemes.First()).Build();
|
||||
_unitOfWork.UserRepository.Add(user2);
|
||||
await _unitOfWork.CommitAsync();
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<KavitaException>(() => _service.UpdateTag(new AppUserCollectionDto()
|
||||
{
|
||||
Title = "Tag 1",
|
||||
Id = 1, // This belongs to user1
|
||||
Promoted = false
|
||||
}, 2)); // User with ID 2
|
||||
|
||||
Assert.Equal("access-denied", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateTag_ShouldThrowException_WhenTitleIsEmpty()
|
||||
{
|
||||
// Arrange
|
||||
await SeedSeries();
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<KavitaException>(() => _service.UpdateTag(new AppUserCollectionDto()
|
||||
{
|
||||
Title = " ", // Empty after trimming
|
||||
Id = 1,
|
||||
Promoted = false
|
||||
}, 1));
|
||||
|
||||
Assert.Equal("collection-tag-title-required", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateTag_ShouldThrowException_WhenTitleAlreadyExists()
|
||||
{
|
||||
// Arrange
|
||||
await SeedSeries();
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<KavitaException>(() => _service.UpdateTag(new AppUserCollectionDto()
|
||||
{
|
||||
Title = "Tag 2", // Already exists
|
||||
Id = 1, // Trying to rename Tag 1 to Tag 2
|
||||
Promoted = false
|
||||
}, 1));
|
||||
|
||||
Assert.Equal("collection-tag-duplicate", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateTag_ShouldUpdateCoverImageSettings()
|
||||
{
|
||||
// Arrange
|
||||
await SeedSeries();
|
||||
|
||||
// Act
|
||||
await _service.UpdateTag(new AppUserCollectionDto()
|
||||
{
|
||||
Title = "Tag 1",
|
||||
Id = 1,
|
||||
CoverImageLocked = true
|
||||
}, 1);
|
||||
|
||||
// Assert
|
||||
var tag = await _unitOfWork.CollectionTagRepository.GetCollectionAsync(1);
|
||||
Assert.NotNull(tag);
|
||||
Assert.True(tag.CoverImageLocked);
|
||||
|
||||
// Now test unlocking the cover image
|
||||
await _service.UpdateTag(new AppUserCollectionDto()
|
||||
{
|
||||
Title = "Tag 1",
|
||||
Id = 1,
|
||||
CoverImageLocked = false
|
||||
}, 1);
|
||||
|
||||
tag = await _unitOfWork.CollectionTagRepository.GetCollectionAsync(1);
|
||||
Assert.NotNull(tag);
|
||||
Assert.False(tag.CoverImageLocked);
|
||||
Assert.Equal(string.Empty, tag.CoverImage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateTag_ShouldAllowPromoteForAdminRole()
|
||||
{
|
||||
// Arrange
|
||||
await SeedSeries();
|
||||
|
||||
// Setup a user with admin role
|
||||
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Collections);
|
||||
Assert.NotNull(user);
|
||||
await AddUserWithRole(user.Id, PolicyConstants.AdminRole);
|
||||
|
||||
|
||||
// Act - Try to promote a tag that wasn't previously promoted
|
||||
await _service.UpdateTag(new AppUserCollectionDto()
|
||||
{
|
||||
Title = "Tag 1",
|
||||
Id = 1,
|
||||
Promoted = true
|
||||
}, 1);
|
||||
|
||||
// Assert
|
||||
var tag = await _unitOfWork.CollectionTagRepository.GetCollectionAsync(1);
|
||||
Assert.NotNull(tag);
|
||||
Assert.True(tag.Promoted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateTag_ShouldAllowPromoteForPromoteRole()
|
||||
{
|
||||
// Arrange
|
||||
await SeedSeries();
|
||||
|
||||
// Setup a user with promote role
|
||||
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Collections);
|
||||
Assert.NotNull(user);
|
||||
|
||||
// Mock to return promote role for the user
|
||||
await AddUserWithRole(user.Id, PolicyConstants.PromoteRole);
|
||||
|
||||
// Act - Try to promote a tag that wasn't previously promoted
|
||||
await _service.UpdateTag(new AppUserCollectionDto()
|
||||
{
|
||||
Title = "Tag 1",
|
||||
Id = 1,
|
||||
Promoted = true
|
||||
}, 1);
|
||||
|
||||
// Assert
|
||||
var tag = await _unitOfWork.CollectionTagRepository.GetCollectionAsync(1);
|
||||
Assert.NotNull(tag);
|
||||
Assert.True(tag.Promoted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateTag_ShouldNotChangePromotion_WhenUserHasNoPermission()
|
||||
{
|
||||
// Arrange
|
||||
await SeedSeries();
|
||||
|
||||
// Setup a user with no special roles
|
||||
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Collections);
|
||||
Assert.NotNull(user);
|
||||
|
||||
// Act - Try to promote a tag without proper role
|
||||
await _service.UpdateTag(new AppUserCollectionDto()
|
||||
{
|
||||
Title = "Tag 1",
|
||||
Id = 1,
|
||||
Promoted = true
|
||||
}, 1);
|
||||
|
||||
// Assert
|
||||
var tag = await _unitOfWork.CollectionTagRepository.GetCollectionAsync(1);
|
||||
Assert.NotNull(tag);
|
||||
Assert.False(tag.Promoted); // Should remain unpromoted
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
|
@ -131,7 +376,7 @@ public class CollectionTagServiceTests : AbstractDbTest
|
|||
await _service.RemoveTagFromSeries(tag, new[] {1});
|
||||
var userCollections = await _unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Collections);
|
||||
Assert.Equal(2, userCollections!.Collections.Count);
|
||||
Assert.Equal(1, tag.Items.Count);
|
||||
Assert.Single(tag.Items);
|
||||
Assert.Equal(2, tag.Items.First().Id);
|
||||
}
|
||||
|
||||
|
@ -175,6 +420,111 @@ public class CollectionTagServiceTests : AbstractDbTest
|
|||
Assert.Null(tag2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RemoveTagFromSeries_ShouldReturnFalse_WhenTagIsNull()
|
||||
{
|
||||
// Act
|
||||
var result = await _service.RemoveTagFromSeries(null, [1]);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RemoveTagFromSeries_ShouldHandleEmptySeriesIdsList()
|
||||
{
|
||||
// Arrange
|
||||
await SeedSeries();
|
||||
|
||||
var tag = await _unitOfWork.CollectionTagRepository.GetCollectionAsync(1);
|
||||
Assert.NotNull(tag);
|
||||
var initialItemCount = tag.Items.Count;
|
||||
|
||||
// Act
|
||||
var result = await _service.RemoveTagFromSeries(tag, Array.Empty<int>());
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
tag = await _unitOfWork.CollectionTagRepository.GetCollectionAsync(1);
|
||||
Assert.NotNull(tag);
|
||||
Assert.Equal(initialItemCount, tag.Items.Count); // No items should be removed
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RemoveTagFromSeries_ShouldHandleNonExistentSeriesIds()
|
||||
{
|
||||
// Arrange
|
||||
await SeedSeries();
|
||||
|
||||
var tag = await _unitOfWork.CollectionTagRepository.GetCollectionAsync(1);
|
||||
Assert.NotNull(tag);
|
||||
var initialItemCount = tag.Items.Count;
|
||||
|
||||
// Act - Try to remove a series that doesn't exist in the tag
|
||||
var result = await _service.RemoveTagFromSeries(tag, [999]);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
tag = await _unitOfWork.CollectionTagRepository.GetCollectionAsync(1);
|
||||
Assert.NotNull(tag);
|
||||
Assert.Equal(initialItemCount, tag.Items.Count); // No items should be removed
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RemoveTagFromSeries_ShouldHandleNullItemsList()
|
||||
{
|
||||
// Arrange
|
||||
await SeedSeries();
|
||||
|
||||
var tag = await _unitOfWork.CollectionTagRepository.GetCollectionAsync(1);
|
||||
Assert.NotNull(tag);
|
||||
|
||||
// Force null items list
|
||||
tag.Items = null;
|
||||
_unitOfWork.CollectionTagRepository.Update(tag);
|
||||
await _unitOfWork.CommitAsync();
|
||||
|
||||
// Act
|
||||
var result = await _service.RemoveTagFromSeries(tag, [1]);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
// The tag should not be removed since the items list was null, not empty
|
||||
var tagAfter = await _unitOfWork.CollectionTagRepository.GetCollectionAsync(1);
|
||||
Assert.Null(tagAfter);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RemoveTagFromSeries_ShouldUpdateAgeRating_WhenMultipleSeriesRemain()
|
||||
{
|
||||
// Arrange
|
||||
await SeedSeries();
|
||||
|
||||
// Add a third series with a different age rating
|
||||
var s3 = new SeriesBuilder("Series 3").WithMetadata(new SeriesMetadataBuilder().WithAgeRating(AgeRating.PG).Build()).Build();
|
||||
_context.Library.First().Series.Add(s3);
|
||||
await _unitOfWork.CommitAsync();
|
||||
|
||||
// Add series 3 to tag 2
|
||||
var tag = await _unitOfWork.CollectionTagRepository.GetCollectionAsync(2);
|
||||
Assert.NotNull(tag);
|
||||
tag.Items.Add(s3);
|
||||
_unitOfWork.CollectionTagRepository.Update(tag);
|
||||
await _unitOfWork.CommitAsync();
|
||||
|
||||
// Act - Remove the series with Mature rating
|
||||
await _service.RemoveTagFromSeries(tag, new[] {1});
|
||||
|
||||
// Assert
|
||||
tag = await _unitOfWork.CollectionTagRepository.GetCollectionAsync(2);
|
||||
Assert.NotNull(tag);
|
||||
Assert.Equal(2, tag.Items.Count);
|
||||
|
||||
// The age rating should be updated to the highest remaining rating (PG)
|
||||
Assert.Equal(AgeRating.PG, tag.AgeRating);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ using API.DTOs.Scrobbling;
|
|||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
using API.Entities.Metadata;
|
||||
using API.Entities.MetadataMatching;
|
||||
using API.Helpers.Builders;
|
||||
using API.Services.Plus;
|
||||
using API.Services.Tasks.Metadata;
|
||||
|
|
|
@ -167,7 +167,6 @@ public class ScannerServiceTests : AbstractDbTest
|
|||
Assert.NotNull(postLib.Series.First().Volumes.FirstOrDefault(v => v.Chapters.FirstOrDefault(c => c.IsSpecial) != null));
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task ScanLibrary_SeriesWithUnbalancedParenthesis()
|
||||
{
|
||||
|
|
|
@ -59,6 +59,7 @@ public class SeriesServiceTests : AbstractDbTest
|
|||
Substitute.For<ITaskScheduler>(), Substitute.For<ILogger<SeriesService>>(),
|
||||
Substitute.For<IScrobblingService>(), locService, Substitute.For<IReadingListService>());
|
||||
}
|
||||
|
||||
#region Setup
|
||||
|
||||
protected override async Task ResetDb()
|
||||
|
|
292
API.Tests/Services/SettingsServiceTests.cs
Normal file
292
API.Tests/Services/SettingsServiceTests.cs
Normal file
|
@ -0,0 +1,292 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO.Abstractions;
|
||||
using System.Threading.Tasks;
|
||||
using API.Data;
|
||||
using API.Data.Repositories;
|
||||
using API.DTOs.KavitaPlus.Metadata;
|
||||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
using API.Entities.MetadataMatching;
|
||||
using API.Services;
|
||||
using API.Services.Tasks.Scanner;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace API.Tests.Services;
|
||||
|
||||
public class SettingsServiceTests
|
||||
{
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly IUnitOfWork _mockUnitOfWork;
|
||||
|
||||
public SettingsServiceTests()
|
||||
{
|
||||
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), new FileSystem());
|
||||
|
||||
_mockUnitOfWork = Substitute.For<IUnitOfWork>();
|
||||
_settingsService = new SettingsService(_mockUnitOfWork, ds,
|
||||
Substitute.For<ILibraryWatcher>(), Substitute.For<ITaskScheduler>(),
|
||||
Substitute.For<ILogger<SettingsService>>());
|
||||
}
|
||||
|
||||
#region UpdateMetadataSettings
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateMetadataSettings_ShouldUpdateExistingSettings()
|
||||
{
|
||||
// Arrange
|
||||
var existingSettings = new MetadataSettings
|
||||
{
|
||||
Id = 1,
|
||||
Enabled = false,
|
||||
EnableSummary = false,
|
||||
EnableLocalizedName = false,
|
||||
EnablePublicationStatus = false,
|
||||
EnableRelationships = false,
|
||||
EnablePeople = false,
|
||||
EnableStartDate = false,
|
||||
EnableGenres = false,
|
||||
EnableTags = false,
|
||||
FirstLastPeopleNaming = false,
|
||||
EnableCoverImage = false,
|
||||
AgeRatingMappings = new Dictionary<string, AgeRating>(),
|
||||
Blacklist = [],
|
||||
Whitelist = [],
|
||||
Overrides = [],
|
||||
PersonRoles = [],
|
||||
FieldMappings = []
|
||||
};
|
||||
|
||||
var settingsRepo = Substitute.For<ISettingsRepository>();
|
||||
settingsRepo.GetMetadataSettings().Returns(Task.FromResult(existingSettings));
|
||||
settingsRepo.GetMetadataSettingDto().Returns(Task.FromResult(new MetadataSettingsDto()));
|
||||
_mockUnitOfWork.SettingsRepository.Returns(settingsRepo);
|
||||
|
||||
var updateDto = new MetadataSettingsDto
|
||||
{
|
||||
Enabled = true,
|
||||
EnableSummary = true,
|
||||
EnableLocalizedName = true,
|
||||
EnablePublicationStatus = true,
|
||||
EnableRelationships = true,
|
||||
EnablePeople = true,
|
||||
EnableStartDate = true,
|
||||
EnableGenres = true,
|
||||
EnableTags = true,
|
||||
FirstLastPeopleNaming = true,
|
||||
EnableCoverImage = true,
|
||||
AgeRatingMappings = new Dictionary<string, AgeRating> { { "Adult", AgeRating.R18Plus } },
|
||||
Blacklist = ["blacklisted-tag"],
|
||||
Whitelist = ["whitelisted-tag"],
|
||||
Overrides = [MetadataSettingField.Summary],
|
||||
PersonRoles = [PersonRole.Writer],
|
||||
FieldMappings =
|
||||
[
|
||||
new MetadataFieldMappingDto
|
||||
{
|
||||
SourceType = MetadataFieldType.Genre,
|
||||
DestinationType = MetadataFieldType.Tag,
|
||||
SourceValue = "Action",
|
||||
DestinationValue = "Fight",
|
||||
ExcludeFromSource = true
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// Act
|
||||
await _settingsService.UpdateMetadataSettings(updateDto);
|
||||
|
||||
// Assert
|
||||
await _mockUnitOfWork.Received(1).CommitAsync();
|
||||
|
||||
// Verify properties were updated
|
||||
Assert.True(existingSettings.Enabled);
|
||||
Assert.True(existingSettings.EnableSummary);
|
||||
Assert.True(existingSettings.EnableLocalizedName);
|
||||
Assert.True(existingSettings.EnablePublicationStatus);
|
||||
Assert.True(existingSettings.EnableRelationships);
|
||||
Assert.True(existingSettings.EnablePeople);
|
||||
Assert.True(existingSettings.EnableStartDate);
|
||||
Assert.True(existingSettings.EnableGenres);
|
||||
Assert.True(existingSettings.EnableTags);
|
||||
Assert.True(existingSettings.FirstLastPeopleNaming);
|
||||
Assert.True(existingSettings.EnableCoverImage);
|
||||
|
||||
// Verify collections were updated
|
||||
Assert.Single(existingSettings.AgeRatingMappings);
|
||||
Assert.Equal(AgeRating.R18Plus, existingSettings.AgeRatingMappings["Adult"]);
|
||||
|
||||
Assert.Single(existingSettings.Blacklist);
|
||||
Assert.Equal("blacklisted-tag", existingSettings.Blacklist[0]);
|
||||
|
||||
Assert.Single(existingSettings.Whitelist);
|
||||
Assert.Equal("whitelisted-tag", existingSettings.Whitelist[0]);
|
||||
|
||||
Assert.Single(existingSettings.Overrides);
|
||||
Assert.Equal(MetadataSettingField.Summary, existingSettings.Overrides[0]);
|
||||
|
||||
Assert.Single(existingSettings.PersonRoles);
|
||||
Assert.Equal(PersonRole.Writer, existingSettings.PersonRoles[0]);
|
||||
|
||||
Assert.Single(existingSettings.FieldMappings);
|
||||
Assert.Equal(MetadataFieldType.Genre, existingSettings.FieldMappings[0].SourceType);
|
||||
Assert.Equal(MetadataFieldType.Tag, existingSettings.FieldMappings[0].DestinationType);
|
||||
Assert.Equal("Action", existingSettings.FieldMappings[0].SourceValue);
|
||||
Assert.Equal("Fight", existingSettings.FieldMappings[0].DestinationValue);
|
||||
Assert.True(existingSettings.FieldMappings[0].ExcludeFromSource);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateMetadataSettings_WithNullCollections_ShouldUseEmptyCollections()
|
||||
{
|
||||
// Arrange
|
||||
var existingSettings = new MetadataSettings
|
||||
{
|
||||
Id = 1,
|
||||
FieldMappings = [new MetadataFieldMapping {Id = 1, SourceValue = "OldValue"}]
|
||||
};
|
||||
|
||||
var settingsRepo = Substitute.For<ISettingsRepository>();
|
||||
settingsRepo.GetMetadataSettings().Returns(Task.FromResult(existingSettings));
|
||||
settingsRepo.GetMetadataSettingDto().Returns(Task.FromResult(new MetadataSettingsDto()));
|
||||
_mockUnitOfWork.SettingsRepository.Returns(settingsRepo);
|
||||
|
||||
var updateDto = new MetadataSettingsDto
|
||||
{
|
||||
AgeRatingMappings = null,
|
||||
Blacklist = null,
|
||||
Whitelist = null,
|
||||
Overrides = null,
|
||||
PersonRoles = null,
|
||||
FieldMappings = null
|
||||
};
|
||||
|
||||
// Act
|
||||
await _settingsService.UpdateMetadataSettings(updateDto);
|
||||
|
||||
// Assert
|
||||
await _mockUnitOfWork.Received(1).CommitAsync();
|
||||
|
||||
Assert.Empty(existingSettings.AgeRatingMappings);
|
||||
Assert.Empty(existingSettings.Blacklist);
|
||||
Assert.Empty(existingSettings.Whitelist);
|
||||
Assert.Empty(existingSettings.Overrides);
|
||||
Assert.Empty(existingSettings.PersonRoles);
|
||||
|
||||
// Verify existing field mappings were cleared
|
||||
settingsRepo.Received(1).RemoveRange(Arg.Any<List<MetadataFieldMapping>>());
|
||||
Assert.Empty(existingSettings.FieldMappings);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateMetadataSettings_WithFieldMappings_ShouldReplaceExistingMappings()
|
||||
{
|
||||
// Arrange
|
||||
var existingSettings = new MetadataSettings
|
||||
{
|
||||
Id = 1,
|
||||
FieldMappings =
|
||||
[
|
||||
new MetadataFieldMapping
|
||||
{
|
||||
Id = 1,
|
||||
SourceType = MetadataFieldType.Genre,
|
||||
DestinationType = MetadataFieldType.Genre,
|
||||
SourceValue = "OldValue",
|
||||
DestinationValue = "OldDestination",
|
||||
ExcludeFromSource = false
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
var settingsRepo = Substitute.For<ISettingsRepository>();
|
||||
settingsRepo.GetMetadataSettings().Returns(Task.FromResult(existingSettings));
|
||||
settingsRepo.GetMetadataSettingDto().Returns(Task.FromResult(new MetadataSettingsDto()));
|
||||
_mockUnitOfWork.SettingsRepository.Returns(settingsRepo);
|
||||
|
||||
var updateDto = new MetadataSettingsDto
|
||||
{
|
||||
FieldMappings =
|
||||
[
|
||||
new MetadataFieldMappingDto
|
||||
{
|
||||
SourceType = MetadataFieldType.Tag,
|
||||
DestinationType = MetadataFieldType.Genre,
|
||||
SourceValue = "NewValue",
|
||||
DestinationValue = "NewDestination",
|
||||
ExcludeFromSource = true
|
||||
},
|
||||
|
||||
new MetadataFieldMappingDto
|
||||
{
|
||||
SourceType = MetadataFieldType.Tag,
|
||||
DestinationType = MetadataFieldType.Tag,
|
||||
SourceValue = "AnotherValue",
|
||||
DestinationValue = "AnotherDestination",
|
||||
ExcludeFromSource = false
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// Act
|
||||
await _settingsService.UpdateMetadataSettings(updateDto);
|
||||
|
||||
// Assert
|
||||
await _mockUnitOfWork.Received(1).CommitAsync();
|
||||
|
||||
// Verify existing field mappings were cleared and new ones added
|
||||
settingsRepo.Received(1).RemoveRange(Arg.Any<List<MetadataFieldMapping>>());
|
||||
Assert.Equal(2, existingSettings.FieldMappings.Count);
|
||||
|
||||
// Verify first mapping
|
||||
Assert.Equal(MetadataFieldType.Tag, existingSettings.FieldMappings[0].SourceType);
|
||||
Assert.Equal(MetadataFieldType.Genre, existingSettings.FieldMappings[0].DestinationType);
|
||||
Assert.Equal("NewValue", existingSettings.FieldMappings[0].SourceValue);
|
||||
Assert.Equal("NewDestination", existingSettings.FieldMappings[0].DestinationValue);
|
||||
Assert.True(existingSettings.FieldMappings[0].ExcludeFromSource);
|
||||
|
||||
// Verify second mapping
|
||||
Assert.Equal(MetadataFieldType.Tag, existingSettings.FieldMappings[1].SourceType);
|
||||
Assert.Equal(MetadataFieldType.Tag, existingSettings.FieldMappings[1].DestinationType);
|
||||
Assert.Equal("AnotherValue", existingSettings.FieldMappings[1].SourceValue);
|
||||
Assert.Equal("AnotherDestination", existingSettings.FieldMappings[1].DestinationValue);
|
||||
Assert.False(existingSettings.FieldMappings[1].ExcludeFromSource);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateMetadataSettings_WithBlacklistWhitelist_ShouldNormalizeAndDeduplicateEntries()
|
||||
{
|
||||
// Arrange
|
||||
var existingSettings = new MetadataSettings
|
||||
{
|
||||
Id = 1,
|
||||
Blacklist = [],
|
||||
Whitelist = []
|
||||
};
|
||||
|
||||
// We need to mock the repository and provide a custom implementation for ToNormalized
|
||||
var settingsRepo = Substitute.For<ISettingsRepository>();
|
||||
settingsRepo.GetMetadataSettings().Returns(Task.FromResult(existingSettings));
|
||||
settingsRepo.GetMetadataSettingDto().Returns(Task.FromResult(new MetadataSettingsDto()));
|
||||
_mockUnitOfWork.SettingsRepository.Returns(settingsRepo);
|
||||
|
||||
var updateDto = new MetadataSettingsDto
|
||||
{
|
||||
// Include duplicates with different casing and whitespace
|
||||
Blacklist = ["tag1", "Tag1", " tag2 ", "", " ", "tag3"],
|
||||
Whitelist = ["allowed1", "Allowed1", " allowed2 ", "", "allowed3"]
|
||||
};
|
||||
|
||||
// Act
|
||||
await _settingsService.UpdateMetadataSettings(updateDto);
|
||||
|
||||
// Assert
|
||||
await _mockUnitOfWork.Received(1).CommitAsync();
|
||||
|
||||
Assert.Equal(3, existingSettings.Blacklist.Count);
|
||||
Assert.Equal(3, existingSettings.Whitelist.Count);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
|
@ -65,13 +65,13 @@ public class VersionUpdaterServiceTests : IDisposable
|
|||
[Fact]
|
||||
public async Task CheckForUpdate_ShouldReturnNull_WhenGithubApiReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
_httpTest.RespondWith("null");
|
||||
|
||||
// Act
|
||||
|
||||
var result = await _service.CheckForUpdate();
|
||||
|
||||
// Assert
|
||||
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
|
@ -79,7 +79,7 @@ public class VersionUpdaterServiceTests : IDisposable
|
|||
//[Fact]
|
||||
public async Task CheckForUpdate_ShouldReturnUpdateNotification_WhenNewVersionIsAvailable()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
var githubResponse = new
|
||||
{
|
||||
tag_name = "v0.6.0",
|
||||
|
@ -91,10 +91,10 @@ public class VersionUpdaterServiceTests : IDisposable
|
|||
|
||||
_httpTest.RespondWithJson(githubResponse);
|
||||
|
||||
// Act
|
||||
|
||||
var result = await _service.CheckForUpdate();
|
||||
|
||||
// Assert
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal("0.6.0", result.UpdateVersion);
|
||||
Assert.Equal("0.5.0.0", result.CurrentVersion);
|
||||
|
@ -121,10 +121,10 @@ public class VersionUpdaterServiceTests : IDisposable
|
|||
|
||||
_httpTest.RespondWithJson(githubResponse);
|
||||
|
||||
// Act
|
||||
|
||||
var result = await _service.CheckForUpdate();
|
||||
|
||||
// Assert
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.True(result.IsReleaseEqual);
|
||||
Assert.False(result.IsReleaseNewer);
|
||||
|
@ -134,7 +134,7 @@ public class VersionUpdaterServiceTests : IDisposable
|
|||
//[Fact]
|
||||
public async Task PushUpdate_ShouldSendUpdateEvent_WhenNewerVersionAvailable()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
var update = new UpdateNotificationDto
|
||||
{
|
||||
UpdateVersion = "0.6.0",
|
||||
|
@ -145,10 +145,10 @@ public class VersionUpdaterServiceTests : IDisposable
|
|||
PublishDate = null
|
||||
};
|
||||
|
||||
// Act
|
||||
|
||||
await _service.PushUpdate(update);
|
||||
|
||||
// Assert
|
||||
|
||||
await _eventHub.Received(1).SendMessageAsync(
|
||||
Arg.Is(MessageFactory.UpdateAvailable),
|
||||
Arg.Any<SignalRMessage>(),
|
||||
|
@ -159,7 +159,7 @@ public class VersionUpdaterServiceTests : IDisposable
|
|||
[Fact]
|
||||
public async Task PushUpdate_ShouldNotSendUpdateEvent_WhenVersionIsEqual()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
var update = new UpdateNotificationDto
|
||||
{
|
||||
UpdateVersion = "0.5.0.0",
|
||||
|
@ -170,10 +170,10 @@ public class VersionUpdaterServiceTests : IDisposable
|
|||
PublishDate = null
|
||||
};
|
||||
|
||||
// Act
|
||||
|
||||
await _service.PushUpdate(update);
|
||||
|
||||
// Assert
|
||||
|
||||
await _eventHub.DidNotReceive().SendMessageAsync(
|
||||
Arg.Any<string>(),
|
||||
Arg.Any<SignalRMessage>(),
|
||||
|
@ -184,7 +184,7 @@ public class VersionUpdaterServiceTests : IDisposable
|
|||
[Fact]
|
||||
public async Task GetAllReleases_ShouldReturnReleases_LimitedByCount()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
var releases = new List<object>
|
||||
{
|
||||
new
|
||||
|
@ -215,10 +215,10 @@ public class VersionUpdaterServiceTests : IDisposable
|
|||
|
||||
_httpTest.RespondWithJson(releases);
|
||||
|
||||
// Act
|
||||
|
||||
var result = await _service.GetAllReleases(2);
|
||||
|
||||
// Assert
|
||||
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Equal("0.7.0.0", result[0].UpdateVersion);
|
||||
Assert.Equal("0.6.0", result[1].UpdateVersion);
|
||||
|
@ -227,7 +227,7 @@ public class VersionUpdaterServiceTests : IDisposable
|
|||
[Fact]
|
||||
public async Task GetAllReleases_ShouldUseCachedData_WhenCacheIsValid()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
var releases = new List<UpdateNotificationDto>
|
||||
{
|
||||
new()
|
||||
|
@ -257,10 +257,10 @@ public class VersionUpdaterServiceTests : IDisposable
|
|||
await File.WriteAllTextAsync(cacheFilePath, System.Text.Json.JsonSerializer.Serialize(releases));
|
||||
File.SetLastWriteTimeUtc(cacheFilePath, DateTime.UtcNow); // Ensure it's fresh
|
||||
|
||||
// Act
|
||||
|
||||
var result = await _service.GetAllReleases();
|
||||
|
||||
// Assert
|
||||
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Empty(_httpTest.CallLog); // No HTTP calls made
|
||||
}
|
||||
|
@ -268,7 +268,7 @@ public class VersionUpdaterServiceTests : IDisposable
|
|||
[Fact]
|
||||
public async Task GetAllReleases_ShouldFetchNewData_WhenCacheIsExpired()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
var releases = new List<UpdateNotificationDto>
|
||||
{
|
||||
new()
|
||||
|
@ -303,10 +303,10 @@ public class VersionUpdaterServiceTests : IDisposable
|
|||
|
||||
_httpTest.RespondWithJson(newReleases);
|
||||
|
||||
// Act
|
||||
|
||||
var result = await _service.GetAllReleases();
|
||||
|
||||
// Assert
|
||||
|
||||
Assert.Equal(1, result.Count);
|
||||
Assert.Equal("0.7.0.0", result[0].UpdateVersion);
|
||||
Assert.NotEmpty(_httpTest.CallLog); // HTTP call was made
|
||||
|
@ -314,7 +314,7 @@ public class VersionUpdaterServiceTests : IDisposable
|
|||
|
||||
public async Task GetNumberOfReleasesBehind_ShouldReturnCorrectCount()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
var releases = new List<object>
|
||||
{
|
||||
new
|
||||
|
@ -345,16 +345,16 @@ public class VersionUpdaterServiceTests : IDisposable
|
|||
|
||||
_httpTest.RespondWithJson(releases);
|
||||
|
||||
// Act
|
||||
|
||||
var result = await _service.GetNumberOfReleasesBehind();
|
||||
|
||||
// Assert
|
||||
|
||||
Assert.Equal(2 + 1, result); // Behind 0.7.0 and 0.6.0 - We have to add 1 because the current release is > 0.7.0
|
||||
}
|
||||
|
||||
public async Task GetNumberOfReleasesBehind_ShouldReturnCorrectCount_WithNightlies()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
var releases = new List<object>
|
||||
{
|
||||
new
|
||||
|
@ -377,17 +377,17 @@ public class VersionUpdaterServiceTests : IDisposable
|
|||
|
||||
_httpTest.RespondWithJson(releases);
|
||||
|
||||
// Act
|
||||
|
||||
var result = await _service.GetNumberOfReleasesBehind();
|
||||
|
||||
// Assert
|
||||
|
||||
Assert.Equal(2, result); // We have to add 1 because the current release is > 0.7.0
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParseReleaseBody_ShouldExtractSections()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
var githubResponse = new
|
||||
{
|
||||
tag_name = "v0.6.0",
|
||||
|
@ -399,10 +399,10 @@ public class VersionUpdaterServiceTests : IDisposable
|
|||
|
||||
_httpTest.RespondWithJson(githubResponse);
|
||||
|
||||
// Act
|
||||
|
||||
var result = await _service.CheckForUpdate();
|
||||
|
||||
// Assert
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(2, result.Added.Count);
|
||||
Assert.Equal(2, result.Fixed.Count);
|
||||
|
@ -414,7 +414,7 @@ public class VersionUpdaterServiceTests : IDisposable
|
|||
[Fact]
|
||||
public async Task GetAllReleases_ShouldHandleNightlyBuilds()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Set BuildInfo.Version to a nightly build version
|
||||
typeof(BuildInfo).GetProperty(nameof(BuildInfo.Version))?.SetValue(null, new Version("0.7.1.0"));
|
||||
|
||||
|
@ -444,10 +444,10 @@ public class VersionUpdaterServiceTests : IDisposable
|
|||
// Mock commit info for develop branch
|
||||
_httpTest.RespondWithJson(new List<object>());
|
||||
|
||||
// Act
|
||||
|
||||
var result = await _service.GetAllReleases();
|
||||
|
||||
// Assert
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.True(result[0].IsOnNightlyInRelease);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue