using SheetMe.Core.Catalog;
namespace SheetMe.Core.Tests;
///
/// DataTableField 배선 문자열의 파싱·조립·검증 테스트.
///
/// 표본은 지어내지 않고 운영 DB 실측(--db-dtf)과 레거시 소스에서 그대로 가져왔다.
/// 이 형식은 어겨도 런타임이 예외를 빈 catch 로 삼켜 아무 표시 없이 값이 안 채워지므로,
/// 여기서 막지 못하면 잘못된 배선이 임상 화면까지 간다.
///
[TestClass]
public sealed class DataTableFieldSpecTests
{
/// 레거시 전용 편집기가 만드는 정본 형태 — 공백이 하나도 없다
[TestMethod]
public void Parse_SelectForm_FromLegacyEditorOutput()
{
var spec = DataTableFieldSpec.Parse("mdt_CodDepUid.Select(\"\")(0).Item(\"CODSTRDTM\")");
Assert.IsNotNull(spec);
Assert.AreEqual(DataTableFieldForm.Select, spec.Form);
Assert.AreEqual("mdt_CodDepUid", spec.TableName);
Assert.AreEqual(string.Empty, spec.Filter);
Assert.AreEqual(0, spec.RowIndex);
Assert.AreEqual("CODSTRDTM", spec.Field);
}
/// 운영 다수파(1,624건, 76%) — 손으로 쓴 Rows 형도 읽어야 한다
[TestMethod]
public void Parse_RowsForm_TheMajorityInProduction()
{
var spec = DataTableFieldSpec.Parse("MDataTable2.Rows(0).Item(\"ALGALGTYP\")");
Assert.IsNotNull(spec);
Assert.AreEqual(DataTableFieldForm.Rows, spec.Form);
Assert.AreEqual("MDataTable2", spec.TableName);
Assert.AreEqual(0, spec.RowIndex);
Assert.AreEqual("ALGALGTYP", spec.Field);
}
///
/// 필드에는 SQL 별칭 표현식이 들어온다 — 식별자 화이트리스트로 막으면 실서식을 못 연다.
/// 실측 출처: [900]CGCH [349]퇴원요약지.
///
[TestMethod]
public void Parse_FieldMayBeSqlExpression()
{
var spec = DataTableFieldSpec.Parse("MDataTable4.Select(\"\")(0).Item(\"EMDOPRDTE||'-'||EMDOPNAME\")");
Assert.IsNotNull(spec);
Assert.AreEqual("EMDOPRDTE||'-'||EMDOPNAME", spec.Field);
Assert.IsNull(spec.Validate());
}
/// 필터가 있는 형태
[TestMethod]
public void Parse_SelectForm_WithFilterAndRow()
{
var spec = DataTableFieldSpec.Parse("MDataTable1.Select(\"CODNAM='A'\")(2).Item(\"CODCOD\")");
Assert.IsNotNull(spec);
Assert.AreEqual("CODNAM='A'", spec.Filter);
Assert.AreEqual(2, spec.RowIndex);
}
///
/// 알아볼 수 없는 값은 null 이어야 한다 — 억지로 해석해 되쓰면 원문이 손상된다.
/// "MDataTable1.Rows" 는 실제로 운영에 234건 있는 미완성 값이고,
/// ")(" 는 레거시 편집기에서 아무것도 안 고르고 확인을 눌렀을 때 남는 찌꺼기다.
///
[DataTestMethod]
[DataRow("MDataTable1.Rows")]
[DataRow(")(")]
[DataRow("MDataTable1")]
[DataRow("MDataTable1.Select(\"\")(0).Item(\"F\") ")] // 뒤 공백은 Trim 되지만
[DataRow("MDataTable1.Select(\"\")( 0 ).Item(\"F\")")] // 내부 공백은 파서가 못 읽는다
[DataRow("MDataTable1.select(\"\")(0).Item(\"F\")")] // 소문자 select — 런타임이 다른 가지로 샌다
public void Parse_ReturnsNull_ForShapesTheRuntimeCannotRead(string value)
{
var spec = DataTableFieldSpec.Parse(value);
// 뒤 공백만 있는 경우는 Trim 후 정상이다 — 그 한 건만 예외로 허용한다
if (value.Trim() == "MDataTable1.Select(\"\")(0).Item(\"F\")")
{
Assert.IsNotNull(spec);
return;
}
Assert.IsNull(spec, $"해석돼서는 안 되는 값이 해석됐습니다: {value}");
}
/// 조립 결과가 다시 같은 값으로 읽혀야 한다
[DataTestMethod]
[DataRow(DataTableFieldForm.Select)]
[DataRow(DataTableFieldForm.Rows)]
public void Compose_RoundTrips(DataTableFieldForm form)
{
var spec = new DataTableFieldSpec
{
Form = form,
TableName = "MDataTable3",
Filter = "CODCOD='X'",
RowIndex = 1,
Field = "CODNAM",
};
Assert.IsTrue(spec.RoundTrips(), spec.Compose());
}
/// 조립에는 공백이 들어가지 않는다 — 레거시 파서가 공백을 못 넘긴다
[TestMethod]
public void Compose_ContainsNoSpaces_AroundSyntax()
{
var spec = new DataTableFieldSpec { TableName = "MDataTable1", RowIndex = 0, Field = "F" };
Assert.AreEqual("MDataTable1.Select(\"\")(0).Item(\"F\")", spec.Compose());
}
/// Rows 형은 필터를 쓰지 않는다 — 값에 섞여 나가면 안 된다
[TestMethod]
public void Compose_RowsForm_DropsFilter()
{
var spec = new DataTableFieldSpec
{
Form = DataTableFieldForm.Rows,
TableName = "MDataTable1",
Filter = "무시돼야 한다",
RowIndex = 3,
Field = "F",
};
Assert.AreEqual("MDataTable1.Rows(3).Item(\"F\")", spec.Compose());
}
/// 런타임을 조용히 깨뜨리는 값은 저장 전에 막는다
[TestMethod]
public void Validate_RejectsQuoteInFilter()
{
var spec = new DataTableFieldSpec
{
TableName = "MDataTable1", Filter = "CODNAM=\"A\"", RowIndex = 0, Field = "F",
};
Assert.IsNotNull(spec.Validate());
}
/// 이름에 마침표가 있으면 런타임이 첫 조각만 떼어내 영영 못 찾는다
[TestMethod]
public void Validate_RejectsDotInTableName()
{
var spec = new DataTableFieldSpec { TableName = "a.b", RowIndex = 0, Field = "F" };
Assert.IsNotNull(spec.Validate());
}
///
/// 같은 서식에 없는 데이터소스를 가리키면 막는다 — 대소문자까지 맞아야 한다.
/// 실측에서 이런 값이 20건 있었고 전부 조용히 실패한다.
///
[TestMethod]
public void Validate_RejectsTableMissingFromThisSheet()
{
var spec = new DataTableFieldSpec { TableName = "MDataTable2", RowIndex = 0, Field = "F" };
Assert.IsNotNull(spec.Validate(new[] { "MDataTable3" }));
Assert.IsNull(spec.Validate(new[] { "MDataTable2" }));
// 대소문자가 다르면 런타임 Hashtable 조회가 실패한다
Assert.IsNotNull(spec.Validate(new[] { "mdatatable2" }));
}
/// 필드가 비면 저장할 수 없다
[TestMethod]
public void Validate_RequiresField()
{
var spec = new DataTableFieldSpec { TableName = "MDataTable1", RowIndex = 0, Field = string.Empty };
Assert.IsNotNull(spec.Validate());
}
}