using System.Text.Json; using Microsoft.Extensions.DependencyInjection; using Moq; using Shouldly; using Tnsu.ApprovalRoute.Application.BasisRequests; using Tnsu.ApprovalRoute.Application.ListPartitioning; 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; /// /// Заявки по одному основанию: видны все (чтение от имени приложения), а /// CanOpen — только там, где у вызывающего есть права на элемент. /// public sealed class BasisRequestsServiceTests : IClassFixture { private const string SiteUrl = "https://tnsukz.sharepoint.com/sites/requests"; private const string CallerEmail = "initiator@tnsu.kz"; private const string BasisKey = "need:8028c7ac-ac23-8dd8-11f0-946facc42464"; private readonly ServiceProvider _services; public BasisRequestsServiceTests(PostgresDatabaseFixture fixture) { _services = TestServiceProviderFactory.Build(fixture.ConnectionString, CallerEmail); } private static JsonElement Json(object value) => JsonSerializer.SerializeToElement(value); private async Task<(BasisRequestsService Service, Mock SharePoint, int TypeId)> BuildAsync( string name, string listUrl, IReadOnlyList items, IReadOnlySet openableIds) { var type = await _services.GetRequiredService().CreateAsync( new CreateRequestTypeRequest($"Оплата {name}", $"/оплата-{name}", listUrl, SiteUrl, "General"), CancellationToken.None); var sharePoint = new Mock(); sharePoint .Setup(x => x.GetItemsByListUrlAsync(SiteUrl, listUrl, It.IsAny(), It.IsAny())) .ReturnsAsync(items.Select(Json).ToList()); sharePoint .Setup(x => x.CanUserViewItemAsync(SiteUrl, listUrl, It.IsAny(), CallerEmail, It.IsAny())) .ReturnsAsync((string _, string _, int id, string _, CancellationToken _) => openableIds.Contains(id)); var service = new BasisRequestsService( _services.GetRequiredService(), sharePoint.Object, _services.GetRequiredService(), new TestCurrentUser(CallerEmail)); return (service, sharePoint, type.Value.Id); } [Fact] public async Task Returns_all_requests_and_marks_only_permitted_as_openable() { const string list = "/sites/requests/Lists/Оплата-all"; var (service, sharePoint, typeId) = await BuildAsync("all", list, [ // Number-колонка приходит дробной («50000.0») — тест держит её такой же. new { Id = 1, RequestUniqueCode = "24-2610-1", RequestStatus = "Согласовано", PayAmount = 50000.0 }, new { Id = 2, RequestUniqueCode = "24-2610-2", RequestStatus = "На согласовании", PayAmount = 35000.5 } ], new HashSet { 2 }); var result = await service.GetByBasisAsync(typeId, BasisKey, CancellationToken.None); result.IsSuccess.ShouldBeTrue(); result.Value.Count.ShouldBe(2); result.Value.Single(x => x.Id == 1).ShouldBe(new BasisRequestDto(1, list, "24-2610-1", "Согласовано", 50000, false)); result.Value.Single(x => x.Id == 2).ShouldBe(new BasisRequestDto(2, list, "24-2610-2", "На согласовании", 35000.5, true)); // Фильтр по основанию уходит в SharePoint, а не применяется в памяти. sharePoint.Verify(x => x.GetItemsByListUrlAsync( SiteUrl, list, It.Is(q => q.Contains("BasisKey eq 'need%3A8028c7ac-ac23-8dd8-11f0-946facc42464'")), It.IsAny()), Times.Once); } [Fact] public async Task Empty_basis_key_is_a_validation_error() { var (service, _, typeId) = await BuildAsync("empty", "/sites/requests/Lists/Оплата-empty", [], new HashSet()); var result = await service.GetByBasisAsync(typeId, " ", CancellationToken.None); result.IsFailed.ShouldBeTrue(); } }