using VideoDownloader.App.Browser.Features; namespace VideoDownloader.Tests.Browser; // ── T2-10 미디어 재생목록 (Brave Playlist 패턴): 감지된 미디어를 쌓고 재생/제거/영속 ── public class MediaPlaylistTests : IDisposable { private readonly string _path = Path.Combine(Path.GetTempPath(), $"playlist_{Guid.NewGuid():N}.json"); private readonly MediaPlaylist _pl = new(); private MediaTrack Mk(string url = "https://v.test/a.mp4", string title = "트랙A", string kind = "Direct") => new(url, title, kind, AddedAt: DateTime.UtcNow); [Fact] public void Add는_재생목록_끝에_추가하고_중복_URL은_무시한다() { var t = Mk(); _pl.Add(t); _pl.Add(Mk(url: "https://v.test/b.mp4", title: "트랙B")); _pl.Add(Mk()); // 동일 URL 중복 Assert.Equal(2, _pl.Tracks.Count); Assert.Equal("트랙A", _pl.Tracks[0].Title); } [Fact] public void Remove와_Clear() { var a = Mk(); var b = Mk("https://v.test/b.mp4", "B"); _pl.Add(a); _pl.Add(b); _pl.Remove(a); Assert.Single(_pl.Tracks); _pl.Clear(); Assert.Empty(_pl.Tracks); } [Fact] public void 다음_이전_트랙은_순환한다() { _pl.Add(Mk("u1", "1")); _pl.Add(Mk("u2", "2")); _pl.Add(Mk("u3", "3")); _pl.CurrentIndex = 0; Assert.Equal(1, _pl.Next()); Assert.Equal(2, _pl.Next()); Assert.Equal(0, _pl.Next()); // 순환 Assert.Equal(2, _pl.Previous()); } [Fact] public void 현재_트랙은_CurrentIndex로_얻는다() { _pl.Add(Mk("u1", "1")); _pl.Add(Mk("u2", "2")); _pl.CurrentIndex = 1; Assert.Equal("u2", _pl.Current?.Url); } [Fact] public void 영속_라운드트립() { _pl.Add(Mk("u1", "제목1", "Hls")); _pl.Add(Mk("u2", "제목2", "Direct")); _pl.CurrentIndex = 1; _pl.Save(_path); var loaded = new MediaPlaylist(); Assert.Equal(0, loaded.Tracks.Count); loaded.Load(_path); Assert.Equal(2, loaded.Tracks.Count); Assert.Equal("제목1", loaded.Tracks[0].Title); Assert.Equal("Hls", loaded.Tracks[0].Kind); Assert.Equal(1, loaded.CurrentIndex); } [Fact] public void 손상된_JSON은_빈_목록으로_시작() { File.WriteAllText(_path, "{broken"); var pl = new MediaPlaylist(); pl.Load(_path); Assert.Empty(pl.Tracks); } [Fact] public void 재생완료_표시와_완료필터() { _pl.Add(Mk("u1", "1")); _pl.Add(Mk("u2", "2")); _pl.MarkPlayed(_pl.Tracks[0]); Assert.True(_pl.Tracks[0].Played); Assert.False(_pl.Tracks[1].Played); } public void Dispose() { try { File.Delete(_path); } catch { } } }