using System.Net; using System.Net.Http.Json; using System.Text.Json; using FluentAssertions; using foodmarket.IntegrationTests.Support; using Xunit; namespace foodmarket.IntegrationTests; /// Фоновая «Проверка данных»: счётчики (без группы/цены), детальный список, /// массовое исправление, «это разные товары». [Collection(ApiCollection.Name)] public class DataQualityTests { private readonly ApiFactory _factory; public DataQualityTests(ApiFactory factory) => _factory = factory; private async Task QuickProductAsync(ApiActor api, string name, decimal? price = null) { var r = await api.Http.PostAsJsonAsync("/api/catalog/products/quick", new { name, salePrice = price }); r.StatusCode.Should().Be(HttpStatusCode.Created, await r.Content.ReadAsStringAsync()); return (await r.Content.ReadFromJsonAsync()).GetProperty("id").GetString()!; } [Fact] public async Task Counts_detail_bulkfix_and_dismiss_work() { var api = new ApiActor(_factory.CreateClient()); await api.SignupAndLoginAsync($"dq-{Guid.NewGuid():N}"); // Товары с проблемами: без цены (и в корне → без группы), + дубль по имени. var p1 = await QuickProductAsync(api, "Молоко разливное"); // нет цены, корень await QuickProductAsync(api, "Хлеб белый", 180m); // есть цена, но корень await QuickProductAsync(api, "Дубль-товар"); await QuickProductAsync(api, "Дубль-товар"); // пара → dup-name-unit // Пересчёт → сводка со счётчиками. var sumResp = await api.Http.PostAsJsonAsync("/api/data-quality/recompute", new { }); sumResp.EnsureSuccessStatusCode(); var sum = await sumResp.Content.ReadFromJsonAsync(); var checks = sum.GetProperty("checks").EnumerateArray() .ToDictionary(x => x.GetProperty("checkType").GetString()!, x => x.GetProperty("count").GetInt32()); checks["no-group"].Should().BeGreaterThanOrEqualTo(4, "все 4 товара в корневой группе"); checks["no-sale-price"].Should().BeGreaterThanOrEqualTo(3, "три без цены"); checks["dup-name-unit"].Should().BeGreaterThanOrEqualTo(1, "пара «Дубль-товар»"); // Детальный список без цены содержит p1. var noPrice = await api.ListAsync("/api/data-quality/no-sale-price/products"); noPrice.Should().Contain(x => x.GetProperty("id").GetString() == p1); // Массовое: задать цену p1 → в списке «без цены» его больше нет. var setPrice = await api.Http.PostAsJsonAsync("/api/data-quality/bulk/set-price", new { productIds = new[] { p1 }, priceTypeId = (string?)null, amount = 300m }); setPrice.EnsureSuccessStatusCode(); var noPrice2 = await api.ListAsync("/api/data-quality/no-sale-price/products"); noPrice2.Should().NotContain(x => x.GetProperty("id").GetString() == p1); // «Это разные товары» для дубля → счётчик дублей падает. var dupList = await api.ListAsync("/api/data-quality/dup-name-unit/products"); var dupId = dupList.First().GetProperty("id").GetString(); (await api.Http.PostAsJsonAsync("/api/data-quality/dismiss", new { checkType = "dup-name-unit", productId = dupId, barcode = (string?)null })).EnsureSuccessStatusCode(); var afterResp = await api.Http.PostAsJsonAsync("/api/data-quality/recompute", new { }); afterResp.EnsureSuccessStatusCode(); var afterDismiss = await afterResp.Content.ReadFromJsonAsync(); var dupAfter = afterDismiss.GetProperty("checks").EnumerateArray() .First(x => x.GetProperty("checkType").GetString() == "dup-name-unit").GetProperty("count").GetInt32(); dupAfter.Should().Be(0, "единственная пара помечена «разные товары»"); } }