using System.Text; using Microsoft.Extensions.Options; using WishNinja.Configuration; using WishNinja.Services; using Xunit; namespace WishNinja.Tests; public class ImageServiceTests : IDisposable { private readonly string _tempDir; private readonly ImageService _svc; public ImageServiceTests() { _tempDir = Path.Combine(Path.GetTempPath(), "wishninja-tests-" + Guid.NewGuid().ToString("N")); var options = Options.Create(new WishNinjaOptions { DataPath = _tempDir, Uploads = new UploadOptions { MaxBytes = 1024 }, // 1 KB cap for the test }); _svc = new ImageService(options, new StubHttpClientFactory()); } private sealed class StubHttpClientFactory : IHttpClientFactory { public HttpClient CreateClient(string name) => new(); } [Theory] [InlineData("image/png", true)] [InlineData("image/jpeg", true)] [InlineData("image/webp", true)] [InlineData("image/gif", true)] [InlineData("application/pdf", false)] [InlineData("text/html", false)] [InlineData(null, false)] public void IsAllowedContentType_enforces_allowlist(string? contentType, bool expected) => Assert.Equal(expected, _svc.IsAllowedContentType(contentType)); [Fact] public async Task SaveAsync_rejects_disallowed_type() { using var stream = new MemoryStream(Encoding.UTF8.GetBytes("not an image")); var result = await _svc.SaveAsync(stream, "application/pdf"); Assert.False(result.Succeeded); Assert.Null(result.FileName); } [Fact] public async Task SaveAsync_rejects_oversize_file_and_cleans_up() { using var stream = new MemoryStream(new byte[2048]); // exceeds 1 KB cap var result = await _svc.SaveAsync(stream, "image/png"); Assert.False(result.Succeeded); Assert.Empty(Directory.GetFiles(_svc.UploadsDirectory)); // partial file removed } [Theory] [InlineData("not-a-url")] [InlineData("ftp://example.com/x.png")] [InlineData("file:///etc/passwd")] public async Task SaveFromUrlAsync_rejects_non_http_urls(string url) { var result = await _svc.SaveFromUrlAsync(url); Assert.False(result.Succeeded); Assert.Empty(Directory.GetFiles(_svc.UploadsDirectory)); } [Fact] public async Task SaveAsync_writes_allowed_file_with_guid_name() { using var stream = new MemoryStream(new byte[512]); var result = await _svc.SaveAsync(stream, "image/png"); Assert.True(result.Succeeded); Assert.EndsWith(".png", result.FileName); Assert.True(File.Exists(Path.Combine(_svc.UploadsDirectory, result.FileName!))); } public void Dispose() { if (Directory.Exists(_tempDir)) Directory.Delete(_tempDir, recursive: true); } }