using SheetMe.Core.Catalog; namespace SheetMe.Core.Tests; /// /// 쿼리 치환 변수 카탈로그 테스트. /// /// 판정 규칙이 바뀌었다. 예전에는 Substring(0, LastIndexOf(".")) 로 접두어를 잘라 /// 완전일치 비교한다고 보고 그렇게 검사했는데, 실제 엔진(clsMDataTable.vb:24-71)에서 그 줄은 /// 주석 처리되어 있고 StartsWith(FullName) + 정규식으로 대체됐다. /// 그래서 DataRow 접근형(.item("컬럼"))도 유효하다 — 옛 규칙으로 검사하면 그것이 전부 오탐이 된다. /// 판정은 하나로 모았다. /// [TestClass] public sealed class LegacyQueryVariableCatalogTests { /// 모든 토큰이 <<…>> 로 감싸이고 중복이 없어야 한다 [TestMethod] public void Tokens_AreWellFormedAndUnique() { var seen = new HashSet(StringComparer.Ordinal); foreach (var variable in LegacyQueryVariableCatalog.All) { StringAssert.StartsWith(variable.Token, "<<", $"{variable.Token}: '<<' 로 시작해야 합니다."); StringAssert.EndsWith(variable.Token, ">>", $"{variable.Token}: '>>' 로 끝나야 합니다."); Assert.IsTrue(seen.Add(variable.Token), $"중복 토큰: {variable.Token}"); Assert.IsFalse(string.IsNullOrWhiteSpace(variable.Description), $"{variable.Token}: 설명이 비어 있습니다."); } } /// /// 모든 토큰이 실제 엔진 규약으로 해석돼야 한다 — 접두어 StartsWith + clsMDataTable 정규식. /// 축약형이 섞이면 여기서 걸린다(치환되지 않고 SQL 에 그대로 남아 ORA 오류가 난다). /// [TestMethod] public void EveryToken_ResolvesUnderRuntimeRule() { foreach (var variable in LegacyQueryVariableCatalog.All) { Assert.IsTrue(LegacyQueryVariableCatalog.IsResolvable(variable.Token), $"{variable.Token}: 런타임이 해석할 수 없는 형태입니다 → 치환되지 않고 SQL 에 그대로 남습니다."); } } /// /// 목록에 띄우는 짧은 형태는 클래스 이름을 빼고 속성만 남겨야 한다. /// 토큰은 60자가 넘고 앞 40자가 모든 줄에서 같아, 그대로 깔면 정작 다른 부분이 잘린다. /// [TestMethod] public void ShortForm_DropsSharedClassPrefix() { foreach (var variable in LegacyQueryVariableCatalog.All) { Assert.IsFalse(variable.ShortForm.Contains("M.CMM", StringComparison.Ordinal), $"{variable.Token}: 짧은 형태에 클래스 이름이 남았습니다 → {variable.ShortForm}"); Assert.IsFalse(string.IsNullOrWhiteSpace(variable.ShortForm), variable.Token); Assert.IsTrue(variable.ShortForm.Length <= 24, $"{variable.Token}: 짧은 형태가 너무 깁니다 → {variable.ShortForm}"); } } /// DataRow 형태는 속성명부터 보여 준다 — 점이 여러 개라 마지막 조각만 쓰면 컬럼명만 남는다 [TestMethod] public void ShortForm_KeepsPropertyNameForDataRowTemplates() { var row = LegacyQueryVariableCatalog.All.First(v => v.Token.Contains("PatInfDR", StringComparison.Ordinal)); Assert.AreEqual("PatInfDR", row.ShortForm, "꼬리(.item(\"컬럼명\"))는 빼고 속성명만 남아야 한다"); } /// 접두어는 클래스 전체 이름이어야 한다 — 축약형 회귀 방지(이 버그가 실제로 있었다) [TestMethod] public void Prefixes_AreFullyQualifiedTypeNames() { foreach (var prefix in LegacyQueryVariableCatalog.Prefixes) { StringAssert.StartsWith(prefix, "M.", $"'{prefix}' 가 어셈블리 네임스페이스로 시작하지 않습니다."); Assert.IsTrue(prefix.Count(c => c == '.') >= 2, $"'{prefix}' 가 클래스 전체 이름 형태가 아닙니다."); } } /// 운영에서 실제로 쓰이는 토큰이 목록에 있어야 한다(2026-08-11 census 근거) [TestMethod] [DataRow("M.CMM.HISOperatingInfo.bzPatientInfo.ComNum")] [DataRow("M.CMM.HISOperatingInfo.bzPatientInfo.ChtNum")] [DataRow("M.CMM.HISOperatingInfo.bzPatientInfo.OemNum1")] [DataRow("M.CMM.HISOperatingInfo.bzPatientInfo.OemNum2")] [DataRow("M.CMM.HISOperatingInfo.bzPatientInfo.OemVar1")] [DataRow("M.CMM.HISOperatingInfo.bzPatientInfo.ComNum_Refer")] [DataRow("M.CMM.HISOperatingInfo.bzPatientInfo.AdpDtm")] [DataRow("M.CMM.HISOperatingInfo.bzWorkInfo.WrkDte")] public void ProductionTokens_ArePresent(string inner) { Assert.IsTrue(LegacyQueryVariableCatalog.All.Any(v => v.Token == $"<<{inner}>>"), $"운영에서 쓰이는 토큰이 목록에 없습니다: <<{inner}>>"); } }