using System.Net; using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text; using System.Text.Json; using FluentAssertions; using foodmarket.IntegrationTests.Support; using Xunit; namespace foodmarket.IntegrationTests; /// Регресс по hotfix-invoice500: до фикса /// POST /api/purchases/invoices/upload ложился в 500 из-за /// NOT NULL constraint на UpdatedAt. Тест ХОДИТ ЧЕРЕЗ HTTP на реальной /// Postgres (Testcontainers), с мульти-файловой multipart-формой, чтобы впредь /// заметить, если кто-то снова сделает UpdatedAt NOT NULL. [Collection(ApiCollection.Name)] public class InvoiceUploadHttpTests { private readonly ApiFactory _factory; public InvoiceUploadHttpTests(ApiFactory factory) => _factory = factory; [Fact] public async Task Upload_single_file_returns_200_and_persists_row() { var http = _factory.CreateClient(); var actor = new ApiActor(http); await actor.SignupAndLoginAsync($"invup-{Guid.NewGuid():N}".Substring(0, 20)); using var form = new MultipartFormDataContent(); var bytes = Encoding.UTF8.GetBytes("not a real jpeg — байты всё равно сохранятся как файл"); var filePart = new ByteArrayContent(bytes); filePart.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg"); form.Add(filePart, "files", "invoice-test.jpg"); var resp = await http.PostAsync("/api/purchases/invoices/upload", form); var body = await resp.Content.ReadAsStringAsync(); resp.StatusCode.Should().Be(HttpStatusCode.OK, $"body: {body}"); var json = JsonDocument.Parse(body).RootElement; var ids = json.GetProperty("ids").EnumerateArray().Select(e => e.GetGuid()).ToList(); ids.Should().HaveCount(1); // detail endpoint тоже должен корректно отвечать 200 (UpdatedAt null в select'е ок) var detail = await http.GetFromJsonAsync($"/api/purchases/invoices/{ids[0]}"); detail.GetProperty("fileName").GetString().Should().Be("invoice-test.jpg"); detail.GetProperty("status").GetString().Should().Be("pending"); } [Fact] public async Task Upload_multiple_files_in_one_request_all_persist() { var http = _factory.CreateClient(); var actor = new ApiActor(http); await actor.SignupAndLoginAsync($"invup2-{Guid.NewGuid():N}".Substring(0, 20)); using var form = new MultipartFormDataContent(); for (var i = 0; i < 3; i++) { var fp = new ByteArrayContent(Encoding.UTF8.GetBytes($"file-{i}")); fp.Headers.ContentType = new MediaTypeHeaderValue("image/png"); form.Add(fp, "files", $"inv-{i}.png"); } var resp = await http.PostAsync("/api/purchases/invoices/upload", form); resp.StatusCode.Should().Be(HttpStatusCode.OK); var ids = (await resp.Content.ReadFromJsonAsync()) .GetProperty("ids").EnumerateArray().Select(e => e.GetGuid()).ToList(); ids.Should().HaveCount(3); } }