IqcBillStatusCompletionContractTests.cs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. using Xunit;
  2. namespace Admin.NET.Plugin.AiDOP.Tests.S5.MaterialWarehouse;
  3. /// <summary>
  4. /// P1-A 源码契约测试:qms_qcp_inspbill.FBILLSTATUS 完成态回写。
  5. ///
  6. /// 固化四条不变量:
  7. /// ① 终态只由流程结束回调 OnFlowCompleted 写,且仅 Approved 才写;
  8. /// ② NG 单不得在主管通过(N2)时提前写完成 —— 靠「只在实例终结回调里写」结构性保证,
  9. /// 故 supervisor-approve / sqe-submit-disposition 所在的 Service 内不得出现 FBILLSTATUS 写入;
  10. /// ③ 取值复用既有 done-word '检验完成',不自造新枚举、不采用无写入者的 COMPLETED/C/A/NG;
  11. /// ④ 回写必须租户隔离,且不越界改 pd/clfs/FINSPECTSTATUS。
  12. /// </summary>
  13. public class IqcBillStatusCompletionContractTests
  14. {
  15. private static string Handler() => File.ReadAllText(FindFile(
  16. "server", "Plugins", "Admin.NET.Plugin.AiDOP", "MaterialWarehouse", "IqcInspBillFlowBizHandler.cs"));
  17. private static string FlowService() => File.ReadAllText(FindFile(
  18. "server", "Plugins", "Admin.NET.Plugin.AiDOP", "MaterialWarehouse", "IqcInspBillFlowService.cs"));
  19. private static string TaskEntryService() => File.ReadAllText(FindFile(
  20. "server", "Plugins", "Admin.NET.Plugin.AiDOP", "MaterialWarehouse", "IqcTaskEntryService.cs"));
  21. private static string InspBillService() => File.ReadAllText(FindFile(
  22. "server", "Plugins", "Admin.NET.Plugin.AiDOP", "MaterialWarehouse", "IqcInspBillService.cs"));
  23. // ── ① 终态只在 OnFlowCompleted 写,且仅 Approved ──
  24. [Fact]
  25. public void Handler_WritesBillStatus_OnlyOnApprovedCompletion()
  26. {
  27. var s = Handler();
  28. Assert.Contains("public async Task OnFlowCompleted", s);
  29. Assert.Contains("if (finalStatus != FlowInstanceStatusEnum.Approved) return;", s);
  30. Assert.Contains("UPDATE qms_qcp_inspbill SET FBILLSTATUS=@Status", s);
  31. }
  32. // ── ② NG 不得提前完成:Service 侧不得出现 FBILLSTATUS 写入 ──
  33. [Fact]
  34. public void FlowService_DoesNotWriteBillStatus()
  35. {
  36. var s = FlowService();
  37. Assert.DoesNotContain("SET FBILLSTATUS", s);
  38. Assert.DoesNotContain("FBILLSTATUS=", s);
  39. // submit-result 仍只写这四列
  40. Assert.Contains("UPDATE qms_qcp_inspbill SET pd=@pd, dhsl=@dhsl, bhgsl=@bhgsl, clfs=@clfs", s);
  41. }
  42. /// <summary>生成时仍写 '检验中',是唯一的初始态写入点。</summary>
  43. [Fact]
  44. public void GenerateInspBill_StillWritesInProgressStatus()
  45. {
  46. var s = TaskEntryService();
  47. Assert.Contains("'检验中'", s);
  48. Assert.DoesNotContain("SET FBILLSTATUS", s);
  49. }
  50. // ── ③ 取值复用既有 done-word,不自造枚举 ──
  51. [Fact]
  52. public void BillStatusDone_ReusesExistingVocabulary()
  53. {
  54. var s = Handler();
  55. Assert.Contains("public const string BillStatusDone = \"检验完成\";", s);
  56. Assert.Contains("IqcInspBillFlowConst.BillStatusDone", s);
  57. // 不得引入库内那些无代码写入者的历史 seed 值
  58. Assert.DoesNotContain("\"COMPLETED\"", s);
  59. Assert.DoesNotContain("\"CREATED\"", s);
  60. }
  61. // ── ④ 租户隔离 + 不越界 ──
  62. [Fact]
  63. public void Handler_CompletionWrite_IsTenantScoped()
  64. {
  65. var s = Handler();
  66. Assert.Contains("WHERE id=@id AND tenant_id=@TenantId", s);
  67. Assert.Contains("_userManager.TenantId", s);
  68. }
  69. [Fact]
  70. public void Handler_DoesNotTouchOtherBusinessFields()
  71. {
  72. var s = Handler();
  73. // 不改判定/处理方式/审核人/报检分录状态
  74. Assert.DoesNotContain("SET pd=", s);
  75. Assert.DoesNotContain("SET clfs=", s);
  76. Assert.DoesNotContain("FAUDITORID=", s);
  77. Assert.DoesNotContain("qms_qcp_insappnentry", s);
  78. }
  79. /// <summary>详情接口仍原样返回 FBILLSTATUS,不得在 GET 侧动态伪造完成态。</summary>
  80. [Fact]
  81. public void DetailApi_ReturnsRawBillStatus_NoDerivedCompletion()
  82. {
  83. var s = InspBillService();
  84. Assert.Contains("ib.FBILLSTATUS AS BillStatus", s);
  85. Assert.DoesNotContain("检验完成", s);
  86. }
  87. /// <summary>B-2 检规快照逻辑未被本次修改波及。</summary>
  88. [Fact]
  89. public void SpecSnapshotColumns_StillWrittenOnlyAtGeneration()
  90. {
  91. var gen = TaskEntryService();
  92. Assert.Contains("jgbh, jgbb, jgsxrq, jygfid", gen);
  93. Assert.Contains("spec.IsMatched", gen);
  94. var handler = Handler();
  95. Assert.DoesNotContain("jgbh", handler);
  96. Assert.DoesNotContain("jygfid", handler);
  97. }
  98. private static string FindFile(params string[] parts)
  99. {
  100. var dir = new DirectoryInfo(AppContext.BaseDirectory);
  101. while (dir != null)
  102. {
  103. var candidate = Path.Combine(new[] { dir.FullName }.Concat(parts).ToArray());
  104. if (File.Exists(candidate)) return candidate;
  105. dir = dir.Parent;
  106. }
  107. throw new FileNotFoundException(string.Join("/", parts));
  108. }
  109. }