using System.Net; using System.Net.Http.Json; using System.Text.Json; using FluentAssertions; using foodmarket.IntegrationTests.Support; using Xunit; namespace foodmarket.IntegrationTests; /// Регрессия на шум в логах stage-api: раньше /// определял «уже сидировано» /// по наличию Product с Article LIKE 'DEMO-%'. После того как «Демо-магазин» /// на стенде наполнили реальным каталогом из МойСклада (не с префиксом DEMO-), /// маркер срабатывал false — сидер шёл заново и падал на unique-индексе /// IX_product_barcodes_OrganizationId_Code (23505) при каждом рестарте. /// Фикс расширил маркер до «любой Product в орге». Тесты фиксируют: /// 1) повторный Seed на орге с товарами не пишет и не падает; /// 2) org с собственным (не-DEMO) каталогом тоже защищена. [Collection(ApiCollection.Name)] public class DemoSeederIdempotencyTests { private readonly ApiFactory _factory; public DemoSeederIdempotencyTests(ApiFactory factory) => _factory = factory; [Fact] public async Task SeedDemo_second_call_on_populated_org_returns_already_seeded() { // Фоновый сценарий: Admin впервые вызвал seed-demo (50 товаров DEMO-*), // перезапуск или повторное нажатие не должны дёрнуть вставку второй раз. var a = new ApiActor(_factory.CreateClient()); await a.SignupAndLoginAsync($"seed-twice-{Guid.NewGuid():N}"); using var first = await a.Http.PostAsync("/api/admin/seed-demo", content: null); first.StatusCode.Should().Be(HttpStatusCode.OK); var firstBody = await first.Content.ReadFromJsonAsync(); firstBody.GetProperty("alreadySeeded").GetBoolean().Should().BeFalse(); firstBody.GetProperty("products").GetInt32().Should().Be(50); using var second = await a.Http.PostAsync("/api/admin/seed-demo", content: null); second.StatusCode.Should().Be(HttpStatusCode.OK); var secondBody = await second.Content.ReadFromJsonAsync(); secondBody.GetProperty("alreadySeeded").GetBoolean().Should().BeTrue(); // Количество товаров не выросло — повторная вставка не произошла. secondBody.GetProperty("products").GetInt32().Should().Be(50); } [Fact] public async Task SeedDemo_on_org_with_imported_catalog_skips_without_duplicate_barcode() { // Главная регрессия: воспроизводит stage-сценарий, где у организации уже // есть товары БЕЗ префикса DEMO-. Старый маркер (Article LIKE 'DEMO-%') // пропускал гард и сидер падал на unique-индексе по баркодам (23505). var a = new ApiActor(_factory.CreateClient()); await a.SignupAndLoginAsync($"seed-imported-{Guid.NewGuid():N}"); var refs = await a.LoadRefsAsync(); // Один товар с «импортным» (не DEMO-*) артикулом — имитирует каталог // из МойСклада. Этого хватает чтобы новый маркер «any Product» сработал. await a.CreateProductAsync(refs, $"ImportedProduct-{Guid.NewGuid():N}", retailPrice: 500m, barcode: $"IMP-{Guid.NewGuid():N}"); using var resp = await a.Http.PostAsync("/api/admin/seed-demo", content: null); resp.StatusCode.Should().Be(HttpStatusCode.OK, "сидер должен no-op'нуть, а не упасть с 500"); var body = await resp.Content.ReadFromJsonAsync(); body.GetProperty("alreadySeeded").GetBoolean().Should().BeTrue(); // Должен быть ровно 1 товар — тот что мы создали; никаких DEMO- вставок. body.GetProperty("products").GetInt32().Should().Be(1); // Status (Peek) тоже отражает «заселено» — чтобы UI не предлагал кнопку. var status = await a.GetJsonAsync("/api/admin/seed-demo/status"); status.GetProperty("alreadySeeded").GetBoolean().Should().BeTrue(); } }