using SheetMe.Core.Catalog; namespace SheetMe.Core.Tests; /// /// SQL 정리 테스트. /// /// 가장 중요한 불변식은 뜻이 바뀌지 않는 것이다. 특히 치환 변수는 대소문자가 맞아야 /// 런타임이 찾으므로, 정리하면서 건드리면 그 서식은 조용히 값을 못 받는다. /// [TestClass] public sealed class SqlFormatterTests { [TestMethod] public void Format_BreaksBeforeMajorClauses() { var formatted = SqlFormatter.Format("select a from t where b=1 and c=2"); var lines = formatted.Split('\n'); StringAssert.StartsWith(lines[0], "SELECT"); Assert.IsTrue(lines.Any(l => l.StartsWith("FROM", StringComparison.Ordinal))); Assert.IsTrue(lines.Any(l => l.StartsWith("WHERE", StringComparison.Ordinal))); Assert.IsTrue(lines.Any(l => l.StartsWith("AND", StringComparison.Ordinal))); } [TestMethod] public void Format_UppercasesKeywordsAndFunctions() { var formatted = SqlFormatter.Format("select nvl(a,0) from dual"); StringAssert.Contains(formatted, "SELECT"); StringAssert.Contains(formatted, "NVL"); StringAssert.Contains(formatted, "DUAL"); } /// 식별자는 건드리지 않는다 — 대소문자를 가리는 곳에서 깨진다 [TestMethod] public void Format_LeavesIdentifiersAlone() { var formatted = SqlFormatter.Format("select ChtNum, PatNam from P_PatMst"); StringAssert.Contains(formatted, "ChtNum"); StringAssert.Contains(formatted, "PatNam"); StringAssert.Contains(formatted, "P_PatMst"); } /// /// 치환 변수는 원문 그대로여야 한다. 대문자로 바꾸면 런타임이 클래스 이름을 못 찾아 /// 그 칸이 조용히 빈다 — 이 포매터가 저지를 수 있는 가장 나쁜 일이다. /// [TestMethod] public void Format_NeverTouchesSubstitutionVariables() { const string token = "<>"; var formatted = SqlFormatter.Format($"select * from t where a = '{token}'"); StringAssert.Contains(formatted, token); } /// 《컨트롤명》도 마찬가지다 [TestMethod] public void Format_NeverTouchesControlSubstitution() { var formatted = SqlFormatter.Format("select * from t where a = 《TextBox1》"); StringAssert.Contains(formatted, "《TextBox1》"); } /// 문자열 안은 손대지 않는다 — 예약어와 같은 낱말이 들어 있어도 [TestMethod] public void Format_LeavesStringLiteralsAlone() { var formatted = SqlFormatter.Format("select 'from where select' from t"); StringAssert.Contains(formatted, "'from where select'"); } [TestMethod] public void Format_IndentsByParenthesisDepth() { var formatted = SqlFormatter.Format("select * from (select a from t)"); Assert.IsTrue(formatted.Split('\n').Any(l => l.StartsWith(" ", StringComparison.Ordinal)), formatted); } [TestMethod] public void Format_LeavesNoTrailingWhitespace() { var formatted = SqlFormatter.Format("select a , b from t where c = 1"); foreach (var line in formatted.Split('\n')) { Assert.AreEqual(line.TrimEnd(), line, $"줄 끝 공백이 남았습니다: '{line}'"); } } [DataTestMethod] [DataRow("")] [DataRow(" ")] public void Format_ReturnsEmptyInputUnchanged(string input) => Assert.AreEqual(input, SqlFormatter.Format(input)); /// 정리해도 다시 정리하면 같아야 한다 — 누를 때마다 모양이 흔들리면 못 쓴다 [TestMethod] public void Format_IsStableWhenAppliedTwice() { var once = SqlFormatter.Format("select a,b from t where c=1 and d=2"); var twice = SqlFormatter.Format(once); Assert.AreEqual(once, twice); } }