using System.Net; using System.Net.Http.Json; using System.Text.Json; using FluentAssertions; using foodmarket.IntegrationTests.Support; using Xunit; namespace foodmarket.IntegrationTests; /// Паритет карточки товара с МойСкладом + правки владельца 08.10: /// обязательно только наименование; единица/группа по умолчанию; штрихкод /// обязателен (автогенерация, если не задан); цена продажи не обязательна для /// сохранения, но продать без неё нельзя. [Collection(ApiCollection.Name)] public class ProductCardParityTests { private readonly ApiFactory _factory; public ProductCardParityTests(ApiFactory factory) => _factory = factory; private static string RandomBarcode() => string.Concat(Enumerable.Range(0, 13).Select(_ => Random.Shared.Next(0, 10))); [Fact] public async Task Create_with_only_name_succeeds_autogenerates_barcode_and_defaults() { var api = new ApiActor(_factory.CreateClient()); await api.SignupAndLoginAsync($"card-{Guid.NewGuid():N}"); // Только наименование — ни единицы, ни группы, ни штрихкода, ни цены. var resp = await api.Http.PostAsJsonAsync("/api/catalog/products", new { name = "Товар без ничего" }); var body = await resp.Content.ReadAsStringAsync(); resp.StatusCode.Should().Be(HttpStatusCode.Created, body); var p = JsonDocument.Parse(body).RootElement; var id = p.GetProperty("id").GetString(); // Штрихкод автогенерирован (13 цифр), единица проставлена. var full = await api.GetJsonAsync($"/api/catalog/products/{id}"); var barcodes = full.GetProperty("barcodes").EnumerateArray().ToList(); barcodes.Should().HaveCount(1, "штрихкод обязателен → автогенерация"); barcodes[0].GetProperty("code").GetString().Should().MatchRegex("^[0-9]{13}$"); full.GetProperty("unitName").GetString().Should().NotBeNullOrEmpty(); } [Fact] public async Task Sale_of_product_without_price_is_rejected_with_clear_message() { var api = new ApiActor(_factory.CreateClient()); await api.SignupAndLoginAsync($"noprice-{Guid.NewGuid():N}"); var refs = await api.LoadRefsAsync(); // Товар без цены продажи. var create = await api.Http.PostAsJsonAsync("/api/catalog/products", new { name = "Без цены" }); create.StatusCode.Should().Be(HttpStatusCode.Created); var pid = JsonDocument.Parse(await create.Content.ReadAsStringAsync()).RootElement.GetProperty("id").GetString(); var sale = await api.Http.PostAsJsonAsync("/api/sales/retail", new { date = DateTime.UtcNow, storeId = refs.StoreId, currencyId = refs.CurrencyId, payment = 0, paidCash = 0m, paidCard = 0m, isReturn = false, lines = new[] { new { productId = pid, quantity = 1m, unitPrice = 0m, discount = 0m, vatPercent = 12m, sortOrder = 0 } }, }); sale.StatusCode.Should().Be(HttpStatusCode.BadRequest); (await sale.Content.ReadAsStringAsync()).Should().Contain("нет цены продажи"); } [Fact] public async Task QuickCreate_by_barcode_creates_minimal_product() { var api = new ApiActor(_factory.CreateClient()); await api.SignupAndLoginAsync($"quick-{Guid.NewGuid():N}"); var bc = RandomBarcode(); var resp = await api.Http.PostAsJsonAsync("/api/catalog/products/quick", new { name = "Быстрый товар", barcode = bc, salePrice = 250m }); var body = await resp.Content.ReadAsStringAsync(); resp.StatusCode.Should().Be(HttpStatusCode.Created, body); var id = JsonDocument.Parse(body).RootElement.GetProperty("id").GetString(); // Находится по тому самому штрихкоду. var byBc = await api.GetJsonAsync($"/api/catalog/products/by-barcode/{bc}"); // by-barcode возвращает товар или {items:[…]}; проверим, что наш id присутствует. byBc.GetRawText().Should().Contain(id!); } }