using Strata.SqlTools.Breakdowns.Snowflake; namespace Strata.SqlTools.SqlBreakdown.Tests.Snowflake; [TestFixture] public class QueryBreakdownTests { #region Constructor Tests [Test] public void Constructor_Default_InitializesEmptyProperties() { // Arrange & Act var queryBreakdown = new QueryBreakdown(); Assert.Multiple(() => { // Assert Assert.That(queryBreakdown.SelectClause.Clause, Is.EqualTo(string.Empty)); Assert.That(queryBreakdown.FromClause.Clause, Is.EqualTo(string.Empty)); Assert.That(queryBreakdown.WhereClause.Clause, Is.EqualTo(string.Empty)); Assert.That(queryBreakdown.OrderByClause.Clause, Is.EqualTo(string.Empty)); Assert.That(queryBreakdown.GroupByClause.Clause, Is.EqualTo(string.Empty)); Assert.That(queryBreakdown.HavingClause.Clause, Is.EqualTo(string.Empty)); }); } [Test] public void Constructor_WithSelectAndFrom_SetsProperties() { // Arrange var selectClause = "ID, NAME"; var fromClause = "USERS"; // Act var queryBreakdown = new QueryBreakdown(selectClause, fromClause); Assert.Multiple(() => { // Assert Assert.That(queryBreakdown.SelectClause.Clause, Is.EqualTo(selectClause)); Assert.That(queryBreakdown.FromClause.Clause, Is.EqualTo(fromClause)); }); } [Test] public void Constructor_WithSelectFromAndWhere_SetsProperties() { // Arrange var selectClause = "ID, NAME"; var fromClause = "USERS"; var whereClause = "ACTIVE = 1"; // Act var queryBreakdown = new QueryBreakdown(selectClause, fromClause, whereClause); Assert.Multiple(() => { // Assert Assert.That(queryBreakdown.SelectClause.Clause, Is.EqualTo(selectClause)); Assert.That(queryBreakdown.FromClause.Clause, Is.EqualTo(fromClause)); Assert.That(queryBreakdown.WhereClause.Clause, Is.EqualTo(whereClause)); }); } #endregion #region GetSql Tests - Snowflake Formatting [Test] public void GetSql_SimpleSelect_UsesSnowflakeFormatting() { // Arrange var queryBreakdown = new QueryBreakdown("ID, NAME", "USERS"); // Act var sql = queryBreakdown.GetSql(); // Assert Assert.That(sql, Does.Contain("SELECT")); Assert.That(sql, Does.Contain("ID, NAME")); Assert.That(sql, Does.Contain("FROM")); Assert.That(sql, Does.Contain("USERS")); // Verify 4-space indentation (Snowflake style) Assert.That(sql, Does.Contain(" ")); } [Test] public void GetSql_WithAllClauses_GeneratesCorrectSnowflakeSql() { // Arrange var queryBreakdown = new QueryBreakdown("DEPARTMENT, COUNT(*) AS EMP_COUNT", "EMPLOYEES"); queryBreakdown.WhereClause.Clause = "ACTIVE = 1"; queryBreakdown.GroupByClause.Clause = "DEPARTMENT"; queryBreakdown.HavingClause.Clause = "COUNT(*) > 5"; queryBreakdown.OrderByClause.Clause = "DEPARTMENT ASC"; // Act var sql = queryBreakdown.GetSql(); // Assert Assert.That(sql, Does.Contain("SELECT")); Assert.That(sql, Does.Contain("FROM")); Assert.That(sql, Does.Contain("WHERE")); Assert.That(sql, Does.Contain("GROUP BY")); Assert.That(sql, Does.Contain("HAVING")); Assert.That(sql, Does.Contain("ORDER BY")); } private static readonly char[] separator = new[] { '\r', '\n' }; [Test] public void GetSql_WithSingleWithClause_UsesSnowflakeIndentation() { // Arrange var mainQueryBreakdown = new QueryBreakdown("P.*, PS.TOTAL_QTY", "PRODUCTS P INNER JOIN PRODUCT_SUMMARY PS ON P.PRODUCT_ID = PS.PRODUCT_ID"); var cte = new QueryBreakdown("PRODUCT_ID, SUM(QUANTITY) AS TOTAL_QTY", "ORDER_DETAILS"); cte.GroupByClause.Clause = "PRODUCT_ID"; mainQueryBreakdown.AddWithClause("PRODUCT_SUMMARY", cte); // Act var sql = mainQueryBreakdown.GetSql(); // Assert Assert.That(sql, Does.Contain("WITH")); Assert.That(sql, Does.Contain("PRODUCT_SUMMARY AS (")); // Verify 4-space Snowflake indentation var lines = sql.Split(separator, StringSplitOptions.RemoveEmptyEntries); var indentedLines = lines.Where(l => l.StartsWith(" ")).ToList(); Assert.That(indentedLines, Is.Not.Empty); } [Test] public void GetSql_WithMultipleWithClauses_GeneratesCorrectSnowflakeSql() { // Arrange var mainQueryBreakdown = new QueryBreakdown("*", "CTE3"); var cte1 = new QueryBreakdown("COL1", "TABLE1"); var cte2 = new QueryBreakdown("COL2", "TABLE2"); var cte3 = new QueryBreakdown("COL3", "TABLE3"); mainQueryBreakdown.AddWithClause("CTE1", cte1); mainQueryBreakdown.AddWithClause("CTE2", cte2); mainQueryBreakdown.AddWithClause("CTE3", cte3); // Act var sql = mainQueryBreakdown.GetSql(); // Assert Assert.That(sql, Does.Contain("WITH")); Assert.That(sql, Does.Contain("CTE1 AS (")); Assert.That(sql, Does.Contain("CTE2 AS (")); Assert.That(sql, Does.Contain("CTE3 AS (")); // Verify commas between CTEs by checking the pattern // CTE1 should be followed by ), then comma, then CTE2 var cte1Index = sql.IndexOf("CTE1 AS ("); var cte2Index = sql.IndexOf("CTE2 AS (", cte1Index); var betweenCtes = sql.Substring(cte1Index, cte2Index - cte1Index); Assert.That(betweenCtes, Does.Contain("),"), "CTE1 should be followed by a comma before CTE2"); // Verify comma between CTE2 and CTE3 var cte3Index = sql.IndexOf("CTE3 AS (", cte2Index); var betweenCte2And3 = sql.Substring(cte2Index, cte3Index - cte2Index); Assert.That(betweenCte2And3, Does.Contain("),"), "CTE2 should be followed by a comma before CTE3"); } #endregion #region AddWithClause Tests - Snowflake Specific [Test] public void AddWithClause_WithSnowflakeSql_DefaultsToSnowflakeParsing() { // Arrange var mainQueryBreakdown = new QueryBreakdown("*", "PRODUCTS P INNER JOIN PRODUCT_ORDERS PO ON P.PRODUCT_ID = PO.PRODUCT_ID"); var cteSql = "SELECT PRODUCT_ID, COUNT(*) AS ORDER_COUNT FROM ORDERS GROUP BY PRODUCT_ID"; // Act - should default to isMicrosoftSql = false mainQueryBreakdown.AddWithClause("PRODUCT_ORDERS", cteSql); // Assert Assert.That(mainQueryBreakdown.WithClauses, Has.Count.EqualTo(1)); Assert.That(mainQueryBreakdown.WithClauses[0].TableName, Is.EqualTo("PRODUCT_ORDERS")); } [Test] public void AddWithClause_WithMicrosoftSqlTrue_UsesTSqlParsing() { // Arrange var mainQueryBreakdown = new QueryBreakdown("*", "Products"); var cteSql = "SELECT ProductID, COUNT(*) AS OrderCount FROM Orders GROUP BY ProductID"; // Act mainQueryBreakdown.AddWithClause("ProductOrders", cteSql, isMicrosoftSql: true); // Assert Assert.That(mainQueryBreakdown.WithClauses, Has.Count.EqualTo(1)); Assert.That(mainQueryBreakdown.WithClauses[0].TableName, Is.EqualTo("ProductOrders")); } [Test] public void AddWithClause_WithQueryBreakdown_AddsToInheritedList() { // Arrange var mainQueryBreakdown = new QueryBreakdown("*", "PRODUCTS"); var cteQuery = new QueryBreakdown("PRODUCT_ID, COUNT(*) AS ORDER_COUNT", "ORDERS"); cteQuery.GroupByClause.Clause = "PRODUCT_ID"; // Act mainQueryBreakdown.AddWithClause("PRODUCT_ORDERS", cteQuery); // Assert Assert.That(mainQueryBreakdown.WithClauses, Has.Count.EqualTo(1)); Assert.That(mainQueryBreakdown.WithClauses[0].Query, Is.EqualTo(cteQuery)); } #endregion #region CTE Integration Tests - Snowflake Specific [Test] public void CTE_NestedCTEs_WithParameterFlow() { // Arrange: Create inner CTE with Snowflake parameters var innerCte = new QueryBreakdown("ID, NAME, CREATED_DATE", "USERS", "CREATED_DATE >= :start_date"); innerCte.AddParameter(":start_date", DateTime.Today.AddDays(-30)); // Arrange: Create middle CTE that references inner CTE var middleCte = new QueryBreakdown("ID, NAME", "RECENT_USERS", "ID > :min_user_id"); middleCte.AddParameter(":min_user_id", 100); // Arrange: Create main query that references middle CTE var mainQuery = new QueryBreakdown("COUNT(*) AS ACTIVE_USER_COUNT", "FILTERED_USERS"); mainQuery.AddWithClause("RECENT_USERS", innerCte); mainQuery.AddWithClause("FILTERED_USERS", middleCte); // Act var sql = mainQuery.GetSql(); var innerParams = innerCte.ParameterList.Count(); var middleParams = middleCte.ParameterList.Count(); Assert.Multiple(() => { // Assert - Each CTE maintains its own parameters Assert.That(innerParams, Is.EqualTo(1)); Assert.That(middleParams, Is.EqualTo(1)); // SQL should contain both WITH clauses Assert.That(sql, Does.Contain("WITH")); }); Assert.That(sql, Does.Contain("RECENT_USERS")); Assert.That(sql, Does.Contain("FILTERED_USERS")); } [Test] public void CTE_MultipleCTEs_WithComplexQueries() { // Arrange: Create CTE 1 - Active users (uppercase for Snowflake style) var activeCte = new QueryBreakdown("ID, EMAIL, STATUS", "USERS", "STATUS = 'active'"); // Arrange: Create CTE 2 - Recent orders with aggregation var ordersCte = new QueryBreakdown("USER_ID, COUNT(*) AS ORDER_COUNT, SUM(TOTAL) AS TOTAL_SPENT", "ORDERS"); ordersCte.GroupByClause.Clause = "USER_ID"; ordersCte.HavingClause.Clause = "COUNT(*) > 0"; // Arrange: Create main query combining both CTEs var mainQuery = new QueryBreakdown("A.ID, A.EMAIL, O.ORDER_COUNT, O.TOTAL_SPENT", "ACTIVE_USERS A LEFT JOIN USER_ORDERS O ON A.ID = O.USER_ID"); mainQuery.OrderByClause.Clause = "O.TOTAL_SPENT DESC"; mainQuery.AddWithClause("ACTIVE_USERS", activeCte); mainQuery.AddWithClause("USER_ORDERS", ordersCte); // Act var sql = mainQuery.GetSql(); // Assert Assert.That(sql, Does.Contain("WITH")); Assert.That(sql, Does.Contain("ACTIVE_USERS")); Assert.That(sql, Does.Contain("USER_ORDERS")); Assert.That(sql, Does.Contain("GROUP BY")); Assert.That(sql, Does.Contain("HAVING")); Assert.That(sql, Does.Contain("LEFT JOIN")); Assert.That(sql, Does.Contain("ORDER BY")); } [Test] public void CTE_GetSql_GeneratesProperCTESyntax() { // Arrange var cteQuery = new QueryBreakdown("PRODUCT_ID, SUM(QUANTITY) AS TOTAL_QUANTITY", "ORDER_DETAILS"); cteQuery.GroupByClause.Clause = "PRODUCT_ID"; var mainQuery = new QueryBreakdown("P.PRODUCT_NAME, PT.TOTAL_QUANTITY", "PRODUCTS P INNER JOIN PRODUCT_TOTALS PT ON P.PRODUCT_ID = PT.PRODUCT_ID"); mainQuery.AddWithClause("PRODUCT_TOTALS", cteQuery); // Act var sql = mainQuery.GetSql(); // Assert - Verify WITH clause appears before main SELECT var withIndex = sql.IndexOf("WITH"); var mainSelectIndex = sql.LastIndexOf("SELECT"); Assert.Multiple(() => { Assert.That(withIndex, Is.GreaterThan(-1)); Assert.That(mainSelectIndex, Is.GreaterThan(-1)); }); Assert.Multiple(() => { Assert.That(withIndex, Is.LessThan(mainSelectIndex)); // Assert - Verify CTE definition Assert.That(sql, Does.Contain("PRODUCT_TOTALS")); }); Assert.That(sql, Does.Contain("AS")); Assert.That(sql, Does.Contain("PRODUCT_ID")); Assert.That(sql, Does.Contain("TOTAL_QUANTITY")); } [Test] public void CTE_WithClauseProperties_RoundTrip() { // Arrange: Create original query var cteQuery = new QueryBreakdown("ID, NAME", "EMPLOYEES", "DEPARTMENT = 'Sales'"); var mainQuery = new QueryBreakdown("*", "SALES_TEAM"); mainQuery.AddWithClause("SALES_TEAM", cteQuery); // Act: Get SQL and verify CTE is properly included var sql = mainQuery.GetSql(); var withClause = mainQuery.WithClauses[0]; Assert.Multiple(() => { // Assert: Verify WithClause properties Assert.That(withClause.TableName, Is.EqualTo("SALES_TEAM")); Assert.That(withClause.Query, Is.Not.Null); Assert.That(withClause.Sql, Is.Not.Null); }); Assert.Multiple(() => { Assert.That(withClause.Sql?.SelectClause?.Clause, Contains.Substring("ID, NAME")); Assert.That(withClause.Sql?.FromClause?.Clause, Contains.Substring("EMPLOYEES")); Assert.That(withClause.Sql?.WhereClause?.Clause, Contains.Substring("DEPARTMENT = 'Sales'")); }); } [Test] public void CTE_IsUsingWithClause_ReturnsCorrectStatus() { // Arrange var query = new QueryBreakdown("*", "DATA"); // Act & Assert - No WITH clauses yet Assert.That(query.IsUsingWithClause, Is.False); // Act - Add a CTE var cte = new QueryBreakdown("ID, NAME", "SOURCE_TABLE"); query.AddWithClause("SOURCE_CTE", cte); Assert.Multiple(() => { // Assert - Now using WITH clause Assert.That(query.IsUsingWithClause, Is.True); Assert.That(query.WithClauses, Has.Count.EqualTo(1)); }); } #endregion #region CTE Parameter Inheritance Tests - Snowflake [Test] public void GetMergedParameters_WithNoCTEs_ReturnsMainQueryParameters() { // Arrange var query = new QueryBreakdown("*", "USERS"); query.AddParameter("UserId", 123); query.AddParameter("ActiveStatus", true); // Act var merged = query.GetMergedParameters(); // Assert // Snowflake stores parameters in both @ and : formats, so count is doubled Assert.That(merged, Has.Count.GreaterThanOrEqualTo(2)); Assert.Multiple(() => { Assert.That(merged.ContainsKey("@UserId") || merged.ContainsKey(":UserId"), Is.True); Assert.That(merged.ContainsKey("@ActiveStatus") || merged.ContainsKey(":ActiveStatus"), Is.True); }); } [Test] public void GetMergedParameters_WithSingleCTE_MergesParameters() { // Arrange var cte = new QueryBreakdown("ID, NAME", "BASE_USERS", "ACTIVE = 1"); cte.AddParameter("MinDate", DateTime.Today.AddDays(-30)); var mainQuery = new QueryBreakdown("*", "ACTIVE_USERS"); mainQuery.AddParameter("ResultLimit", 100); mainQuery.AddWithClause("ACTIVE_USERS", cte); // Act var merged = mainQuery.GetMergedParameters(); // Assert // Should have parameters from both CTE and main query var keys = merged.Keys.ToList(); Assert.Multiple(() => { Assert.That(keys.Any(k => k.Contains("MinDate"))); Assert.That(keys.Any(k => k.Contains("ResultLimit"))); }); } [Test] public void GetMergedParameters_WithMultipleCTEs_MergesAllParameters() { // Arrange var cte1 = new QueryBreakdown("ID, STATUS", "USERS", "STATUS = 'active'"); cte1.AddParameter("Status1", "active"); var cte2 = new QueryBreakdown("USER_ID, CREATED", "ORDERS", "CREATED >= :DATE"); cte2.AddParameter("MinDate", DateTime.Today.AddDays(-30)); var mainQuery = new QueryBreakdown("*", "RESULTS"); mainQuery.AddParameter("ResultLimit", 50); mainQuery.AddWithClause("ACTIVE_USERS", cte1); mainQuery.AddWithClause("RECENT_ORDERS", cte2); // Act var merged = mainQuery.GetMergedParameters(); // Assert var keys = merged.Keys.ToList(); Assert.Multiple(() => { Assert.That(keys.Any(k => k.Contains("Status1"))); Assert.That(keys.Any(k => k.Contains("MinDate"))); Assert.That(keys.Any(k => k.Contains("ResultLimit"))); }); } [Test] public void GetMergedParameters_WithParameterConflict_MainQueryTakesPrecedence() { // Arrange var cte = new QueryBreakdown("ID", "USERS"); cte.AddParameter("QueryLimit", 10); // CTE sets to 10 var mainQuery = new QueryBreakdown("*", "FINAL_RESULT"); mainQuery.AddParameter("QueryLimit", 100); // Main query sets to 100 mainQuery.AddWithClause("BASE_DATA", cte); // Act var merged = mainQuery.GetMergedParameters(); // Assert // Main query value should win var keys = merged.Keys.Where(k => k.Contains("QueryLimit")).ToList(); Assert.That(keys, Is.Not.Empty); // All QueryLimit entries should be 100 (main query's value) foreach (var key in keys) { Assert.That(merged[key], Is.EqualTo(100)); } } [Test] public void GetMergedParameters_WithNestedCTEs_CollectsAllParameters() { // Arrange: Create deeply nested CTEs var innerCte = new QueryBreakdown("ID", "BASE_TABLE"); innerCte.AddParameter("InnerParam", "inner_value"); var middleCte = new QueryBreakdown("ID, NAME", "LAYER1"); middleCte.AddParameter("MiddleParam", "middle_value"); middleCte.AddWithClause("LAYER1_SOURCE", innerCte); var mainQuery = new QueryBreakdown("ID, NAME, STATUS", "LAYER2"); mainQuery.AddParameter("MainParam", "main_value"); mainQuery.AddWithClause("LAYER2_SOURCE", middleCte); // Act var merged = mainQuery.GetMergedParameters(); // Assert - All parameters from all levels should be present var keys = merged.Keys.ToList(); Assert.Multiple(() => { Assert.That(keys.Any(k => k.Contains("InnerParam"))); Assert.That(keys.Any(k => k.Contains("MiddleParam"))); Assert.That(keys.Any(k => k.Contains("MainParam"))); }); } [Test] public void GetMergedParameters_PreservesParameterTypes() { // Arrange var cte = new QueryBreakdown("ID", "USERS"); cte.AddParameter("IntValue", 123); cte.AddParameter("StringValue", "test"); cte.AddParameter("DateValue", DateTime.Today); cte.AddParameter("DecimalValue", 99.99m); var mainQuery = new QueryBreakdown("*", "RESULTS"); mainQuery.AddWithClause("SOURCE", cte); // Act var merged = mainQuery.GetMergedParameters(); // Assert // Verify all values are present and types are preserved var intValues = merged.Values.OfType().Count(); var stringValues = merged.Values.OfType().Count(); var dateValues = merged.Values.OfType().Count(); var decimalValues = merged.Values.OfType().Count(); Assert.Multiple(() => { Assert.That(intValues, Is.GreaterThan(0), "Should have at least one int value"); Assert.That(stringValues, Is.GreaterThan(0), "Should have at least one string value"); Assert.That(dateValues, Is.GreaterThan(0), "Should have at least one DateTime value"); Assert.That(decimalValues, Is.GreaterThan(0), "Should have at least one decimal value"); }); } #endregion #region Parse Tests - Snowflake Specific [Test] public void Parse_SimpleSnowflakeSelect_ParsesCorrectly() { // Arrange var sql = "SELECT ID, NAME FROM USERS"; // Act var queryBreakdown = QueryBreakdown.Parse(sql, false); Assert.Multiple(() => { // Assert Assert.That(queryBreakdown.SelectClause.Clause?.Trim(), Does.Contain("ID, NAME")); Assert.That(queryBreakdown.FromClause.Clause?.Trim(), Does.Contain("USERS")); }); } [Test] public void Parse_WithLimitClause_ParsesCorrectly() { // Arrange var sql = "SELECT ID, NAME FROM USERS ORDER BY NAME LIMIT 10"; // Act var queryBreakdown = QueryBreakdown.Parse(sql, false); // Assert Assert.That(queryBreakdown.OrderByClause.Clause?.Trim(), Does.Contain("NAME")); Assert.That(queryBreakdown.OrderByClause.Clause?.Trim(), Does.Contain("LIMIT 10")); } [Test] public void Parse_WithQualifyClause_ParsesCorrectly() { // Arrange var sql = @"SELECT ID, NAME, ROW_NUMBER() OVER (PARTITION BY DEPT ORDER BY SALARY DESC) AS RN FROM EMPLOYEES QUALIFY RN = 1"; // Act var queryBreakdown = QueryBreakdown.Parse(sql, false); // Assert Assert.That(queryBreakdown.SelectClause.Clause?.Trim(), Does.Contain("ROW_NUMBER()")); // QUALIFY is typically parsed as part of WHERE or as a separate extension in Snowflake } [Test] public void Parse_WithColonParameters_ExtractsSnowflakeParameters() { // Arrange var sql = "SELECT ID, NAME FROM USERS WHERE USER_ID = :userId AND STATUS = :status"; // Act var queryBreakdown = QueryBreakdown.Parse(sql, false); // Assert Assert.That(queryBreakdown.Parameters, Does.ContainKey(":userId")); Assert.That(queryBreakdown.Parameters, Does.ContainKey(":status")); } [Test] public void Parse_WithAtParameters_ExtractsAndNormalizesParameters() { // Arrange var sql = "SELECT ID, NAME FROM USERS WHERE USER_ID = :userId AND STATUS = @status"; // Act var queryBreakdown = QueryBreakdown.Parse(sql, false); // Assert Assert.That(queryBreakdown.Parameters, Does.ContainKey(":userId")); Assert.That(queryBreakdown.Parameters, Does.Not.ContainKey("@status")); } [Test] public void Parse_WithMixedParameters_ExtractsBothFormats() { // Arrange var sql = "SELECT ID, NAME FROM USERS WHERE USER_ID = :userId AND STATUS = @status"; // Act var queryBreakdown = QueryBreakdown.Parse(sql, false); // Assert Assert.That(queryBreakdown.Parameters, Is.Not.Empty); } [Test] public void Parse_WithMicrosoftSqlTrue_DelegatesToBaseClass() { // Arrange var sql = "SELECT ID, Name FROM Users WHERE UserID = @UserId"; // Act var queryBreakdown = QueryBreakdown.Parse(sql, true); // Assert Assert.That(queryBreakdown, Is.Not.Null); Assert.That(queryBreakdown, Is.InstanceOf()); Assert.That(queryBreakdown.Parameters, Does.ContainKey("@UserId")); } [Test] public void Parse_WithSnowflakeWithClause_ParsesCorrectly() { // Arrange var sql = @"WITH PRODUCT_SUMMARY AS ( SELECT PRODUCT_ID, SUM(QUANTITY) AS TOTAL_QTY FROM ORDER_DETAILS GROUP BY PRODUCT_ID ) SELECT P.*, PS.TOTAL_QTY FROM PRODUCTS P INNER JOIN PRODUCT_SUMMARY PS ON P.PRODUCT_ID = PS.PRODUCT_ID"; // Act var queryBreakdown = QueryBreakdown.Parse(sql, false); Assert.Multiple(() => { // Assert Assert.That(queryBreakdown.IsUsingWithClause, Is.True); Assert.That(queryBreakdown.SelectClause.Clause?.Trim(), Does.Contain("P.*, PS.TOTAL_QTY")); }); } [Test] public void Parse_WithNullSql_ThrowsArgumentNullException() { // Act & Assert Assert.Throws(() => QueryBreakdown.Parse(null!)); } [Test] public void Parse_WithInvalidSql_ThrowsFormatException() { // Arrange var sql = "INVALID SNOWFLAKE SQL"; // Act & Assert Assert.Throws(() => QueryBreakdown.Parse(sql, false)); } [Test] public void TryParse_WithValidSnowflakeSql_ReturnsTrue() { // Arrange var sql = "SELECT ID FROM USERS"; // Act var success = QueryBreakdown.TryParse(sql, out var queryBreakdown, false); Assert.Multiple(() => { // Assert Assert.That(success, Is.True); Assert.That(queryBreakdown, Is.Not.Null); }); Assert.That(queryBreakdown, Is.InstanceOf()); } [Test] public void TryParse_WithInvalidSql_ReturnsFalse() { // Arrange var sql = "INVALID SQL"; // Act var success = QueryBreakdown.TryParse(sql, out var queryBreakdown, out var error, false); Assert.Multiple(() => { // Assert Assert.That(success, Is.False); Assert.That(queryBreakdown, Is.Null); Assert.That(error, Is.Not.Null); }); } #endregion #region Inheritance Tests [Test] public void InheritsFromQueryBreakdown_CanUseBaseClassMethods() { // Arrange var queryBreakdown = new QueryBreakdown("*", "USERS"); // Act queryBreakdown.AddParameter("UserId", 123); queryBreakdown.AddWhereClause("ACTIVE = 1"); Assert.Multiple(() => { // Assert Assert.That(queryBreakdown.Parameters, Does.ContainKey("@UserId")); Assert.That(queryBreakdown.WhereClause.Clause, Does.Contain("ACTIVE = 1")); }); } [Test] public void InheritsWithClauses_SharesSameStorage() { // Arrange var queryBreakdown = new QueryBreakdown("*", "PRODUCTS"); var cte = new QueryBreakdown("*", "ORDERS"); // Act queryBreakdown.AddWithClause("ORDER_CTE", cte); Assert.Multiple(() => { // Assert Assert.That(queryBreakdown.WithClauses, Has.Count.EqualTo(1)); Assert.That(queryBreakdown.IsUsingWithClause, Is.True); }); } [Test] public void Clone_CreatesSnowflakeInstance() { // Arrange var original = new QueryBreakdown("*", "USERS"); original.WhereClause.Clause = "ACTIVE = 1"; // Act var clone = (QueryBreakdown)original.Clone(); // Assert Assert.That(clone, Is.InstanceOf()); Assert.That(clone.SelectClause.Clause, Is.EqualTo(original.SelectClause.Clause)); } #endregion #region Integration Tests [Test] public void ComplexQuery_WithMultipleCTEsAndAllClauses_WorksCorrectly() { // Arrange var mainQueryBreakdown = new QueryBreakdown("*", "FINAL_DATA FD"); mainQueryBreakdown.WhereClause.Clause = "FD.TOTAL_SALES > :minSales"; mainQueryBreakdown.OrderByClause.Clause = "FD.TOTAL_SALES DESC LIMIT 100"; var salesCte = new QueryBreakdown( "REGION_ID, SUM(AMOUNT) AS TOTAL_SALES", "SALES"); salesCte.WhereClause.Clause = "SALE_DATE >= :startDate"; salesCte.GroupByClause.Clause = "REGION_ID"; var productCte = new QueryBreakdown("*", "PRODUCTS"); productCte.WhereClause.Clause = "ACTIVE = 1"; // Act mainQueryBreakdown.AddWithClause("SALES_SUMMARY", salesCte); mainQueryBreakdown.AddWithClause("ACTIVE_PRODUCTS", productCte); mainQueryBreakdown.AddParameter("minSales", 10000); mainQueryBreakdown.AddParameter("startDate", new DateTime(2025, 1, 1, 0, 0, 0, DateTimeKind.Utc)); var sql = mainQueryBreakdown.GetSql(); // Assert Assert.That(sql, Does.Contain("WITH")); Assert.That(sql, Does.Contain("SALES_SUMMARY AS (")); Assert.That(sql, Does.Contain("ACTIVE_PRODUCTS AS (")); Assert.Multiple(() => { Assert.That(sql, Does.Contain("LIMIT 100")); Assert.That(mainQueryBreakdown.Parameters, Does.ContainKey(":minSales")); }); Assert.That(mainQueryBreakdown.Parameters, Does.ContainKey(":startDate")); } [Test] public void NestedCTEs_ParseAndGenerate_WorksCorrectly() { // Arrange - Create nested WITH clause structure var innerCte = new QueryBreakdown("ORDER_ID, CUSTOMER_ID", "ORDERS"); var middleCte = new QueryBreakdown("CUSTOMER_ID, COUNT(*) AS ORDER_COUNT", "INNER_CTE"); middleCte.GroupByClause.Clause = "CUSTOMER_ID"; middleCte.AddWithClause("INNER_CTE", innerCte); var mainQueryBreakdown = new QueryBreakdown("*", "MIDDLE_CTE"); mainQueryBreakdown.AddWithClause("MIDDLE_CTE", middleCte); // Act var sql = mainQueryBreakdown.GetSql(); // Assert Assert.That(sql, Does.Contain("WITH")); Assert.Multiple(() => { Assert.That(sql, Does.Contain("MIDDLE_CTE AS (")); Assert.That(mainQueryBreakdown.WithClauses, Has.Count.EqualTo(1)); }); } [Test] public void RoundTrip_ParseAndGetSql_ProducesSimilarSql() { // Arrange var originalSql = @"SELECT ID, NAME, EMAIL FROM USERS WHERE ACTIVE = 1 ORDER BY NAME ASC"; // Act var parsed = QueryBreakdown.Parse(originalSql, false); var generatedSql = parsed.GetSql(); // Assert Assert.That(generatedSql, Does.Contain("SELECT")); Assert.That(generatedSql, Does.Contain("ID, NAME, EMAIL")); Assert.That(generatedSql, Does.Contain("FROM")); Assert.That(generatedSql, Does.Contain("USERS")); Assert.That(generatedSql, Does.Contain("WHERE")); Assert.That(generatedSql, Does.Contain("ACTIVE = 1")); Assert.That(generatedSql, Does.Contain("ORDER BY")); Assert.That(generatedSql, Does.Contain("NAME ASC")); } #endregion #region Edge Cases [Test] public void Parse_WithComplexSubqueries_HandlesCorrectly() { // Arrange var sql = @"SELECT U.ID, U.NAME, (SELECT COUNT(*) FROM ORDERS O WHERE O.USER_ID = U.ID) AS ORDER_COUNT FROM USERS U WHERE U.ID IN (SELECT DISTINCT USER_ID FROM ORDERS WHERE AMOUNT > 100)"; // Act var queryBreakdown = QueryBreakdown.Parse(sql, false); // Assert Assert.That(queryBreakdown, Is.Not.Null); Assert.That(queryBreakdown.SelectClause.Clause, Does.Contain("(SELECT COUNT(*)")); } [Test] public void GetSql_EmptyQuery_GeneratesMinimalSql() { // Arrange var queryBreakdown = new QueryBreakdown(); // Act var sql = queryBreakdown.GetSql(); // Assert Assert.That(sql, Does.Contain("SELECT")); // Should have minimal structure even when empty } [Test] public void Parse_WithCommentsInSql_HandlesGracefully() { // Arrange var sql = @"-- This is a comment SELECT ID, NAME -- inline comment FROM USERS /* Multi-line comment */ WHERE ACTIVE = 1"; // Act & Assert // May throw or handle comments - test should verify behavior Assert.DoesNotThrow(() => { _ = QueryBreakdown.Parse(sql, false); }); } #endregion #region Outlier Conditions - Snowflake-Specific Edge Cases [Test] public void Parse_WithDoubleColonCast_ParsesCorrectly() { // Arrange - Snowflake uses :: for casting var sql = "SELECT ID::VARCHAR, AMOUNT::DECIMAL(10,2) FROM ORDERS"; // Act var queryBreakdown = QueryBreakdown.Parse(sql, false); // Assert Assert.That(queryBreakdown.SelectClause.Clause, Does.Contain("::")); } [Test] public void Parse_WithVariantDataType_ParsesCorrectly() { // Arrange var sql = "SELECT JSON_DATA:name::STRING, JSON_DATA:age::NUMBER FROM JSON_TABLE"; // Act var queryBreakdown = QueryBreakdown.Parse(sql, false); // Assert Assert.That(queryBreakdown.SelectClause.Clause, Does.Contain("JSON_DATA:name")); } [Test] public void Parse_WithFlattenFunction_ParsesCorrectly() { // Arrange var sql = @"SELECT F.VALUE:id::NUMBER AS ID, F.VALUE:name::STRING AS NAME FROM JSON_TABLE, LATERAL FLATTEN(input => JSON_DATA:items) F"; // Act var queryBreakdown = QueryBreakdown.Parse(sql, false); Assert.Multiple(() => { // Assert Assert.That(queryBreakdown.SelectClause.Clause, Does.Contain("F.VALUE")); Assert.That(queryBreakdown.FromClause.Clause, Does.Contain("FLATTEN")); }); } [Test] public void Parse_WithSampleClause_ParsesCorrectly() { // Arrange var sql = "SELECT * FROM LARGE_TABLE SAMPLE (10 ROWS)"; // Act var queryBreakdown = QueryBreakdown.Parse(sql, false); // Assert Assert.That(queryBreakdown.FromClause.Clause, Does.Contain("SAMPLE")); } [Test] public void Parse_WithAtTimestamp_ParsesCorrectly() { // Arrange - Snowflake Time Travel var sql = "SELECT * FROM USERS AT(TIMESTAMP => '2024-01-01 00:00:00'::TIMESTAMP)"; // Act var queryBreakdown = QueryBreakdown.Parse(sql, false); // Assert Assert.That(queryBreakdown.FromClause.Clause, Does.Contain("AT(TIMESTAMP")); } [Test] public void Parse_WithMatchRecognize_HandlesGracefully() { // Arrange - Complex Snowflake pattern matching var sql = @"SELECT * FROM EVENTS MATCH_RECOGNIZE( PARTITION BY USER_ID ORDER BY EVENT_TIME MEASURES A.EVENT_TIME AS START_TIME PATTERN (A B+ C) DEFINE B AS B.EVENT_TYPE = 'CLICK' )"; // Act & Assert Assert.DoesNotThrow(() => QueryBreakdown.Parse(sql, false)); } [Test] public void Parse_WithWindowFunctionAndQualify_ParsesCorrectly() { // Arrange var sql = @"SELECT ID, NAME, ROW_NUMBER() OVER (PARTITION BY DEPARTMENT ORDER BY SALARY DESC) AS RN FROM EMPLOYEES QUALIFY ROW_NUMBER() OVER (PARTITION BY DEPARTMENT ORDER BY SALARY DESC) = 1"; // Act var queryBreakdown = QueryBreakdown.Parse(sql, false); // Assert Assert.That(queryBreakdown.SelectClause.Clause, Does.Contain("ROW_NUMBER()")); } #endregion #region Outlier Conditions - Parameter Formats [Test] public void Parse_WithManyColonParameters_ExtractsAll() { // Arrange var sql = @"SELECT * FROM ORDERS WHERE USER_ID = :userId AND ORDER_DATE >= :startDate AND ORDER_DATE <= :endDate AND STATUS = :status AND AMOUNT > :minAmount"; // Act var queryBreakdown = QueryBreakdown.Parse(sql, false); // Assert Assert.That(queryBreakdown.Parameters, Has.Count.GreaterThanOrEqualTo(5)); Assert.That(queryBreakdown.Parameters, Does.ContainKey(":userId")); Assert.That(queryBreakdown.Parameters, Does.ContainKey(":startDate")); Assert.That(queryBreakdown.Parameters, Does.ContainKey(":endDate")); Assert.That(queryBreakdown.Parameters, Does.ContainKey(":status")); Assert.That(queryBreakdown.Parameters, Does.ContainKey(":minAmount")); } [Test] public void Parse_WithParameterInJsonPath_DoesNotExtractAsParameter() { // Arrange - Colon in JSON path should not be treated as parameter var sql = "SELECT JSON_DATA:user:name FROM JSON_TABLE"; // Act var queryBreakdown = QueryBreakdown.Parse(sql, false); // Assert - Should not extract 'user' or 'name' as parameters Assert.That(queryBreakdown.SelectClause.Clause, Does.Contain("JSON_DATA:user:name")); } [Test] public void AddParameter_WithColonPrefix_StoresWithColon() { // Arrange var queryBreakdown = new QueryBreakdown(); // Act queryBreakdown.AddParameter(":snowflakeParam", 123); // Assert // Base class normalizes to @, but Snowflake might handle differently Assert.That(queryBreakdown.Parameters, Is.Not.Empty); } #endregion #region Outlier Conditions - Snowflake String Handling [Test] public void Parse_WithDollarQuotedString_HandlesGracefully() { // Arrange - Snowflake supports $$ for string literals var sql = "SELECT $$This is a 'quoted' string$$ AS TEXT FROM DUAL"; // Act & Assert Assert.DoesNotThrow(() => QueryBreakdown.Parse(sql, false)); } [Test] public void Parse_WithEscapedSingleQuotes_ParsesCorrectly() { // Arrange var sql = "SELECT NAME FROM USERS WHERE NAME = 'O''Brien'"; // Act var queryBreakdown = QueryBreakdown.Parse(sql, false); // Assert Assert.That(queryBreakdown.WhereClause.Clause, Does.Contain("O''Brien")); } #endregion #region Outlier Conditions - Complex Snowflake Queries [Test] public void Parse_WithMultipleSetOperators_HandlesGracefully() { // Arrange var sql = @"SELECT ID, NAME FROM USERS_A UNION ALL SELECT ID, NAME FROM USERS_B EXCEPT SELECT ID, NAME FROM USERS_C INTERSECT SELECT ID, NAME FROM USERS_D"; // Act & Assert Assert.DoesNotThrow(() => QueryBreakdown.Parse(sql, false)); } [Test] public void Parse_WithPivotClause_HandlesGracefully() { // Arrange var sql = @"SELECT * FROM SALES PIVOT(SUM(AMOUNT) FOR MONTH IN ('JAN', 'FEB', 'MAR'))"; // Act & Assert Assert.DoesNotThrow(() => QueryBreakdown.Parse(sql, false)); } [Test] public void GetSql_WithVeryLongSnowflakeQuery_GeneratesCorrectly() { // Arrange - Build a complex query with many CTEs var mainQueryBreakdown = new QueryBreakdown("*", "FINAL_RESULT"); for (int i = 0; i < 15; i++) { var cte = new QueryBreakdown( $"COL{i}, COUNT(*) AS CNT{i}", $"TABLE_{i}"); cte.WhereClause.Clause = $"ACTIVE = 1 AND TYPE = {i}"; cte.GroupByClause.Clause = $"COL{i}"; mainQueryBreakdown.AddWithClause($"CTE_{i}", cte); } // Act var sql = mainQueryBreakdown.GetSql(); // Assert Assert.That(sql, Does.Contain("CTE_0 AS (")); Assert.That(sql, Does.Contain("CTE_14 AS (")); Assert.That(sql, Has.Length.GreaterThan(1000)); } #endregion #region Outlier Conditions - Whitespace and Formatting [Test] public void Parse_WithExcessiveWhitespace_NormalizesCorrectly() { // Arrange var sql = "SELECT ID, NAME FROM USERS WHERE ACTIVE=1"; // Act var queryBreakdown = QueryBreakdown.Parse(sql, false); // Assert Assert.That(queryBreakdown, Is.Not.Null); Assert.That(queryBreakdown.SelectClause.Clause, Does.Contain("ID")); } [Test] public void GetSql_ComparesIndentationToBaseClass_UsesFourSpaces() { // Arrange var QueryBreakdown = new QueryBreakdown("ID", "USERS"); var baseQueryBreakdown = new QueryBreakdown("ID", "USERS"); // Act var snowflakeSql = QueryBreakdown.GetSql(); _ = baseQueryBreakdown.GetSql(); // Assert - Snowflake should use 4-space indent, base uses 5-space var snowflakeLines = snowflakeSql.Split(separator, StringSplitOptions.RemoveEmptyEntries); var snowflakeIndentedLines = snowflakeLines.Where(l => l.StartsWith(" ") && !l.StartsWith(" ")).ToList(); Assert.That(snowflakeIndentedLines, Is.Not.Empty, "Snowflake should use 4-space indentation"); } #endregion #region Outlier Conditions - Boundary and Error Cases [Test] public void Parse_WithOnlySelectNoFrom_HandlesGracefully() { // Arrange - Snowflake allows SELECT without FROM using DUAL or for constants var sql = "SELECT 1 AS ONE, CURRENT_TIMESTAMP() AS NOW"; // Act & Assert Assert.DoesNotThrow(() => QueryBreakdown.Parse(sql, false)); } [Test] public void TryParse_WithMalformedSnowflakeSQL_ReturnsFalseWithError() { // Arrange var sql = "SELET * FORM USERS WERE ID = 1"; // Intentional typos // Act var success = QueryBreakdown.TryParse(sql, out _, out var error, false); Assert.Multiple(() => { // Assert Assert.That(success, Is.False); Assert.That(error, Is.Not.Null.And.Not.Empty); }); } [Test] public void Parse_WithVeryLongTableName_ParsesCorrectly() { // Arrange var longTableName = "VERY_LONG_TABLE_NAME_" + new string('X', 200); var sql = $"SELECT ID FROM {longTableName}"; // Act var queryBreakdown = QueryBreakdown.Parse(sql, false); // Assert Assert.That(queryBreakdown.FromClause.Clause, Does.Contain(longTableName)); } [Test] public void MergeWith_SnowflakeQueryWithBaseQuery_WorksCorrectly() { // Arrange var QueryBreakdown = new QueryBreakdown("*", "USERS U"); var baseQuery = new QueryBreakdown(); baseQuery.FromClause.Clause = "INNER JOIN ORDERS O ON U.ID = O.USER_ID"; // Act QueryBreakdown.MergeWith(baseQuery); // Assert Assert.That(QueryBreakdown.FromClause.Clause, Does.Contain("USERS U")); Assert.That(QueryBreakdown.FromClause.Clause, Does.Contain("INNER JOIN ORDERS")); } #endregion #region Outlier Conditions - Real-World Snowflake Scenarios [Test] public void Parse_WithSnowflakeStageReference_ParsesCorrectly() { // Arrange var sql = "SELECT $1, $2, $3 FROM @MY_STAGE/data/file.csv"; // Act var queryBreakdown = QueryBreakdown.Parse(sql, false); Assert.Multiple(() => { // Assert Assert.That(queryBreakdown.SelectClause.Clause, Does.Contain("$1")); Assert.That(queryBreakdown.FromClause.Clause, Does.Contain("@MY_STAGE")); }); } [Test] public void Parse_WithSequenceNextval_ParsesCorrectly() { // Arrange var sql = "SELECT MY_SEQUENCE.NEXTVAL, NAME FROM USERS"; // Act var queryBreakdown = QueryBreakdown.Parse(sql, false); // Assert Assert.That(queryBreakdown.SelectClause.Clause, Does.Contain("NEXTVAL")); } [Test] public void ComplexRealWorldQuery_WithAllSnowflakeFeatures_ParsesAndGenerates() { // Arrange - Realistic complex Snowflake query var salesCte = new QueryBreakdown( "REGION_ID, PRODUCT_ID, SUM(AMOUNT::DECIMAL(18,2)) AS TOTAL_SALES", "SALES_DATA"); salesCte.WhereClause.Clause = "SALE_DATE >= :startDate AND SALE_DATE < :endDate"; salesCte.GroupByClause.Clause = "REGION_ID, PRODUCT_ID"; var topProductsCte = new QueryBreakdown( "REGION_ID, PRODUCT_ID, TOTAL_SALES, ROW_NUMBER() OVER (PARTITION BY REGION_ID ORDER BY TOTAL_SALES DESC) AS RN", "SALES_SUMMARY"); var mainQueryBreakdown = new QueryBreakdown("R.REGION_NAME, T.PRODUCT_ID, T.TOTAL_SALES", "REGIONS R INNER JOIN TOP_PRODUCTS T ON R.REGION_ID = T.REGION_ID"); mainQueryBreakdown.WhereClause.Clause = "T.RN <= 10"; mainQueryBreakdown.OrderByClause.Clause = "R.REGION_NAME, T.TOTAL_SALES DESC LIMIT 100"; mainQueryBreakdown.AddWithClause("SALES_SUMMARY", salesCte); mainQueryBreakdown.AddWithClause("TOP_PRODUCTS", topProductsCte); mainQueryBreakdown.AddParameter("startDate", "2024-01-01"); mainQueryBreakdown.AddParameter("endDate", "2024-12-31"); // Act var sql = mainQueryBreakdown.GetSql(); // Assert Assert.That(sql, Does.Contain("WITH")); Assert.That(sql, Does.Contain("SALES_SUMMARY AS (")); Assert.That(sql, Does.Contain("TOP_PRODUCTS AS (")); Assert.That(sql, Does.Contain("ROW_NUMBER()")); Assert.Multiple(() => { Assert.That(sql, Does.Contain("LIMIT 100")); Assert.That(mainQueryBreakdown.Parameters, Has.Count.GreaterThanOrEqualTo(2)); }); } #endregion #region Microsoft SQL Server Compatibility Tests [Test] public void Parse_MicrosoftSqlQuery_ReturnsQueryBreakdown() { // Arrange var tsqlQuery = "SELECT Id, Name FROM Users WHERE Active = 1"; // Act var result = QueryBreakdown.Parse(tsqlQuery, isMicrosoftSql: true); // Assert Assert.That(result, Is.Not.Null); Assert.That(result, Is.InstanceOf(), "Should return Snowflake QueryBreakdown instance"); Assert.Multiple(() => { Assert.That(result.SelectClause.Clause?.Trim(), Does.Contain("Id, Name")); Assert.That(result.FromClause.Clause?.Trim(), Does.Contain("Users")); Assert.That(result.WhereClause.Clause?.Trim(), Does.Contain("Active = 1")); }); } [Test] public void Parse_MicrosoftSqlWithParameters_ExtractsParameters() { // Arrange var tsqlQuery = "SELECT ProductId, ProductName FROM Products WHERE CategoryId = @categoryId AND Price > @minPrice"; // Act var result = QueryBreakdown.Parse(tsqlQuery, isMicrosoftSql: true); // Assert Assert.That(result.Parameters, Does.ContainKey("@categoryId")); Assert.That(result.Parameters, Does.ContainKey("@minPrice")); } [Test] public void Parse_MicrosoftSqlWithAllClauses_ParsesAllComponents() { // Arrange var tsqlQuery = @"SELECT DepartmentId, COUNT(*) AS EmployeeCount, AVG(Salary) AS AvgSalary FROM Employees WHERE Active = 1 GROUP BY DepartmentId HAVING COUNT(*) > 5 ORDER BY EmployeeCount DESC"; // Act var result = QueryBreakdown.Parse(tsqlQuery, isMicrosoftSql: true); // Assert Assert.That(result.SelectClause.Clause?.Trim(), Does.Contain("DepartmentId")); Assert.Multiple(() => { Assert.That(result.SelectClause.Clause?.Trim(), Does.Contain("COUNT(*)")); Assert.That(result.FromClause.Clause?.Trim(), Does.Contain("Employees")); Assert.That(result.WhereClause.Clause?.Trim(), Does.Contain("Active = 1")); Assert.That(result.GroupByClause.Clause?.Trim(), Does.Contain("DepartmentId")); Assert.That(result.HavingClause.Clause?.Trim(), Does.Contain("COUNT(*) > 5")); Assert.That(result.OrderByClause.Clause?.Trim(), Does.Contain("EmployeeCount DESC")); }); } [Test] public void GetSql_FromMicrosoftSqlParsedQuery_GeneratesSnowflakeSyntax() { // Arrange var tsqlQuery = "SELECT Id, Name FROM Users WHERE Active = 1 ORDER BY Name"; var result = QueryBreakdown.Parse(tsqlQuery, isMicrosoftSql: true); // Act var snowflakeSql = result.GetSql(); // Assert Assert.That(snowflakeSql, Does.Contain("SELECT")); Assert.That(snowflakeSql, Does.Contain("FROM")); Assert.That(snowflakeSql, Does.Contain("WHERE")); Assert.That(snowflakeSql, Does.Contain("ORDER BY")); // Verify Snowflake-style formatting (4-space indentation) var lines = snowflakeSql.Split(separator, StringSplitOptions.RemoveEmptyEntries); var indentedLines = lines.Where(l => l.StartsWith(" ")).ToList(); Assert.That(indentedLines, Is.Not.Empty, "Should have Snowflake-style indentation"); } [Test] public void Parse_MicrosoftSqlWithJoins_ParsesCorrectly() { // Arrange var tsqlQuery = @"SELECT o.OrderId, c.CustomerName, p.ProductName FROM Orders o INNER JOIN Customers c ON o.CustomerId = c.CustomerId LEFT JOIN OrderDetails od ON o.OrderId = od.OrderId LEFT JOIN Products p ON od.ProductId = p.ProductId WHERE o.OrderDate >= @startDate"; // Act var result = QueryBreakdown.Parse(tsqlQuery, isMicrosoftSql: true); Assert.Multiple(() => { // Assert Assert.That(result.SelectClause.Clause?.Trim(), Does.Contain("o.OrderId")); Assert.That(result.FromClause.Clause?.Trim(), Does.Contain("INNER JOIN")); }); Assert.Multiple(() => { Assert.That(result.FromClause.Clause?.Trim(), Does.Contain("LEFT JOIN")); Assert.That(result.WhereClause.Clause?.Trim(), Does.Contain("OrderDate")); }); } [Test] public void Parse_MicrosoftSqlWithSubquery_ParsesCorrectly() { // Arrange var tsqlQuery = @"SELECT CustomerId, CustomerName FROM Customers WHERE CustomerId IN (SELECT CustomerId FROM Orders WHERE OrderDate >= @startDate)"; // Act var result = QueryBreakdown.Parse(tsqlQuery, isMicrosoftSql: true); Assert.Multiple(() => { // Assert Assert.That(result.SelectClause.Clause?.Trim(), Does.Contain("CustomerId, CustomerName")); Assert.That(result.WhereClause.Clause?.Trim(), Does.Contain("IN")); }); Assert.That(result.WhereClause.Clause?.Trim(), Does.Contain("SELECT")); } [Test] public void TryParse_MicrosoftSqlValidQuery_ReturnsTrue() { // Arrange var tsqlQuery = "SELECT TOP 100 Id, Name FROM Users ORDER BY Name"; // Act var success = QueryBreakdown.TryParse(tsqlQuery, out var result, isMicrosoftSql: true); Assert.Multiple(() => { // Assert Assert.That(success, Is.True); Assert.That(result, Is.Not.Null); }); Assert.That(result, Is.InstanceOf()); } [Test] public void TryParse_MicrosoftSqlInvalidQuery_ReturnsFalse() { // Arrange var invalidQuery = "INVALID T-SQL QUERY"; // Act var success = QueryBreakdown.TryParse(invalidQuery, out var result, out var errorMessage, isMicrosoftSql: true); Assert.Multiple(() => { // Assert Assert.That(success, Is.False); Assert.That(result, Is.Null); Assert.That(errorMessage, Is.Not.Null.And.Not.Empty); }); } [Test] public void Parse_MicrosoftSqlWithBracketedIdentifiers_ParsesCorrectly() { // Arrange var tsqlQuery = "SELECT [Order Id], [Customer Name] FROM [Order Details] WHERE [Active] = 1"; // Act var result = QueryBreakdown.Parse(tsqlQuery, isMicrosoftSql: true); Assert.Multiple(() => { // Assert Assert.That(result.SelectClause.Clause?.Trim(), Does.Contain("[Order Id]")); Assert.That(result.FromClause.Clause?.Trim(), Does.Contain("[Order Details]")); Assert.That(result.WhereClause.Clause?.Trim(), Does.Contain("[Active]")); }); } [Test] public void Parse_MicrosoftSqlWithCTE_ParsesCorrectly() { // Arrange var tsqlQuery = @"WITH EmployeeCTE AS ( SELECT DepartmentId, COUNT(*) AS EmployeeCount FROM Employees GROUP BY DepartmentId ) SELECT d.DepartmentName, e.EmployeeCount FROM Departments d INNER JOIN EmployeeCTE e ON d.DepartmentId = e.DepartmentId"; // Act var result = QueryBreakdown.Parse(tsqlQuery, isMicrosoftSql: true); Assert.Multiple(() => { // Assert Assert.That(result.IsUsingWithClause, Is.True); Assert.That(result.SelectClause.Clause?.Trim(), Does.Contain("DepartmentName")); }); } [Test] public void GetSql_MicrosoftSqlWithCTE_GeneratesSnowflakeFormattedOutput() { // Arrange var tsqlQuery = @"WITH SalesSummary AS ( SELECT ProductId, SUM(Quantity) AS TotalQty FROM OrderDetails GROUP BY ProductId ) SELECT p.ProductName, s.TotalQty FROM Products p INNER JOIN SalesSummary s ON p.ProductId = s.ProductId"; var result = QueryBreakdown.Parse(tsqlQuery, isMicrosoftSql: true); // Act var snowflakeSql = result.GetSql(); // Assert Assert.That(snowflakeSql, Does.Contain("WITH")); Assert.Multiple(() => { Assert.That(snowflakeSql, Does.Contain("SELECT")); Assert.That(result.IsUsingWithClause, Is.True, "Should have WITH clause parsed"); }); // Note: WITH clause content is stored in WithClause property when parsed via isMicrosoftSql // The GetSql() will output it if WithClauses collection is populated or if we manually add it } [Test] public void Parse_MicrosoftSqlComplexQuery_ParsesAndConvertsToSnowflake() { // Arrange var tsqlQuery = @"SELECT YEAR(o.OrderDate) AS OrderYear, MONTH(o.OrderDate) AS OrderMonth, c.Region, COUNT(DISTINCT o.OrderId) AS OrderCount, SUM(od.Quantity * od.UnitPrice) AS TotalRevenue FROM Orders o INNER JOIN OrderDetails od ON o.OrderId = od.OrderId INNER JOIN Customers c ON o.CustomerId = c.CustomerId WHERE o.OrderDate >= @startDate AND o.OrderDate < @endDate GROUP BY YEAR(o.OrderDate), MONTH(o.OrderDate), c.Region HAVING COUNT(DISTINCT o.OrderId) > 10 ORDER BY OrderYear DESC, OrderMonth DESC, TotalRevenue DESC"; // Act var result = QueryBreakdown.Parse(tsqlQuery, isMicrosoftSql: true); var snowflakeSql = result.GetSql(); // Assert - Verify parsing Assert.That(result, Is.InstanceOf()); Assert.Multiple(() => { Assert.That(result.SelectClause.Clause?.Trim(), Does.Contain("YEAR")); Assert.That(result.GroupByClause.Clause?.Trim(), Does.Contain("YEAR")); Assert.That(result.HavingClause.Clause?.Trim(), Does.Contain("COUNT")); Assert.That(result.Parameters, Does.ContainKey("@startDate")); }); Assert.Multiple(() => { Assert.That(result.Parameters, Does.ContainKey("@endDate")); // Assert - Verify Snowflake SQL generation Assert.That(snowflakeSql, Does.Contain("SELECT")); }); Assert.That(snowflakeSql, Does.Contain("FROM")); Assert.That(snowflakeSql, Does.Contain("WHERE")); Assert.That(snowflakeSql, Does.Contain("GROUP BY")); Assert.That(snowflakeSql, Does.Contain("HAVING")); Assert.That(snowflakeSql, Does.Contain("ORDER BY")); } [Test] public void AddWithClause_ToMicrosoftSqlParsedQuery_WorksCorrectly() { // Arrange var mainQuery = "SELECT * FROM ProductSummary"; var result = QueryBreakdown.Parse(mainQuery, isMicrosoftSql: true); var cteQuery = new QueryBreakdown("ProductId, COUNT(*) AS OrderCount", "Orders"); cteQuery.GroupByClause.Clause = "ProductId"; // Act result.AddWithClause("ProductSummary", cteQuery); var snowflakeSql = result.GetSql(); Assert.Multiple(() => { // Assert Assert.That(result.WithClauses, Has.Count.EqualTo(1)); Assert.That(snowflakeSql, Does.Contain("WITH")); }); Assert.That(snowflakeSql, Does.Contain("ProductSummary AS (")); } [Test] public void Clone_MicrosoftSqlParsedQuery_CreatesSnowflakeInstance() { // Arrange var tsqlQuery = "SELECT Id, Name FROM Users WHERE Active = 1"; var original = QueryBreakdown.Parse(tsqlQuery, isMicrosoftSql: true); // Act var clone = (QueryBreakdown)original.Clone(); // Assert Assert.That(clone, Is.InstanceOf()); Assert.Multiple(() => { Assert.That(clone.SelectClause.Clause, Is.EqualTo(original.SelectClause.Clause)); Assert.That(clone.FromClause.Clause, Is.EqualTo(original.FromClause.Clause)); Assert.That(clone.WhereClause.Clause, Is.EqualTo(original.WhereClause.Clause)); }); } #endregion #region AddSelectExpression Tests [Test] public void AddSelectExpression_WithSnowflakeSyntax_GeneratesCorrectSql() { // Arrange var query = new QueryBreakdown(); var expr = new Strata.SqlTools.SqlBreakdown.Expressions.GenericColumnExpression("CUSTOMER_ID", "CUSTOMERS"); // Act query.AddSelectExpression(expr, null, isMicrosoftSql: false); // Assert // Snowflake doesn't use brackets Assert.That(query.SelectClause.Clause, Is.EqualTo("CUSTOMERS.CUSTOMER_ID")); } [Test] public void AddSelectExpression_WithSqlServerSyntax_GeneratesCorrectSql() { // Arrange var query = new QueryBreakdown(); var expr = new Strata.SqlTools.SqlBreakdown.Expressions.GenericColumnExpression("CustomerID", "Customers"); // Act query.AddSelectExpression(expr, null, isMicrosoftSql: true); // Assert // SQL Server uses brackets Assert.That(query.SelectClause.Clause, Is.EqualTo("[Customers].[CustomerID]")); } [Test] public void AddSelectExpression_DefaultBehavior_UsesSnowflakeSyntax() { // Arrange var query = new QueryBreakdown(); var expr = new Strata.SqlTools.SqlBreakdown.Expressions.GenericColumnExpression("CUSTOMER_ID", "CUSTOMERS"); // Act query.AddSelectExpression(expr); // Default should be Snowflake // Assert Assert.That(query.SelectClause.Clause, Is.EqualTo("CUSTOMERS.CUSTOMER_ID")); Assert.That(query.SelectClause.Clause, Does.Not.Contain("[")); } [Test] public void AddSelectExpression_WithMultipleExpressions_AppendsWithComma() { // Arrange var query = new QueryBreakdown(); var expr1 = new Strata.SqlTools.SqlBreakdown.Expressions.GenericColumnExpression("CUSTOMER_ID", "CUSTOMERS"); var expr2 = new Strata.SqlTools.SqlBreakdown.Expressions.GenericColumnExpression("CUSTOMER_NAME", "CUSTOMERS"); // Act query.AddSelectExpression(expr1); query.AddSelectExpression(expr2); // Assert Assert.That(query.SelectClause.Clause, Is.EqualTo("CUSTOMERS.CUSTOMER_ID, CUSTOMERS.CUSTOMER_NAME")); } [Test] public void AddSelectExpression_WithComment_AddsCommentToSelectClause() { // Arrange var query = new QueryBreakdown(); var expr = new Strata.SqlTools.SqlBreakdown.Expressions.GenericColumnExpression("CUSTOMER_ID", "CUSTOMERS"); // Act query.AddSelectExpression(expr, "/* Primary key */"); Assert.Multiple(() => { // Assert Assert.That(query.SelectClause.Clause, Is.EqualTo("CUSTOMERS.CUSTOMER_ID")); Assert.That(query.SelectClause.Comment, Is.EqualTo("/* Primary key */")); }); } [Test] public void AddSelectExpression_WithNullExpression_ThrowsArgumentNullException() { // Arrange var query = new QueryBreakdown(); // Act & Assert Assert.Throws(() => query.AddSelectExpression(null!)); } [Test] public void AddSelectExpression_WithBooleanLiteral_UsesSnowflakeSyntax() { // Arrange var query = new QueryBreakdown(); var expr = new Strata.SqlTools.SqlBreakdown.Expressions.Literals.BooleanLiteralExpression(true); // Act query.AddSelectExpression(expr, null, isMicrosoftSql: false); // Assert // Snowflake uses TRUE/FALSE Assert.That(query.SelectClause.Clause, Is.EqualTo("TRUE")); } [Test] public void AddSelectExpression_WithBooleanLiteral_UsesSqlServerSyntax() { // Arrange var query = new QueryBreakdown(); var expr = new Strata.SqlTools.SqlBreakdown.Expressions.Literals.BooleanLiteralExpression(true); // Act query.AddSelectExpression(expr, null, isMicrosoftSql: true); // Assert // SQL Server uses 1/0 Assert.That(query.SelectClause.Clause, Is.EqualTo("1")); } #endregion #region AddWhereExpression Tests [Test] public void AddWhereExpression_WithSnowflakeSyntax_GeneratesCorrectSql() { // Arrange var query = new QueryBreakdown(); var col = new Strata.SqlTools.SqlBreakdown.Expressions.GenericColumnExpression("ACTIVE", "USERS"); var expr = col == 1; // Act query.AddWhereExpression(expr, null, "and", isMicrosoftSql: false); // Assert Assert.That(query.WhereClause.Clause, Does.Contain("USERS.ACTIVE")); Assert.That(query.WhereClause.Clause, Does.Not.Contain("[")); } [Test] public void AddWhereExpression_WithSqlServerSyntax_GeneratesCorrectSql() { // Arrange var query = new QueryBreakdown(); var col = new Strata.SqlTools.SqlBreakdown.Expressions.GenericColumnExpression("Active", "Users"); var expr = col == 1; // Act query.AddWhereExpression(expr, null, "and", isMicrosoftSql: true); // Assert Assert.That(query.WhereClause.Clause, Does.Contain("[Users].[Active]")); } [Test] public void AddWhereExpression_DefaultBehavior_UsesSnowflakeSyntax() { // Arrange var query = new QueryBreakdown(); var col = new Strata.SqlTools.SqlBreakdown.Expressions.GenericColumnExpression("ACTIVE", "USERS"); var expr = col == 1; // Act query.AddWhereExpression(expr); // Default should be Snowflake // Assert Assert.That(query.WhereClause.Clause, Does.Contain("USERS.ACTIVE")); Assert.That(query.WhereClause.Clause, Does.Not.Contain("[")); } [Test] public void AddWhereExpression_WithMultipleExpressions_AppendsWithAnd() { // Arrange var query = new QueryBreakdown(); var col1 = new Strata.SqlTools.SqlBreakdown.Expressions.GenericColumnExpression("ACTIVE", "USERS"); var col2 = new Strata.SqlTools.SqlBreakdown.Expressions.GenericColumnExpression("AGE", "USERS"); var expr1 = col1 == 1; var expr2 = col2 >= 18; // Act query.AddWhereExpression(expr1); query.AddWhereExpression(expr2); // Assert Assert.That(query.WhereClause.Clause, Does.Contain("USERS.ACTIVE")); Assert.That(query.WhereClause.Clause, Does.Contain(" and ")); Assert.That(query.WhereClause.Clause, Does.Contain("USERS.AGE")); } [Test] public void AddWhereExpression_WithOrOperation_AppendsWithOr() { // Arrange var query = new QueryBreakdown(); var col1 = new Strata.SqlTools.SqlBreakdown.Expressions.GenericColumnExpression("STATUS", "ORDERS"); var col2 = new Strata.SqlTools.SqlBreakdown.Expressions.GenericColumnExpression("PRIORITY", "ORDERS"); var expr1 = col1 == "Urgent"; var expr2 = col2 > 5; // Act query.AddWhereExpression(expr1); query.AddWhereExpression(expr2, null, "or"); // Assert Assert.That(query.WhereClause.Clause, Does.Contain("ORDERS.STATUS")); Assert.That(query.WhereClause.Clause, Does.Contain(" or ")); Assert.That(query.WhereClause.Clause, Does.Contain("ORDERS.PRIORITY")); } [Test] public void AddWhereExpression_WithComment_AddsCommentToWhereClause() { // Arrange var query = new QueryBreakdown(); var col = new Strata.SqlTools.SqlBreakdown.Expressions.GenericColumnExpression("ACTIVE", "USERS"); var expr = col == 1; // Act query.AddWhereExpression(expr, "/* Filter active users */"); // Assert Assert.That(query.WhereClause.Comment, Is.EqualTo("/* Filter active users */")); } [Test] public void AddWhereExpression_WithMultipleComments_AppendsComments() { // Arrange var query = new QueryBreakdown(); var col1 = new Strata.SqlTools.SqlBreakdown.Expressions.GenericColumnExpression("ACTIVE", "USERS"); var col2 = new Strata.SqlTools.SqlBreakdown.Expressions.GenericColumnExpression("VERIFIED", "USERS"); var expr1 = col1 == 1; var expr2 = col2 == new Strata.SqlTools.SqlBreakdown.Expressions.Literals.BooleanLiteralExpression(true); // Act query.AddWhereExpression(expr1, "/* Active users */"); query.AddWhereExpression(expr2, "/* Verified only */"); // Assert Assert.That(query.WhereClause.Comment, Is.EqualTo("/* Active users */ /* Verified only */")); } [Test] public void AddWhereExpression_WithNullExpression_ThrowsArgumentNullException() { // Arrange var query = new QueryBreakdown(); // Act & Assert Assert.Throws(() => query.AddWhereExpression(null!)); } [Test] public void AddWhereExpression_WithStringComparison_UsesCorrectQuotes() { // Arrange var query = new QueryBreakdown(); var col = new Strata.SqlTools.SqlBreakdown.Expressions.GenericColumnExpression("NAME", "USERS"); var expr = col == "John"; // Act query.AddWhereExpression(expr); // Assert Assert.That(query.WhereClause.Clause, Does.Contain("USERS.NAME")); Assert.That(query.WhereClause.Clause, Does.Contain("'John'")); } [Test] public void AddWhereExpression_MixingSyntaxModes_AllowsBothInSameQuery() { // Arrange var query = new QueryBreakdown(); var col1 = new Strata.SqlTools.SqlBreakdown.Expressions.GenericColumnExpression("ACTIVE", "USERS"); var col2 = new Strata.SqlTools.SqlBreakdown.Expressions.GenericColumnExpression("Status", "Users"); var expr1 = col1 == 1; var expr2 = col2 == "Active"; // Act query.AddWhereExpression(expr1, null, "and", isMicrosoftSql: false); // Snowflake query.AddWhereExpression(expr2, null, "and", isMicrosoftSql: true); // SQL Server // Assert // Should contain both syntaxes Assert.That(query.WhereClause.Clause, Does.Contain("USERS.ACTIVE")); // Snowflake style Assert.That(query.WhereClause.Clause, Does.Contain("[Users].[Status]")); // SQL Server style } [Test] public void AddWhereExpression_WithComplexExpression_GeneratesCorrectSql() { // Arrange var query = new QueryBreakdown(); var col1 = new Strata.SqlTools.SqlBreakdown.Expressions.GenericColumnExpression("PRICE", "PRODUCTS"); var col2 = new Strata.SqlTools.SqlBreakdown.Expressions.GenericColumnExpression("DISCOUNT", "PRODUCTS"); var expr = (col1 - col2) > 100; // Act query.AddWhereExpression(expr); // Assert Assert.That(query.WhereClause.Clause, Does.Contain("PRODUCTS.PRICE")); Assert.That(query.WhereClause.Clause, Does.Contain("-")); Assert.That(query.WhereClause.Clause, Does.Contain("PRODUCTS.DISCOUNT")); Assert.That(query.WhereClause.Clause, Does.Contain(">")); } [Test] public void AddSelectExpression_ToExistingClause_PreservesExistingContent() { // Arrange var query = new QueryBreakdown(); query.SelectClause.Clause = "ID"; var expr = new Strata.SqlTools.SqlBreakdown.Expressions.GenericColumnExpression("NAME", "USERS"); // Act query.AddSelectExpression(expr); // Assert Assert.That(query.SelectClause.Clause, Does.StartWith("ID, ")); Assert.That(query.SelectClause.Clause, Does.Contain("USERS.NAME")); } [Test] public void AddWhereExpression_ToExistingClause_PreservesExistingContent() { // Arrange var query = new QueryBreakdown(); query.WhereClause.Clause = "STATUS = 'ACTIVE'"; var col = new Strata.SqlTools.SqlBreakdown.Expressions.GenericColumnExpression("AGE", "USERS"); var expr = col > 18; // Act query.AddWhereExpression(expr); // Assert Assert.That(query.WhereClause.Clause, Does.StartWith("STATUS = 'ACTIVE' and ")); Assert.That(query.WhereClause.Clause, Does.Contain("USERS.AGE")); } #endregion #region AddWhereClause Parameter Extraction Tests [Test] public void AddWhereClause_WithSnowflakeParameters_ExtractsParametersAutomatically() { // Arrange var query = new QueryBreakdown(); // Act - Use Snowflake :param syntax query.AddWhereClause("USER_ID = :UserId AND STATUS = :Status", false); Assert.Multiple(() => { // Assert Assert.That(query.WhereClause.Clause, Is.EqualTo("USER_ID = :UserId AND STATUS = :Status")); Assert.That(query.Parameters, Does.ContainKey(":UserId")); }); Assert.That(query.Parameters, Does.ContainKey(":Status")); Assert.Multiple(() => { Assert.That(query.Parameters[":UserId"], Is.Null); Assert.That(query.Parameters[":Status"], Is.Null); }); } [Test] public void AddWhereClause_WithMicrosoftParameters_ExtractsParametersAutomatically() { // Arrange var query = new QueryBreakdown(); // Act - Use Microsoft @param syntax with isMicrosoftSql=true query.AddWhereClause("USER_ID = @UserId AND STATUS = @Status", "and", true); Assert.Multiple(() => { // Assert Assert.That(query.WhereClause.Clause, Is.EqualTo("USER_ID = @UserId AND STATUS = @Status")); Assert.That(query.Parameters, Does.ContainKey("@UserId")); }); Assert.That(query.Parameters, Does.ContainKey("@Status")); } [Test] public void AddWhereClause_WithExistingParameter_PreservesValue() { // Arrange var query = new QueryBreakdown(); query.Parameters[":UserId"] = 123; // Act - Add where clause with same parameter query.AddWhereClause("USER_ID = :UserId", false); // Assert - Original value should be preserved Assert.That(query.Parameters[":UserId"], Is.EqualTo(123)); } [Test] public void AddWhereClause_WithMultipleCalls_AccumulatesSnowflakeParameters() { // Arrange var query = new QueryBreakdown(); // Act query.AddWhereClause("USER_ID = :UserId", false); query.AddWhereClause("STATUS = :Status", "and", false); query.AddWhereClause("CREATED_DATE > :StartDate", "and", false); // Assert Assert.That(query.Parameters, Has.Count.GreaterThanOrEqualTo(3)); Assert.That(query.Parameters, Does.ContainKey(":UserId")); Assert.That(query.Parameters, Does.ContainKey(":Status")); Assert.That(query.Parameters, Does.ContainKey(":StartDate")); } #endregion }