using dy.net.job; using dy.net.model.dto; using dy.net.model.entity; using dy.net.model.response; using dy.net.service; using Quartz; namespace dy.net.Tests; public class QuartzTaskContextTests { [Fact] public void EmptyScheduledJobData_DoesNotRequireManualTaskKeys() { var context = DouyinBasicSyncJob.ResolveTaskContext(new JobDataMap()); Assert.Null(context.TaskId); Assert.Equal(VideoTaskTrigger.Scheduled, context.Trigger); } [Fact] public void ManualJobData_ReusesProvidedTaskId() { var data = new JobDataMap { ["video-task-id"] = "task-id", ["video-task-trigger"] = "manual" }; var context = DouyinBasicSyncJob.ResolveTaskContext(data); Assert.Equal("task-id", context.TaskId); Assert.Equal(VideoTaskTrigger.Manual, context.Trigger); } [Fact] public void LocalRecord_IsReplacedWhenCurrentStorageIsWebDav() { var local = new dy.net.model.entity.DouyinVideo { StorageType = StorageType.Local }; var remote = new dy.net.model.entity.DouyinVideo { StorageType = StorageType.WebDav }; Assert.True(DouyinBasicSyncJob.ShouldReplaceOldStorageRecord(local, StorageType.WebDav)); Assert.False(DouyinBasicSyncJob.ShouldReplaceOldStorageRecord(remote, StorageType.WebDav)); Assert.False(DouyinBasicSyncJob.ShouldReplaceOldStorageRecord(remote, StorageType.Local)); Assert.False(DouyinBasicSyncJob.ShouldReplaceOldStorageRecord(null, StorageType.WebDav)); } [Theory] [InlineData(true, null, null)] [InlineData(false, true, null)] [InlineData(false, false, true)] [InlineData(false, false, false)] public void BestMatchedVideoUrl_MissingMediaData_ReturnsNull( bool nullAweme, bool? nullVideo, bool? nullBitRates) { Aweme aweme = null; if (!nullAweme) { aweme = new Aweme(); if (nullVideo != true) aweme.Video = new Video { BitRate = nullBitRates == true ? null : new List() }; } var selected = DouyinBasicSyncJob.GetBestMatchedVideoUrl(aweme, new AppConfig()); Assert.Null(selected); } [Fact] public void BestMatchedVideoUrl_IgnoresMalformedBitRatesAndEmptyUrls() { var aweme = new Aweme { Video = new Video { BitRate = new List { null, new() { PlayAddr = null }, new() { PlayAddr = new PlayAddr { UrlList = null } }, new() { PlayAddr = new PlayAddr { UrlList = new List { "", " " } } } } } }; Assert.Null(DouyinBasicSyncJob.GetBestMatchedVideoUrl(aweme, new AppConfig())); Assert.Empty(DouyinBasicSyncJob.GetMediaCandidates(aweme, null)); } [Fact] public void BestMatchedVideoUrl_PrefersH265AndFallsBackToH264() { var h264Low = BitRate("https://media.test/h264-low", isH265: 0, value: 100); var h264High = BitRate("https://media.test/h264-high", isH265: 0, value: 300); var h265 = BitRate("https://media.test/h265", isH265: 1, value: 200); var aweme = new Aweme { Video = new Video { BitRate = new List { h264Low, h265, h264High } } }; Assert.Same(h265, DouyinBasicSyncJob.GetBestMatchedVideoUrl(aweme, new AppConfig { VideoEncoder = 265 })); Assert.Same(h264High, DouyinBasicSyncJob.GetBestMatchedVideoUrl(aweme, new AppConfig { VideoEncoder = 264 })); aweme.Video.BitRate = new List { h264Low, h264High }; Assert.Same(h264High, DouyinBasicSyncJob.GetBestMatchedVideoUrl(aweme, new AppConfig { VideoEncoder = 265 })); } [Fact] public void MediaCandidates_UsesSelectedFirstAndRemovesEmptyAndDuplicateUrls() { var selected = BitRate("https://media.test/preferred", isH265: 1, value: 200); selected.PlayAddr.UrlList.Add("https://media.test/shared"); var fallback = BitRate("https://media.test/shared", isH265: 0, value: 100); fallback.PlayAddr.UrlList.Add(""); fallback.PlayAddr.UrlList.Add("https://media.test/fallback"); var aweme = new Aweme { Video = new Video { BitRate = new List { null, fallback, selected } } }; var candidates = DouyinBasicSyncJob.GetMediaCandidates(aweme, selected); Assert.Equal(new[] { "https://media.test/preferred", "https://media.test/shared", "https://media.test/fallback" }, candidates); } [Theory] [InlineData(VideoTypeEnum.dy_favorite, true)] [InlineData(VideoTypeEnum.dy_collects, true)] [InlineData(VideoTypeEnum.dy_follows, true)] [InlineData(VideoTypeEnum.dy_custom_collect, true)] [InlineData(VideoTypeEnum.dy_mix, true)] [InlineData(VideoTypeEnum.dy_series, true)] [InlineData(VideoTypeEnum.ImageVideo, false)] [InlineData(VideoTypeEnum.dy_followuser, false)] [InlineData(VideoTypeEnum.dy_followuser_once, false)] [InlineData(VideoTypeEnum.dy_live_monitor, false)] public void ManualVideoSyncType_OnlyAllowsDownloadJobs(VideoTypeEnum type, bool expected) { Assert.Equal(expected, DouyinQuartzJobService.IsVideoSyncType(type)); } private static VideoBitRate BitRate(string url, int isH265, int value) => new() { IsH265 = isH265, BitRateValue = value, PlayAddr = new PlayAddr { UrlList = new List { url } } }; }