теперь поля nullable
This commit is contained in:
+8
-8
@@ -5,10 +5,10 @@ namespace Tnsu.ApprovalRoute.Application.TemplateRouteSteps;
|
||||
public sealed record TemplateRouteStepDto(
|
||||
int Id,
|
||||
int OrderNo,
|
||||
string ExecutorEmail,
|
||||
string Position,
|
||||
ProjectRole ProjectRole,
|
||||
HeadType HeadType,
|
||||
string? ExecutorEmail,
|
||||
string? Position,
|
||||
ProjectRole? ProjectRole,
|
||||
HeadType? HeadType,
|
||||
RouteStepStatus Status,
|
||||
int RouteId,
|
||||
bool AllowReassignToOtherApprovers,
|
||||
@@ -18,8 +18,8 @@ public sealed record CreateTemplateRouteStepRequest(
|
||||
int OrderNo,
|
||||
string? ExecutorEmail,
|
||||
string? Position,
|
||||
ProjectRole ProjectRole,
|
||||
HeadType HeadType,
|
||||
ProjectRole? ProjectRole,
|
||||
HeadType? HeadType,
|
||||
RouteStepStatus Status,
|
||||
bool AllowReassignToOtherApprovers);
|
||||
|
||||
@@ -27,8 +27,8 @@ public sealed record UpdateTemplateRouteStepRequest(
|
||||
int OrderNo,
|
||||
string? ExecutorEmail,
|
||||
string? Position,
|
||||
ProjectRole ProjectRole,
|
||||
HeadType HeadType,
|
||||
ProjectRole? ProjectRole,
|
||||
HeadType? HeadType,
|
||||
RouteStepStatus Status,
|
||||
bool AllowReassignToOtherApprovers);
|
||||
|
||||
|
||||
+13
-20
@@ -37,15 +37,12 @@ public sealed class TemplateRouteStepService(ApprovalRouteDbContext db)
|
||||
if (route is null)
|
||||
return Result.Fail<TemplateRouteStepDto>(new NotFoundError("Маршрут", routeId));
|
||||
|
||||
if (!route.ByProject && r.ProjectRole != ProjectRole.None)
|
||||
if (!route.ByProject && r.ProjectRole is not (null or ProjectRole.None))
|
||||
return Result.Fail<TemplateRouteStepDto>(new ValidationError(
|
||||
"Роль в проекте недопустима для шага маршрута, который не учитывает проект (by_project = false)."));
|
||||
|
||||
var executorEmail = r.ExecutorEmail ?? string.Empty;
|
||||
var position = r.Position ?? string.Empty;
|
||||
|
||||
var duplicate = await HasDuplicateAsync(
|
||||
route.Id, executorEmail, position, r.ProjectRole, r.HeadType, r.Status, excludeId: null, cancellationToken);
|
||||
route.Id, r.ExecutorEmail, r.Position, r.ProjectRole, r.HeadType, r.Status, excludeId: null, cancellationToken);
|
||||
if (duplicate)
|
||||
return Result.Fail<TemplateRouteStepDto>(new ConflictError(
|
||||
"Шаг с такими исполнителем, должностью, ролью в проекте, типом руководителя и статусом уже существует в этом маршруте."));
|
||||
@@ -54,8 +51,8 @@ public sealed class TemplateRouteStepService(ApprovalRouteDbContext db)
|
||||
{
|
||||
RouteId = route.Id,
|
||||
OrderNo = r.OrderNo,
|
||||
ExecutorEmail = executorEmail,
|
||||
Position = position,
|
||||
ExecutorEmail = r.ExecutorEmail,
|
||||
Position = r.Position,
|
||||
ProjectRole = r.ProjectRole,
|
||||
HeadType = r.HeadType,
|
||||
Status = r.Status,
|
||||
@@ -78,22 +75,19 @@ public sealed class TemplateRouteStepService(ApprovalRouteDbContext db)
|
||||
if (entity is null)
|
||||
return Result.Fail<TemplateRouteStepDto>(new NotFoundError("Шаг маршрута", id));
|
||||
|
||||
if (!route.ByProject && r.ProjectRole != ProjectRole.None)
|
||||
if (!route.ByProject && r.ProjectRole is not (null or ProjectRole.None))
|
||||
return Result.Fail<TemplateRouteStepDto>(new ValidationError(
|
||||
"Роль в проекте недопустима для шага маршрута, который не учитывает проект (by_project = false)."));
|
||||
|
||||
var executorEmail = r.ExecutorEmail ?? string.Empty;
|
||||
var position = r.Position ?? string.Empty;
|
||||
|
||||
var duplicate = await HasDuplicateAsync(
|
||||
route.Id, executorEmail, position, r.ProjectRole, r.HeadType, r.Status, excludeId: entity.Id, cancellationToken);
|
||||
route.Id, r.ExecutorEmail, r.Position, r.ProjectRole, r.HeadType, r.Status, excludeId: entity.Id, cancellationToken);
|
||||
if (duplicate)
|
||||
return Result.Fail<TemplateRouteStepDto>(new ConflictError(
|
||||
"Шаг с такими исполнителем, должностью, ролью в проекте, типом руководителя и статусом уже существует в этом маршруте."));
|
||||
|
||||
entity.OrderNo = r.OrderNo;
|
||||
entity.ExecutorEmail = executorEmail;
|
||||
entity.Position = position;
|
||||
entity.ExecutorEmail = r.ExecutorEmail;
|
||||
entity.Position = r.Position;
|
||||
entity.ProjectRole = r.ProjectRole;
|
||||
entity.HeadType = r.HeadType;
|
||||
entity.Status = r.Status;
|
||||
@@ -120,14 +114,13 @@ public sealed class TemplateRouteStepService(ApprovalRouteDbContext db)
|
||||
var headType = r.HeadType ?? entity.HeadType;
|
||||
var status = r.Status ?? entity.Status;
|
||||
|
||||
if (!route.ByProject && projectRole != ProjectRole.None)
|
||||
if (!route.ByProject && projectRole is not (null or ProjectRole.None))
|
||||
return Result.Fail<TemplateRouteStepDto>(new ValidationError(
|
||||
"Роль в проекте недопустима для шага маршрута, который не учитывает проект (by_project = false)."));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(executorEmail) && string.IsNullOrWhiteSpace(position) &&
|
||||
projectRole == ProjectRole.None && headType == HeadType.None)
|
||||
if (TemplateRouteStepFields.CountFilled(executorEmail, position, projectRole, headType) != 1)
|
||||
return Result.Fail<TemplateRouteStepDto>(new ValidationError(
|
||||
"Должно быть заполнено хотя бы одно из полей: исполнитель, должность, роль в проекте или тип руководителя."));
|
||||
"Должно быть заполнено ровно одно из полей: исполнитель, должность, роль в проекте или тип руководителя."));
|
||||
|
||||
var duplicate = await HasDuplicateAsync(
|
||||
route.Id, executorEmail, position, projectRole, headType, status, excludeId: entity.Id, cancellationToken);
|
||||
@@ -160,7 +153,7 @@ public sealed class TemplateRouteStepService(ApprovalRouteDbContext db)
|
||||
}
|
||||
|
||||
private Task<bool> HasDuplicateAsync(
|
||||
int routeId, string executorEmail, string position, ProjectRole projectRole, HeadType headType,
|
||||
int routeId, string? executorEmail, string? position, ProjectRole? projectRole, HeadType? headType,
|
||||
RouteStepStatus status, int? excludeId, CancellationToken cancellationToken) =>
|
||||
db.TemplateRouteSteps.AnyAsync(x =>
|
||||
x.Id != excludeId &&
|
||||
@@ -179,7 +172,7 @@ public sealed class TemplateRouteStepService(ApprovalRouteDbContext db)
|
||||
if (route.ByProject)
|
||||
{
|
||||
var hasProjectRoleStep = await db.TemplateRouteSteps
|
||||
.AnyAsync(x => x.RouteId == route.Id && x.ProjectRole != ProjectRole.None, cancellationToken);
|
||||
.AnyAsync(x => x.RouteId == route.Id && x.ProjectRole != null && x.ProjectRole != ProjectRole.None, cancellationToken);
|
||||
if (!hasProjectRoleStep)
|
||||
warning = "Должен быть хотя бы один шаг с ролью в проекте.";
|
||||
}
|
||||
|
||||
+13
-12
@@ -8,12 +8,8 @@ public sealed class CreateTemplateRouteStepRequestValidator : AbstractValidator<
|
||||
public CreateTemplateRouteStepRequestValidator()
|
||||
{
|
||||
RuleFor(x => x)
|
||||
.Must(x =>
|
||||
!string.IsNullOrWhiteSpace(x.ExecutorEmail) ||
|
||||
!string.IsNullOrWhiteSpace(x.Position) ||
|
||||
x.ProjectRole != ProjectRole.None ||
|
||||
x.HeadType != HeadType.None)
|
||||
.WithMessage("Должно быть заполнено хотя бы одно из полей: исполнитель, должность, роль в проекте или тип руководителя.");
|
||||
.Must(x => TemplateRouteStepFields.CountFilled(x.ExecutorEmail, x.Position, x.ProjectRole, x.HeadType) == 1)
|
||||
.WithMessage("Должно быть заполнено ровно одно из полей: исполнитель, должность, роль в проекте или тип руководителя.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,11 +18,16 @@ public sealed class UpdateTemplateRouteStepRequestValidator : AbstractValidator<
|
||||
public UpdateTemplateRouteStepRequestValidator()
|
||||
{
|
||||
RuleFor(x => x)
|
||||
.Must(x =>
|
||||
!string.IsNullOrWhiteSpace(x.ExecutorEmail) ||
|
||||
!string.IsNullOrWhiteSpace(x.Position) ||
|
||||
x.ProjectRole != ProjectRole.None ||
|
||||
x.HeadType != HeadType.None)
|
||||
.WithMessage("Должно быть заполнено хотя бы одно из полей: исполнитель, должность, роль в проекте или тип руководителя.");
|
||||
.Must(x => TemplateRouteStepFields.CountFilled(x.ExecutorEmail, x.Position, x.ProjectRole, x.HeadType) == 1)
|
||||
.WithMessage("Должно быть заполнено ровно одно из полей: исполнитель, должность, роль в проекте или тип руководителя.");
|
||||
}
|
||||
}
|
||||
|
||||
internal static class TemplateRouteStepFields
|
||||
{
|
||||
public static int CountFilled(string? executorEmail, string? position, ProjectRole? projectRole, HeadType? headType) =>
|
||||
(string.IsNullOrWhiteSpace(executorEmail) ? 0 : 1) +
|
||||
(string.IsNullOrWhiteSpace(position) ? 0 : 1) +
|
||||
(projectRole is null or ProjectRole.None ? 0 : 1) +
|
||||
(headType is null or HeadType.None ? 0 : 1);
|
||||
}
|
||||
|
||||
+4
-4
@@ -5,10 +5,10 @@ namespace Tnsu.ApprovalRoute.Application.TemplateRoutes;
|
||||
public sealed record ResolvedRouteStepDto(
|
||||
int Id,
|
||||
int OrderNo,
|
||||
string ExecutorEmail,
|
||||
string Position,
|
||||
ProjectRole ProjectRole,
|
||||
HeadType HeadType,
|
||||
string? ExecutorEmail,
|
||||
string? Position,
|
||||
ProjectRole? ProjectRole,
|
||||
HeadType? HeadType,
|
||||
RouteStepStatus Status,
|
||||
bool AllowReassignToOtherApprovers);
|
||||
|
||||
|
||||
@@ -187,7 +187,7 @@ public sealed class TemplateRouteService(
|
||||
|
||||
private static ResolvedRouteStepDto ResolveStep(TemplateRouteStep step, IReadOnlyList<ProjectExecutorInfo> projectExecutors)
|
||||
{
|
||||
if (step.ProjectRole != ProjectRole.None)
|
||||
if (step.ProjectRole is not (null or ProjectRole.None))
|
||||
{
|
||||
var projectExecutor = projectExecutors.FirstOrDefault(e => e.ProjectRole == step.ProjectRole);
|
||||
if (projectExecutor is not null)
|
||||
|
||||
@@ -7,10 +7,10 @@ public class TemplateRouteStep
|
||||
public int Id { get; set; }
|
||||
public int OrderNo { get; set; }
|
||||
|
||||
public string ExecutorEmail { get; set; } = string.Empty;
|
||||
public string Position { get; set; } = string.Empty;
|
||||
public ProjectRole ProjectRole { get; set; } = ProjectRole.None;
|
||||
public HeadType HeadType { get; set; } = HeadType.None;
|
||||
public string? ExecutorEmail { get; set; }
|
||||
public string? Position { get; set; }
|
||||
public ProjectRole? ProjectRole { get; set; }
|
||||
public HeadType? HeadType { get; set; }
|
||||
|
||||
public RouteStepStatus Status { get; set; } = RouteStepStatus.Approval;
|
||||
public int RouteId { get; set; }
|
||||
|
||||
@@ -29,10 +29,10 @@ public class ApprovalRouteDbContext(DbContextOptions<ApprovalRouteDbContext> opt
|
||||
modelBuilder.Entity<TemplateRouteStep>(e =>
|
||||
{
|
||||
e.HasKey(x => x.Id);
|
||||
e.Property(x => x.ExecutorEmail).HasMaxLength(256).IsRequired();
|
||||
e.Property(x => x.Position).HasMaxLength(128).IsRequired();
|
||||
e.Property(x => x.ProjectRole).HasConversion<string>().HasMaxLength(32).IsRequired();
|
||||
e.Property(x => x.HeadType).HasConversion<string>().HasMaxLength(32).IsRequired();
|
||||
e.Property(x => x.ExecutorEmail).HasMaxLength(256);
|
||||
e.Property(x => x.Position).HasMaxLength(128);
|
||||
e.Property(x => x.ProjectRole).HasConversion<string>().HasMaxLength(32);
|
||||
e.Property(x => x.HeadType).HasConversion<string>().HasMaxLength(32);
|
||||
e.Property(x => x.Status).HasConversion<string>().HasMaxLength(32).IsRequired();
|
||||
e.HasOne(x => x.Route).WithMany(x => x.Steps).HasForeignKey(x => x.RouteId);
|
||||
e.HasIndex(x => new { x.RouteId, x.OrderNo });
|
||||
|
||||
+1
-5
@@ -12,7 +12,7 @@ using Tnsu.ApprovalRoute.Infrastructure.Persistence;
|
||||
namespace Tnsu.ApprovalRoute.Infrastructure.Persistence.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApprovalRouteDbContext))]
|
||||
[Migration("20260724114550_InitialCreate")]
|
||||
[Migration("20260727103117_InitialCreate")]
|
||||
partial class InitialCreate
|
||||
{
|
||||
/// <inheritdoc />
|
||||
@@ -111,13 +111,11 @@ namespace Tnsu.ApprovalRoute.Infrastructure.Persistence.Migrations
|
||||
.HasColumnName("allow_reassign_to_other_approvers");
|
||||
|
||||
b.Property<string>("ExecutorEmail")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)")
|
||||
.HasColumnName("executor_email");
|
||||
|
||||
b.Property<string>("HeadType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("head_type");
|
||||
@@ -127,13 +125,11 @@ namespace Tnsu.ApprovalRoute.Infrastructure.Persistence.Migrations
|
||||
.HasColumnName("order_no");
|
||||
|
||||
b.Property<string>("Position")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)")
|
||||
.HasColumnName("position");
|
||||
|
||||
b.Property<string>("ProjectRole")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("project_role");
|
||||
+4
-4
@@ -57,10 +57,10 @@ namespace Tnsu.ApprovalRoute.Infrastructure.Persistence.Migrations
|
||||
id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
order_no = table.Column<int>(type: "integer", nullable: false),
|
||||
executor_email = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
||||
position = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
|
||||
project_role = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
head_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
executor_email = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
position = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: true),
|
||||
project_role = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true),
|
||||
head_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true),
|
||||
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
route_id = table.Column<int>(type: "integer", nullable: false),
|
||||
allow_reassign_to_other_approvers = table.Column<bool>(type: "boolean", nullable: false)
|
||||
-4
@@ -108,13 +108,11 @@ namespace Tnsu.ApprovalRoute.Infrastructure.Persistence.Migrations
|
||||
.HasColumnName("allow_reassign_to_other_approvers");
|
||||
|
||||
b.Property<string>("ExecutorEmail")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)")
|
||||
.HasColumnName("executor_email");
|
||||
|
||||
b.Property<string>("HeadType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("head_type");
|
||||
@@ -124,13 +122,11 @@ namespace Tnsu.ApprovalRoute.Infrastructure.Persistence.Migrations
|
||||
.HasColumnName("order_no");
|
||||
|
||||
b.Property<string>("Position")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)")
|
||||
.HasColumnName("position");
|
||||
|
||||
b.Property<string>("ProjectRole")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("project_role");
|
||||
|
||||
+2
-2
@@ -160,7 +160,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
||||
var stepService = _services.GetRequiredService<TemplateRouteStepService>();
|
||||
await stepService.CreateAsync(
|
||||
route.Value.Id,
|
||||
new CreateTemplateRouteStepRequest(1, "approver@tnsu.kz", "Lead", ProjectRole.None, HeadType.Chief, RouteStepStatus.Approval, false),
|
||||
new CreateTemplateRouteStepRequest(1, "approver@tnsu.kz", "Lead", null, HeadType.Chief, RouteStepStatus.Approval, false),
|
||||
CancellationToken.None);
|
||||
|
||||
// Action
|
||||
@@ -187,7 +187,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
||||
var stepService = _services.GetRequiredService<TemplateRouteStepService>();
|
||||
await stepService.CreateAsync(
|
||||
route.Value.Id,
|
||||
new CreateTemplateRouteStepRequest(1, "", "Lead", ProjectRole.ProjectManager, HeadType.None, RouteStepStatus.Approval, false),
|
||||
new CreateTemplateRouteStepRequest(1, "", "Lead", ProjectRole.ProjectManager, null, RouteStepStatus.Approval, false),
|
||||
CancellationToken.None);
|
||||
|
||||
// Action
|
||||
|
||||
+59
-16
@@ -42,7 +42,7 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
||||
// Action
|
||||
var result = await _service.CreateAsync(
|
||||
999999,
|
||||
new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", ProjectRole.None, HeadType.None, RouteStepStatus.Approval, false),
|
||||
new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", null, null, RouteStepStatus.Approval, false),
|
||||
CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
@@ -58,7 +58,7 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
||||
// Action
|
||||
var result = await _service.CreateAsync(
|
||||
routeId,
|
||||
new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", ProjectRole.ProjectManager, HeadType.None, RouteStepStatus.Approval, false),
|
||||
new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", ProjectRole.ProjectManager, null, RouteStepStatus.Approval, false),
|
||||
CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
@@ -70,14 +70,14 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
||||
{
|
||||
// Arrange
|
||||
var routeId = await CreateRouteAsync("Route with duplicate step", byProject: false);
|
||||
var request = new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", ProjectRole.None, HeadType.Chief, RouteStepStatus.Approval, false);
|
||||
var request = new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", null, HeadType.Chief, RouteStepStatus.Approval, false);
|
||||
var first = await _service.CreateAsync(routeId, request, CancellationToken.None);
|
||||
first.IsSuccess.ShouldBeTrue();
|
||||
|
||||
// Action
|
||||
var duplicate = await _service.CreateAsync(
|
||||
routeId,
|
||||
new CreateTemplateRouteStepRequest(2, "a@b.kz", "Engineer", ProjectRole.None, HeadType.Chief, RouteStepStatus.Approval, true),
|
||||
new CreateTemplateRouteStepRequest(2, "a@b.kz", "Engineer", null, HeadType.Chief, RouteStepStatus.Approval, true),
|
||||
CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
@@ -93,13 +93,13 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
||||
// Action
|
||||
var created = await _service.CreateAsync(
|
||||
routeId,
|
||||
new CreateTemplateRouteStepRequest(1, null, null, ProjectRole.None, HeadType.Chief, RouteStepStatus.Approval, false),
|
||||
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.Chief, RouteStepStatus.Approval, false),
|
||||
CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
created.IsSuccess.ShouldBeTrue();
|
||||
created.Value.HeadType.ShouldBe(HeadType.Chief);
|
||||
created.Value.ExecutorEmail.ShouldBe(string.Empty);
|
||||
created.Value.ExecutorEmail.ShouldBeNull();
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Создание шага маршрута и получение его по Id возвращает сохранённые данные")]
|
||||
@@ -111,7 +111,7 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
||||
// Action
|
||||
var created = await _service.CreateAsync(
|
||||
routeId,
|
||||
new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", ProjectRole.None, HeadType.None, RouteStepStatus.Approval, false),
|
||||
new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", null, null, RouteStepStatus.Approval, false),
|
||||
CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
@@ -131,7 +131,7 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
||||
// Action
|
||||
var created = await _service.CreateAsync(
|
||||
routeId,
|
||||
new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", ProjectRole.None, HeadType.None, RouteStepStatus.Approval, false),
|
||||
new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", null, null, RouteStepStatus.Approval, false),
|
||||
CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
@@ -149,7 +149,7 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
||||
// Action
|
||||
var created = await _service.CreateAsync(
|
||||
routeId,
|
||||
new CreateTemplateRouteStepRequest(1, "", "", ProjectRole.ProjectManager, HeadType.None, RouteStepStatus.Approval, false),
|
||||
new CreateTemplateRouteStepRequest(1, "", "", ProjectRole.ProjectManager, null, RouteStepStatus.Approval, false),
|
||||
CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
@@ -164,14 +164,14 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
||||
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),
|
||||
new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", null, null, RouteStepStatus.Approval, false),
|
||||
CancellationToken.None);
|
||||
|
||||
// Action
|
||||
var updated = await _service.UpdateAsync(
|
||||
routeId,
|
||||
created.Value.Id,
|
||||
new UpdateTemplateRouteStepRequest(2, "b@b.kz", "Senior Engineer", ProjectRole.None, HeadType.Chief, RouteStepStatus.Execution, true),
|
||||
new UpdateTemplateRouteStepRequest(2, "b@b.kz", "Senior Engineer", null, HeadType.Chief, RouteStepStatus.Execution, true),
|
||||
CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
@@ -188,23 +188,66 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
||||
var routeId = await CreateRouteAsync("Route to patch step", byProject: false);
|
||||
var created = await _service.CreateAsync(
|
||||
routeId,
|
||||
new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", ProjectRole.None, HeadType.None, RouteStepStatus.Approval, false),
|
||||
new CreateTemplateRouteStepRequest(1, "a@b.kz", null, null, null, RouteStepStatus.Approval, false),
|
||||
CancellationToken.None);
|
||||
|
||||
// Action
|
||||
var patched = await _service.PatchAsync(
|
||||
routeId,
|
||||
created.Value.Id,
|
||||
new PatchTemplateRouteStepRequest(OrderNo: null, ExecutorEmail: "c@b.kz", Position: null, ProjectRole: null, HeadType: null, Status: RouteStepStatus.Execution, AllowReassignToOtherApprovers: null),
|
||||
new PatchTemplateRouteStepRequest(OrderNo: 2, ExecutorEmail: null, Position: null, ProjectRole: null, HeadType: null, Status: RouteStepStatus.Execution, AllowReassignToOtherApprovers: null),
|
||||
CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
patched.IsSuccess.ShouldBeTrue();
|
||||
patched.Value.ExecutorEmail.ShouldBe("c@b.kz");
|
||||
patched.Value.Position.ShouldBe("Engineer");
|
||||
patched.Value.ExecutorEmail.ShouldBe("a@b.kz");
|
||||
patched.Value.OrderNo.ShouldBe(2);
|
||||
patched.Value.Status.ShouldBe(RouteStepStatus.Execution);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Патч, после которого заполнено больше одного из полей исполнителя, возвращает неуспешный результат")]
|
||||
public async Task PatchAsync_ResultsInMoreThanOneFilledField_ReturnsFailedResult()
|
||||
{
|
||||
// Arrange
|
||||
var routeId = await CreateRouteAsync("Route to patch step into conflict", byProject: false);
|
||||
var created = await _service.CreateAsync(
|
||||
routeId,
|
||||
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.Chief, RouteStepStatus.Approval, false),
|
||||
CancellationToken.None);
|
||||
|
||||
// Action
|
||||
var patched = await _service.PatchAsync(
|
||||
routeId,
|
||||
created.Value.Id,
|
||||
new PatchTemplateRouteStepRequest(OrderNo: null, ExecutorEmail: "a@b.kz", Position: null, ProjectRole: null, HeadType: null, Status: null, AllowReassignToOtherApprovers: null),
|
||||
CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
patched.IsFailed.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Явные ProjectRole.None и HeadType.None при патче считаются незаполненными, как и null")]
|
||||
public async Task PatchAsync_ExplicitNoneValues_TreatedAsUnfilled()
|
||||
{
|
||||
// Arrange
|
||||
var routeId = await CreateRouteAsync("Route with explicit None values on patch", byProject: false);
|
||||
var created = await _service.CreateAsync(
|
||||
routeId,
|
||||
new CreateTemplateRouteStepRequest(1, "a@b.kz", null, null, null, RouteStepStatus.Approval, false),
|
||||
CancellationToken.None);
|
||||
|
||||
// Action
|
||||
var patched = await _service.PatchAsync(
|
||||
routeId,
|
||||
created.Value.Id,
|
||||
new PatchTemplateRouteStepRequest(OrderNo: null, ExecutorEmail: null, Position: null, ProjectRole: ProjectRole.None, HeadType: HeadType.None, Status: null, AllowReassignToOtherApprovers: null),
|
||||
CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
patched.IsSuccess.ShouldBeTrue();
|
||||
patched.Value.ExecutorEmail.ShouldBe("a@b.kz");
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Удаление шага маршрута убирает его из базы")]
|
||||
public async Task DeleteAsync_RemovesStep()
|
||||
{
|
||||
@@ -212,7 +255,7 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
||||
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),
|
||||
new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", null, null, RouteStepStatus.Approval, false),
|
||||
CancellationToken.None);
|
||||
|
||||
// Action
|
||||
|
||||
Reference in New Issue
Block a user