валидация статуса

This commit is contained in:
gorbenko.pavel
2026-07-29 10:19:38 +05:00
parent 8f640d4324
commit 13d929eb5b
3 changed files with 26 additions and 0 deletions
@@ -114,6 +114,9 @@ public sealed class TemplateRouteStepService(ApprovalRouteDbContext db)
var headType = r.HeadType ?? entity.HeadType; var headType = r.HeadType ?? entity.HeadType;
var status = r.Status ?? entity.Status; var status = r.Status ?? entity.Status;
if (status == RouteStepStatus.None)
return Result.Fail<TemplateRouteStepDto>(new ValidationError("Статус обязателен: Approval или Execution."));
if (!route.ByProject && projectRole is not (null or ProjectRole.None)) if (!route.ByProject && projectRole is not (null or ProjectRole.None))
return Result.Fail<TemplateRouteStepDto>(new ValidationError( return Result.Fail<TemplateRouteStepDto>(new ValidationError(
"Роль в проекте недопустима для шага маршрута, который не учитывает проект (by_project = false).")); "Роль в проекте недопустима для шага маршрута, который не учитывает проект (by_project = false)."));
@@ -7,6 +7,7 @@ public sealed class CreateTemplateRouteStepRequestValidator : AbstractValidator<
{ {
public CreateTemplateRouteStepRequestValidator() public CreateTemplateRouteStepRequestValidator()
{ {
RuleFor(x => x.Status).NotEqual(RouteStepStatus.None).WithMessage("Статус обязателен: Approval или Execution.");
RuleFor(x => x) RuleFor(x => x)
.Must(x => TemplateRouteStepFields.CountFilled(x.ExecutorEmail, x.Position, x.ProjectRole, x.HeadType) == 1) .Must(x => TemplateRouteStepFields.CountFilled(x.ExecutorEmail, x.Position, x.ProjectRole, x.HeadType) == 1)
.WithMessage("Должно быть заполнено ровно одно из полей: исполнитель, должность, роль в проекте или тип руководителя."); .WithMessage("Должно быть заполнено ровно одно из полей: исполнитель, должность, роль в проекте или тип руководителя.");
@@ -17,6 +18,7 @@ public sealed class UpdateTemplateRouteStepRequestValidator : AbstractValidator<
{ {
public UpdateTemplateRouteStepRequestValidator() public UpdateTemplateRouteStepRequestValidator()
{ {
RuleFor(x => x.Status).NotEqual(RouteStepStatus.None).WithMessage("Статус обязателен: Approval или Execution.");
RuleFor(x => x) RuleFor(x => x)
.Must(x => TemplateRouteStepFields.CountFilled(x.ExecutorEmail, x.Position, x.ProjectRole, x.HeadType) == 1) .Must(x => TemplateRouteStepFields.CountFilled(x.ExecutorEmail, x.Position, x.ProjectRole, x.HeadType) == 1)
.WithMessage("Должно быть заполнено ровно одно из полей: исполнитель, должность, роль в проекте или тип руководителя."); .WithMessage("Должно быть заполнено ровно одно из полей: исполнитель, должность, роль в проекте или тип руководителя.");
@@ -226,6 +226,27 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
patched.IsFailed.ShouldBeTrue(); patched.IsFailed.ShouldBeTrue();
} }
[Fact(DisplayName = "Патч со статусом None возвращает неуспешный результат")]
public async Task PatchAsync_StatusNone_ReturnsFailedResult()
{
// Arrange
var routeId = await CreateRouteAsync("Route to patch step status none", 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: null, HeadType: null, Status: RouteStepStatus.None, AllowReassignToOtherApprovers: null),
CancellationToken.None);
// Assert
patched.IsFailed.ShouldBeTrue();
}
[Fact(DisplayName = "Явные ProjectRole.None и HeadType.None при патче считаются незаполненными, как и null")] [Fact(DisplayName = "Явные ProjectRole.None и HeadType.None при патче считаются незаполненными, как и null")]
public async Task PatchAsync_ExplicitNoneValues_TreatedAsUnfilled() public async Task PatchAsync_ExplicitNoneValues_TreatedAsUnfilled()
{ {