| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- using Xunit;
- namespace Admin.NET.Plugin.AiDOP.Tests.S5.MaterialWarehouse;
- /// <summary>
- /// P1-A 源码契约测试:qms_qcp_inspbill.FBILLSTATUS 完成态回写。
- ///
- /// 固化四条不变量:
- /// ① 终态只由流程结束回调 OnFlowCompleted 写,且仅 Approved 才写;
- /// ② NG 单不得在主管通过(N2)时提前写完成 —— 靠「只在实例终结回调里写」结构性保证,
- /// 故 supervisor-approve / sqe-submit-disposition 所在的 Service 内不得出现 FBILLSTATUS 写入;
- /// ③ 取值复用既有 done-word '检验完成',不自造新枚举、不采用无写入者的 COMPLETED/C/A/NG;
- /// ④ 回写必须租户隔离,且不越界改 pd/clfs/FINSPECTSTATUS。
- /// </summary>
- public class IqcBillStatusCompletionContractTests
- {
- private static string Handler() => File.ReadAllText(FindFile(
- "server", "Plugins", "Admin.NET.Plugin.AiDOP", "MaterialWarehouse", "IqcInspBillFlowBizHandler.cs"));
- private static string FlowService() => File.ReadAllText(FindFile(
- "server", "Plugins", "Admin.NET.Plugin.AiDOP", "MaterialWarehouse", "IqcInspBillFlowService.cs"));
- private static string TaskEntryService() => File.ReadAllText(FindFile(
- "server", "Plugins", "Admin.NET.Plugin.AiDOP", "MaterialWarehouse", "IqcTaskEntryService.cs"));
- private static string InspBillService() => File.ReadAllText(FindFile(
- "server", "Plugins", "Admin.NET.Plugin.AiDOP", "MaterialWarehouse", "IqcInspBillService.cs"));
- // ── ① 终态只在 OnFlowCompleted 写,且仅 Approved ──
- [Fact]
- public void Handler_WritesBillStatus_OnlyOnApprovedCompletion()
- {
- var s = Handler();
- Assert.Contains("public async Task OnFlowCompleted", s);
- Assert.Contains("if (finalStatus != FlowInstanceStatusEnum.Approved) return;", s);
- Assert.Contains("UPDATE qms_qcp_inspbill SET FBILLSTATUS=@Status", s);
- }
- // ── ② NG 不得提前完成:Service 侧不得出现 FBILLSTATUS 写入 ──
- [Fact]
- public void FlowService_DoesNotWriteBillStatus()
- {
- var s = FlowService();
- Assert.DoesNotContain("SET FBILLSTATUS", s);
- Assert.DoesNotContain("FBILLSTATUS=", s);
- // submit-result 仍只写这四列
- Assert.Contains("UPDATE qms_qcp_inspbill SET pd=@pd, dhsl=@dhsl, bhgsl=@bhgsl, clfs=@clfs", s);
- }
- /// <summary>生成时仍写 '检验中',是唯一的初始态写入点。</summary>
- [Fact]
- public void GenerateInspBill_StillWritesInProgressStatus()
- {
- var s = TaskEntryService();
- Assert.Contains("'检验中'", s);
- Assert.DoesNotContain("SET FBILLSTATUS", s);
- }
- // ── ③ 取值复用既有 done-word,不自造枚举 ──
- [Fact]
- public void BillStatusDone_ReusesExistingVocabulary()
- {
- var s = Handler();
- Assert.Contains("public const string BillStatusDone = \"检验完成\";", s);
- Assert.Contains("IqcInspBillFlowConst.BillStatusDone", s);
- // 不得引入库内那些无代码写入者的历史 seed 值
- Assert.DoesNotContain("\"COMPLETED\"", s);
- Assert.DoesNotContain("\"CREATED\"", s);
- }
- // ── ④ 租户隔离 + 不越界 ──
- [Fact]
- public void Handler_CompletionWrite_IsTenantScoped()
- {
- var s = Handler();
- Assert.Contains("WHERE id=@id AND tenant_id=@TenantId", s);
- Assert.Contains("_userManager.TenantId", s);
- }
- [Fact]
- public void Handler_DoesNotTouchOtherBusinessFields()
- {
- var s = Handler();
- // 不改判定/处理方式/审核人/报检分录状态
- Assert.DoesNotContain("SET pd=", s);
- Assert.DoesNotContain("SET clfs=", s);
- Assert.DoesNotContain("FAUDITORID=", s);
- Assert.DoesNotContain("qms_qcp_insappnentry", s);
- }
- /// <summary>详情接口仍原样返回 FBILLSTATUS,不得在 GET 侧动态伪造完成态。</summary>
- [Fact]
- public void DetailApi_ReturnsRawBillStatus_NoDerivedCompletion()
- {
- var s = InspBillService();
- Assert.Contains("ib.FBILLSTATUS AS BillStatus", s);
- Assert.DoesNotContain("检验完成", s);
- }
- /// <summary>B-2 检规快照逻辑未被本次修改波及。</summary>
- [Fact]
- public void SpecSnapshotColumns_StillWrittenOnlyAtGeneration()
- {
- var gen = TaskEntryService();
- Assert.Contains("jgbh, jgbb, jgsxrq, jygfid", gen);
- Assert.Contains("spec.IsMatched", gen);
- var handler = Handler();
- Assert.DoesNotContain("jgbh", handler);
- Assert.DoesNotContain("jygfid", handler);
- }
- private static string FindFile(params string[] parts)
- {
- var dir = new DirectoryInfo(AppContext.BaseDirectory);
- while (dir != null)
- {
- var candidate = Path.Combine(new[] { dir.FullName }.Concat(parts).ToArray());
- if (File.Exists(candidate)) return candidate;
- dir = dir.Parent;
- }
- throw new FileNotFoundException(string.Join("/", parts));
- }
- }
|