using Strata.SqlTools.Breakdowns.LinqToSql; using Strata.SqlTools.Builders.LinqToSql; using Strata.SqlTools.SqlBreakdown.Classes; namespace Strata.SqlTools.SqlBreakdown.Tests.LinqToSql; [TestFixture] public class LinqQueryBreakdownWithClauseTests { #region Basic WITH Clause Tests [Test] public void LinqQueryBreakdown_SupportsWithClause_ViaInheritance() { // Arrange var cte = new LinqQueryBreakdown("id, name", "users", "status = 'active'"); var mainQuery = new LinqQueryBreakdown("*", "active_users_cte"); // Act mainQuery.AddWithClause("active_users", cte); // Assert Assert.That(mainQuery.WithClauses, Has.Count.EqualTo(1)); Assert.Multiple(() => { Assert.That(mainQuery.WithClauses[0].TableName, Is.EqualTo("active_users")); Assert.That(mainQuery.WithClauses[0].Query, Is.EqualTo(cte)); }); } [Test] public void LinqQueryBreakdown_GeneratesSql_WithSimpleCTE() { // Arrange var cte = new LinqQueryBreakdown("id, name", "users"); var mainQuery = new LinqQueryBreakdown("*", "active_users_cte"); mainQuery.AddWithClause("active_users", cte); // Act var sql = mainQuery.GetSql(); // Assert Assert.That(sql, Contains.Substring("WITH")); Assert.That(sql, Contains.Substring("active_users AS")); } [Test] public void LinqQueryBreakdown_WithMultipleCTEs_GeneratesAllClauses() { // Arrange var cte1 = new LinqQueryBreakdown("id, status", "users", "status = 'active'"); var cte2 = new LinqQueryBreakdown("user_id, created", "orders", "created >= CURRENT_DATE"); var mainQuery = new LinqQueryBreakdown("*", "results"); mainQuery.AddWithClause("active_users", cte1); mainQuery.AddWithClause("recent_orders", cte2); // Act var sql = mainQuery.GetSql(); // Assert Assert.That(sql, Contains.Substring("WITH")); Assert.That(sql, Contains.Substring("active_users AS")); Assert.That(sql, Contains.Substring("recent_orders AS")); } #endregion #region CTE Parameter Inheritance Tests [Test] public void LinqQueryBreakdown_CollectsCTEParameters() { // Arrange var cte = new LinqQueryBreakdown("id, name", "users"); cte.AddParameter("Status", "active"); var mainQuery = new LinqQueryBreakdown("*", "results"); mainQuery.AddParameter("Limit", 100); mainQuery.AddWithClause("active_users", cte); // Act var merged = mainQuery.GetMergedParameters(); // Assert var keys = merged.Keys.ToList(); Assert.Multiple(() => { Assert.That(keys.Any(k => k.Contains("Status")), "Should include CTE parameters"); Assert.That(keys.Any(k => k.Contains("Limit")), "Should include main query parameters"); }); } [Test] public void LinqQueryBreakdown_MainQueryParamsPrecedence() { // Arrange var cte = new LinqQueryBreakdown("id", "data"); cte.AddParameter("Value", "cte_value"); var mainQuery = new LinqQueryBreakdown("*", "results"); mainQuery.AddParameter("Value", "main_value"); mainQuery.AddWithClause("data_cte", cte); // Act var merged = mainQuery.GetMergedParameters(); // Assert var valueKey = merged.Keys.FirstOrDefault(k => k.Contains("Value")); Assert.Multiple(() => { Assert.That(valueKey, Is.Not.Null, "Should have a Value parameter"); Assert.That(merged[valueKey!], Is.EqualTo("main_value"), "Main query parameter should take precedence"); }); } #endregion #region Builder Integration Tests [Test] public void LinqQueryBreakdownBuilder_BuiltQuerySupportsWithClauses() { // Arrange var builder = LinqQueryBreakdownBuilder.CreateForTable("users") .SelectAll() .Where("id > 0"); var mainQuery = builder.Build(); var cte = new LinqQueryBreakdown("id, name", "active_users_data"); cte.AddParameter("Status", "active"); // Act mainQuery.AddWithClause("active_users", cte); var sql = mainQuery.GetSql(); // Assert Assert.That(sql, Contains.Substring("WITH")); Assert.That(sql, Contains.Substring("active_users AS")); } [Test] public void LinqQueryBreakdownBuilder_CanAddCTEToBuiltQuery() { // Arrange var builder = LinqQueryBreakdownBuilder.CreateForTable("orders"); builder.SelectAll().Where("status != 'cancelled'"); var query = builder.Build(); var cte1 = new LinqQueryBreakdown("id, order_id", "recent_orders", "created >= CURRENT_DATE"); cte1.AddParameter("MinDate", DateTime.Today.AddDays(-30)); var cte2 = new LinqQueryBreakdown("id, name", "users", "active = 1"); cte2.AddParameter("Status", "active"); // Act query.AddWithClause("recent_orders", cte1); query.AddWithClause("active_users", cte2); var merged = query.GetMergedParameters(); // Assert Assert.That(query.WithClauses, Has.Count.EqualTo(2)); var keys = merged.Keys.ToList(); Assert.Multiple(() => { Assert.That(keys.Any(k => k.Contains("MinDate"))); Assert.That(keys.Any(k => k.Contains("Status"))); }); } #endregion #region Recursive CTE Tests [Test] public void LinqQueryBreakdown_SupportsRecursiveCTE_ViaInheritance() { // Arrange var anchorQuery = new LinqQueryBreakdown("id, manager_id, name, 1 as level", "employees", "manager_id IS NULL"); var recursiveQuery = new LinqQueryBreakdown( "e.id, e.manager_id, e.name, h.level + 1", "employees e INNER JOIN hierarchy h ON e.manager_id = h.id" ); var mainQuery = new LinqQueryBreakdown("*", "hierarchy"); var withClause = new WithClause("hierarchy", anchorQuery) { IsRecursive = true, RecursiveQuery = recursiveQuery }; mainQuery.AddWithClause(withClause); // Act var sql = mainQuery.GetSql(); // Assert Assert.That(sql, Contains.Substring("WITH RECURSIVE")); Assert.That(sql, Contains.Substring("hierarchy AS")); Assert.That(sql, Contains.Substring("UNION ALL")); } [Test] public void LinqQueryBreakdown_RecursiveCTE_CollectsAllParameters() { // Arrange var anchorQuery = new LinqQueryBreakdown("id, manager_id", "employees"); anchorQuery.AddParameter("AnchorLevel", 1); var recursiveQuery = new LinqQueryBreakdown("e.id, e.manager_id", "employees e INNER JOIN org_hier h ON e.manager_id = h.id"); recursiveQuery.AddParameter("MaxLevel", 10); var mainQuery = new LinqQueryBreakdown("*", "org_hierarchy"); mainQuery.AddParameter("QueryLimit", 100); var withClause = new WithClause("org_hierarchy", anchorQuery) { IsRecursive = true, RecursiveQuery = recursiveQuery }; mainQuery.AddWithClause(withClause); // Act var merged = mainQuery.GetMergedParameters(); // Assert var keys = merged.Keys.ToList(); Assert.Multiple(() => { Assert.That(keys.Any(k => k.Contains("AnchorLevel")), "Should include anchor parameters"); Assert.That(keys.Any(k => k.Contains("MaxLevel")), "Should include recursive parameters"); Assert.That(keys.Any(k => k.Contains("QueryLimit")), "Should include main query parameters"); }); } #endregion #region SQL Generation Tests [Test] public void LinqQueryBreakdown_GeneratesProperSqlWithParameters() { // Arrange var cte = new LinqQueryBreakdown("id, name, salary", "employees", "salary > @MinSalary"); cte.AddParameter("MinSalary", 50000); var mainQuery = new LinqQueryBreakdown("user_id, user_name, compensation", "employee_summary"); mainQuery.AddParameter("DepartmentId", 1); mainQuery.AddWithClause("high_earners", cte); // Act var sql = mainQuery.GetSql(); // Assert Assert.That(sql, Contains.Substring("WITH")); Assert.That(sql, Contains.Substring("high_earners AS")); } [Test] public void LinqQueryBreakdown_ToSqlServerSql_WithCTE() { // Arrange var cte = new LinqQueryBreakdown("id, name", "base_data"); var mainQuery = new LinqQueryBreakdown("*", "cte_data"); mainQuery.AddWithClause("base", cte); // Act var sql = mainQuery.GetSql(); // Assert Assert.That(sql, Is.Not.Null); // GetSql() should include WITH clause (inherited from QueryBreakdown) Assert.That(sql, Contains.Substring("WITH"), "GetSql() should generate WITH clause"); } [Test] public void LinqQueryBreakdown_ComplexQuery_WithNestdCTEsAndParameters() { // Arrange var innerCte = new LinqQueryBreakdown("id, dept_id", "employees"); innerCte.AddParameter("InnerParam", "inner_value"); var outerCte = new LinqQueryBreakdown("*", "filtered_employees"); outerCte.AddParameter("OuterParam", "outer_value"); outerCte.AddWithClause("emp_summary", innerCte); var mainQuery = new LinqQueryBreakdown("*", "final_results"); mainQuery.AddParameter("MainParam", "main_value"); mainQuery.AddWithClause("emp_data", outerCte); // Act var merged = mainQuery.GetMergedParameters(); var sql = mainQuery.GetSql(); // Assert var keys = merged.Keys.ToList(); Assert.Multiple(() => { Assert.That(keys.Any(k => k.Contains("InnerParam"))); Assert.That(keys.Any(k => k.Contains("OuterParam"))); Assert.That(keys.Any(k => k.Contains("MainParam"))); Assert.That(sql, Contains.Substring("WITH")); }); } #endregion }