Files
sql-utilities/tests/Strata.SqlTools.LinqToSql.Tests/LinqQueryBreakdownWithClauseTests.cs
T
Thom LambandClaude Opus 4.7 f55d8130e6
SonarQube Analysis / sonarqube (pull_request) Successful in 3m41s
chore(sonar): clear residual test-file smells (NUnit2046, NUnit2045, CS8604)
- **NUnit2046 (15)**: extend the prior regex sweep to also catch the
  `Is.GreaterThan(n)` / `Is.GreaterThanOrEqualTo(n)` variants on
  `.Count` and `.Length` — the previous pass only handled `Is.EqualTo`.
  Affects PG/Snowflake `QueryBreakdownTests`, PG `StatementReaderTests`,
  `LinqToSql` `QueryComparatorTests` / `QueryValidatorTests`,
  `Markdown.Tests` `ExpressionGeneratorTests` /
  `QueryBreakdownGeneratorTests`, and `JsonTokenReaderTests`.

- **CS8604 (8)**: `merged[someKey]` access inside `Assert.Multiple(() =>
  { ... })` lambdas where `someKey` is `string?` from `FirstOrDefault`.
  The preceding `Assert.That(someKey, Is.Not.Null)` does not propagate
  null-narrowing into the lambda scope, so add `!` to the dictionary
  index. (Tests fail loud with a meaningful message if the key really
  is null, so this is safe.)

- **NUnit2045 (1)**: wrap a four-assert block in
  `JsonTokenReaderTests.cs:65-68` in `Assert.Multiple`. Mirrors the
  surrounding two `Assert.Multiple` groups in that method.

All 1180 tests stay green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 15:29:20 -05:00

308 lines
10 KiB
C#

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
}