using SheetMe.Core.Catalog; namespace SheetMe.Core.Tests; /// /// 자동완성 문맥 판단 테스트. /// /// 문맥을 잘못 잡으면 목록이 엉뚱한 것을 보여 주고, 그러면 안 쓰느니만 못하다. /// 특히 << 직후는 이 편집기에만 있는 문법이라 여기서 놓치면 손으로 외워 쳐야 한다. /// [TestClass] public sealed class SqlCompletionTests { /// 커서를 | 로 표시한 텍스트를 받아 분석한다 — 테스트가 읽히게 private static SqlCompletionQuery? At(string marked) { var caret = marked.IndexOf('|'); return SqlCompletion.Analyze(marked.Remove(caret, 1), caret); } [TestMethod] public void Analyze_AfterOpenAngle_SuggestsVariables() { var query = At("WHERE a = <이미 닫힌 변수 뒤에서는 변수 문맥이 아니다 [TestMethod] public void Analyze_AfterClosedVariable_IsNotVariableContext() { var query = At("WHERE a = <> AND na|"); Assert.IsNotNull(query); Assert.AreNotEqual(SqlCompletionContext.Variable, query.Context); } [DataTestMethod] [DataRow("SELECT * FROM P_|")] [DataRow("SELECT * FROM t JOIN P_|")] public void Analyze_AfterFromOrJoin_SuggestsTables(string marked) { var query = At(marked); Assert.IsNotNull(query); Assert.AreEqual(SqlCompletionContext.TableName, query.Context); Assert.AreEqual("P_", query.Prefix); } /// FROM 바로 뒤(아직 한 글자도 안 침)에서도 테이블을 제안해야 한다 [TestMethod] public void Analyze_ImmediatelyAfterFrom_SuggestsTablesWithEmptyPrefix() { var query = At("SELECT * FROM |"); Assert.IsNotNull(query); Assert.AreEqual(SqlCompletionContext.TableName, query.Context); Assert.AreEqual(string.Empty, query.Prefix); } [TestMethod] public void Analyze_AfterDot_SuggestsColumnsOfQualifier() { var query = At("SELECT p.Cht| FROM P_PatMst p"); Assert.IsNotNull(query); Assert.AreEqual(SqlCompletionContext.ColumnOf, query.Context); Assert.AreEqual("p", query.Qualifier); Assert.AreEqual("Cht", query.Prefix); } /// 아무것도 안 친 일반 위치에서는 목록을 띄우지 않는다 — 방해가 된다 [TestMethod] public void Analyze_EmptyPrefixInGeneralPosition_ReturnsNull() => Assert.IsNull(At("SELECT a, |")); [TestMethod] public void Analyze_TypingWord_SuggestsGeneral() { var query = At("SEL|"); Assert.IsNotNull(query); Assert.AreEqual(SqlCompletionContext.General, query.Context); Assert.AreEqual("SEL", query.Prefix); Assert.AreEqual(0, query.ReplaceStart); } /// 별칭을 실제 테이블로 풀어야 컬럼을 찾을 수 있다 [DataTestMethod] [DataRow("SELECT p.x FROM P_PatMst p", "p", "P_PatMst")] [DataRow("SELECT p.x FROM P_PatMst AS p", "p", "P_PatMst")] [DataRow("SELECT c.x FROM P_PatMst p JOIN P_ComInf c ON p.a=c.a", "c", "P_ComInf")] public void ResolveTable_FindsAliasTarget(string sql, string qualifier, string expected) => Assert.AreEqual(expected, SqlCompletion.ResolveTable(sql, qualifier)); /// 별칭이 없으면 한정자 자체가 테이블 이름이다 [TestMethod] public void ResolveTable_FallsBackToQualifier() => Assert.AreEqual("P_PatMst", SqlCompletion.ResolveTable("SELECT * FROM P_PatMst", "P_PatMst")); /// /// 별칭 자리에 키워드가 오면 별칭이 아니다 — FROM P_PatMst WHERE ... 에서 /// WHERE 를 별칭으로 잡으면 엉뚱한 테이블을 가리킨다. /// [TestMethod] public void ResolveTable_IgnoresKeywordAsAlias() => Assert.AreEqual("WHERE", SqlCompletion.ResolveTable("SELECT * FROM P_PatMst WHERE a=1", "WHERE")); [TestMethod] public void Filter_PutsPrefixMatchesFirst() { var items = new[] { new SqlCompletionItem("PrintBackColor", SqlCompletionKind.Column), new SqlCompletionItem("PrintOutPut", SqlCompletionKind.Column), new SqlCompletionItem("XPrint", SqlCompletionKind.Column), }; var filtered = SqlCompletion.Filter(items, "Print"); Assert.AreEqual(3, filtered.Count, "부분 일치도 포함해야 한다"); Assert.AreEqual("PrintOutPut", filtered[0].Text, "접두 일치 중 짧은 것이 먼저"); Assert.AreEqual("XPrint", filtered[^1].Text, "부분 일치는 뒤로"); } [TestMethod] public void BuiltIns_CoverKeywordsAndFunctions() { var items = SqlCompletion.BuiltIns(); Assert.IsTrue(items.Any(i => i.Text == "SELECT" && i.Kind == SqlCompletionKind.Keyword)); Assert.IsTrue(items.Any(i => i.Text == "NVL" && i.Kind == SqlCompletionKind.Function)); } }