S6ProcessInspectionContractTests.cs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. using Xunit;
  2. namespace Admin.NET.Plugin.AiDOP.Tests.S6.Manufacturing;
  3. /// <summary>
  4. /// S6 Phase 1B 源码契约测试:解析器 map-only + tenant-safe;生成服务 事务/幂等/快照/仅MATCHED写;前端无人工选择器。
  5. /// </summary>
  6. public class S6ProcessInspectionContractTests
  7. {
  8. private static string Resolver() => File.ReadAllText(FindFile("server", "Plugins", "Admin.NET.Plugin.AiDOP", "Manufacturing", "S6ProcessSpecResolver.cs"));
  9. private static string Service() => File.ReadAllText(FindFile("server", "Plugins", "Admin.NET.Plugin.AiDOP", "Manufacturing", "S6ProcessInspectionService.cs"));
  10. private static string Vue() => File.ReadAllText(FindFile("Web", "src", "views", "aidop", "s6", "production-record", "productionInstructionList.vue"));
  11. [Fact]
  12. public void Resolver_RuntimeReadsMapTable_TenantAndItemScoped()
  13. {
  14. var s = Resolver();
  15. Assert.Contains("FROM ado_s6_ipqc_spec_material_map", s);
  16. Assert.Contains("m.tenant_id=@TenantId AND m.item_code=@ItemCode AND m.match_status='EXACT'", s);
  17. Assert.Contains("g.tenant_id=@TenantId", s); // 检规同租户
  18. }
  19. [Fact]
  20. public void Resolver_NoIdDescFallback_NoManualPick()
  21. {
  22. var s = Resolver();
  23. Assert.DoesNotContain("ORDER BY g.id DESC", s);
  24. Assert.DoesNotContain("ORDER BY id DESC LIMIT 1", s);
  25. // 裁决按 wjbh 分组
  26. Assert.Contains("GroupBy(c => (c.Wjbh", s);
  27. }
  28. [Fact]
  29. public void Service_StrictTenantScope_EveryQuery()
  30. {
  31. var s = Service();
  32. Assert.Contains("AidopTenantScope.ResolveOrThrow", s);
  33. Assert.DoesNotContain("tenant_id IS NULL", s);
  34. Assert.DoesNotContain("tenant_id = 0", s);
  35. var n = s.Split("tenant_id=@TenantId").Length - 1;
  36. Assert.True(n >= 4, $"expected tenant predicate on load/idempotency/insert/snapshot/detail, found {n}");
  37. }
  38. [Fact]
  39. public void Service_OnlyMatchedWrites()
  40. {
  41. var s = Service();
  42. Assert.Contains("resolve.Status != S6SpecResolveStatus.Matched", s);
  43. Assert.Contains("return res; // 非唯一解析:不写库", s);
  44. Assert.Contains("resolve.CandidateCount != 1", s);
  45. }
  46. [Fact]
  47. public void Service_TransactionalGeneration()
  48. {
  49. var s = Service();
  50. Assert.Contains("BeginTran()", s);
  51. Assert.Contains("CommitTran()", s);
  52. Assert.Contains("RollbackTran()", s);
  53. }
  54. [Fact]
  55. public void Service_IdempotentReuseActiveBill()
  56. {
  57. var s = Service();
  58. Assert.Contains("inspection_status IN ('DRAFT','ACTIVE')", s);
  59. Assert.Contains("Reused = true", s);
  60. }
  61. [Fact]
  62. public void Service_SnapshotFreezesFromS0Detail_WithResultType()
  63. {
  64. var s = Service();
  65. Assert.Contains("INSERT INTO ado_s6_process_inspection_snapshot_head", s);
  66. Assert.Contains("INSERT INTO ado_s6_process_inspection_snapshot_line", s);
  67. Assert.Contains("FROM qms_gcjygfzb z", s); // 快照冻结自 S0 明细
  68. Assert.Contains("z.lrlx", s); // ResultType 进快照
  69. }
  70. [Fact]
  71. public void Frontend_NoManualSpecSelector_CallsPrepare()
  72. {
  73. var v = Vue();
  74. Assert.Contains("prepareProcessInspection", v);
  75. Assert.DoesNotContain(":disabled=\"true\"", v); // 按钮不再禁用
  76. // 无人工检规下拉/选择器
  77. Assert.DoesNotContain("specSelect", v);
  78. Assert.DoesNotContain("选择检规", v);
  79. }
  80. private static string FindFile(params string[] parts)
  81. {
  82. var dir = new DirectoryInfo(AppContext.BaseDirectory);
  83. while (dir != null)
  84. {
  85. var candidate = Path.Combine(new[] { dir.FullName }.Concat(parts).ToArray());
  86. if (File.Exists(candidate)) return candidate;
  87. dir = dir.Parent;
  88. }
  89. throw new FileNotFoundException(string.Join("/", parts));
  90. }
  91. }