using FluentAssertions;
using foodmarket.Application.Common.Fiscal;
using foodmarket.Domain.Sales;
using foodmarket.Infrastructure.Fiscal;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit;
namespace foodmarket.UnitTests;
/// Контракт MockFiscalProvider'а: возвращает префикс MOCK-, тот же
/// чек двумя вызовами даёт тот же FiscalNumber (идемпотентность), QR содержит
/// FiscalNumber, ProviderTxId не пуст. SimulatedLatency обнулена — тест
/// должен быть мгновенным.
public class FiscalMockProviderTests
{
private static MockFiscalProvider New() => new(NullLogger.Instance)
{
SimulatedLatency = TimeSpan.Zero,
};
private static RetailSale SaleStub(Guid? id = null) => new()
{
Id = id ?? Guid.NewGuid(),
Number = "TST-000001",
OrganizationId = Guid.NewGuid(),
StoreId = Guid.NewGuid(),
CurrencyId = Guid.NewGuid(),
Total = 1000m,
};
[Fact]
public void Kind_is_Mock()
=> New().Kind.Should().Be(FiscalProviderKind.Mock);
[Fact]
public async Task Register_returns_mock_prefixed_fiscal_number()
{
var sale = SaleStub();
var r = await New().RegisterAsync(sale, CancellationToken.None);
r.FiscalNumber.Should().StartWith("MOCK-").And.HaveLength("MOCK-".Length + 8);
r.FiscalQrCode.Should().Contain(r.FiscalNumber);
r.FiscalUrl.Should().StartWith("https://mock.ofd.local/check/");
r.ProviderTxId.Should().NotBeNullOrEmpty().And.StartWith("mock-tx-");
}
[Fact]
public async Task Register_is_idempotent_per_sale_id()
{
var id = Guid.NewGuid();
var r1 = await New().RegisterAsync(SaleStub(id), CancellationToken.None);
var r2 = await New().RegisterAsync(SaleStub(id), CancellationToken.None);
r1.FiscalNumber.Should().Be(r2.FiscalNumber);
r1.ProviderTxId.Should().Be(r2.ProviderTxId);
}
[Fact]
public async Task Different_sales_get_different_fiscal_numbers()
{
var a = await New().RegisterAsync(SaleStub(), CancellationToken.None);
var b = await New().RegisterAsync(SaleStub(), CancellationToken.None);
a.FiscalNumber.Should().NotBe(b.FiscalNumber);
}
[Fact]
public async Task Simulated_latency_actually_delays()
{
var prov = new MockFiscalProvider(NullLogger.Instance)
{
SimulatedLatency = TimeSpan.FromMilliseconds(150),
};
var sw = System.Diagnostics.Stopwatch.StartNew();
await prov.RegisterAsync(SaleStub(), CancellationToken.None);
sw.Stop();
sw.ElapsedMilliseconds.Should().BeGreaterThanOrEqualTo(140);
}
}