- NUnit2046: `Assert.That(x.Count, Is.EqualTo(n))` → `Assert.That(x, Has.Count.EqualTo(n))` (or `Is.Empty` when n==0)
- NUnit2011: `Assert.That(s.Contains(x))` → `Assert.That(s, Does.Contain(x))` for richer failure messages
- CA1866: `.StartsWith("$"|"@"|":")` → `.StartsWith('$'|'@'|':')` char overload
Driven by `dotnet format analyzers --diagnostics NUnit2046 NUnit2011 CA1866`
for the cases the Roslyn fixer handles, plus a regex sweep for the remaining
`Count == n` (n>0) cases which the fixer doesn't address. CA1866 had no
associated code fix and was edited by hand (3 sites in 2 files). All tests
green (1180 passing).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
505 lines
15 KiB
C#
505 lines
15 KiB
C#
using Strata.SqlTools.Breakdowns.SqlServer;
|
|
using Strata.SqlTools.Extensions;
|
|
|
|
namespace Strata.SqlTools.SqlBreakdown.Tests.Extensions;
|
|
|
|
[TestFixture]
|
|
public class QueryBreakdownExtensionsTests
|
|
{
|
|
#region Basic Clause Tests
|
|
|
|
[Test]
|
|
public void Select_SetsSelectClause()
|
|
{
|
|
// Arrange
|
|
var query = new QueryBreakdown();
|
|
|
|
// Act
|
|
var result = query.Select("id, name");
|
|
|
|
// Assert
|
|
Assert.That(result, Is.SameAs(query));
|
|
Assert.That(query.SelectClause.Clause, Is.EqualTo("id, name"));
|
|
}
|
|
|
|
[Test]
|
|
public void From_SetsFromClause()
|
|
{
|
|
// Arrange
|
|
var query = new QueryBreakdown();
|
|
|
|
// Act
|
|
var result = query.From("users");
|
|
|
|
// Assert
|
|
Assert.That(result, Is.SameAs(query));
|
|
Assert.That(query.FromClause.Clause, Is.EqualTo("users"));
|
|
}
|
|
|
|
[Test]
|
|
public void Where_SetsWhereClause()
|
|
{
|
|
// Arrange
|
|
var query = new QueryBreakdown();
|
|
|
|
// Act
|
|
var result = query.Where("active = 1");
|
|
|
|
// Assert
|
|
Assert.That(result, Is.SameAs(query));
|
|
Assert.That(query.WhereClause.Clause, Is.EqualTo("active = 1"));
|
|
}
|
|
|
|
[Test]
|
|
public void GroupBy_SetsGroupByClause()
|
|
{
|
|
// Arrange
|
|
var query = new QueryBreakdown();
|
|
|
|
// Act
|
|
var result = query.GroupBy("department");
|
|
|
|
// Assert
|
|
Assert.That(result, Is.SameAs(query));
|
|
Assert.That(query.GroupByClause.Clause, Is.EqualTo("department"));
|
|
}
|
|
|
|
[Test]
|
|
public void Having_SetsHavingClause()
|
|
{
|
|
// Arrange
|
|
var query = new QueryBreakdown();
|
|
|
|
// Act
|
|
var result = query.Having("COUNT(*) > 5");
|
|
|
|
// Assert
|
|
Assert.That(result, Is.SameAs(query));
|
|
Assert.That(query.HavingClause.Clause, Is.EqualTo("COUNT(*) > 5"));
|
|
}
|
|
|
|
[Test]
|
|
public void OrderBy_SetsOrderByClause()
|
|
{
|
|
// Arrange
|
|
var query = new QueryBreakdown();
|
|
|
|
// Act
|
|
var result = query.OrderBy("name ASC");
|
|
|
|
// Assert
|
|
Assert.That(result, Is.SameAs(query));
|
|
Assert.That(query.OrderByClause.Clause, Is.EqualTo("name ASC"));
|
|
}
|
|
|
|
[Test]
|
|
public void AddWhere_AppendsToWhereClause()
|
|
{
|
|
// Arrange
|
|
var query = new QueryBreakdown().Where("active = 1");
|
|
|
|
// Act
|
|
var result = query.AddWhere("AND age > 18");
|
|
|
|
// Assert
|
|
Assert.That(result, Is.SameAs(query));
|
|
Assert.That(query.WhereClause.Clause, Does.Contain("active = 1"));
|
|
Assert.That(query.WhereClause.Clause, Does.Contain("AND age > 18"));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Method Chaining Tests
|
|
|
|
[Test]
|
|
public void FluentAPI_CanChainSelectFromWhere()
|
|
{
|
|
// Act
|
|
var query = new QueryBreakdown()
|
|
.Select("id, name")
|
|
.From("users")
|
|
.Where("active = 1");
|
|
|
|
// 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"));
|
|
}
|
|
|
|
[Test]
|
|
public void FluentAPI_CanChainAllClauses()
|
|
{
|
|
// Act
|
|
var query = new QueryBreakdown()
|
|
.Select("department, COUNT(*) as employee_count")
|
|
.From("employees")
|
|
.Where("salary > 50000")
|
|
.GroupBy("department")
|
|
.Having("COUNT(*) > 5")
|
|
.OrderBy("employee_count DESC");
|
|
|
|
// Assert
|
|
Assert.That(query.SelectClause.Clause, Is.EqualTo("department, COUNT(*) as employee_count"));
|
|
Assert.That(query.FromClause.Clause, Is.EqualTo("employees"));
|
|
Assert.That(query.WhereClause.Clause, Is.EqualTo("salary > 50000"));
|
|
Assert.That(query.GroupByClause.Clause, Is.EqualTo("department"));
|
|
Assert.That(query.HavingClause.Clause, Is.EqualTo("COUNT(*) > 5"));
|
|
Assert.That(query.OrderByClause.Clause, Is.EqualTo("employee_count DESC"));
|
|
}
|
|
|
|
[Test]
|
|
public void FluentAPI_GeneratesCorrectSQL()
|
|
{
|
|
// Arrange & Act
|
|
var query = new QueryBreakdown()
|
|
.Select("id, name, email")
|
|
.From("users")
|
|
.Where("active = 1")
|
|
.OrderBy("name ASC");
|
|
|
|
var sql = query.GetSql();
|
|
|
|
// Assert
|
|
Assert.That(sql, Does.Contain("SELECT"));
|
|
Assert.That(sql, Does.Contain("id, name, email"));
|
|
Assert.That(sql, Does.Contain("FROM"));
|
|
Assert.That(sql, Does.Contain("users"));
|
|
Assert.That(sql, Does.Contain("WHERE"));
|
|
Assert.That(sql, Does.Contain("active = 1"));
|
|
Assert.That(sql, Does.Contain("ORDER BY"));
|
|
Assert.That(sql, Does.Contain("name ASC"));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region WithCte Tests
|
|
|
|
[Test]
|
|
public void WithCte_WithConfigureAction_AddsCtesToQuery()
|
|
{
|
|
// Arrange & Act
|
|
var query = new QueryBreakdown()
|
|
.WithCte("active_users", cte => cte
|
|
.Select("id, name")
|
|
.From("users")
|
|
.Where("active = 1"))
|
|
.Select("*")
|
|
.From("active_users");
|
|
|
|
// Assert
|
|
Assert.That(query.IsUsingWithClause, Is.True);
|
|
Assert.That(query.WithClauses, Has.Count.EqualTo(1));
|
|
Assert.That(query.WithClauses[0].TableName, Is.EqualTo("active_users"));
|
|
Assert.That(query.SelectClause.Clause, Is.EqualTo("*"));
|
|
Assert.That(query.FromClause.Clause, Is.EqualTo("active_users"));
|
|
}
|
|
|
|
[Test]
|
|
public void WithCte_GeneratesCorrectSQL()
|
|
{
|
|
// Arrange & Act
|
|
var query = new QueryBreakdown()
|
|
.WithCte("active_users", cte => cte
|
|
.Select("id, name")
|
|
.From("users")
|
|
.Where("active = 1"))
|
|
.Select("*")
|
|
.From("active_users");
|
|
|
|
var sql = query.GetSql();
|
|
|
|
// Assert
|
|
Assert.That(sql, Does.Contain("WITH"));
|
|
Assert.That(sql, Does.Contain("active_users AS"));
|
|
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"));
|
|
Assert.That(sql, Does.Contain("WHERE"));
|
|
Assert.That(sql, Does.Contain("active = 1"));
|
|
}
|
|
|
|
[Test]
|
|
public void WithCte_SupportsMultipleCTEs()
|
|
{
|
|
// Arrange & Act
|
|
var query = new QueryBreakdown()
|
|
.WithCte("active_users", cte => cte
|
|
.Select("id, name")
|
|
.From("users")
|
|
.Where("active = 1"))
|
|
.WithCte("recent_orders", cte => cte
|
|
.Select("order_id, user_id, total")
|
|
.From("orders")
|
|
.Where("created_date > '2024-01-01'"))
|
|
.Select("u.name, COUNT(o.order_id) as order_count")
|
|
.From("active_users u LEFT JOIN recent_orders o ON u.id = o.user_id")
|
|
.GroupBy("u.id, u.name");
|
|
|
|
// Assert
|
|
Assert.That(query.IsUsingWithClause, Is.True);
|
|
Assert.That(query.WithClauses, Has.Count.EqualTo(2));
|
|
Assert.That(query.WithClauses[0].TableName, Is.EqualTo("active_users"));
|
|
Assert.That(query.WithClauses[1].TableName, Is.EqualTo("recent_orders"));
|
|
}
|
|
|
|
private static readonly string[] columns = new[] { "id", "name", "email" };
|
|
|
|
[Test]
|
|
public void WithCte_WithColumnList_AddsCtesWithColumns()
|
|
{
|
|
// Arrange & Act
|
|
var query = new QueryBreakdown()
|
|
.WithCte("active_users", columns, cte => cte
|
|
.Select("user_id, user_name, user_email")
|
|
.From("users")
|
|
.Where("status = 'active'"))
|
|
.Select("*")
|
|
.From("active_users");
|
|
|
|
var sql = query.GetSql();
|
|
|
|
// Assert
|
|
Assert.That(query.IsUsingWithClause, Is.True);
|
|
Assert.That(query.WithClauses, Has.Count.EqualTo(1));
|
|
Assert.That(sql, Does.Contain("active_users (id, name, email)"));
|
|
}
|
|
|
|
[Test]
|
|
public void WithCte_WithIQueryBreakdown_AddsCte()
|
|
{
|
|
// Arrange
|
|
var cteQuery = new QueryBreakdown()
|
|
.Select("id, name")
|
|
.From("users")
|
|
.Where("active = 1");
|
|
|
|
// Act
|
|
var query = new QueryBreakdown()
|
|
.WithCte("active_users", cteQuery)
|
|
.Select("*")
|
|
.From("active_users");
|
|
|
|
// Assert
|
|
Assert.That(query.IsUsingWithClause, Is.True);
|
|
Assert.That(query.WithClauses, Has.Count.EqualTo(1));
|
|
Assert.That(query.WithClauses[0].TableName, Is.EqualTo("active_users"));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Complex Query Tests
|
|
|
|
[Test]
|
|
public void FluentAPI_ComplexQueryWithGrouping()
|
|
{
|
|
// Arrange & Act
|
|
var query = new QueryBreakdown()
|
|
.Select("department, AVG(salary) as avg_salary, COUNT(*) as emp_count")
|
|
.From("employees")
|
|
.Where("hire_date >= '2020-01-01'")
|
|
.GroupBy("department")
|
|
.Having("COUNT(*) >= 10")
|
|
.OrderBy("avg_salary DESC");
|
|
|
|
var sql = query.GetSql();
|
|
|
|
// Assert
|
|
Assert.That(sql, Does.Contain("SELECT"));
|
|
Assert.That(sql, Does.Contain("department, AVG(salary) as avg_salary, COUNT(*) as emp_count"));
|
|
Assert.That(sql, Does.Contain("GROUP BY"));
|
|
Assert.That(sql, Does.Contain("HAVING"));
|
|
Assert.That(sql, Does.Contain("COUNT(*) >= 10"));
|
|
}
|
|
|
|
[Test]
|
|
public void FluentAPI_RealWorldExample_SalesSummaryReport()
|
|
{
|
|
// Arrange & Act - Build a sales summary report with CTE
|
|
var query = new QueryBreakdown()
|
|
.WithCte("monthly_sales", cte => cte
|
|
.Select("YEAR(order_date) as year, MONTH(order_date) as month, SUM(total_amount) as total_sales")
|
|
.From("orders")
|
|
.Where("status = 'completed'")
|
|
.GroupBy("YEAR(order_date), MONTH(order_date)"))
|
|
.Select("year, month, total_sales, LAG(total_sales) OVER (ORDER BY year, month) as prev_month_sales")
|
|
.From("monthly_sales")
|
|
.OrderBy("year DESC, month DESC");
|
|
|
|
var sql = query.GetSql();
|
|
|
|
// Assert
|
|
Assert.That(sql, Does.Contain("WITH"));
|
|
Assert.That(sql, Does.Contain("monthly_sales AS"));
|
|
Assert.That(sql, Does.Contain("LAG(total_sales)"));
|
|
Assert.That(query.IsUsingWithClause, Is.True);
|
|
}
|
|
|
|
[Test]
|
|
public void FluentAPI_RealWorldExample_ActiveUserHierarchy()
|
|
{
|
|
// Arrange & Act - Build a hierarchical user/order report
|
|
var query = new QueryBreakdown()
|
|
.WithCte("active_users", cte => cte
|
|
.Select("id, name, email")
|
|
.From("users")
|
|
.Where("active = 1 AND last_login > DATEADD(month, -3, GETDATE())"))
|
|
.WithCte("user_orders", cte => cte
|
|
.Select("u.id as user_id, u.name, COUNT(o.id) as order_count, SUM(o.total) as total_spent")
|
|
.From("active_users u LEFT JOIN orders o ON u.id = o.user_id")
|
|
.GroupBy("u.id, u.name"))
|
|
.Select("name, order_count, total_spent, CASE WHEN total_spent > 1000 THEN 'VIP' ELSE 'Regular' END as customer_tier")
|
|
.From("user_orders")
|
|
.Where("order_count > 0")
|
|
.OrderBy("total_spent DESC");
|
|
|
|
var sql = query.GetSql();
|
|
|
|
// Assert
|
|
Assert.That(query.WithClauses, Has.Count.EqualTo(2));
|
|
Assert.That(sql, Does.Contain("active_users AS"));
|
|
Assert.That(sql, Does.Contain("user_orders AS"));
|
|
Assert.That(sql, Does.Contain("customer_tier"));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Validation Tests
|
|
|
|
[Test]
|
|
public void Select_WithNullQuery_ThrowsArgumentNullException()
|
|
{
|
|
// Arrange
|
|
QueryBreakdown? query = null;
|
|
|
|
// Act & Assert
|
|
Assert.Throws<ArgumentNullException>(() => query!.Select("*"));
|
|
}
|
|
|
|
[Test]
|
|
public void From_WithNullQuery_ThrowsArgumentNullException()
|
|
{
|
|
// Arrange
|
|
QueryBreakdown? query = null;
|
|
|
|
// Act & Assert
|
|
Assert.Throws<ArgumentNullException>(() => query!.From("users"));
|
|
}
|
|
|
|
[Test]
|
|
public void Where_WithNullQuery_ThrowsArgumentNullException()
|
|
{
|
|
// Arrange
|
|
QueryBreakdown? query = null;
|
|
|
|
// Act & Assert
|
|
Assert.Throws<ArgumentNullException>(() => query!.Where("active = 1"));
|
|
}
|
|
|
|
[Test]
|
|
public void WithCte_WithNullQuery_ThrowsArgumentNullException()
|
|
{
|
|
// Arrange
|
|
QueryBreakdown? query = null;
|
|
|
|
// Act & Assert
|
|
Assert.Throws<ArgumentNullException>(() =>
|
|
query!.WithCte("test", cte => cte.Select("*").From("users")));
|
|
}
|
|
|
|
[Test]
|
|
public void WithCte_WithEmptyTableName_ThrowsArgumentException()
|
|
{
|
|
// Arrange
|
|
var query = new QueryBreakdown();
|
|
|
|
// Act & Assert
|
|
Assert.Throws<ArgumentException>(() =>
|
|
query.WithCte("", cte => cte.Select("*").From("users")));
|
|
}
|
|
|
|
[Test]
|
|
public void WithCte_WithNullConfigureAction_ThrowsArgumentNullException()
|
|
{
|
|
// Arrange
|
|
var query = new QueryBreakdown();
|
|
|
|
// Act & Assert
|
|
Assert.Throws<ArgumentNullException>(() =>
|
|
query.WithCte("test", (Action<QueryBreakdown>)null!));
|
|
}
|
|
|
|
[Test]
|
|
public void WithCte_WithColumnList_WithEmptyColumns_ThrowsArgumentException()
|
|
{
|
|
// Arrange
|
|
var query = new QueryBreakdown();
|
|
|
|
// Act & Assert
|
|
Assert.Throws<ArgumentException>(() =>
|
|
query.WithCte("test", Array.Empty<string>(), cte => cte.Select("*").From("users")));
|
|
}
|
|
|
|
[Test]
|
|
public void WithCte_WithIQueryBreakdown_WithNullCteQuery_ThrowsArgumentNullException()
|
|
{
|
|
// Arrange
|
|
var query = new QueryBreakdown();
|
|
|
|
// Act & Assert
|
|
Assert.Throws<ArgumentNullException>(() =>
|
|
query.WithCte("test", (QueryBreakdown)null!));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Edge Case Tests
|
|
|
|
[Test]
|
|
public void FluentAPI_CanOverwriteClauses()
|
|
{
|
|
// Arrange
|
|
var query = new QueryBreakdown()
|
|
.Select("*")
|
|
.From("users")
|
|
.Where("active = 1");
|
|
|
|
// Act - Overwrite clauses
|
|
query.Select("id, name").Where("active = 0");
|
|
|
|
// Assert
|
|
Assert.That(query.SelectClause.Clause, Is.EqualTo("id, name"));
|
|
Assert.That(query.WhereClause.Clause, Is.EqualTo("active = 0"));
|
|
}
|
|
|
|
[Test]
|
|
public void FluentAPI_HandlesEmptyStrings()
|
|
{
|
|
// Arrange & Act
|
|
var query = new QueryBreakdown()
|
|
.Select("")
|
|
.From("")
|
|
.Where("");
|
|
|
|
// Assert
|
|
Assert.That(query.SelectClause.Clause, Is.EqualTo(""));
|
|
Assert.That(query.FromClause.Clause, Is.EqualTo(""));
|
|
Assert.That(query.WhereClause.Clause, Is.EqualTo(""));
|
|
}
|
|
|
|
[Test]
|
|
public void FluentAPI_PreservesWhitespace()
|
|
{
|
|
// Arrange & Act
|
|
var query = new QueryBreakdown()
|
|
.Select(" id, name ")
|
|
.From(" users ");
|
|
|
|
// Assert
|
|
Assert.That(query.SelectClause.Clause, Is.EqualTo(" id, name "));
|
|
Assert.That(query.FromClause.Clause, Is.EqualTo(" users "));
|
|
}
|
|
|
|
#endregion
|
|
}
|