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()!;
}
}