using Strata.SqlTools.Breakdowns.SqlServer; using Strata.SqlTools.SqlBreakdown.Classes; using Strata.SqlTools.SqlBreakdown.Expressions; namespace Strata.SqlTools.SqlBreakdown.Tests.SqlServer; [TestFixture] public class WithClauseTests { #region Constructor Tests [Test] public void Constructor_Default_CreatesEmptyInstance() { // Arrange & Act var withClause = new WithClause(); Assert.Multiple(() => { // Assert Assert.That(withClause.TableName, Is.EqualTo(string.Empty)); Assert.That(withClause.Sql, Is.Null); Assert.That(withClause.Query, Is.Null); Assert.That(withClause.Clause, Is.Null.Or.Empty); }); } [Test] public void Constructor_WithTableNameAndQuery_SetsProperties() { // Arrange var tableName = "active_users"; var query = new QueryBreakdown("id, name", "users", "active = 1"); // Act var withClause = new WithClause(tableName, query); Assert.Multiple(() => { // Assert Assert.That(withClause.TableName, Is.EqualTo(tableName)); Assert.That(withClause.Query, Is.EqualTo(query)); Assert.That(withClause.Sql, Is.Not.Null); }); } [Test] public void Constructor_WithTableNameAndQuery_ThrowsOnNullTableName() { // Arrange var query = new QueryBreakdown("id, name", "users"); // Act & Assert Assert.Throws(() => new WithClause(null!, query)); } [Test] public void Constructor_WithTableNameAndQuery_ThrowsOnNullQuery() { // Arrange var tableName = "active_users"; // Act & Assert Assert.Throws(() => new WithClause(tableName, (QueryBreakdown)null!)); } [Test] public void Constructor_WithTableNameAndSql_SetsProperties() { // Arrange var tableName = "active_users"; var sqlClauses = new SqlClauses { SelectClause = new SqlExpressionClause { Clause = "id, name" }, FromClause = new SqlClause { Clause = "users" } }; // Act var withClause = new WithClause(tableName, sqlClauses); Assert.Multiple(() => { // Assert Assert.That(withClause.TableName, Is.EqualTo(tableName)); Assert.That(withClause.Sql, Is.EqualTo(sqlClauses)); Assert.That(withClause.Query, Is.Null); }); } [Test] public void Constructor_WithTableNameAndSql_ThrowsOnNullTableName() { // Arrange var sqlClauses = new SqlClauses { SelectClause = new SqlExpressionClause { Clause = "id" } }; // Act & Assert Assert.Throws(() => new WithClause(null!, sqlClauses)); } [Test] public void Constructor_WithTableNameAndSql_ThrowsOnNullSql() { // Arrange var tableName = "active_users"; // Act & Assert Assert.Throws(() => new WithClause(tableName, (SqlClauses)null!)); } #endregion #region Property Synchronization Tests [Test] public void Sql_WhenQueryExists_ReturnsComputedClauses() { // Arrange var query = new QueryBreakdown("user_id, name", "users"); var withClause = new WithClause("active_users", query); // Act var sql = withClause.Sql; // Assert Assert.That(sql, Is.Not.Null); Assert.Multiple(() => { Assert.That(sql!.SelectClause?.Clause, Is.EqualTo("user_id, name")); Assert.That(sql.FromClause?.Clause, Is.EqualTo("users")); }); } [Test] public void Sql_WhenQueryNull_ReturnsStoredClauses() { // Arrange var sqlClauses = new SqlClauses { SelectClause = new SqlExpressionClause { Clause = "column1" }, FromClause = new SqlClause { Clause = "table1" } }; var withClause = new WithClause("test_cte", sqlClauses); // Act var sql = withClause.Sql; // Assert Assert.That(sql, Is.EqualTo(sqlClauses)); } [Test] public void Sql_SetWhenQueryNull_StoresClauses() { // Arrange var withClause = new WithClause { TableName = "test_cte" }; var sqlClauses = new SqlClauses { SelectClause = new SqlExpressionClause { Clause = "column1" }, FromClause = new SqlClause { Clause = "table1" } }; // Act withClause.Sql = sqlClauses; Assert.Multiple(() => { // Assert Assert.That(withClause.Sql, Is.EqualTo(sqlClauses)); Assert.That(withClause.Query, Is.Null); }); } [Test] public void Sql_SetWhenQueryExists_AppliesToQuery() { // Arrange var query = new QueryBreakdown(); var withClause = new WithClause { TableName = "test_cte", Query = query }; var sqlClauses = new SqlClauses { SelectClause = new SqlExpressionClause { Clause = "column1" }, FromClause = new SqlClause { Clause = "table1" } }; // Act withClause.Sql = sqlClauses; Assert.Multiple(() => { // Assert - Clauses are applied to the Query Assert.That(query.SelectClause.Clause, Is.EqualTo("column1")); Assert.That(query.FromClause.Clause, Is.EqualTo("table1")); // And Sql property returns the applied clauses (from Query, not stored) Assert.That(withClause.Sql?.SelectClause?.Clause, Is.EqualTo("column1")); Assert.That(withClause.Sql?.FromClause?.Clause, Is.EqualTo("table1")); }); } [Test] public void Query_SetWhenSqlAlreadySet_AppliesSqlToQuery() { // Arrange var withClause = new WithClause { TableName = "test_cte" }; var sqlClauses = new SqlClauses { SelectClause = new SqlExpressionClause { Clause = "column1" }, FromClause = new SqlClause { Clause = "table1" } }; withClause.Sql = sqlClauses; // Act var query = new QueryBreakdown(); withClause.Query = query; Assert.Multiple(() => { // Assert - Stored SQL is applied to Query Assert.That(query.SelectClause.Clause, Is.EqualTo("column1")); Assert.That(query.FromClause.Clause, Is.EqualTo("table1")); }); } [Test] public void Query_SetWhenSqlAlreadySet_ClearsSqlStorage() { // Arrange var withClause = new WithClause { TableName = "test_cte" }; var sqlClauses = new SqlClauses { SelectClause = new SqlExpressionClause { Clause = "column1" } }; withClause.Sql = sqlClauses; // Act var query = new QueryBreakdown(); withClause.Query = query; // Assert - Stored _sql should be cleared, but Sql property still returns from Query Assert.That(withClause.Sql, Is.Not.Null); } [Test] public void Sql_SetThenQuerySet_BidirectionalSync() { // Arrange: Set Sql first, then Query var withClause = new WithClause { TableName = "test_cte" }; var sqlClauses = new SqlClauses { SelectClause = new SqlExpressionClause { Clause = "old_column" }, FromClause = new SqlClause { Clause = "old_table" } }; withClause.Sql = sqlClauses; // Act: Setting new Query should apply stored Sql var query = new QueryBreakdown(); withClause.Query = query; Assert.Multiple(() => { // Assert Assert.That(query.SelectClause.Clause, Is.EqualTo("old_column")); Assert.That(query.FromClause.Clause, Is.EqualTo("old_table")); Assert.That(withClause.Query, Is.EqualTo(query)); }); } [Test] public void Query_SetThenSqlSet_BidirectionalSync() { // Arrange: Set Query first var query = new QueryBreakdown(); var withClause = new WithClause { TableName = "test_cte", Query = query }; // Act: Setting new Sql should apply to Query var sqlClauses = new SqlClauses { SelectClause = new SqlExpressionClause { Clause = "new_column" }, FromClause = new SqlClause { Clause = "new_table" } }; withClause.Sql = sqlClauses; Assert.Multiple(() => { // Assert Assert.That(query.SelectClause.Clause, Is.EqualTo("new_column")); Assert.That(query.FromClause.Clause, Is.EqualTo("new_table")); }); } #endregion #region GetClauses Integration Tests [Test] public void GetClauses_FromQueryBreakdown_ReturnsAllClauseProperties() { // Arrange var query = new QueryBreakdown("id, name", "users", "active = 1"); query.GroupByClause = new SqlExpressionClause { Clause = "department" }; query.HavingClause = new SqlExpressionClause { Clause = "COUNT(*) > 5" }; query.OrderByClause = new SqlExpressionClause { Clause = "name ASC" }; var withClause = new WithClause("user_summary", query); // Act var sql = withClause.Sql; // Assert Assert.That(sql, Is.Not.Null); Assert.Multiple(() => { Assert.That(sql!.SelectClause?.Clause, Is.EqualTo("id, name")); Assert.That(sql.FromClause?.Clause, Is.EqualTo("users")); Assert.That(sql.WhereClause?.Clause, Is.EqualTo("active = 1")); Assert.That(sql.GroupByClause?.Clause, Is.EqualTo("department")); Assert.That(sql.HavingClause?.Clause, Is.EqualTo("COUNT(*) > 5")); Assert.That(sql.OrderByClause?.Clause, Is.EqualTo("name ASC")); }); } #endregion #region ApplyClauses Integration Tests [Test] public void ApplyClauses_WhenSqlSet_AppliesAllClauses() { // Arrange var query = new QueryBreakdown(); var withClause = new WithClause { TableName = "test_cte", Query = query }; var sqlClauses = new SqlClauses { SelectClause = new SqlExpressionClause { Clause = "id, name" }, FromClause = new SqlClause { Clause = "users" }, WhereClause = new SqlExpressionClause { Clause = "active = 1" }, GroupByClause = new SqlExpressionClause { Clause = "department" }, HavingClause = new SqlExpressionClause { Clause = "COUNT(*) > 5" }, OrderByClause = new SqlExpressionClause { Clause = "name ASC" } }; // Act withClause.Sql = sqlClauses; Assert.Multiple(() => { // Assert Assert.That(query.SelectClause.Clause, Is.EqualTo("id, name")); Assert.That(query.FromClause.Clause, Is.EqualTo("users")); Assert.That(query.WhereClause.Clause, Is.EqualTo("active = 1")); Assert.That(query.GroupByClause.Clause, Is.EqualTo("department")); Assert.That(query.HavingClause.Clause, Is.EqualTo("COUNT(*) > 5")); Assert.That(query.OrderByClause.Clause, Is.EqualTo("name ASC")); }); } [Test] public void ApplyClauses_WhenSqlPartial_AppliesOnlyNonNullClauses() { // Arrange var query = new QueryBreakdown("original_select", "original_from"); var withClause = new WithClause { TableName = "test_cte", Query = query }; var sqlClauses = new SqlClauses { SelectClause = new SqlExpressionClause { Clause = "new_select" } // Only SelectClause is set; others are null }; // Act withClause.Sql = sqlClauses; Assert.Multiple(() => { // Assert Assert.That(query.SelectClause.Clause, Is.EqualTo("new_select")); Assert.That(query.FromClause.Clause, Is.EqualTo("original_from")); // Unchanged }); } #endregion #region Comment Handling Tests [Test] public void WithClause_PreservesCommentProperty() { // Arrange var comment = "-- This CTE selects active users"; var withClause = new WithClause { TableName = "active_users", Comment = comment }; // Act & Assert Assert.That(withClause.Comment, Is.EqualTo(comment)); } [Test] public void WithClause_PreservesClauseProperty() { // Arrange var clause = "SELECT id, name FROM users WHERE active = 1"; var withClause = new WithClause { TableName = "active_users", Clause = clause }; // Act & Assert Assert.That(withClause.Clause, Is.EqualTo(clause)); } #endregion #region Edge Cases and Null Handling [Test] public void Sql_SetToNull_ClearsStorage() { // Arrange var sqlClauses = new SqlClauses { SelectClause = new SqlExpressionClause { Clause = "id" } }; var withClause = new WithClause { TableName = "test_cte", Sql = sqlClauses }; // Act withClause.Sql = null; // Assert Assert.That(withClause.Sql, Is.Null); } [Test] public void Query_SetToNull_ClearsReference() { // Arrange var query = new QueryBreakdown("id", "users"); var withClause = new WithClause("test_cte", query); // Act withClause.Query = null; // Assert Assert.That(withClause.Query, Is.Null); } [Test] public void ApplyClauses_WithNullSqlClauses_DoesNotThrow() { // Arrange var query = new QueryBreakdown("id", "users"); var withClause = new WithClause { TableName = "test_cte", Query = query }; // Act & Assert Assert.DoesNotThrow(() => withClause.Sql = null); } [Test] public void Sql_WhenBothQueryAndStorageNull_ReturnsNull() { // Arrange var withClause = new WithClause { TableName = "test_cte" }; // Act var result = withClause.Sql; // Assert Assert.That(result, Is.Null); } #endregion #region ComplexScenario Tests [Test] public void ComplexScenario_NestedCTE_WithParameters() { // Arrange: Create inner CTE with parameters var innerQuery = new QueryBreakdown("id, name", "users", "created_date >= @StartDate"); innerQuery.AddParameter("@StartDate", DateTime.Today); var innerCte = new WithClause("recent_users", innerQuery); // Arrange: Create outer CTE that uses inner CTE var outerQuery = new QueryBreakdown("*", "recent_users"); var outerCte = new WithClause("user_report", outerQuery); // Act var innerSql = innerCte.Sql; var outerSql = outerCte.Sql; Assert.Multiple(() => { // Assert - Inner CTE has parameter and correct clauses Assert.That(innerQuery.ParameterList.Count(), Is.EqualTo(1)); Assert.That(innerSql?.SelectClause?.Clause, Is.EqualTo("id, name")); Assert.That(innerSql?.WhereClause?.Clause, Is.EqualTo("created_date >= @StartDate")); // Assert - Outer CTE references inner CTE Assert.That(outerSql?.SelectClause?.Clause, Is.EqualTo("*")); Assert.That(outerSql?.FromClause?.Clause, Is.EqualTo("recent_users")); }); } [Test] public void ComplexScenario_MultipleClauseUpdates() { // Arrange var query = new QueryBreakdown("id", "users"); var withClause = new WithClause("test_cte", query); // Act: Update clauses via Sql property multiple times var sql1 = new SqlClauses { SelectClause = new SqlExpressionClause { Clause = "updated_column_1" } }; withClause.Sql = sql1; var sql2 = new SqlClauses { SelectClause = new SqlExpressionClause { Clause = "updated_column_2" } }; withClause.Sql = sql2; // Assert Assert.That(query.SelectClause.Clause, Is.EqualTo("updated_column_2")); } #endregion #region GetSql Integration Tests [Test] public void GetSql_WithSingleCTE_GeneratesCorrectSyntax() { // Arrange var cteQuery = new QueryBreakdown("id, name", "users", "active = 1"); var mainQuery = new QueryBreakdown("*", "active_users"); mainQuery.AddWithClause("active_users", cteQuery); // Act var sql = mainQuery.GetSql(); // Assert Assert.That(sql, Does.Contain("WITH")); Assert.That(sql, Does.Contain("active_users")); Assert.That(sql, Does.Contain("AS")); Assert.Multiple(() => { // Check for keywords/content (allowing for formatting variations) Assert.That(sql.ToUpper(), Does.Contain("SELECT")); Assert.That(sql, Does.Contain("id, name")); }); Assert.That(sql, Does.Contain("users")); Assert.That(sql, Does.Contain("active = 1")); } [Test] public void GetSql_WithMultipleCTEs_GeneratesCorrectSyntax() { // Arrange var cte1 = new QueryBreakdown("id, name", "users", "active = 1"); var cte2 = new QueryBreakdown("user_id, order_count", "orders"); var mainQuery = new QueryBreakdown("*", "active_users"); mainQuery.AddWithClause("active_users", cte1); mainQuery.AddWithClause("user_orders", cte2); // 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")); // Verify order: first CTE comes before second CTE var pos1 = sql.IndexOf("active_users"); var pos2 = sql.IndexOf("user_orders"); Assert.Multiple(() => { Assert.That(pos1, Is.GreaterThan(-1)); Assert.That(pos2, Is.GreaterThan(-1)); }); Assert.That(pos1, Is.LessThan(pos2)); } #endregion }