mirror of
https://github.com/Kareadita/Kavita.git
synced 2026-01-18 01:50:36 -05:00
196 lines
6.7 KiB
C#
196 lines
6.7 KiB
C#
using API.DTOs.Progress;
|
|
using API.Helpers;
|
|
using Xunit;
|
|
|
|
namespace API.Tests.Helpers;
|
|
#nullable enable
|
|
|
|
public class KoreaderHelperTests
|
|
{
|
|
#region UpdateProgressDto Tests
|
|
|
|
[Theory]
|
|
[InlineData("/body/DocFragment[11]/body/div/a", 10, null)] // Anchor tags return null BookScrollId
|
|
[InlineData("/body/DocFragment[1]/body/div/p[40]", 0, "//body/div/p[40]")]
|
|
[InlineData("/body/DocFragment[5]/body/section/div[2]", 4, "//body/section/div[2]")]
|
|
public void UpdateProgressDto_StandardXPath(string koreaderPosition, int expectedPage, string? expectedScrollId)
|
|
{
|
|
var actual = EmptyProgressDto();
|
|
|
|
KoreaderHelper.UpdateProgressDto(actual, koreaderPosition);
|
|
|
|
Assert.Equal(expectedPage, actual.PageNum);
|
|
Assert.Equal(expectedScrollId, actual.BookScrollId);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("/body/DocFragment[8]/body/div/p[28]/text().264", 7, "//body/div/p[28]")]
|
|
[InlineData("/body/DocFragment[3]/body/h1/text().0", 2, "//body/h1")]
|
|
[InlineData("/body/DocFragment[9]/body/p[52]/text().248", 8, "//body/p[52]")]
|
|
[InlineData("/body/DocFragment[6]/body/div/span.0", 5, "//body/div/span")] // Trailing .0 stripped
|
|
public void UpdateProgressDto_WithTextOffsets(string koreaderPosition, int expectedPage, string? expectedScrollId)
|
|
{
|
|
var actual = EmptyProgressDto();
|
|
|
|
KoreaderHelper.UpdateProgressDto(actual, koreaderPosition);
|
|
|
|
Assert.Equal(expectedPage, actual.PageNum);
|
|
Assert.Equal(expectedScrollId, actual.BookScrollId);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("/body/DocFragment[10].0", 9, null)] // Short path - no scroll ID determinable
|
|
[InlineData("/body/DocFragment[5]", 4, null)]
|
|
[InlineData("/body/DocFragment[1]/body", 0, null)] // Too short for full path extraction
|
|
public void UpdateProgressDto_ShortPaths(string koreaderPosition, int expectedPage, string? expectedScrollId)
|
|
{
|
|
var actual = EmptyProgressDto();
|
|
|
|
KoreaderHelper.UpdateProgressDto(actual, koreaderPosition);
|
|
|
|
Assert.Equal(expectedPage, actual.PageNum);
|
|
Assert.Equal(expectedScrollId, actual.BookScrollId);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("#_doc_fragment_10", 9, null)]
|
|
[InlineData("#_doc_fragment_1", 0, null)]
|
|
[InlineData("#_doc_fragment_10_ some_anchor", 9, null)] // With trailing anchor
|
|
[InlineData("#_doc_fragment10", 9, null)] // Legacy format without underscore
|
|
[InlineData("#_doc_fragment1", 0, null)]
|
|
public void UpdateProgressDto_HashFragmentFormat(string koreaderPosition, int expectedPage, string? expectedScrollId)
|
|
{
|
|
var actual = EmptyProgressDto();
|
|
|
|
KoreaderHelper.UpdateProgressDto(actual, koreaderPosition);
|
|
|
|
Assert.Equal(expectedPage, actual.PageNum);
|
|
Assert.Equal(expectedScrollId, actual.BookScrollId);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("5", 4)] // Archive/PDF page number (1-indexed from KOReader)
|
|
[InlineData("1", 0)]
|
|
[InlineData("100", 99)]
|
|
public void UpdateProgressDto_NumericOnly(string koreaderPosition, int expectedPage)
|
|
{
|
|
var actual = EmptyProgressDto();
|
|
|
|
KoreaderHelper.UpdateProgressDto(actual, koreaderPosition);
|
|
|
|
Assert.Equal(expectedPage, actual.PageNum);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("/body/DocFragment[11]/body/id(\"chapter1\")", 10, null)] // id() selectors not supported
|
|
[InlineData("/body/DocFragment[5]/body/div/id(\"section2\")/p[1]", 4, null)]
|
|
public void UpdateProgressDto_IdSelectors(string koreaderPosition, int expectedPage, string? expectedScrollId)
|
|
{
|
|
var actual = EmptyProgressDto();
|
|
|
|
KoreaderHelper.UpdateProgressDto(actual, koreaderPosition);
|
|
|
|
Assert.Equal(expectedPage, actual.PageNum);
|
|
Assert.Equal(expectedScrollId, actual.BookScrollId);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(null)]
|
|
[InlineData("")]
|
|
[InlineData(" ")]
|
|
public void UpdateProgressDto_EmptyOrNull_NoChanges(string? koreaderPosition)
|
|
{
|
|
var actual = EmptyProgressDto();
|
|
actual.PageNum = 5;
|
|
actual.BookScrollId = "//body/p[1]";
|
|
|
|
KoreaderHelper.UpdateProgressDto(actual, koreaderPosition!);
|
|
|
|
// Should remain unchanged
|
|
Assert.Equal(5, actual.PageNum);
|
|
Assert.Equal("//body/p[1]", actual.BookScrollId);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetKoreaderPosition Tests
|
|
|
|
[Theory]
|
|
[InlineData("//body/p[20]", 4, "/body/DocFragment[5]/body/p[20].0")]
|
|
[InlineData("//body/div/section[3]", 0, "/body/DocFragment[1]/body/div/section[3].0")]
|
|
[InlineData("//body/h1", 9, "/body/DocFragment[10]/body/h1.0")]
|
|
public void GetKoreaderPosition_WithScrollId(string? scrollId, int page, string expectedPosition)
|
|
{
|
|
var given = EmptyProgressDto();
|
|
given.BookScrollId = scrollId;
|
|
given.PageNum = page;
|
|
|
|
var result = KoreaderHelper.GetKoreaderPosition(given);
|
|
|
|
Assert.Equal(expectedPosition, result, ignoreCase: true);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(null, 9, "/body/DocFragment[10].0")]
|
|
[InlineData("", 0, "/body/DocFragment[1].0")]
|
|
[InlineData(null, 4, "/body/DocFragment[5].0")]
|
|
public void GetKoreaderPosition_NoScrollId_ReturnsFragmentOnly(string? scrollId, int page, string expectedPosition)
|
|
{
|
|
var given = EmptyProgressDto();
|
|
given.BookScrollId = scrollId;
|
|
given.PageNum = page;
|
|
|
|
var result = KoreaderHelper.GetKoreaderPosition(given);
|
|
|
|
Assert.Equal(expectedPosition, result, ignoreCase: true);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("id(\"chapter1\")", 5, "/body/DocFragment[6].0")]
|
|
[InlineData("id(\"h2\")", 10, "/body/DocFragment[11].0")]
|
|
public void GetKoreaderPosition_IdSelector_ReturnsFragmentOnly(string scrollId, int page, string expectedPosition)
|
|
{
|
|
var given = EmptyProgressDto();
|
|
given.BookScrollId = scrollId;
|
|
given.PageNum = page;
|
|
|
|
var result = KoreaderHelper.GetKoreaderPosition(given);
|
|
|
|
Assert.Equal(expectedPosition, result, ignoreCase: true);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region HashContents Tests
|
|
|
|
[Theory]
|
|
[InlineData("./Data/AesopsFables.epub", "8795ACA4BF264B57C1EEDF06A0CEE688")]
|
|
public void HashContents_ValidFile_ReturnsExpectedHash(string filePath, string expectedHash)
|
|
{
|
|
Assert.Equal(expectedHash, KoreaderHelper.HashContents(filePath));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(null)]
|
|
[InlineData("")]
|
|
[InlineData("./Data/NonExistent.epub")]
|
|
public void HashContents_InvalidFile_ReturnsNull(string? filePath)
|
|
{
|
|
Assert.Null(KoreaderHelper.HashContents(filePath!));
|
|
}
|
|
|
|
#endregion
|
|
|
|
private static ProgressDto EmptyProgressDto()
|
|
{
|
|
return new ProgressDto
|
|
{
|
|
ChapterId = 0,
|
|
PageNum = 0,
|
|
VolumeId = 0,
|
|
SeriesId = 0,
|
|
LibraryId = 0
|
|
};
|
|
}
|
|
}
|