using DeepDrftModels.DTOs;
using DeepDrftPublic.Client.Services;
using DeepDrftPublic.Client.ViewModels;
using Models.Common;
using NetBlocks.Models;
namespace DeepDrftTests;
///
/// Unit tests for the release-embed track resolution (). The VM is
/// the data half of FramePlayer's release path: resolve a release EntryKey to its ordered track list,
/// which the page then stages (track 0) and arms into the queue. It is pure async logic over the two
/// data-service seams, so it is exercised here against recording fakes — no browser, no JS, no HTTP.
/// Coverage: ordered non-empty resolution, single-track release, release-not-found, and a release
/// that resolves to no tracks (the "leave the bar idle, don't throw" path).
///
[TestFixture]
public class FramePlayerReleaseResolutionTests
{
private FakeReleaseData _releaseData = null!;
private FakeTrackData _trackData = null!;
private FramePlayerViewModel _vm = null!;
private const string ReleaseKey = "release-key-1";
private const long ReleaseId = 42;
[SetUp]
public void SetUp()
{
_releaseData = new FakeReleaseData();
_trackData = new FakeTrackData();
_vm = new FramePlayerViewModel(_releaseData, _trackData);
}
private static ReleaseDto Release() => new() { Id = ReleaseId, EntryKey = ReleaseKey, Title = "Album" };
private static List Tracks(int count) =>
Enumerable.Range(1, count)
.Select(i => new TrackDto { EntryKey = $"track-{i}", TrackName = $"Track {i}", TrackNumber = i })
.ToList();
[Test]
public async Task Load_ResolvesOrderedNonEmptyTrackList_AndQueriesByResolvedReleaseId()
{
_releaseData.Release = Release();
_trackData.Page = Tracks(3);
await _vm.Load(ReleaseKey);
Assert.Multiple(() =>
{
Assert.That(_vm.Tracks.Select(t => t.EntryKey),
Is.EqualTo(new[] { "track-1", "track-2", "track-3" }));
// The track page must be narrowed by the resolved release.Id (the int FK join), sorted by
// the explicit TrackNumber ordinal — the same resolution the Cut detail page uses.
Assert.That(_trackData.LastReleaseId, Is.EqualTo(ReleaseId));
Assert.That(_trackData.LastSortColumn, Is.EqualTo("TrackNumber"));
});
}
[Test]
public async Task Load_WithSingleTrackRelease_ResolvesAOneItemList()
{
_releaseData.Release = Release();
_trackData.Page = Tracks(1);
await _vm.Load(ReleaseKey);
Assert.That(_vm.Tracks, Has.Count.EqualTo(1));
Assert.That(_vm.Tracks[0].EntryKey, Is.EqualTo("track-1"));
}
[Test]
public async Task Load_WhenReleaseNotFound_LeavesTracksEmptyAndDoesNotQueryTracks()
{
_releaseData.Release = null; // GetByEntryKey returns a fail result.
await _vm.Load(ReleaseKey);
Assert.Multiple(() =>
{
Assert.That(_vm.Tracks, Is.Empty);
Assert.That(_trackData.WasQueried, Is.False);
});
}
[Test]
public async Task Load_WhenReleaseResolvesToNoTracks_LeavesTracksEmptyWithoutThrowing()
{
_releaseData.Release = Release();
_trackData.Page = new List(); // empty page
await _vm.Load(ReleaseKey);
Assert.That(_vm.Tracks, Is.Empty);
}
[Test]
public async Task Load_ResetsTracksFromAPriorLoad()
{
_releaseData.Release = Release();
_trackData.Page = Tracks(3);
await _vm.Load(ReleaseKey);
Assert.That(_vm.Tracks, Has.Count.EqualTo(3));
// A second load whose release is not found must not retain the prior album's tracks.
_releaseData.Release = null;
await _vm.Load("another-key");
Assert.That(_vm.Tracks, Is.Empty);
}
private sealed class FakeReleaseData : IReleaseDataService
{
public ReleaseDto? Release { get; set; }
public Task> GetByEntryKey(string entryKey)
=> Task.FromResult(Release is null
? ApiResult.CreateFailResult("not found")
: ApiResult.CreatePassResult(Release));
public Task>> GetPaged(
string? medium, int page, int pageSize, string? sortColumn = null,
bool sortDescending = false, string? search = null, string? genre = null)
=> throw new NotSupportedException("FramePlayerViewModel does not page releases.");
}
private sealed class FakeTrackData : ITrackDataService
{
public List Page { get; set; } = new();
public bool WasQueried { get; private set; }
public long? LastReleaseId { get; private set; }
public string? LastSortColumn { get; private set; }
public Task>> GetPage(
int pageNumber, int pageSize, string? sortColumn = null, bool sortDescending = false,
string? searchText = null, string? album = null, string? genre = null, long? releaseId = null)
{
WasQueried = true;
LastReleaseId = releaseId;
LastSortColumn = sortColumn;
var paged = new PagedResult
{
Items = Page,
TotalCount = Page.Count,
Page = pageNumber,
PageSize = pageSize,
};
return Task.FromResult(ApiResult>.CreatePassResult(paged));
}
// Inert remainder — FramePlayerViewModel only calls GetPage.
public Task>> GetAlbums() => throw new NotSupportedException();
public Task>> GetGenres() => throw new NotSupportedException();
public Task> GetTrack(string trackId) => throw new NotSupportedException();
public Task> GetTrackWaveform(string trackEntryKey) => throw new NotSupportedException();
public Task> GetRandomTrack() => throw new NotSupportedException();
}
}