aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorShadowghost <Ghost_of_Stone@web.de>2026-06-19 21:51:57 +0200
committerShadowghost <Ghost_of_Stone@web.de>2026-06-19 21:51:57 +0200
commit0fb042b7403ebd7578b696aba35ba0c582ccf6ba (patch)
treed7e46069c8500ecf5680283748448fc465b3ec6f /tests
parenta5706c2fa6b09019828b2a9c5c6822f5bfa3327f (diff)
Surface the played version for resume
Diffstat (limited to 'tests')
-rw-r--r--tests/Jellyfin.Controller.Tests/Library/VersionResumeDataTests.cs30
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Item/AlternateVersionQueryTranslationTests.cs36
2 files changed, 37 insertions, 29 deletions
diff --git a/tests/Jellyfin.Controller.Tests/Library/VersionResumeDataTests.cs b/tests/Jellyfin.Controller.Tests/Library/VersionResumeDataTests.cs
index 5b0f003019..8642ab07f8 100644
--- a/tests/Jellyfin.Controller.Tests/Library/VersionResumeDataTests.cs
+++ b/tests/Jellyfin.Controller.Tests/Library/VersionResumeDataTests.cs
@@ -9,36 +9,40 @@ namespace Jellyfin.Controller.Tests.Library;
public class VersionResumeDataTests
{
[Fact]
- public void ApplyTo_OverridesResumeFieldsAndPercentage()
+ public void ApplyTo_PropagatesCompletionButNotPosition()
{
var lastPlayed = new DateTime(2026, 1, 2, 0, 0, 0, DateTimeKind.Utc);
var resume = new VersionResumeData(
- new UserItemData { Key = "version", PlaybackPositionTicks = 25, Played = true, LastPlayedDate = lastPlayed },
- RunTimeTicks: 100);
+ new UserItemData { Key = "version", PlaybackPositionTicks = 25, Played = true, LastPlayedDate = lastPlayed });
var dto = new UserItemDataDto { Key = "primary", PlaybackPositionTicks = 1, Played = false, PlayedPercentage = 1 };
resume.ApplyTo(dto);
- Assert.Equal(25, dto.PlaybackPositionTicks);
+ // Completion state propagates to the primary...
Assert.True(dto.Played);
Assert.Equal(lastPlayed, dto.LastPlayedDate);
- // The percentage is based on the resume version's own runtime, not the primary's.
- Assert.NotNull(dto.PlayedPercentage);
- Assert.Equal(25.0, dto.PlayedPercentage.Value, 5);
+ // ...but the in-progress resume position stays on the version that owns it.
+ Assert.Equal(1, dto.PlaybackPositionTicks);
+ Assert.Equal(1.0, dto.PlayedPercentage);
}
[Fact]
- public void ApplyTo_WithoutRuntime_LeavesPercentageUntouched()
+ public void ApplyTo_DoesNotUnsetExistingPlayedOrRegressLastPlayed()
{
- var resume = new VersionResumeData(new UserItemData { Key = "version", PlaybackPositionTicks = 25 }, null);
- var dto = new UserItemDataDto { Key = "primary", PlayedPercentage = 42 };
+ var primaryLastPlayed = new DateTime(2026, 1, 5, 0, 0, 0, DateTimeKind.Utc);
+ var versionLastPlayed = new DateTime(2026, 1, 2, 0, 0, 0, DateTimeKind.Utc);
+ var resume = new VersionResumeData(
+ new UserItemData { Key = "version", Played = false, LastPlayedDate = versionLastPlayed });
+
+ var dto = new UserItemDataDto { Key = "primary", Played = true, LastPlayedDate = primaryLastPlayed };
resume.ApplyTo(dto);
- Assert.Equal(25, dto.PlaybackPositionTicks);
- Assert.NotNull(dto.PlayedPercentage);
- Assert.Equal(42.0, dto.PlayedPercentage.Value, 5);
+ // A not-yet-completed version must not clear the primary's own completion, and the more recent
+ // LastPlayedDate is kept.
+ Assert.True(dto.Played);
+ Assert.Equal(primaryLastPlayed, dto.LastPlayedDate);
}
}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Item/AlternateVersionQueryTranslationTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Item/AlternateVersionQueryTranslationTests.cs
index 823ff566a7..1f0de153a0 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Item/AlternateVersionQueryTranslationTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Item/AlternateVersionQueryTranslationTests.cs
@@ -37,39 +37,43 @@ public sealed class AlternateVersionQueryTranslationTests : IDisposable
}
[Fact]
- public void ResumeFilter_VersionProgress_SurfacesPrimary()
+ public void ResumeFilter_VersionProgress_SurfacesPlayedVersion()
{
- Guid userId, primaryId, otherId;
+ Guid userId, primaryId, versionId, otherId;
using (var ctx = CreateDbContext())
{
- (userId, primaryId, otherId) = Seed(ctx);
+ (userId, primaryId, versionId, otherId) = Seed(ctx);
}
using (var ctx = CreateDbContext())
{
- // Mirrors the resumable filter in BaseItemRepository.TranslateQuery: progress on any
- // version coalesces onto the primary's id.
var inProgress = ctx.UserData
.Where(ud => ud.UserId == userId && ud.PlaybackPositionTicks > 0);
- var resumableMovieIds = inProgress
- .Join(ctx.BaseItems, ud => ud.ItemId, bi => bi.Id, (ud, bi) => bi.PrimaryVersionId ?? bi.Id);
// Scope to the seeded items; EnsureCreated also seeds a placeholder row.
- var seededIds = new[] { primaryId, otherId };
+ var seededIds = new[] { primaryId, versionId, otherId };
+ // Mirrors the resumable=true filter in BaseItemRepository.TranslateQuery.
+ var inProgressIds = inProgress.Select(ud => ud.ItemId);
var resumable = ctx.BaseItems
- .Where(e => seededIds.Contains(e.Id) && e.PrimaryVersionId == null)
- .Where(e => resumableMovieIds.Contains(e.Id))
+ .Where(e => seededIds.Contains(e.Id))
+ .Where(e => inProgressIds.Contains(e.Id))
+ .Where(e => !ctx.BaseItems
+ .Where(s => s.Id != e.Id && (s.PrimaryVersionId ?? s.Id) == (e.PrimaryVersionId ?? e.Id))
+ .Any(s => inProgress.Where(su => su.ItemId == s.Id).Max(su => su.LastPlayedDate)
+ > inProgress.Where(eu => eu.ItemId == e.Id).Max(eu => eu.LastPlayedDate)))
.Select(e => e.Id)
.ToList();
- Assert.Equal([primaryId], resumable);
+ Assert.Equal([versionId], resumable);
- // The inverse (not-resumable) direction must exclude the primary as well.
+ // The not-resumable direction keeps primaries only.
+ var resumableMovieIds = inProgress
+ .Join(ctx.BaseItems, ud => ud.ItemId, bi => bi.Id, (ud, bi) => bi.PrimaryVersionId ?? bi.Id);
var notResumable = ctx.BaseItems
.Where(e => seededIds.Contains(e.Id) && e.PrimaryVersionId == null)
- .Where(e => resumableMovieIds.Contains(e.Id) == false)
+ .Where(e => !resumableMovieIds.Contains(e.Id))
.Select(e => e.Id)
.ToList();
@@ -84,7 +88,7 @@ public sealed class AlternateVersionQueryTranslationTests : IDisposable
using (var ctx = CreateDbContext())
{
- (userId, primaryId, otherId) = Seed(ctx);
+ (userId, primaryId, _, otherId) = Seed(ctx);
}
using (var ctx = CreateDbContext())
@@ -106,7 +110,7 @@ public sealed class AlternateVersionQueryTranslationTests : IDisposable
}
}
- private static (Guid UserId, Guid PrimaryId, Guid OtherId) Seed(JellyfinDbContext ctx)
+ private static (Guid UserId, Guid PrimaryId, Guid VersionId, Guid OtherId) Seed(JellyfinDbContext ctx)
{
var user = new User("test", "auth-provider", "reset-provider");
ctx.Users.Add(user);
@@ -129,7 +133,7 @@ public sealed class AlternateVersionQueryTranslationTests : IDisposable
});
ctx.SaveChanges();
- return (user.Id, primary.Id, other.Id);
+ return (user.Id, primary.Id, version.Id, other.Id);
}
private JellyfinDbContext CreateDbContext()