using FluentAssertions; using foodmarket.IntegrationTests.Support; using Microsoft.Extensions.DependencyInjection; using Xunit; namespace foodmarket.IntegrationTests; /// Тест абстракции : /// в default-конфиге (Storage:Type=Local) загружаем картинку товара через /// существующий endpoint, потом читаем по public-URL через UploadsController. /// Проверяем что file round-trip'aет. [Collection(ApiCollection.Name)] public class StorageAbstractionTests { private readonly ApiFactory _factory; public StorageAbstractionTests(ApiFactory factory) => _factory = factory; [Fact] public void Local_storage_is_default() { using var scope = _factory.Services.CreateScope(); var storage = scope.ServiceProvider.GetRequiredService(); storage.Kind.Should().Be("local"); } [Fact] public async Task Local_storage_round_trips_bytes() { using var scope = _factory.Services.CreateScope(); var storage = scope.ServiceProvider.GetRequiredService(); var key = $"products/{Guid.NewGuid():N}/test.png"; // Минимальный валидный 1x1 PNG. var png = Convert.FromBase64String( "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNgAAIAAAUAAen63NgAAAAASUVORK5CYII="); using (var ms = new MemoryStream(png)) { await storage.SaveAsync(key, ms, "image/png", CancellationToken.None); } var read = await storage.OpenAsync(key, CancellationToken.None); read.Should().NotBeNull(); using var output = new MemoryStream(); await read!.Value.Stream.CopyToAsync(output); output.ToArray().Should().BeEquivalentTo(png); await storage.DeleteAsync(key, CancellationToken.None); var afterDelete = await storage.OpenAsync(key, CancellationToken.None); afterDelete.Should().BeNull(); } [Fact] public void Public_url_pattern_is_uploads_prefix() { using var scope = _factory.Services.CreateScope(); var storage = scope.ServiceProvider.GetRequiredService(); storage.PublicUrl("products/abc/x.png").Should().Be("/uploads/products/abc/x.png"); // Leading slash в key не должен дублировать. storage.PublicUrl("/products/abc/x.png").Should().Be("/uploads/products/abc/x.png"); } }