S6ProcessInspectionReviewContractTests.cs 4.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. using Xunit;
  2. namespace Admin.NET.Plugin.AiDOP.Tests.S6.Manufacturing;
  3. /// <summary>
  4. /// S6 Phase 1D 桥接契约测试:Phase 1C bill 接入 ApprovalFlow(BizType=S6_PROCESS_INSPECTION)。
  5. /// 覆盖:独立 BizType 绑 bill.id、提交门槛 COMPLETED、FAIL→disposition_required、质量结果不改写、tenant/幂等/N1=Initiator。
  6. /// </summary>
  7. public class S6ProcessInspectionReviewContractTests
  8. {
  9. private static string Svc() => File.ReadAllText(FindFile("server", "Plugins", "Admin.NET.Plugin.AiDOP", "Manufacturing", "S6ProcessInspectionReviewService.cs"));
  10. private static string Handler() => File.ReadAllText(FindFile("server", "Plugins", "Admin.NET.Plugin.AiDOP", "Manufacturing", "S6ProcessInspectionFlowBizHandler.cs"));
  11. private static string Const() => File.ReadAllText(FindFile("server", "Plugins", "Admin.NET.Plugin.AiDOP", "Manufacturing", "S6ProcessInspectionFlowConst.cs"));
  12. private static string Seed() => File.ReadAllText(FindFile("server", "Plugins", "Admin.NET.Plugin.AiDOP", "Infrastructure", "S6ProcessInspectionFlowSeed.cs"));
  13. [Fact]
  14. public void BizType_IsIndependent_BoundToProcessInspectionBill()
  15. {
  16. Assert.Contains("\"S6_PROCESS_INSPECTION\"", Const());
  17. var s = Svc();
  18. // business_id = process_inspection_bill.id(StartFlow BizId=input.BillId),不绑 qms_gcjyd
  19. Assert.Contains("BizId = input.BillId", s);
  20. Assert.Contains("FROM ado_s6_process_inspection_bill", s);
  21. Assert.DoesNotContain("qms_gcjyd", s);
  22. }
  23. [Fact]
  24. public void Submit_Gate_RequiresCompleted()
  25. {
  26. var s = Svc();
  27. Assert.Contains("INSPECTION_NOT_COMPLETED", s);
  28. Assert.Contains("StatusCompleted", s);
  29. }
  30. [Fact]
  31. public void Gateway_FailDrivesDisposition_FromOverallResult()
  32. {
  33. // 网关判据 disposition_required 由 overall_result=FAIL 决定,只读、不改写质量结果
  34. var h = Handler();
  35. Assert.Contains("overall_result", h);
  36. Assert.Contains("ResultFail", h);
  37. Assert.Contains("[\"disposition_required\"]", h);
  38. // Handler 不 UPDATE 质量结果
  39. Assert.DoesNotContain("UPDATE ado_s6_process_inspection_result", h);
  40. }
  41. [Fact]
  42. public void Disposition_DoesNotOverwrite_QualityResult()
  43. {
  44. var s = Svc();
  45. // 处置落 ado_s6_ipqc_quality_disposition,绝不 UPDATE overall_result(§20 硬门)
  46. Assert.Contains("AdoS6IpqcQualityDisposition", s);
  47. Assert.DoesNotContain("UPDATE ado_s6_process_inspection_result SET overall_result", s);
  48. Assert.DoesNotContain("SET overall_result", s);
  49. }
  50. [Fact]
  51. public void Idempotent_And_TenantSafe_And_Transactional()
  52. {
  53. var s = Svc();
  54. // S6-PI-RESUBMIT-1:「一单一 active flow」这一原意不变,但不再由「Running 就拒绝」这句话承担
  55. // ——那句会连主管退回后的合法重提一起挡死。现在改由两条共同保证:
  56. // ① 已在 N2/N3 的重复提交被节点判定拒绝;② 重提沿用同一实例,只有 taskId==null 才 StartFlow。
  57. Assert.Contains("当前流程节点不允许提交审核", s);
  58. Assert.Contains("if (taskId == null)", s);
  59. Assert.Contains("ResolveTenantOrThrow", s);
  60. Assert.Contains("UseTranAsync", s);
  61. Assert.DoesNotContain("tenant_id IS NULL", s);
  62. }
  63. [Fact]
  64. public void N1_IsInitiator_ReuseSupervisorAndQeRoles()
  65. {
  66. var seed = Seed();
  67. Assert.Contains("nameof(ApproverTypeEnum.Initiator)", seed); // N1=发起人
  68. // S6-TENANT-AUTHORITY-1:审批权威由物理 RoleId 改为 RoleCode(原断言锁的是已被本批修复的
  69. // 「FlowJson 写死默认租户 RoleId」实现细节)。「N2/N3 复用 IPQC 角色」这一原意不变,只是改按 Code 表达。
  70. Assert.Contains("RoleSupervisorCode", seed); // N2 复用 IPQC 主管角色
  71. Assert.Contains("RoleQualityEngineerCode", seed); // N3 复用 IPQC QE 角色
  72. // 复用 IPQC 角色定义(不新造角色码)
  73. Assert.Contains("IpqcInspectionFlowConst.RoleSupervisorCode", Const());
  74. }
  75. private static string FindFile(params string[] parts)
  76. {
  77. var dir = new DirectoryInfo(AppContext.BaseDirectory);
  78. while (dir != null)
  79. {
  80. var candidate = Path.Combine(new[] { dir.FullName }.Concat(parts).ToArray());
  81. if (File.Exists(candidate)) return candidate;
  82. dir = dir.Parent;
  83. }
  84. throw new FileNotFoundException(string.Join("/", parts));
  85. }
  86. }