using System.Text.Json;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Moq;
using Shouldly;
using Tnsu.ApprovalRoute.Application.Reminders;
using Tnsu.ApprovalRoute.Application.RequestTypes;
using Tnsu.ApprovalRoute.Infrastructure.Persistence;
using Tnsu.ApprovalRoute.Infrastructure.SharePoint;
using Tnsu.ApprovalRoute.IntegrationTests.Infrastructure;
namespace Tnsu.ApprovalRoute.IntegrationTests.ApplicationServices;
///
/// Проверяется прежде всего вычисление даты, с которой отсчитывается SLA:
/// отдельной колонки «шаг стал активным» в списке согласования нет, и момент
/// выводится из соседних строк — это самая хрупкая часть эндпоинта.
///
/// SharePoint подменён моком, отдающим сырой JSON ровно в том виде, в каком его
/// возвращает REST, — так тесты заодно покрывают и разбор ответа.
///
public sealed class ReminderServiceTests : IClassFixture
{
private const string SiteUrl = "https://tnsukz.sharepoint.com/sites/requests";
private const string DataListTitle = "Заявка тест — данные";
private const string ApprovalListTitle = "Заявка тест — согласование";
private static readonly DateTimeOffset Now = new(2026, 8, 12, 12, 0, 0, TimeSpan.Zero);
private readonly ServiceProvider _services;
private readonly RequestTypeService _requestTypeService;
public ReminderServiceTests(PostgresDatabaseFixture fixture)
{
_services = TestServiceProviderFactory.Build(fixture.ConnectionString);
_requestTypeService = _services.GetRequiredService();
}
// База у тестов класса общая, а сервис обходит ВСЕ типы с настроенным SLA —
// поэтому у каждого теста свой список: типы соседних тестов на мок не
// настроены, заголовок списка для них не найдётся, и в выборку они не попадут.
private async Task CreateTypeWithSlaAsync(string name, int slaHours, bool? askOverdueReason = null)
{
var listUrl = $"/sites/requests/Lists/{name.Replace(" ", string.Empty)}";
await _requestTypeService.CreateAsync(
new CreateRequestTypeRequest(name, $"/{name}", listUrl, SiteUrl, "General", slaHours, askOverdueReason),
CancellationToken.None);
return listUrl;
}
private static JsonElement Json(object value) => JsonSerializer.SerializeToElement(value);
private static object Row(
int id, int ticketId, int stepOrder, string status, string phase,
string? date = null, string email = "approver@tnsu.kz", int? slaHours = null, bool? ask = null) =>
new
{
Id = id,
TicketId = ticketId,
StepOrder = stepOrder,
ApprovalStatus = status,
Phase = phase,
Date = date,
SLAhours = slaHours,
AskOverdueReason = ask,
TansuSiteApprover = new { EMail = email }
};
private ReminderService BuildService(
string listUrl, IReadOnlyList