[CLAUDE] PurchaseEvaluation: require quy trinh duyet V2 o create+submit (dong lo hong quy-trinh-cu)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 4m58s

Validator NotEmpty(ApprovalWorkflowId) + submit guard (sau Section-3, truoc mutate phase). Dong lo hong validate FE-only -> phieu null-workflow ket nhanh V1-legacy "quy trinh cu" (khong Buoc/Cap, khong route duyet). Test-before (RED confirmed): +2 validator test (PeWorkItemGuardTests) + rewrite test 7/13 PeSubmitGuardAndBypass (V1-submit deprecated, data V1 wipe S59). 356 pass (45D+311I).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-06-22 17:06:46 +07:00
parent 2c7fd635b9
commit fc1f19db8c
4 changed files with 82 additions and 26 deletions

View File

@ -74,7 +74,7 @@ public class PeWorkItemGuardTests
}
private static CreatePurchaseEvaluationCommand BuildCreateCommand(
Guid projectId, Guid? workItemId)
Guid projectId, Guid? workItemId, Guid? approvalWorkflowId = null)
=> new(
Type: PurchaseEvaluationType.DuyetNcc,
TenGoiThau: "Gói thầu test",
@ -84,7 +84,7 @@ public class PeWorkItemGuardTests
MoTa: null,
PaymentTerms: null,
BudgetPeriodAmount: null,
ApprovalWorkflowId: null,
ApprovalWorkflowId: approvalWorkflowId,
WorkItemId: workItemId);
// ============================================================
@ -127,10 +127,10 @@ public class PeWorkItemGuardTests
[Fact]
public void Validator_WorkItemIdPresent_NoErrorOnWorkItemId()
{
// Chỉ assert rule WorkItemId pass — command còn lại đã hợp lệ ở BuildCreateCommand
// nên result.IsValid=true; nhưng narrow assert vào property để test đúng rule này.
// Chỉ assert rule WorkItemId pass — command còn lại đã hợp lệ ở BuildCreateCommand.
// [S83] +ApprovalWorkflowId (cũng NotEmpty từ S83) để command FULL-valid → IsValid=true.
var validator = new CreatePurchaseEvaluationCommandValidator();
var cmd = BuildCreateCommand(Guid.NewGuid(), workItemId: Guid.NewGuid());
var cmd = BuildCreateCommand(Guid.NewGuid(), workItemId: Guid.NewGuid(), approvalWorkflowId: Guid.NewGuid());
var result = validator.Validate(cmd);
@ -138,6 +138,38 @@ public class PeWorkItemGuardTests
result.IsValid.Should().BeTrue();
}
// ============================================================
// 1b. VALIDATOR — RuleFor(ApprovalWorkflowId).NotEmpty() [S83 bug fix]
// ============================================================
// Đóng lỗ hổng validate FE-only: phiếu null-workflow lọt vào ChoDuyet = kẹt nhánh
// V1-legacy ("quy trình cũ", không Bước/Cấp, không route duyệt). FE canSubmit bắt
// buộc chọn quy trình lúc create → BE validator phải mirror (defense-in-depth).
[Fact]
public void Validator_ApprovalWorkflowIdNull_IsInvalid_WithErrorOnWorkflow()
{
var validator = new CreatePurchaseEvaluationCommandValidator();
var cmd = BuildCreateCommand(Guid.NewGuid(), workItemId: Guid.NewGuid(), approvalWorkflowId: null);
var result = validator.Validate(cmd);
result.IsValid.Should().BeFalse();
result.Errors.Should().Contain(
e => e.PropertyName == nameof(CreatePurchaseEvaluationCommand.ApprovalWorkflowId));
}
[Fact]
public void Validator_ApprovalWorkflowIdPresent_NoErrorOnWorkflow()
{
var validator = new CreatePurchaseEvaluationCommandValidator();
var cmd = BuildCreateCommand(Guid.NewGuid(), workItemId: Guid.NewGuid(), approvalWorkflowId: Guid.NewGuid());
var result = validator.Validate(cmd);
result.Errors.Should().NotContain(
e => e.PropertyName == nameof(CreatePurchaseEvaluationCommand.ApprovalWorkflowId));
}
// ============================================================
// 2. CREATE HANDLER — FK-invariant guard
// ============================================================