using System.Net; using System.Net.Http.Json; using System.Text.Json; using FluentAssertions; using foodmarket.IntegrationTests.Support; using Xunit; namespace foodmarket.IntegrationTests; /// Этап «Настройка кабинета» (14.10): реквизиты организации (п.1), /// счета точки продаж (п.8), PIN кассира + вход grant_type=pin (п.9), сброс /// пароля сотрудника (п.10), имя владельца «Владелец» (п.11). [Collection(ApiCollection.Name)] public class CabinetSetupTests { private readonly ApiFactory _factory; public CabinetSetupTests(ApiFactory factory) => _factory = factory; private static async Task> SettingsDictAsync(ApiActor api) { var s = await api.GetJsonAsync("/api/organization/settings"); return JsonSerializer.Deserialize>(s.GetRawText())!; } [Fact] public async Task Requisites_save_and_validate_bin() { var api = new ApiActor(_factory.CreateClient()); await api.SignupAndLoginAsync($"req-{Guid.NewGuid():N}"); var dict = await SettingsDictAsync(api); dict["bin"] = JsonSerializer.SerializeToElement("123456789012"); dict["address"] = JsonSerializer.SerializeToElement("Алматы, Абая 1"); dict["isVatPayer"] = JsonSerializer.SerializeToElement(false); (await api.Http.PutAsJsonAsync("/api/organization/settings", dict)).EnsureSuccessStatusCode(); var after = await api.GetJsonAsync("/api/organization/settings"); after.GetProperty("bin").GetString().Should().Be("123456789012"); after.GetProperty("address").GetString().Should().Be("Алматы, Абая 1"); after.GetProperty("isVatPayer").GetBoolean().Should().BeFalse(); // БИН неверной длины — 400. var bad = await SettingsDictAsync(api); bad["bin"] = JsonSerializer.SerializeToElement("123"); var resp = await api.Http.PutAsJsonAsync("/api/organization/settings", bad); resp.StatusCode.Should().Be(HttpStatusCode.BadRequest); } [Fact] public async Task Owner_employee_named_vladelec() { var api = new ApiActor(_factory.CreateClient()); await api.SignupAndLoginAsync($"own-{Guid.NewGuid():N}"); var list = await api.ListAsync("/api/organization/employees?pageSize=50"); var owner = list.FirstOrDefault(e => e.GetProperty("isOwner").GetBoolean()); owner.ValueKind.Should().Be(JsonValueKind.Object, "владелец есть в списке сотрудников"); owner.GetProperty("lastName").GetString().Should().Be("Владелец"); owner.GetProperty("firstName").GetString().Should().Be(""); } [Fact] public async Task RetailPoint_stores_cash_and_card_accounts() { var api = new ApiActor(_factory.CreateClient()); await api.SignupAndLoginAsync($"rp-{Guid.NewGuid():N}"); var refs = await api.LoadRefsAsync(); // Создаём Cash и Acquiring счета. var cash = await CreateAccountAsync(api, "Касса", kind: 1, refs.CurrencyId, storeId: refs.StoreId); var card = await CreateAccountAsync(api, "Эквайринг", kind: 4, refs.CurrencyId, storeId: null); var point = (await api.ListAsync("/api/catalog/retail-points?pageSize=50")).First(); var pointId = point.GetProperty("id").GetString()!; var put = await api.Http.PutAsJsonAsync($"/api/catalog/retail-points/{pointId}", new { name = point.GetProperty("name").GetString(), code = point.GetProperty("code").GetString(), storeId = point.GetProperty("storeId").GetString(), address = (string?)null, phone = (string?)null, fiscalSerial = (string?)null, fiscalRegNumber = (string?)null, isActive = true, cashAccountId = cash, cardAccountId = card, }); put.EnsureSuccessStatusCode(); var after = await api.GetJsonAsync($"/api/catalog/retail-points/{pointId}"); after.GetProperty("cashAccountId").GetString().Should().Be(cash); after.GetProperty("cardAccountId").GetString().Should().Be(card); } [Fact] public async Task Cashier_pin_set_and_pin_login() { var api = new ApiActor(_factory.CreateClient()); var slug = $"pin-{Guid.NewGuid():N}"; await api.SignupAndLoginAsync(slug); var cashierRoleId = (await api.ListAsync("/api/organization/employee-roles?pageSize=50")) .First(r => r.GetProperty("name").GetString() == "Кассир").GetProperty("id").GetString(); var pointId = (await api.ListAsync("/api/catalog/retail-points?pageSize=50")).First().GetProperty("id").GetString()!; var cashierEmail = $"cashier-{slug}@example.kz"; var createResp = await api.Http.PostAsJsonAsync("/api/organization/employees", new { lastName = "Кассирова", firstName = "Айгерим", roleId = cashierRoleId, isActive = true, email = cashierEmail, createAccount = true, retailPointIds = new[] { pointId }, }); createResp.EnsureSuccessStatusCode(); var empId = (await createResp.Content.ReadFromJsonAsync()) .GetProperty("employee").GetProperty("id").GetString(); (await api.Http.PostAsJsonAsync($"/api/organization/employees/{empId}/pin", new { pin = "1234" })) .EnsureSuccessStatusCode(); // Факт «PIN задан» виден в списке. var emp = await api.GetJsonAsync($"/api/organization/employees/{empId}"); emp.GetProperty("pinSet").GetBoolean().Should().BeTrue(); // Вход grant_type=pin по точке. using var anon = _factory.CreateClient(); var tok = await anon.PostAsync("/connect/token", new FormUrlEncodedContent(new Dictionary { ["grant_type"] = "pin", ["retail_point_id"] = pointId, ["pin"] = "1234", ["client_id"] = "food-market-web", ["scope"] = "openid profile email roles api offline_access", })); var body = await tok.Content.ReadAsStringAsync(); tok.IsSuccessStatusCode.Should().BeTrue(body); (await tok.Content.ReadFromJsonAsync()).GetProperty("access_token").GetString() .Should().NotBeNullOrEmpty(); } [Fact] public async Task Reset_password_issues_working_password() { var api = new ApiActor(_factory.CreateClient()); var slug = $"rst-{Guid.NewGuid():N}"; await api.SignupAndLoginAsync(slug); var roleId = (await api.ListAsync("/api/organization/employee-roles?pageSize=50")) .First(r => r.GetProperty("name").GetString() == "Кладовщик").GetProperty("id").GetString(); var email = $"mgr-{slug}@example.kz"; var createResp = await api.Http.PostAsJsonAsync("/api/organization/employees", new { lastName = "Петров", firstName = "Пётр", roleId, isActive = true, email, createAccount = true, }); createResp.EnsureSuccessStatusCode(); var empId = (await createResp.Content.ReadFromJsonAsync()) .GetProperty("employee").GetProperty("id").GetString(); var reset = await api.Http.PostAsJsonAsync($"/api/organization/employees/{empId}/reset-password", new { }); reset.IsSuccessStatusCode.Should().BeTrue(await reset.Content.ReadAsStringAsync()); var newPassword = (await reset.Content.ReadFromJsonAsync()).GetProperty("password").GetString()!; newPassword.Should().NotBeNullOrEmpty(); // Новый пароль работает. using var anon = _factory.CreateClient(); var login = new ApiActor(anon); var token = await login.TokenAsync(email, newPassword); token.Should().NotBeNullOrEmpty(); } private static async Task CreateAccountAsync(ApiActor api, string name, int kind, string currencyId, string? storeId) { var resp = await api.Http.PostAsJsonAsync("/api/money/accounts", new { name, kind, bankName = (string?)null, accountNumber = (string?)null, storeId, currencyId, openingBalance = 0m, openingDate = DateTime.UtcNow, isActive = true, isDefault = false, }); resp.IsSuccessStatusCode.Should().BeTrue(await resp.Content.ReadAsStringAsync()); return (await resp.Content.ReadFromJsonAsync()).GetProperty("id").GetString()!; } }