using System; using System.IO; using System.Net; using System.Net.Http; using System.Text; using System.Threading.Tasks; using Paca.App.Services; using Paca.Core.Config; using Xunit; namespace Paca.Tests.App; public class ThumbnailCacheServiceGateTests : IDisposable { private readonly string _tempDir; private readonly ThumbnailCacheService _service; public ThumbnailCacheServiceGateTests() { _tempDir = Path.Combine(Path.GetTempPath(), "paca_thumb_test_" + Guid.NewGuid().ToString("N")); Directory.CreateDirectory(_tempDir); _service = new ThumbnailCacheService(new AppConfig(), _tempDir); } public void Dispose() { try { if (Directory.Exists(_tempDir)) Directory.Delete(_tempDir, true); } catch { } } [Fact] public async Task Gate_SaveBase64Thumbnail_DecodesAndWritesFile() { // 1x1 투명/단색 픽셀 mock base64 (JFIF header) var fakeJpgBytes = new byte[] { 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0xFF, 0xD9 }; var b64 = "data:image/jpeg;base64," + Convert.ToBase64String(fakeJpgBytes); var filePath = await _service.SaveBase64ThumbnailAsync(b64, "video_123"); Assert.NotNull(filePath); Assert.True(File.Exists(filePath)); var readBytes = await File.ReadAllBytesAsync(filePath); Assert.Equal(fakeJpgBytes, readBytes); } [Fact] public async Task Gate_DownloadAndCacheThumbnail_SendsRefererAndCaches() { var fakeJpgBytes = new byte[] { 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0xFF, 0xD9 }; string? capturedReferer = null; var handler = new FakeHttpHandler(req => { capturedReferer = req.Headers.Referrer?.ToString(); return new HttpResponseMessage(HttpStatusCode.OK) { Content = new ByteArrayContent(fakeJpgBytes) }; }); var customService = new ThumbnailCacheService(new AppConfig(), _tempDir, new HttpClient(handler)); var filePath = await customService.DownloadAndCacheThumbnailAsync( "https://img.ruliweb.com/thumb/1a099d.jpg", "https://bbs.ruliweb.com/best/123", "ruliweb_123" ); Assert.NotNull(filePath); Assert.True(File.Exists(filePath)); Assert.Equal("https://bbs.ruliweb.com/best/123", capturedReferer); } [Fact] public async Task Gate_EnsureThumbnail_RoutesDataUriOrHttpProperly() { var fakeJpgBytes = new byte[] { 0xFF, 0xD8, 0xFF, 0xD9 }; var b64 = "data:image/jpeg;base64," + Convert.ToBase64String(fakeJpgBytes); var result = await _service.EnsureThumbnailAsync( rawThumbOrPoster: b64, mediaUrl: "https://example.com/video.mp4", referer: "https://example.com" ); Assert.NotNull(result); Assert.True(File.Exists(result)); } private sealed class FakeHttpHandler : HttpMessageHandler { private readonly Func _handler; public FakeHttpHandler(Func handler) => _handler = handler; protected override Task SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) => Task.FromResult(_handler(request)); } }