Files
sql-utilities/tests/Strata.SqlTools.PostgreSql.Tests/PostgreSql/QueryBreakdownCollectionTests.cs
T
Thom LambandClaude Opus 4.7 4fe9eb36e6
SonarQube Analysis / sonarqube (pull_request) Successful in 2m59s
chore(sonar): second sweep — catch .Length, Is.Not.Empty, and newly-exposed Multiple groups (NUnit2046, NUnit2045)
Re-runs `dotnet format analyzers --diagnostics NUnit2046 NUnit2045` after
the Tier 2 Assert.Multiple wrap, which exposed:

- `Has.Length.EqualTo(n)` rewrites for `string[]`/array `.Length` checks
  (the first pass only knew about `.Count`).
- `Is.Not.Empty` rewrites for `Count, Is.GreaterThan(0)`.
- A handful of new NUnit2045 groups that became wrappable once the
  initial Multiple blocks settled the surrounding indentation.

Tests still 1180/1180 passing.

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

353 lines
9.0 KiB
C#

using Strata.SqlTools.Breakdowns.PostgreSql;
namespace Strata.SqlTools.SqlBreakdown.Tests.PostgreSql;
[TestFixture]
public class QueryBreakdownCollectionTests
{
private QueryBreakdownCollection _collection = null!;
[SetUp]
public void Setup()
{
_collection = new QueryBreakdownCollection();
}
#region Constructor Tests
[Test]
public void Constructor_Default_CreatesEmptyCollection()
{
// Arrange & Act
var collection = new QueryBreakdownCollection();
// Assert
Assert.That(collection, Is.Empty);
}
#endregion
#region Add Tests
[Test]
public void Add_SingleQueryBreakdown_AddsSuccessfully()
{
// Arrange
var query = new QueryBreakdown("id, name", "users");
// Act
_collection.Add(query);
// Assert
Assert.That(_collection, Is.Not.Empty);
}
[Test]
public void Add_MultipleQueryBreakdowns_AddsMultiple()
{
// Arrange
var query1 = new QueryBreakdown("id, name", "users");
var query2 = new QueryBreakdown("order_id, amount", "orders");
var query3 = new QueryBreakdown("product_id, price", "products");
// Act
_collection.Add(query1);
_collection.Add(query2);
_collection.Add(query3);
// Assert
Assert.That(_collection, Has.Count.EqualTo(3));
}
#endregion
#region GetPostgreSqlBatch Tests
[Test]
public void GetPostgreSqlBatch_EmptyCollection_ReturnsEmptyString()
{
// Arrange - empty collection created in SetUp
// Act
var sql = _collection.GetPostgreSqlBatch();
// Assert
Assert.That(sql, Is.Not.Null);
}
[Test]
public void GetPostgreSqlBatch_SingleQuery_GeneratesValidSql()
{
// Arrange
var query = new QueryBreakdown("id, name", "users");
_collection.Add(query);
// Act
var sql = _collection.GetPostgreSqlBatch();
// Assert
Assert.That(sql, Is.Not.Null);
Assert.That(sql, Does.Contain("SELECT").Or.Contain("select"));
}
[Test]
public void GetPostgreSqlBatch_MultipleQueries_SeparatesWithSemicolon()
{
// Arrange
var query1 = new QueryBreakdown("id", "users");
var query2 = new QueryBreakdown("id", "orders");
_collection.Add(query1);
_collection.Add(query2);
// Act
var sql = _collection.GetPostgreSqlBatch();
// Assert
Assert.That(sql, Is.Not.Null);
// PostgreSQL batch should contain semicolons separating statements
Assert.That(sql, Does.Contain(";"));
}
[Test]
public void GetPostgreSqlBatch_WithWhereClause_IncludesConditions()
{
// Arrange
var query = new QueryBreakdown("id, name", "users");
query.WhereClause.Clause = "active = true";
_collection.Add(query);
// Act
var sql = _collection.GetPostgreSqlBatch();
// Assert
Assert.That(sql, Does.Contain("active"));
}
[Test]
public void GetPostgreSqlBatch_WithOrderBy_IncludesOrderBy()
{
// Arrange
var query = new QueryBreakdown("id, name", "users");
query.OrderByClause.Clause = "name ASC";
_collection.Add(query);
// Act
var sql = _collection.GetPostgreSqlBatch();
// Assert
Assert.That(sql, Does.Contain("ORDER BY").Or.Contain("order by"));
}
[Test]
public void GetPostgreSqlBatch_WithGroupBy_IncludesGroupBy()
{
// Arrange
var query = new QueryBreakdown("category, COUNT(*) as count", "products");
query.GroupByClause.Clause = "category";
_collection.Add(query);
// Act
var sql = _collection.GetPostgreSqlBatch();
// Assert
Assert.That(sql, Does.Contain("GROUP BY").Or.Contain("group by"));
}
[Test]
public void GetPostgreSqlBatch_WithHaving_IncludesHaving()
{
// Arrange
var query = new QueryBreakdown("category, COUNT(*) as count", "products");
query.GroupByClause.Clause = "category";
query.HavingClause.Clause = "COUNT(*) > 5";
_collection.Add(query);
// Act
var sql = _collection.GetPostgreSqlBatch();
// Assert
Assert.That(sql, Does.Contain("HAVING").Or.Contain("having"));
}
#endregion
#region ParseBatch Tests
[Test]
public void ParseBatch_WithValidSql_Succeeds()
{
// Arrange
var batchSql = "SELECT id, name FROM users WHERE active = true; SELECT order_id FROM orders;";
// Act
var result = _collection.ParseBatch(batchSql);
Assert.Multiple(() =>
{
// Assert
Assert.That(result, Is.True);
Assert.That(_collection, Is.Not.Empty);
});
}
[Test]
public void ParseBatch_WithMultipleStatements_ParsesAllQueries()
{
// Arrange
var batchSql = @"
SELECT id, name FROM users WHERE active = true;
SELECT order_id, amount FROM orders WHERE user_id = $1;
";
// Act
var result = _collection.ParseBatch(batchSql);
Assert.Multiple(() =>
{
// Assert
Assert.That(result, Is.True);
Assert.That(_collection, Is.Not.Empty);
});
}
[Test]
public void ParseBatch_WithSingleStatement_ParsesSingleQuery()
{
// Arrange
var batchSql = "SELECT id, name FROM users";
// Act
var result = _collection.ParseBatch(batchSql);
// Assert
Assert.That(result, Is.True);
}
#endregion
#region Complex Query Tests
[Test]
public void GetPostgreSqlBatch_ComplexQueryWithMultipleClauses_GeneratesValidBatch()
{
// Arrange
var query1 = new QueryBreakdown("id, name", "users");
query1.WhereClause.Clause = "id = $1";
var query2 = new QueryBreakdown("order_id, amount", "orders");
query2.WhereClause.Clause = "user_id = $1";
query2.OrderByClause.Clause = "created_at DESC";
_collection.Add(query1);
_collection.Add(query2);
// Act
var sql = _collection.GetPostgreSqlBatch();
// Assert
Assert.That(sql, Does.Contain(";"));
Assert.That(sql, Does.Contain("SELECT"));
Assert.That(sql, Does.Contain("WHERE"));
}
[Test]
public void GetPostgreSqlBatch_WithParameters_IncludesParameterValues()
{
// Arrange
var query = new QueryBreakdown("id, name", "users");
query.AddParameter("userId", 123);
_collection.Add(query);
// Act
var sql = _collection.GetPostgreSqlBatch();
// Assert
Assert.That(sql, Does.Contain("SELECT"));
Assert.That(sql, Is.Not.Null);
}
[Test]
public void GetPostgreSqlBatch_MultipleQueriesWithDifferentClauses_GeneratesValidBatch()
{
// Arrange
var query1 = new QueryBreakdown("id, name", "users");
query1.WhereClause.Clause = "active = true";
var query2 = new QueryBreakdown("order_id, total", "orders");
query2.WhereClause.Clause = "status = 'pending'";
query2.OrderByClause.Clause = "created_at DESC";
var query3 = new QueryBreakdown("product_id, price", "products");
query3.WhereClause.Clause = "in_stock = true";
query3.OrderByClause.Clause = "price ASC";
_collection.Add(query1);
_collection.Add(query2);
_collection.Add(query3);
// Act
var sql = _collection.GetPostgreSqlBatch();
// Assert
Assert.That(sql, Does.Contain("SELECT"));
Assert.Multiple(() =>
{
Assert.That(sql, Does.Contain(";"));
Assert.That(_collection, Has.Count.EqualTo(3));
});
}
#endregion
#region Edge Cases
[Test]
public void GetPostgreSqlBatch_MultipleQueriesWithJoins_GeneratesValidBatch()
{
// Arrange
var query = new QueryBreakdown("u.id, u.name, o.order_id", "users u LEFT JOIN orders o ON u.id = o.user_id");
query.WhereClause.Clause = "u.active = true";
_collection.Add(query);
// Act
var sql = _collection.GetPostgreSqlBatch();
// Assert
Assert.That(sql, Does.Contain("JOIN"));
}
[Test]
public void GetPostgreSqlBatch_WithDoubleQuotedIdentifiers_PreservesQuotes()
{
// Arrange
var query = new QueryBreakdown("\"Id\", \"Name\"", "\"Users\"");
_collection.Add(query);
// Act
var sql = _collection.GetPostgreSqlBatch();
// Assert
Assert.That(sql, Does.Contain("\""));
}
[Test]
public void ParseBatch_WithComments_IgnoresComments()
{
// Arrange
var batchSql = @"
-- This is a user query
SELECT id, name FROM users;
-- This is an orders query
SELECT order_id FROM orders;
";
// Act
var result = _collection.ParseBatch(batchSql);
// Assert
Assert.That(result, Is.True);
}
#endregion
}