using LiveRecorder.Application.Abstractions.Persistence; using LiveRecorder.Application.Services; using LiveRecorder.Domain.Entities; using LiveRecorder.Domain.Enums; namespace LiveRecorder.Tests; public sealed class SystemSettingsServiceTests { [Fact] public async Task GetAsync_reads_new_and_legacy_platform_settings() { var repository = new InMemoryAppSettingRepository( [ new AppSetting("platform_request.tiktok.proxy.enabled", "true", DateTimeOffset.UtcNow), new AppSetting("platform_request.tiktok.proxy.url", "http://127.0.0.1:8899", DateTimeOffset.UtcNow), new AppSetting("platform_request.tiktok.user_agent", "TikTok UA", DateTimeOffset.UtcNow), new AppSetting("platform_request.tiktok.referer", "https://www.tiktok.com/", DateTimeOffset.UtcNow), new AppSetting("platform_request.tiktok.cookie", "sessionid=tiktok", DateTimeOffset.UtcNow), new AppSetting("platform_proxy.douyin.enabled", "true", DateTimeOffset.UtcNow), new AppSetting("platform_proxy.douyin.url", "http://127.0.0.1:7890", DateTimeOffset.UtcNow), new AppSetting("douyin.user_agent", "Legacy Douyin UA", DateTimeOffset.UtcNow), new AppSetting("douyin.referer", "https://live.douyin.com/", DateTimeOffset.UtcNow), new AppSetting("douyin.cookie", "ttwid=legacy", DateTimeOffset.UtcNow) ]); var service = new SystemSettingsService(repository, new NoOpUnitOfWork()); var settings = await service.GetAsync(); var tiktok = settings.GetPlatformRequestSettings(LivePlatformType.TikTok); Assert.True(tiktok.Proxy.Enabled); Assert.Equal("http://127.0.0.1:8899", tiktok.Proxy.ProxyUrl); Assert.Equal("TikTok UA", tiktok.UserAgent); Assert.Equal("sessionid=tiktok", tiktok.Cookie); var douyin = settings.GetPlatformRequestSettings(LivePlatformType.Douyin); Assert.True(douyin.Proxy.Enabled); Assert.Equal("http://127.0.0.1:7890", douyin.Proxy.ProxyUrl); Assert.Equal("Legacy Douyin UA", douyin.UserAgent); Assert.Equal("ttwid=legacy", douyin.Cookie); } [Fact] public async Task UpdateAsync_persists_platform_request_keys() { var repository = new InMemoryAppSettingRepository([]); var service = new SystemSettingsService(repository, new NoOpUnitOfWork()); var request = new Application.Models.Settings.UpdateSystemSettingsRequest { PlatformRequestSettings = Application.Models.Settings.SystemSettingsDto.CreatePlatformRequestSettingsMap() }; request.GetPlatformRequestSettings(LivePlatformType.YouTube).Proxy.Enabled = true; request.GetPlatformRequestSettings(LivePlatformType.YouTube).Proxy.ProxyUrl = "http://127.0.0.1:8001"; request.GetPlatformRequestSettings(LivePlatformType.YouTube).UserAgent = "YouTube UA"; request.GetPlatformRequestSettings(LivePlatformType.Twitch).Cookie = "auth-token=123"; await service.UpdateAsync(request); var allSettings = await repository.ListAsync(); Assert.Contains(allSettings, static item => item.Key == "platform_request.youtube.proxy.enabled" && item.Value == "True"); Assert.Contains(allSettings, static item => item.Key == "platform_request.youtube.proxy.url" && item.Value == "http://127.0.0.1:8001"); Assert.Contains(allSettings, static item => item.Key == "platform_request.youtube.user_agent" && item.Value == "YouTube UA"); Assert.Contains(allSettings, static item => item.Key == "platform_request.twitch.cookie" && item.Value == "auth-token=123"); } [Fact] public async Task StorageThresholds_NormalizeUnsafeLegacyValuesAndKeepSafetyGap() { var repository = new InMemoryAppSettingRepository( [ new AppSetting("storage.guard.green_threshold_percent", "5", DateTimeOffset.UtcNow), new AppSetting("storage.guard.red_threshold_percent", "1", DateTimeOffset.UtcNow) ]); var service = new SystemSettingsService(repository, new NoOpUnitOfWork()); var settings = await service.GetAsync(); Assert.Equal(10, settings.StorageGreenThresholdPercent); Assert.Equal(5, settings.StorageRedThresholdPercent); var request = new Application.Models.Settings.UpdateSystemSettingsRequest { StorageGreenThresholdPercent = 20, StorageRedThresholdPercent = 19, PlatformRequestSettings = Application.Models.Settings.SystemSettingsDto.CreatePlatformRequestSettingsMap() }; await service.UpdateAsync(request); var updated = await service.GetAsync(); Assert.Equal(20, updated.StorageGreenThresholdPercent); Assert.Equal(15, updated.StorageRedThresholdPercent); } private sealed class InMemoryAppSettingRepository : IAppSettingRepository { private readonly Dictionary _items; public InMemoryAppSettingRepository(IEnumerable items) { _items = items.ToDictionary(static item => item.Key, StringComparer.OrdinalIgnoreCase); } public Task GetByKeyAsync(string key, CancellationToken cancellationToken = default) { _items.TryGetValue(key, out var item); return Task.FromResult(item); } public Task> ListAsync(CancellationToken cancellationToken = default) => Task.FromResult>(_items.Values.ToList()); public Task AddAsync(AppSetting setting, CancellationToken cancellationToken = default) { _items[setting.Key] = setting; return Task.CompletedTask; } public void Update(AppSetting setting) { _items[setting.Key] = setting; } } private sealed class NoOpUnitOfWork : IUnitOfWork { public Task SaveChangesAsync(CancellationToken cancellationToken = default) => Task.FromResult(0); } }