2
0
forked from sim0n00ps/OF-DL
OF-DL/OF DL.Tests/Services/DownloadOrchestrationServiceTests.cs

303 lines
13 KiB
C#

using Newtonsoft.Json.Linq;
using OF_DL.Models.Config;
using OF_DL.Models.Downloads;
using OF_DL.Services;
using MessageEntities = OF_DL.Models.Entities.Messages;
using PostEntities = OF_DL.Models.Entities.Posts;
using PurchasedEntities = OF_DL.Models.Entities.Purchased;
namespace OF_DL.Tests.Services;
public class DownloadOrchestrationServiceTests
{
[Fact]
public async Task GetAvailableUsersAsync_FiltersIgnoredUsers()
{
Config config = CreateConfig(c =>
{
c.IncludeExpiredSubscriptions = true;
c.IgnoredUsersListName = "ignored";
});
FakeConfigService configService = new(config);
ConfigurableApiService apiService = new()
{
ActiveSubscriptionsHandler = (_, _) =>
Task.FromResult<Dictionary<string, long>?>(new Dictionary<string, long> { { "alice", 1 } }),
ExpiredSubscriptionsHandler = (_, _) =>
Task.FromResult<Dictionary<string, long>?>(new Dictionary<string, long> { { "bob", 2 } }),
ListsHandler =
_ => Task.FromResult<Dictionary<string, long>?>(new Dictionary<string, long> { { "ignored", 10 } }),
ListUsersHandler = _ => Task.FromResult<List<string>?>(["alice"])
};
UserTrackingDbService dbService = new();
DownloadOrchestrationService service =
new(apiService, configService, new OrchestrationDownloadServiceStub(), dbService);
UserListResult result = await service.GetAvailableUsersAsync();
Assert.Single(result.Users);
Assert.True(result.Users.ContainsKey("bob"));
Assert.Null(result.IgnoredListError);
Assert.NotNull(dbService.CreatedUsers);
Assert.True(dbService.CreatedUsers.ContainsKey("bob"));
Assert.False(dbService.CreatedUsers.ContainsKey("alice"));
}
[Fact]
public async Task GetAvailableUsersAsync_SetsIgnoredListErrorWhenMissing()
{
Config config = CreateConfig(c =>
{
c.IncludeExpiredSubscriptions = false;
c.IgnoredUsersListName = "missing";
});
FakeConfigService configService = new(config);
ConfigurableApiService apiService = new()
{
ActiveSubscriptionsHandler = (_, _) =>
Task.FromResult<Dictionary<string, long>?>(new Dictionary<string, long> { { "alice", 1 } }),
ListsHandler = _ => Task.FromResult<Dictionary<string, long>?>(new Dictionary<string, long>())
};
UserTrackingDbService dbService = new();
DownloadOrchestrationService service =
new(apiService, configService, new OrchestrationDownloadServiceStub(), dbService);
UserListResult result = await service.GetAvailableUsersAsync();
Assert.NotNull(result.IgnoredListError);
Assert.Single(result.Users);
Assert.True(result.Users.ContainsKey("alice"));
}
[Fact]
public async Task GetUsersForListAsync_ReturnsUsersInList()
{
FakeConfigService configService = new(CreateConfig());
ConfigurableApiService apiService = new() { ListUsersHandler = _ => Task.FromResult<List<string>?>(["bob"]) };
DownloadOrchestrationService service =
new(apiService, configService, new OrchestrationDownloadServiceStub(), new UserTrackingDbService());
Dictionary<string, long> allUsers = new() { { "alice", 1 }, { "bob", 2 } };
Dictionary<string, long> lists = new() { { "mylist", 5 } };
Dictionary<string, long> result = await service.GetUsersForListAsync("mylist", allUsers, lists);
Assert.Single(result);
Assert.Equal(2, result["bob"]);
}
[Fact]
public void ResolveDownloadPath_UsesConfiguredPathWhenSet()
{
Config config = CreateConfig(c => c.DownloadPath = "C:\\Downloads");
DownloadOrchestrationService service =
new(new ConfigurableApiService(), new FakeConfigService(config), new OrchestrationDownloadServiceStub(),
new UserTrackingDbService());
string path = service.ResolveDownloadPath("creator");
Assert.Equal(Path.Combine("C:\\Downloads", "creator"), path);
}
[Fact]
public void ResolveDownloadPath_UsesDefaultWhenBlank()
{
Config config = CreateConfig(c => c.DownloadPath = "");
DownloadOrchestrationService service =
new(new ConfigurableApiService(), new FakeConfigService(config), new OrchestrationDownloadServiceStub(),
new UserTrackingDbService());
string path = service.ResolveDownloadPath("creator");
Assert.Equal("__user_data__/sites/OnlyFans/creator", path);
}
[Fact]
public async Task PrepareUserFolderAsync_CreatesFolderAndDb()
{
using TempFolder temp = new();
string userPath = Path.Combine(temp.Path, "creator");
UserTrackingDbService dbService = new();
DownloadOrchestrationService service =
new(new ConfigurableApiService(), new FakeConfigService(CreateConfig()),
new OrchestrationDownloadServiceStub(), dbService);
await service.PrepareUserFolderAsync("creator", 99, userPath);
Assert.True(Directory.Exists(userPath));
Assert.True(dbService.CheckedUser.HasValue);
Assert.Equal("creator", dbService.CheckedUser.Value.user.Key);
Assert.Equal(99, dbService.CheckedUser.Value.user.Value);
Assert.Equal(userPath, dbService.CheckedUser.Value.path);
Assert.Contains(userPath, dbService.CreatedDbs);
}
[Fact]
public async Task DownloadSinglePostAsync_WhenMissingPost_SendsMessage()
{
ConfigurableApiService apiService = new()
{
PostHandler = (_, _) => Task.FromResult(new PostEntities.SinglePostCollection())
};
OrchestrationDownloadServiceStub downloadService = new();
RecordingDownloadEventHandler eventHandler = new();
DownloadOrchestrationService service =
new(apiService, new FakeConfigService(CreateConfig()), downloadService, new UserTrackingDbService());
await service.DownloadSinglePostAsync("creator", 42, "/tmp", new Dictionary<string, long>(),
true, true, eventHandler);
Assert.Contains("Getting Post", eventHandler.Messages);
Assert.Contains("Couldn't find post", eventHandler.Messages);
Assert.False(downloadService.SinglePostCalled);
}
[Fact]
public async Task DownloadSinglePostAsync_WhenDownloaded_SendsDownloadedMessage()
{
PostEntities.SinglePostCollection collection = new()
{
SinglePosts = new Dictionary<long, string> { { 1, "https://example.com/post.jpg" } }
};
ConfigurableApiService apiService = new() { PostHandler = (_, _) => Task.FromResult(collection) };
OrchestrationDownloadServiceStub downloadService = new()
{
SinglePostResult = new DownloadResult { NewDownloads = 1, TotalCount = 1 }
};
RecordingDownloadEventHandler eventHandler = new();
DownloadOrchestrationService service =
new(apiService, new FakeConfigService(CreateConfig()), downloadService, new UserTrackingDbService());
await service.DownloadSinglePostAsync("creator", 99, "/tmp", new Dictionary<string, long>(),
true, true, eventHandler);
Assert.Contains("Post 99 downloaded", eventHandler.Messages);
Assert.True(downloadService.SinglePostCalled);
Assert.True(eventHandler.ProgressCalls.Count > 0);
}
[Fact]
public async Task DownloadSinglePaidMessageAsync_WithPreviewDownloads()
{
PurchasedEntities.SinglePaidMessageCollection collection = new()
{
PreviewSingleMessages = new Dictionary<long, string> { { 1, "https://example.com/preview.jpg" } },
SingleMessages = new Dictionary<long, string> { { 2, "https://example.com/full.jpg" } },
SingleMessageObjects = [new MessageEntities.SingleMessage()]
};
ConfigurableApiService apiService = new() { PaidMessageHandler = (_, _) => Task.FromResult(collection) };
OrchestrationDownloadServiceStub downloadService = new()
{
SinglePaidMessageResult = new DownloadResult { TotalCount = 1, NewDownloads = 1 }
};
RecordingDownloadEventHandler eventHandler = new();
DownloadOrchestrationService service =
new(apiService, new FakeConfigService(CreateConfig()), downloadService, new UserTrackingDbService());
await service.DownloadSinglePaidMessageAsync("creator", 5, "/tmp", new Dictionary<string, long>(),
true, true, eventHandler);
Assert.Contains(eventHandler.ContentFound, entry => entry.contentType == "Preview Paid Messages");
Assert.True(downloadService.SinglePaidMessageCalled);
}
[Fact]
public async Task DownloadCreatorContentAsync_DownloadsStoriesWhenEnabled()
{
using TempFolder temp = new();
string path = Path.Combine(temp.Path, "creator");
Config config = CreateConfig(c =>
{
c.DownloadStories = true;
c.ShowScrapeSize = false;
});
FakeConfigService configService = new(config);
ConfigurableApiService apiService = new()
{
MediaHandler = (_, _, _, _) => Task.FromResult<Dictionary<long, string>?>(
new Dictionary<long, string>
{
{ 1, "https://example.com/one.jpg" }, { 2, "https://example.com/two.jpg" }
})
};
OrchestrationDownloadServiceStub downloadService = new()
{
StoriesResult = new DownloadResult { TotalCount = 2, NewDownloads = 2 }
};
RecordingDownloadEventHandler eventHandler = new();
DownloadOrchestrationService service =
new(apiService, configService, downloadService, new UserTrackingDbService());
CreatorDownloadResult result = await service.DownloadCreatorContentAsync("creator", 1, path,
new Dictionary<string, long>(), true, true, eventHandler);
Assert.Equal(2, result.StoriesCount);
Assert.Contains(eventHandler.ContentFound, entry => entry.contentType == "Stories");
Assert.Contains(eventHandler.DownloadCompletes, entry => entry.contentType == "Stories");
}
[Fact]
public async Task ResolveUsernameAsync_ReturnsDeletedPlaceholderWhenMissing()
{
ConfigurableApiService apiService = new() { UserInfoByIdHandler = _ => Task.FromResult<JObject?>(null) };
DownloadOrchestrationService service =
new(apiService, new FakeConfigService(CreateConfig()), new OrchestrationDownloadServiceStub(),
new UserTrackingDbService());
string? result = await service.ResolveUsernameAsync(123);
Assert.Equal("Deleted User - 123", result);
}
[Fact]
public async Task ResolveUsernameAsync_ReturnsUsernameWhenPresent()
{
JObject payload = new() { ["5"] = new JObject { ["username"] = "creator" } };
ConfigurableApiService apiService = new() { UserInfoByIdHandler = _ => Task.FromResult<JObject?>(payload) };
DownloadOrchestrationService service =
new(apiService, new FakeConfigService(CreateConfig()), new OrchestrationDownloadServiceStub(),
new UserTrackingDbService());
string? result = await service.ResolveUsernameAsync(5);
Assert.Equal("creator", result);
}
private static Config CreateConfig(Action<Config>? configure = null)
{
Config config = new()
{
DownloadAvatarHeaderPhoto = false,
DownloadPaidPosts = false,
DownloadPosts = false,
DownloadArchived = false,
DownloadStreams = false,
DownloadStories = false,
DownloadHighlights = false,
DownloadMessages = false,
DownloadPaidMessages = false,
DownloadImages = false,
DownloadVideos = false,
DownloadAudios = false,
IncludeExpiredSubscriptions = false,
IncludeRestrictedSubscriptions = false,
SkipAds = false,
IgnoreOwnMessages = false,
DownloadPostsIncrementally = false,
BypassContentForCreatorsWhoNoLongerExist = false,
DownloadDuplicatedMedia = false,
DownloadOnlySpecificDates = false,
NonInteractiveModePurchasedTab = false,
LimitDownloadRate = false,
FolderPerPaidPost = false,
FolderPerPost = false,
FolderPerPaidMessage = false,
FolderPerMessage = false,
ShowScrapeSize = false,
DisableBrowserAuth = false
};
configure?.Invoke(config);
return config;
}
}