using Admin.NET.Plugin.AiDOP.Manufacturing; using Admin.NET.Plugin.AiDOP.Manufacturing.Dto; using Xunit; namespace Admin.NET.Plugin.AiDOP.Tests.S6.Manufacturing; /// /// S6 Phase 1B 过程检规解析器 裁决逻辑单测(纯函数 Decide,无 DB)。 /// 覆盖 MATCHED / NO_MATCH / AMBIGUOUS / VERSION / INVALID_SPEC + 日期归一化 + tokenize。 /// public class S6ProcessSpecResolverTests { private static S6SpecCandidate C(long id, string wjbh, int version, string sxrj, int detail) => new() { Id = id, Wjbh = wjbh, Bb = "A." + version, Version = version, Sxrj = sxrj, DetailCount = detail }; private static readonly DateTime Ref = new(2026, 9, 1); [Fact] public void Matched_SingleEffectiveWjbh() { var r = S6ProcessSpecResolver.Decide(new[] { C(101, "UATD-GC-001", 1, "2026-08-28", 2) }, Ref); Assert.Equal(S6SpecResolveStatus.Matched, r.Status); Assert.Equal(101, r.MatchedSpecId); Assert.Equal(1, r.CandidateCount); } [Fact] public void NoMatch_EmptyCandidates() { var r = S6ProcessSpecResolver.Decide(System.Array.Empty(), Ref); Assert.Equal(S6SpecResolveStatus.NoMatch, r.Status); } [Fact] public void Ambiguous_TwoDistinctWjbhBothEffective() { var r = S6ProcessSpecResolver.Decide(new[] { C(201, "UATD-GC-002", 1, "2026-08-28", 2), C(202, "UATD-GC-006", 1, "2026-08-28", 2), }, Ref); Assert.Equal(S6SpecResolveStatus.Ambiguous, r.Status); Assert.Equal(2, r.CandidateCount); Assert.Null(r.MatchedSpecId); } [Fact] public void Version_SameWjbhPicksNewestEffective() { var r = S6ProcessSpecResolver.Decide(new[] { C(301, "UATD-GC-003", 1, "2026-08-28", 2), C(302, "UATD-GC-003", 2, "2026-08-29", 2), }, Ref); Assert.Equal(S6SpecResolveStatus.Matched, r.Status); Assert.Equal(302, r.MatchedSpecId); // 同 wjbh 取 sxrj 最新版本 Assert.Equal(1, r.CandidateCount); } [Fact] public void InvalidSpec_MatchedButNoDetail() { var r = S6ProcessSpecResolver.Decide(new[] { C(401, "UATD-GC-009", 1, "2026-08-28", 0) }, Ref); Assert.Equal(S6SpecResolveStatus.InvalidSpec, r.Status); } [Fact] public void NoMatch_OnlyFutureEffective() { var r = S6ProcessSpecResolver.Decide(new[] { C(501, "UATD-GC-010", 1, "2027-01-01", 2) }, Ref); Assert.Equal(S6SpecResolveStatus.NoMatch, r.Status); } [Fact] public void NormalizeDate_HandlesFreeTextFormats() { Assert.NotNull(S6ProcessSpecResolver.NormalizeDate("2026-08-28")); Assert.NotNull(S6ProcessSpecResolver.NormalizeDate("2025.12.12")); Assert.NotNull(S6ProcessSpecResolver.NormalizeDate("2025-05-28 00:00:00")); Assert.Null(S6ProcessSpecResolver.NormalizeDate("")); Assert.Null(S6ProcessSpecResolver.NormalizeDate("包装(灭菌类)")); } [Fact] public void Tokenize_SplitsMixedDelimiters() { var t = S6ProcessSpecResolver.Tokenize("91HC1N7;91HC1N8,98BC19A、982C01911 91HC12N"); Assert.Contains("91HC1N7", t); Assert.Contains("98BC19A", t); Assert.Contains("982C01911", t); Assert.Equal(5, t.Count); } }