using SheetMe.Core.Catalog;
namespace SheetMe.Core.Tests;
///
/// 치환 변수 형태 판정 테스트 — 실제 엔진 clsMDataTable.ConvertQuery 규약을 옮긴 것.
///
/// 규약을 어기면 <<...>> 가 SQL 에 그대로 남아 ORA 오류가 나거나
/// 빈 문자열로 조용히 치환된다. 둘 다 화면에는 아무 표시가 없어 배포 후에야 드러난다.
///
[TestClass]
public sealed class QueryVariableResolveTests
{
private const string Patient = "M.CMM.HISOperatingInfo.bzPatientInfo";
private const string Sheet = "M.EMR.SheetLoadOperatingInfo.bzSheetInfo";
private const string Work = "M.CMM.HISOperatingInfo.bzWorkInfo";
/// 카탈로그에 실린 토큰은 전부 런타임이 해석할 수 있어야 한다 — 자기모순 방지
[TestMethod]
public void EveryCatalogTokenIsResolvable()
{
foreach (var variable in LegacyQueryVariableCatalog.All)
{
Assert.IsTrue(LegacyQueryVariableCatalog.IsResolvable(variable.Token),
$"카탈로그 토큰이 규약에 안 맞습니다: {variable.Token}");
}
}
/// 컬럼 채우기 틀은 NeedsColumn 표시가 있어야 편집기가 그 자리를 선택해 준다
[TestMethod]
public void ColumnTemplatesAreFlagged()
{
foreach (var variable in LegacyQueryVariableCatalog.All)
{
var hasPlaceholder = variable.Token.Contains("컬럼명", StringComparison.Ordinal);
Assert.AreEqual(hasPlaceholder, variable.NeedsColumn,
$"NeedsColumn 표시가 토큰과 어긋납니다: {variable.Token}");
}
}
[DataTestMethod]
[DataRow("ChtNum")]
[DataRow("ComNum_Refer")]
[DataRow("OemVar5")]
public void ScalarPropertyResolves(string property)
=> Assert.IsTrue(LegacyQueryVariableCatalog.IsResolvable($"<<{Patient}.{property}>>"));
///
/// DataRow 접근형 2가지가 모두 유효하다 — clsMDataTable.vb:113 정규식이 둘 다 받는다.
/// 이 파일의 예전 주석은 '반드시 실패한다'고 했으나 그 근거였던 LastIndexOf(".") 줄은
/// 실제 엔진에서 주석 처리되어 있다(clsMDataTable.vb:32).
///
[DataTestMethod]
[DataRow("PatInfDR.item(\"PatNam\")")]
[DataRow("PatInfDR(\"PatNam\")")]
[DataRow("CowInfDR.item(\"CowWadCod\")")]
public void DataRowAccessResolves(string path)
=> Assert.IsTrue(LegacyQueryVariableCatalog.IsResolvable($"<<{Patient}.{path}>>"));
/// item 은 반드시 소문자 — 정규식에 IgnoreCase 가 없다
[DataTestMethod]
[DataRow("PatInfDR.Item(\"PatNam\")")]
[DataRow("PatInfDR.ITEM(\"PatNam\")")]
public void UpperCaseItemDoesNotResolve(string path)
=> Assert.IsFalse(LegacyQueryVariableCatalog.IsResolvable($"<<{Patient}.{path}>>"),
"item 을 대문자로 쓰면 런타임이 값을 못 찾고 빈 문자열로 치환한다");
/// 접두어를 줄여 쓰면 치환되지 않는다 — 가장 흔한 실수다
[DataTestMethod]
[DataRow("<>")]
[DataRow("<>")]
[DataRow("<>")]
public void ShortenedPrefixDoesNotResolve(string token)
=> Assert.IsFalse(LegacyQueryVariableCatalog.IsResolvable(token));
/// 클래스명 뒤에 점이 없으면 안 된다(접두어가 더 긴 이름의 앞부분인 경우 방지)
[TestMethod]
public void PrefixMustBeFollowedByDot()
=> Assert.IsFalse(LegacyQueryVariableCatalog.IsResolvable($"<<{Patient}Extra.ChtNum>>"));
/// 중첩 경로는 정규식이 받지 않는다 — 한 단계만 허용한다
[TestMethod]
public void NestedPathDoesNotResolve()
=> Assert.IsFalse(LegacyQueryVariableCatalog.IsResolvable($"<<{Patient}.PatientInfoBiz_Refer.ComNum>>"));
[TestMethod]
public void SheetAndWorkPrefixesResolve()
{
Assert.IsTrue(LegacyQueryVariableCatalog.IsResolvable($"<<{Sheet}.ShtCod>>"));
Assert.IsTrue(LegacyQueryVariableCatalog.IsResolvable($"<<{Work}.WrkUid>>"));
}
[DataTestMethod]
[DataRow("ChtNum")]
[DataRow("< Assert.IsFalse(LegacyQueryVariableCatalog.IsResolvable(token));
///
/// 편집기가 실제로 쓰는 흐름 — 쿼리에서 뽑은 변수를 그대로 판정에 넘긴다.
///
[TestMethod]
public void TokenizerOutputFeedsResolverDirectly()
{
var sql = $"SELECT * FROM P_PatMst WHERE ChtNum = '<<{Patient}.ChtNum>>' " +
$"AND Nam = '<<{Patient}.PatInfDR.item(\"PatNam\")>>' AND X = '<>'";
var bad = SqlTokenizer.VariablesIn(sql)
.Where(v => !LegacyQueryVariableCatalog.IsResolvable(v))
.ToList();
CollectionAssert.AreEqual(new[] { "<>" }, bad.ToArray());
}
}