Files
WishNinja/tests/WishNinja.Tests/ImageServiceTests.cs
T
Jim BassoandClaude Opus 4.8 7dee999759
Build & Push Docker image / test (push) Failing after 1m33s
Build & Push Docker image / docker (push) Has been skipped
Initial build: WishNinja self-hosted gift wishlist manager
ASP.NET Core Blazor (.NET 10) + EF Core/SQLite + Identity. Features:
- Wishlists & items with local image storage (upload, clipboard paste,
  or URL fetched and stored locally)
- Owner-hidden claims (enforced at the query layer) to preserve surprises
- Admin-invite-only onboarding with email-based password resets
- All state under /data; ships as a single Docker image

Includes Dockerfile, docker-compose, Gitea Actions CI (test + push image),
Unraid template, and xUnit tests (claim privacy, invite lifecycle, image validation).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-10 15:57:42 -05:00

85 lines
2.8 KiB
C#

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);
}
}