diff --git a/OF DL.Core/Models/Config/Config.cs b/OF DL.Core/Models/Config/Config.cs index 478a661..8bab3fa 100644 --- a/OF DL.Core/Models/Config/Config.cs +++ b/OF DL.Core/Models/Config/Config.cs @@ -118,22 +118,22 @@ public class Config : IFileNameFormatConfig if (CreatorConfigs.TryGetValue(username, out CreatorConfig? creatorConfig)) { - if (creatorConfig.PaidPostFileNameFormat != null) + if (!string.IsNullOrEmpty(creatorConfig.PaidPostFileNameFormat)) { combinedFilenameFormatConfig.PaidPostFileNameFormat = creatorConfig.PaidPostFileNameFormat; } - if (creatorConfig.PostFileNameFormat != null) + if (!string.IsNullOrEmpty(creatorConfig.PostFileNameFormat)) { combinedFilenameFormatConfig.PostFileNameFormat = creatorConfig.PostFileNameFormat; } - if (creatorConfig.PaidMessageFileNameFormat != null) + if (!string.IsNullOrEmpty(creatorConfig.PaidMessageFileNameFormat)) { combinedFilenameFormatConfig.PaidMessageFileNameFormat = creatorConfig.PaidMessageFileNameFormat; } - if (creatorConfig.MessageFileNameFormat != null) + if (!string.IsNullOrEmpty(creatorConfig.MessageFileNameFormat)) { combinedFilenameFormatConfig.MessageFileNameFormat = creatorConfig.MessageFileNameFormat; } diff --git a/OF DL.Core/Services/DownloadService.cs b/OF DL.Core/Services/DownloadService.cs index d61ae3a..8e6a00e 100644 --- a/OF DL.Core/Services/DownloadService.cs +++ b/OF DL.Core/Services/DownloadService.cs @@ -1555,11 +1555,12 @@ public class DownloadService( PurchasedEntities.ListItem? messageInfo = paidMessageCollection.PaidMessageObjects.FirstOrDefault(p => p.Media?.Any(m => m.Id == kvpEntry.Key) == true); string filenameFormat = - configService.CurrentConfig.GetCreatorFileNameFormatConfig(username).MessageFileNameFormat ?? ""; + configService.CurrentConfig.GetCreatorFileNameFormatConfig(username).PaidMessageFileNameFormat ?? ""; string paidMsgPath = configService.CurrentConfig.FolderPerPaidMessage && messageInfo != null && messageInfo.Id != 0 && messageInfo.CreatedAt is not null ? $"/Messages/Paid/{messageInfo.Id} {messageInfo.CreatedAt.Value:yyyy-MM-dd HH-mm-ss}" : "/Messages/Paid"; + object? messageAuthor = messageInfo?.FromUser ?? (object?)messageInfo?.Author; if (kvpEntry.Value.Contains("cdn3.onlyfans.com/dash/files")) { @@ -1576,12 +1577,12 @@ public class DownloadService( isNew = await DownloadDrmVideo(parsed[1], parsed[2], parsed[3], parsed[0], drmInfo.Value.decryptionKey, path, drmInfo.Value.lastModified, kvpEntry.Key, "Messages", progressReporter, paidMsgPath + "/Videos", filenameFormat, - messageInfo, mediaInfo, messageInfo?.FromUser, users, drmInfo.Value.mpdDurationSeconds); + messageInfo, mediaInfo, messageAuthor, users, drmInfo.Value.mpdDurationSeconds); } else { isNew = await DownloadMedia(kvpEntry.Value, path, kvpEntry.Key, "Messages", progressReporter, - paidMsgPath, filenameFormat, messageInfo, mediaInfo, messageInfo?.FromUser, users); + paidMsgPath, filenameFormat, messageInfo, mediaInfo, messageAuthor, users); } if (isNew) @@ -1835,11 +1836,12 @@ public class DownloadService( PurchasedEntities.ListItem? postInfo = purchasedPosts.PaidPostObjects.FirstOrDefault(p => p.Media?.Any(m => m.Id == postKvp.Key) == true); string filenameFormat = - configService.CurrentConfig.GetCreatorFileNameFormatConfig(username).PostFileNameFormat ?? ""; + configService.CurrentConfig.GetCreatorFileNameFormatConfig(username).PaidPostFileNameFormat ?? ""; string paidPostPath = configService.CurrentConfig.FolderPerPaidPost && postInfo != null && postInfo.Id != 0 && postInfo.PostedAt is not null ? $"/Posts/Paid/{postInfo.Id} {postInfo.PostedAt.Value:yyyy-MM-dd HH-mm-ss}" : "/Posts/Paid"; + object? postAuthor = postInfo?.FromUser ?? (object?)postInfo?.Author; if (postKvp.Value.Contains("cdn3.onlyfans.com/dash/files")) { @@ -1856,12 +1858,12 @@ public class DownloadService( isNew = await DownloadDrmVideo(parsed[1], parsed[2], parsed[3], parsed[0], drmInfo.Value.decryptionKey, path, drmInfo.Value.lastModified, postKvp.Key, "Posts", progressReporter, paidPostPath + "/Videos", filenameFormat, - postInfo, mediaInfo, postInfo?.FromUser, users, drmInfo.Value.mpdDurationSeconds); + postInfo, mediaInfo, postAuthor, users, drmInfo.Value.mpdDurationSeconds); } else { isNew = await DownloadMedia(postKvp.Value, path, postKvp.Key, "Posts", progressReporter, - paidPostPath, filenameFormat, postInfo, mediaInfo, postInfo?.FromUser, users); + paidPostPath, filenameFormat, postInfo, mediaInfo, postAuthor, users); } if (isNew) @@ -1925,6 +1927,7 @@ public class DownloadService( postInfo.Id != 0 && postInfo.PostedAt is not null ? $"/Posts/Paid/{postInfo.Id} {postInfo.PostedAt.Value:yyyy-MM-dd HH-mm-ss}" : "/Posts/Paid"; + object? postAuthor = postInfo?.FromUser ?? (object?)postInfo?.Author; if (purchasedPostKvp.Value.Contains("cdn3.onlyfans.com/dash/files")) { @@ -1940,13 +1943,13 @@ public class DownloadService( isNew = await DownloadDrmVideo(parsed[1], parsed[2], parsed[3], parsed[0], drmInfo.Value.decryptionKey, path, drmInfo.Value.lastModified, purchasedPostKvp.Key, "Posts", progressReporter, paidPostPath + "/Videos", filenameFormat, - postInfo, mediaInfo, postInfo?.FromUser, users, drmInfo.Value.mpdDurationSeconds); + postInfo, mediaInfo, postAuthor, users, drmInfo.Value.mpdDurationSeconds); } else { isNew = await DownloadMedia(purchasedPostKvp.Value, path, purchasedPostKvp.Key, "Posts", progressReporter, - paidPostPath, filenameFormat, postInfo, mediaInfo, postInfo?.FromUser, users); + paidPostPath, filenameFormat, postInfo, mediaInfo, postAuthor, users); } if (isNew) @@ -2009,6 +2012,7 @@ public class DownloadService( messageInfo.Id != 0 && messageInfo.CreatedAt is not null ? $"/Messages/Paid/{messageInfo.Id} {messageInfo.CreatedAt.Value:yyyy-MM-dd HH-mm-ss}" : "/Messages/Paid"; + object? messageAuthor = messageInfo?.FromUser ?? (object?)messageInfo?.Author; if (paidMessageKvp.Value.Contains("cdn3.onlyfans.com/dash/files")) { @@ -2024,13 +2028,13 @@ public class DownloadService( isNew = await DownloadDrmVideo(parsed[1], parsed[2], parsed[3], parsed[0], drmInfo.Value.decryptionKey, path, drmInfo.Value.lastModified, paidMessageKvp.Key, "Messages", progressReporter, paidMsgPath + "/Videos", filenameFormat, - messageInfo, mediaInfo, messageInfo?.FromUser, users, drmInfo.Value.mpdDurationSeconds); + messageInfo, mediaInfo, messageAuthor, users, drmInfo.Value.mpdDurationSeconds); } else { isNew = await DownloadMedia(paidMessageKvp.Value, path, paidMessageKvp.Key, "Messages", progressReporter, - paidMsgPath, filenameFormat, messageInfo, mediaInfo, messageInfo?.FromUser, users); + paidMsgPath, filenameFormat, messageInfo, mediaInfo, messageAuthor, users); } if (isNew) diff --git a/OF DL.Core/Services/FileNameService.cs b/OF DL.Core/Services/FileNameService.cs index 92414e8..4290c72 100644 --- a/OF DL.Core/Services/FileNameService.cs +++ b/OF DL.Core/Services/FileNameService.cs @@ -200,8 +200,18 @@ public class FileNameService(IAuthService authService) : IFileNameService object? value = source; foreach (string propertyName in propertyPath.Split('.')) { - PropertyInfo property = value?.GetType().GetProperty(propertyName) ?? - throw new ArgumentException($"Property '{propertyName}' not found."); + if (value == null) + { + return null; + } + + PropertyInfo? property = value.GetType().GetProperty(propertyName, + BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase); + if (property == null) + { + return null; + } + value = property.GetValue(value); } diff --git a/OF DL.Tests/Models/Config/ConfigTests.cs b/OF DL.Tests/Models/Config/ConfigTests.cs new file mode 100644 index 0000000..4f99a0f --- /dev/null +++ b/OF DL.Tests/Models/Config/ConfigTests.cs @@ -0,0 +1,122 @@ +using OF_DL.Models.Config; + +namespace OF_DL.Tests.Models.Config; + +public class ConfigTests +{ + [Fact] + public void GetCreatorFileNameFormatConfig_UsesCreatorFormatWhenDefined() + { + OF_DL.Models.Config.Config config = new() + { + PaidPostFileNameFormat = "global-paid-post", + PostFileNameFormat = "global-post", + PaidMessageFileNameFormat = "global-paid-message", + MessageFileNameFormat = "global-message", + CreatorConfigs = new Dictionary + { + ["creator"] = new() + { + PaidPostFileNameFormat = "creator-paid-post", + PostFileNameFormat = "creator-post", + PaidMessageFileNameFormat = "creator-paid-message", + MessageFileNameFormat = "creator-message" + } + } + }; + + IFileNameFormatConfig result = config.GetCreatorFileNameFormatConfig("creator"); + + Assert.Equal("creator-paid-post", result.PaidPostFileNameFormat); + Assert.Equal("creator-post", result.PostFileNameFormat); + Assert.Equal("creator-paid-message", result.PaidMessageFileNameFormat); + Assert.Equal("creator-message", result.MessageFileNameFormat); + } + + [Fact] + public void GetCreatorFileNameFormatConfig_FallsBackToGlobalWhenCreatorFormatIsNullOrEmpty() + { + OF_DL.Models.Config.Config config = new() + { + PaidPostFileNameFormat = "global-paid-post", + PostFileNameFormat = "global-post", + PaidMessageFileNameFormat = "global-paid-message", + MessageFileNameFormat = "global-message", + CreatorConfigs = new Dictionary + { + ["creator"] = new() + { + PaidPostFileNameFormat = null, + PostFileNameFormat = "", + PaidMessageFileNameFormat = null, + MessageFileNameFormat = "" + } + } + }; + + IFileNameFormatConfig result = config.GetCreatorFileNameFormatConfig("creator"); + + Assert.Equal("global-paid-post", result.PaidPostFileNameFormat); + Assert.Equal("global-post", result.PostFileNameFormat); + Assert.Equal("global-paid-message", result.PaidMessageFileNameFormat); + Assert.Equal("global-message", result.MessageFileNameFormat); + } + + [Fact] + public void GetCreatorFileNameFormatConfig_UsesGlobalWhenCreatorConfigDoesNotExist() + { + OF_DL.Models.Config.Config config = new() + { + PaidPostFileNameFormat = "global-paid-post", + PostFileNameFormat = "global-post", + PaidMessageFileNameFormat = "global-paid-message", + MessageFileNameFormat = "global-message", + CreatorConfigs = new Dictionary + { + ["other-creator"] = new() + { + PaidPostFileNameFormat = "other-paid-post", + PostFileNameFormat = "other-post", + PaidMessageFileNameFormat = "other-paid-message", + MessageFileNameFormat = "other-message" + } + } + }; + + IFileNameFormatConfig result = config.GetCreatorFileNameFormatConfig("creator"); + + Assert.Equal("global-paid-post", result.PaidPostFileNameFormat); + Assert.Equal("global-post", result.PostFileNameFormat); + Assert.Equal("global-paid-message", result.PaidMessageFileNameFormat); + Assert.Equal("global-message", result.MessageFileNameFormat); + } + + [Fact] + public void GetCreatorFileNameFormatConfig_ReturnsEmptyFormatsWhenCreatorAndGlobalAreUndefined() + { + OF_DL.Models.Config.Config config = new() + { + PaidPostFileNameFormat = "", + PostFileNameFormat = "", + PaidMessageFileNameFormat = "", + MessageFileNameFormat = "", + CreatorConfigs = new Dictionary + { + ["creator"] = new() + { + PaidPostFileNameFormat = "", + PostFileNameFormat = null, + PaidMessageFileNameFormat = "", + MessageFileNameFormat = null + } + } + }; + + IFileNameFormatConfig result = config.GetCreatorFileNameFormatConfig("creator"); + + Assert.True(string.IsNullOrEmpty(result.PaidPostFileNameFormat)); + Assert.True(string.IsNullOrEmpty(result.PostFileNameFormat)); + Assert.True(string.IsNullOrEmpty(result.PaidMessageFileNameFormat)); + Assert.True(string.IsNullOrEmpty(result.MessageFileNameFormat)); + } +} diff --git a/OF DL.Tests/Services/DownloadServiceTests.cs b/OF DL.Tests/Services/DownloadServiceTests.cs index cf5fa5c..81c10ab 100644 --- a/OF DL.Tests/Services/DownloadServiceTests.cs +++ b/OF DL.Tests/Services/DownloadServiceTests.cs @@ -1,6 +1,10 @@ using System.Reflection; using OF_DL.Models.Config; using OF_DL.Models.Downloads; +using OF_DL.Models; +using PostEntities = OF_DL.Models.Entities.Posts; +using PurchasedEntities = OF_DL.Models.Entities.Purchased; +using MessageEntities = OF_DL.Models.Entities.Messages; using OF_DL.Services; namespace OF_DL.Tests.Services; @@ -183,11 +187,218 @@ public class DownloadServiceTests Assert.Equal(2, progress.Total); } + [Fact] + public async Task DownloadFreePosts_UsesDefaultFilenameWhenNoGlobalOrCreatorFormatIsDefined() + { + using TempFolder temp = new(); + string folder = NormalizeFolder(Path.Combine(temp.Path, "creator")); + const string serverFilename = "server-name"; + string existingFilePath = $"{folder}/Posts/Free/Images/{serverFilename}.jpg"; + Directory.CreateDirectory(Path.GetDirectoryName(existingFilePath) ?? throw new InvalidOperationException()); + await File.WriteAllTextAsync(existingFilePath, "abc"); + + Config config = new() + { + ShowScrapeSize = false, + PostFileNameFormat = "", + CreatorConfigs = new Dictionary + { + ["creator"] = new() { PostFileNameFormat = "" } + } + }; + + MediaTrackingDbService dbService = new() { CheckDownloadedResult = false }; + DownloadService service = CreateService(new FakeConfigService(config), dbService); + ProgressRecorder progress = new(); + PostEntities.PostCollection posts = new() + { + Posts = new Dictionary { { 1, $"https://example.com/{serverFilename}.jpg" } } + }; + + DownloadResult result = await service.DownloadFreePosts("creator", 1, folder, new Dictionary(), + false, false, posts, progress); + + Assert.Equal(1, result.TotalCount); + Assert.Equal(0, result.NewDownloads); + Assert.Equal(1, result.ExistingDownloads); + Assert.NotNull(dbService.LastUpdateMedia); + Assert.Equal($"{serverFilename}.jpg", dbService.LastUpdateMedia.Value.filename); + Assert.Equal(1, progress.Total); + } + + [Fact] + public async Task DownloadFreePosts_UsesGlobalCustomFormatWhenCreatorCustomFormatNotDefined() + { + using TempFolder temp = new(); + string folder = NormalizeFolder(Path.Combine(temp.Path, "creator")); + const string serverFilename = "server-name"; + string existingFilePath = $"{folder}/Posts/Free/Images/{serverFilename}.jpg"; + Directory.CreateDirectory(Path.GetDirectoryName(existingFilePath) ?? throw new InvalidOperationException()); + await File.WriteAllTextAsync(existingFilePath, "abc"); + + Config config = new() + { + ShowScrapeSize = false, + PostFileNameFormat = "global-custom-name", + CreatorConfigs = new Dictionary + { + ["creator"] = new() { PostFileNameFormat = "" } + } + }; + + MediaTrackingDbService dbService = new() { CheckDownloadedResult = false }; + DownloadService service = CreateService(new FakeConfigService(config), dbService, + fileNameService: new DeterministicFileNameService()); + ProgressRecorder progress = new(); + PostEntities.PostCollection posts = CreatePostCollection(1, serverFilename); + + DownloadResult result = await service.DownloadFreePosts("creator", 1, folder, new Dictionary(), + false, false, posts, progress); + + string renamedPath = $"{folder}/Posts/Free/Images/global-custom-name.jpg"; + Assert.Equal(1, result.TotalCount); + Assert.Equal(0, result.NewDownloads); + Assert.Equal(1, result.ExistingDownloads); + Assert.False(File.Exists(existingFilePath)); + Assert.True(File.Exists(renamedPath)); + Assert.NotNull(dbService.LastUpdateMedia); + Assert.Equal("global-custom-name.jpg", dbService.LastUpdateMedia.Value.filename); + Assert.Equal(1, progress.Total); + } + + [Fact] + public async Task DownloadFreePosts_UsesCreatorCustomFormatWhenGlobalAndCreatorFormatsAreDefined() + { + using TempFolder temp = new(); + string folder = NormalizeFolder(Path.Combine(temp.Path, "creator")); + const string serverFilename = "server-name"; + string existingFilePath = $"{folder}/Posts/Free/Images/{serverFilename}.jpg"; + Directory.CreateDirectory(Path.GetDirectoryName(existingFilePath) ?? throw new InvalidOperationException()); + await File.WriteAllTextAsync(existingFilePath, "abc"); + + Config config = new() + { + ShowScrapeSize = false, + PostFileNameFormat = "global-custom-name", + CreatorConfigs = new Dictionary + { + ["creator"] = new() { PostFileNameFormat = "creator-custom-name" } + } + }; + + MediaTrackingDbService dbService = new() { CheckDownloadedResult = false }; + DownloadService service = CreateService(new FakeConfigService(config), dbService, + fileNameService: new DeterministicFileNameService()); + ProgressRecorder progress = new(); + PostEntities.PostCollection posts = CreatePostCollection(1, serverFilename); + + DownloadResult result = await service.DownloadFreePosts("creator", 1, folder, new Dictionary(), + false, false, posts, progress); + + string renamedPath = $"{folder}/Posts/Free/Images/creator-custom-name.jpg"; + Assert.Equal(1, result.TotalCount); + Assert.Equal(0, result.NewDownloads); + Assert.Equal(1, result.ExistingDownloads); + Assert.False(File.Exists(existingFilePath)); + Assert.True(File.Exists(renamedPath)); + Assert.NotNull(dbService.LastUpdateMedia); + Assert.Equal("creator-custom-name.jpg", dbService.LastUpdateMedia.Value.filename); + Assert.Equal(1, progress.Total); + } + + [Fact] + public async Task DownloadPaidPosts_AppliesPaidCustomFormatForDrm_WhenAuthorExistsButFromUserMissing() + { + using TempFolder temp = new(); + string folder = NormalizeFolder(Path.Combine(temp.Path, "creator")); + const string customName = "paid-custom-name"; + const string drmBaseFilename = "video-file"; + string basePath = $"{folder}/Posts/Paid/Videos"; + Directory.CreateDirectory(basePath); + await File.WriteAllTextAsync($"{basePath}/{customName}.mp4", "custom"); + await File.WriteAllTextAsync($"{basePath}/{drmBaseFilename}_source.mp4", "server"); + + Config config = new() { ShowScrapeSize = false, PaidPostFileNameFormat = customName, PostFileNameFormat = "" }; + + MediaTrackingDbService dbService = new() { CheckDownloadedResult = false }; + StaticApiService apiService = new(); + FakeAuthService authService = new() + { + CurrentAuth = new Auth { Cookie = "sess=test;", UserAgent = "unit-test-agent" } + }; + + DownloadService service = CreateService(new FakeConfigService(config), dbService, + apiService, new DeterministicFileNameService(), authService); + ProgressRecorder progress = new(); + PurchasedEntities.PaidPostCollection posts = CreatePaidPostCollectionForDrm(1, + $"https://cdn3.onlyfans.com/dash/files/{drmBaseFilename}.mpd,policy,signature,kvp,1,2"); + + DownloadResult result = await service.DownloadPaidPosts("creator", 1, folder, new Dictionary(), + false, false, posts, progress); + + Assert.Equal(1, result.TotalCount); + Assert.Equal(0, result.NewDownloads); + Assert.Equal(1, result.ExistingDownloads); + Assert.NotNull(dbService.LastUpdateMedia); + Assert.Equal($"{customName}.mp4", dbService.LastUpdateMedia.Value.filename); + Assert.Equal(1, progress.Total); + } + private static DownloadService CreateService(FakeConfigService configService, MediaTrackingDbService dbService, - StaticApiService? apiService = null) => - new(new FakeAuthService(), configService, dbService, new FakeFileNameService(), + StaticApiService? apiService = null, IFileNameService? fileNameService = null, + IAuthService? authService = null) => + new(authService ?? new FakeAuthService(), configService, dbService, + fileNameService ?? new FakeFileNameService(), apiService ?? new StaticApiService()); - private static string NormalizeFolder(string folder) => folder.Replace("\\", "/"); -} + private static PostEntities.PostCollection CreatePostCollection(long mediaId, string serverFilename) + { + PostEntities.Medium media = new() { Id = mediaId }; + PostEntities.ListItem post = new() + { + Id = 10, + PostedAt = new DateTime(2024, 1, 1), + Author = new OF_DL.Models.Entities.Common.Author { Id = 99 }, + Media = [media] + }; + return new PostEntities.PostCollection + { + Posts = new Dictionary { { mediaId, $"https://example.com/{serverFilename}.jpg" } }, + PostMedia = [media], + PostObjects = [post] + }; + } + + private static PurchasedEntities.PaidPostCollection CreatePaidPostCollectionForDrm(long mediaId, string drmUrl) + { + MessageEntities.Medium media = new() { Id = mediaId }; + PurchasedEntities.ListItem post = new() + { + Id = 20, + PostedAt = new DateTime(2024, 1, 1), + Author = new OF_DL.Models.Entities.Common.Author { Id = 99 }, + FromUser = null, + Media = [media] + }; + + return new PurchasedEntities.PaidPostCollection + { + PaidPosts = new Dictionary { { mediaId, drmUrl } }, + PaidPostMedia = [media], + PaidPostObjects = [post] + }; + } + + private static string NormalizeFolder(string folder) => folder.Replace("\\", "/"); + + private sealed class DeterministicFileNameService : IFileNameService + { + public Task BuildFilename(string fileFormat, Dictionary values) => + Task.FromResult(fileFormat); + + public Task> GetFilename(object info, object media, object author, + List selectedProperties, string username, Dictionary? users = null) => + Task.FromResult(new Dictionary()); + } +} diff --git a/OF DL.Tests/Services/FileNameServiceTestModels.cs b/OF DL.Tests/Services/FileNameServiceTestModels.cs index 9d1098b..6e961e8 100644 --- a/OF DL.Tests/Services/FileNameServiceTestModels.cs +++ b/OF DL.Tests/Services/FileNameServiceTestModels.cs @@ -20,7 +20,7 @@ internal sealed class TestMedia internal sealed class TestMediaFiles { - public TestMediaFull Full { get; set; } = new(); + public TestMediaFull? Full { get; set; } = new(); public object? Drm { get; set; } } diff --git a/OF DL.Tests/Services/FileNameServiceTests.cs b/OF DL.Tests/Services/FileNameServiceTests.cs index 3d8805d..a3686fc 100644 --- a/OF DL.Tests/Services/FileNameServiceTests.cs +++ b/OF DL.Tests/Services/FileNameServiceTests.cs @@ -76,4 +76,24 @@ public class FileNameServiceTests Assert.Equal("creator_99", result); } + + [Fact] + public async Task GetFilename_UsesDrmFilenameWhenFullUrlMissing() + { + TestMedia media = new() + { + Id = 99, + Files = new TestMediaFiles + { + Full = null, + Drm = new { Manifest = new { Dash = "https://cdn.test/drm-name.mpd" } } + } + }; + FileNameService service = new(new FakeAuthService()); + + Dictionary values = + await service.GetFilename(new TestInfo(), media, new TestAuthor(), ["filename"], "creator"); + + Assert.Equal("drm-name_source", values["filename"]); + } }