S6ProcessInspectionEntryContractTests.cs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. using Xunit;
  2. namespace Admin.NET.Plugin.AiDOP.Tests.S6.Manufacturing;
  3. /// <summary>S6 Phase 1C 录入/判定/完成 源码契约测试:快照唯一判定源、后端判定、幂等、事务、完成校验、tenant、入参不信任判定。</summary>
  4. public class S6ProcessInspectionEntryContractTests
  5. {
  6. private static string Svc() => File.ReadAllText(FindFile("server", "Plugins", "Admin.NET.Plugin.AiDOP", "Manufacturing", "S6ProcessInspectionService.cs"));
  7. private static string Dto() => File.ReadAllText(FindFile("server", "Plugins", "Admin.NET.Plugin.AiDOP", "Manufacturing", "Dto", "S6ProcessInspectionDto.cs"));
  8. private static string Judge() => File.ReadAllText(FindFile("server", "Plugins", "Admin.NET.Plugin.AiDOP", "Manufacturing", "S6ProcessInspectionJudge.cs"));
  9. [Fact]
  10. public void Save_BackendJudges_FromSnapshotConfig_NotLiveSpec()
  11. {
  12. var s = Svc();
  13. Assert.Contains("S6ProcessInspectionJudge.JudgeSample", s);
  14. Assert.Contains("LoadSnapshotConfigAsync", s);
  15. Assert.Contains("FROM ado_s6_process_inspection_snapshot_line", s);
  16. // 判定路径不回查 S0 检规
  17. Assert.DoesNotContain("FROM qms_gcjygfzb z\n WHERE z.tenant_id=@TenantId AND z.glid=@SpecId AND", s); // 仅生成时读,判定不读
  18. Assert.Contains("result_type AS ResultType, lower_limit AS LowerLimit, upper_limit AS UpperLimit, sample_count AS SampleCount", s);
  19. }
  20. [Fact]
  21. public void SaveInput_DoesNotAccept_JudgementOrTenantOrLimits()
  22. {
  23. var d = Dto();
  24. // 入参 DTO 只有 SnapshotLineId/SampleIndex/ActualValue/Remark
  25. Assert.Contains("public long SnapshotLineId", d);
  26. Assert.Contains("public string? ActualValue", d);
  27. Assert.DoesNotContain("public string? Judgement { get; set; }\n public long TenantId", d);
  28. // S6SaveResultSample 不含 Judgement / TenantId / LowerLimit / OverallResult
  29. var sampleClass = d.Substring(d.IndexOf("class S6SaveResultSample"));
  30. sampleClass = sampleClass.Substring(0, sampleClass.IndexOf("}") + 1);
  31. Assert.DoesNotContain("Judgement", sampleClass);
  32. Assert.DoesNotContain("TenantId", sampleClass);
  33. Assert.DoesNotContain("LowerLimit", sampleClass);
  34. }
  35. [Fact]
  36. public void Save_TenantScoped_AndTransactional()
  37. {
  38. var s = Svc();
  39. Assert.Contains("AidopTenantScope.ResolveOrThrow", s);
  40. Assert.Contains("BeginTran()", s);
  41. Assert.Contains("CommitTran()", s);
  42. Assert.Contains("RollbackTran()", s);
  43. Assert.DoesNotContain("tenant_id IS NULL", s);
  44. // 结果表读写均带 tenant
  45. Assert.Contains("WHERE tenant_id=@t AND bill_id=@bill", s);
  46. }
  47. [Fact]
  48. public void Save_IdempotentUpsert_ByBillLineSample()
  49. {
  50. var s = Svc();
  51. Assert.Contains("INSERT INTO ado_s6_process_inspection_result_item", s);
  52. Assert.Contains("ON DUPLICATE KEY UPDATE", s); // UK(bill,line,sample) 幂等
  53. }
  54. [Fact]
  55. public void Complete_EnforcesCompleteness()
  56. {
  57. var s = Svc();
  58. Assert.Contains("AllItemsCompleted", s);
  59. Assert.Contains("INSPECTION_INCOMPLETE", s);
  60. }
  61. [Fact]
  62. public void Header_CompletedAt_ConsistentWithStatus()
  63. {
  64. // completed_at 只在 COMPLETED 有值;回退时清空,避免 IN_PROGRESS 残留完成时间。
  65. var s = Svc();
  66. Assert.Contains("completed_at=CASE WHEN @status='COMPLETED' THEN @completedAt ELSE NULL END", s);
  67. }
  68. [Fact]
  69. public void Overall_And_Item_BackendComputed()
  70. {
  71. var s = Svc();
  72. Assert.Contains("AggregateItem", s);
  73. Assert.Contains("AggregateOverall", s);
  74. Assert.Contains("RecomputeAndPersistHeaderAsync", s);
  75. }
  76. [Fact]
  77. public void Judge_NonNumeric_AutoOkPassNgFail()
  78. {
  79. var j = Judge();
  80. Assert.Contains("\"OK\" => S6Judgement.Pass", j);
  81. Assert.Contains("\"NG\" => S6Judgement.Fail", j);
  82. // NUMERIC 无界限 → INVALID(不默认 PASS)
  83. Assert.Contains("if (lo == null && up == null) return S6Judgement.Invalid", j);
  84. Assert.Contains("if (actual == null) return S6Judgement.Invalid", j);
  85. }
  86. private static string FindFile(params string[] parts)
  87. {
  88. var dir = new DirectoryInfo(AppContext.BaseDirectory);
  89. while (dir != null)
  90. {
  91. var candidate = Path.Combine(new[] { dir.FullName }.Concat(parts).ToArray());
  92. if (File.Exists(candidate)) return candidate;
  93. dir = dir.Parent;
  94. }
  95. throw new FileNotFoundException(string.Join("/", parts));
  96. }
  97. }