using FluentAssertions; using foodmarket.Api.Infrastructure.Invoices; using foodmarket.Domain.Purchases; using Xunit; namespace foodmarket.UnitTests; /// Owner-policy 2026-10-05 (handoff §5): фото накладных на сервере /// НЕ копим. тихо удаляет и /// одиночный файл, и папку фото-пачки, обнуляя StoredPath, после чего /// запись InvoiceUpload (строки в ParsedJson) остаётся жить. public class InvoicePhotoRetentionTests { [Fact] public void DeleteStored_removes_single_file() { var tmp = Path.Combine(Path.GetTempPath(), $"fm-{Guid.NewGuid():N}.jpg"); File.WriteAllBytes(tmp, new byte[] { 0xFF, 0xD8, 0xFF }); var up = new InvoiceUpload { StoredPath = tmp, FileName = "x.jpg" }; InvoiceProcessor.DeleteStored(up); File.Exists(tmp).Should().BeFalse(); up.StoredPath.Should().BeEmpty(); } [Fact] public void DeleteStored_removes_photo_folder_recursively() { var dir = Path.Combine(Path.GetTempPath(), $"fm-photos-{Guid.NewGuid():N}"); Directory.CreateDirectory(dir); File.WriteAllText(Path.Combine(dir, "01.jpg"), ""); File.WriteAllText(Path.Combine(dir, "02.jpg"), ""); File.WriteAllText(Path.Combine(dir, "codes.json"), "[]"); var up = new InvoiceUpload { StoredPath = dir, Source = "photos", FileName = "фото-приёмка" }; InvoiceProcessor.DeleteStored(up); Directory.Exists(dir).Should().BeFalse(); up.StoredPath.Should().BeEmpty(); } [Fact] public void DeleteStored_is_idempotent() { // Файла нет на диске — не должно падать (сработает повторно, если // SweepAbandonedPhotos дошёл раньше Apply). var up = new InvoiceUpload { StoredPath = "/tmp/fm-does-not-exist-" + Guid.NewGuid() }; FluentActions.Invoking(() => InvoiceProcessor.DeleteStored(up)) .Should().NotThrow(); up.StoredPath.Should().BeEmpty(); } }