using Microsoft.Extensions.DependencyInjection; using Shouldly; using Tnsu.ApprovalRoute.Application.RequestTypes; using Tnsu.ApprovalRoute.Application.TemplateRoutes; using Tnsu.ApprovalRoute.Application.TemplateRouteSteps; using Tnsu.ApprovalRoute.Domain.Enums; using Tnsu.ApprovalRoute.IntegrationTests.Infrastructure; namespace Tnsu.ApprovalRoute.IntegrationTests.ApplicationServices; [CollectionDefinition(Name)] public sealed class TemplateRouteStepServiceCollection : ICollectionFixture { public const string Name = nameof(TemplateRouteStepServiceCollection); } [Collection(TemplateRouteStepServiceCollection.Name)] public sealed class TemplateRouteStepServiceTests : IDisposable { private static readonly Guid CompanyId = Guid.Parse("00000000-0000-0000-0000-000000000002"); private readonly ServiceProvider _services; private readonly TemplateRouteStepService _service; private readonly TemplateRouteService _routeService; private readonly RequestTypeService _requestTypeService; public TemplateRouteStepServiceTests(PostgresDatabaseFixture fixture) { _services = TestServiceProviderFactory.Build(fixture.ConnectionString); _service = _services.GetRequiredService(); _routeService = _services.GetRequiredService(); _requestTypeService = _services.GetRequiredService(); } public void Dispose() => _services.Dispose(); private async Task CreateRouteAsync(string requestTypeName, bool byProject) { var requestTypeId = (await _requestTypeService.CreateAsync( new CreateRequestTypeRequest(requestTypeName, $"/{requestTypeName}"), CancellationToken.None)).Value.Id; var route = await _routeService.CreateAsync( new CreateTemplateRouteRequest(RouteType.Sequential, requestTypeId, null, CompanyId, byProject, RequiresControl: false), CancellationToken.None); return route.Value.Id; } [Fact] public async Task CreateAsync_UnknownRoute_ReturnsFailedResult() { var result = await _service.CreateAsync( Guid.NewGuid(), new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", ProjectRole.None, HeadType.None, RouteStepStatus.Approval, false), CancellationToken.None); result.IsFailed.ShouldBeTrue(); } [Fact] public async Task CreateAsync_ProjectRoleOnNonProjectRoute_ReturnsFailedResult() { var routeId = await CreateRouteAsync("Non project route", byProject: false); var result = await _service.CreateAsync( routeId, new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", ProjectRole.Manager, HeadType.None, RouteStepStatus.Approval, false), CancellationToken.None); result.IsFailed.ShouldBeTrue(); } [Fact] public async Task CreateAsync_ThenGetAsync_ReturnsCreatedStep() { var routeId = await CreateRouteAsync("Simple route", byProject: false); var created = await _service.CreateAsync( routeId, new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", ProjectRole.None, HeadType.None, RouteStepStatus.Approval, false), CancellationToken.None); created.IsSuccess.ShouldBeTrue(); var fetched = await _service.GetAsync(routeId, created.Value.Id, CancellationToken.None); fetched.IsSuccess.ShouldBeTrue(); fetched.Value.ExecutorEmail.ShouldBe("a@b.kz"); } [Fact] public async Task CreateAsync_ByProjectRouteWithoutProjectRoleStep_ReturnsWarning() { var routeId = await CreateRouteAsync("By-project route without role", byProject: true); var created = await _service.CreateAsync( routeId, new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", ProjectRole.None, HeadType.None, RouteStepStatus.Approval, false), CancellationToken.None); created.IsSuccess.ShouldBeTrue(); created.Reasons.ShouldContain(r => r.Message.Contains("ролью в проекте")); } [Fact] public async Task CreateAsync_ByProjectRouteWithProjectRoleStep_HasNoWarning() { var routeId = await CreateRouteAsync("By-project route with role", byProject: true); var created = await _service.CreateAsync( routeId, new CreateTemplateRouteStepRequest(1, "", "", ProjectRole.Manager, HeadType.None, RouteStepStatus.Approval, false), CancellationToken.None); created.IsSuccess.ShouldBeTrue(); created.Reasons.ShouldBeEmpty(); } [Fact] public async Task UpdateAsync_ChangesFields() { var routeId = await CreateRouteAsync("Route to update step", byProject: false); var created = await _service.CreateAsync( routeId, new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", ProjectRole.None, HeadType.None, RouteStepStatus.Approval, false), CancellationToken.None); var updated = await _service.UpdateAsync( routeId, created.Value.Id, new UpdateTemplateRouteStepRequest(2, "b@b.kz", "Senior Engineer", ProjectRole.None, HeadType.Chief, RouteStepStatus.Execution, true), CancellationToken.None); updated.IsSuccess.ShouldBeTrue(); updated.Value.ExecutorEmail.ShouldBe("b@b.kz"); updated.Value.Status.ShouldBe(RouteStepStatus.Execution); updated.Value.AllowReassignToOtherApprovers.ShouldBeTrue(); } [Fact] public async Task DeleteAsync_RemovesStep() { var routeId = await CreateRouteAsync("Route to delete step", byProject: false); var created = await _service.CreateAsync( routeId, new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", ProjectRole.None, HeadType.None, RouteStepStatus.Approval, false), CancellationToken.None); var deleted = await _service.DeleteAsync(routeId, created.Value.Id, CancellationToken.None); deleted.IsSuccess.ShouldBeTrue(); var fetched = await _service.GetAsync(routeId, created.Value.Id, CancellationToken.None); fetched.IsFailed.ShouldBeTrue(); } }