- 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>
893 lines
30 KiB
C#
893 lines
30 KiB
C#
using Strata.SqlTools.Breakdowns.LinqToSql;
|
|
using Strata.SqlTools.Breakdowns.SqlServer;
|
|
|
|
namespace Strata.SqlTools.LinqToSql.Tests;
|
|
|
|
[TestFixture]
|
|
public class LinqQueryBreakdownTests
|
|
{
|
|
private TestDataContext _context = null!;
|
|
private static readonly string[] separator = new[] { "AND" };
|
|
|
|
[SetUp]
|
|
public void Setup()
|
|
{
|
|
_context = new TestDataContext();
|
|
}
|
|
|
|
[Test]
|
|
public void Analyze_SimpleSelectQuery_ExtractsCorrectClauses()
|
|
{
|
|
// Arrange
|
|
var query = from user in TestDataContext.Users
|
|
select user;
|
|
|
|
// Act
|
|
var breakdown = LinqQueryBreakdown.Analyze(query);
|
|
|
|
// Assert
|
|
Assert.That(breakdown, Is.Not.Null);
|
|
Assert.That(breakdown.EntityType, Is.EqualTo(typeof(User)));
|
|
Assert.That(breakdown.FromClause?.Clause, Does.Contain("User"));
|
|
}
|
|
|
|
[Test]
|
|
public void Analyze_WhereClause_ExtractsCondition()
|
|
{
|
|
// Arrange
|
|
var query = from user in TestDataContext.Users
|
|
where user.Age > 21
|
|
select user;
|
|
|
|
// Act
|
|
var breakdown = LinqQueryBreakdown.Analyze(query);
|
|
|
|
// Assert
|
|
Assert.That(breakdown.WhereClause?.Clause, Is.Not.Null);
|
|
Assert.That(breakdown.WhereClause?.Clause, Does.Contain("Age"));
|
|
Assert.That(breakdown.WhereClause?.Clause, Does.Contain(">"));
|
|
}
|
|
|
|
[Test]
|
|
public void Analyze_SelectWithProjection_ExtractsSelectedFields()
|
|
{
|
|
// Arrange
|
|
var query = from user in TestDataContext.Users
|
|
select new { user.Id, user.Name };
|
|
|
|
// Act
|
|
var breakdown = LinqQueryBreakdown.Analyze(query);
|
|
|
|
// Assert
|
|
Assert.That(breakdown.SelectClause?.Clause, Is.Not.Null);
|
|
Assert.That(breakdown.SelectClause?.Clause, Does.Contain("Id"));
|
|
Assert.That(breakdown.SelectClause?.Clause, Does.Contain("Name"));
|
|
}
|
|
|
|
[Test]
|
|
public void Analyze_OrderByClause_ExtractsOrdering()
|
|
{
|
|
// Arrange
|
|
var query = from user in TestDataContext.Users
|
|
orderby user.Name
|
|
select user;
|
|
|
|
// Act
|
|
var breakdown = LinqQueryBreakdown.Analyze(query);
|
|
|
|
// Assert
|
|
Assert.That(breakdown.OrderByClause?.Clause, Is.Not.Null);
|
|
Assert.That(breakdown.OrderByClause?.Clause, Does.Contain("Name"));
|
|
}
|
|
|
|
[Test]
|
|
public void Analyze_MethodSyntax_ExtractsCorrectClauses()
|
|
{
|
|
// Arrange
|
|
var query = TestDataContext.Users
|
|
.Where(u => u.Age > 18)
|
|
.OrderBy(u => u.Name)
|
|
.Select(u => new { u.Id, u.Name });
|
|
|
|
// Act
|
|
var breakdown = LinqQueryBreakdown.Analyze(query);
|
|
|
|
// Assert
|
|
Assert.That(breakdown.WhereClause?.Clause, Is.Not.Null);
|
|
Assert.That(breakdown.OrderByClause?.Clause, Is.Not.Null);
|
|
Assert.That(breakdown.SelectClause?.Clause, Is.Not.Null);
|
|
}
|
|
|
|
[Test]
|
|
public void Analyze_MethodSyntax_TracksMethodChain()
|
|
{
|
|
// Arrange
|
|
var query = TestDataContext.Users
|
|
.Where(u => u.Age > 18)
|
|
.OrderBy(u => u.Name)
|
|
.Select(u => u.Name);
|
|
|
|
// Act
|
|
var breakdown = LinqQueryBreakdown.Analyze(query);
|
|
|
|
// Assert
|
|
Assert.That(breakdown.MethodCallChain, Is.Not.Empty);
|
|
Assert.That(breakdown.MethodCallChain, Contains.Item("Where"));
|
|
Assert.That(breakdown.MethodCallChain, Contains.Item("OrderBy"));
|
|
Assert.That(breakdown.MethodCallChain, Contains.Item("Select"));
|
|
}
|
|
|
|
[Test]
|
|
public void GetQuerySummary_ReturnsFormattedString()
|
|
{
|
|
// Arrange
|
|
var query = TestDataContext.Users.Where(u => u.Age > 21);
|
|
var breakdown = LinqQueryBreakdown.Analyze(query);
|
|
|
|
// Act
|
|
var summary = breakdown.GetQuerySummary();
|
|
|
|
// Assert
|
|
Assert.That(summary, Is.Not.Empty);
|
|
Assert.That(summary, Does.Contain("SELECT"));
|
|
Assert.That(summary, Does.Contain("FROM"));
|
|
Assert.That(summary, Does.Contain("WHERE"));
|
|
}
|
|
|
|
[Test]
|
|
public void GetMethodChain_ReturnsMethodSequence()
|
|
{
|
|
// Arrange
|
|
var query = TestDataContext.Users.Where(u => u.Age > 18).OrderBy(u => u.Name);
|
|
var breakdown = LinqQueryBreakdown.Analyze(query);
|
|
|
|
// Act
|
|
var methodChain = breakdown.GetMethodChain();
|
|
|
|
// Assert
|
|
Assert.That(methodChain, Does.Contain("Where"));
|
|
Assert.That(methodChain, Does.Contain("OrderBy"));
|
|
Assert.That(methodChain, Does.Contain("->"));
|
|
}
|
|
|
|
[Test]
|
|
public void TryAnalyze_ValidQuery_ReturnsTrue()
|
|
{
|
|
// Arrange
|
|
var query = TestDataContext.Users.Where(u => u.Age > 21);
|
|
|
|
// Act
|
|
var success = LinqQueryBreakdown.TryAnalyze(query, out var breakdown, out var error);
|
|
|
|
// Assert
|
|
Assert.That(success, Is.True);
|
|
Assert.That(breakdown, Is.Not.Null);
|
|
Assert.That(error, Is.Empty);
|
|
}
|
|
|
|
[Test]
|
|
public void TryAnalyze_NullQuery_ReturnsFalse()
|
|
{
|
|
// Arrange
|
|
IQueryable<User>? query = null;
|
|
|
|
// Act
|
|
var success = LinqQueryBreakdown.TryAnalyze(query!, out var breakdown, out var error);
|
|
|
|
// Assert
|
|
Assert.That(success, Is.False);
|
|
Assert.That(error, Is.Not.Empty);
|
|
}
|
|
|
|
[Test]
|
|
public void Analyze_ComplexQuery_HandlesCombinedClauses()
|
|
{
|
|
// Arrange
|
|
var query = from user in TestDataContext.Users
|
|
where user.Age > 21 && user.IsActive
|
|
orderby user.Name descending
|
|
select new { user.Id, user.Name, user.Email };
|
|
|
|
// Act
|
|
var breakdown = LinqQueryBreakdown.Analyze(query);
|
|
|
|
// Assert
|
|
Assert.That(breakdown.SelectClause?.Clause, Does.Contain("Id"));
|
|
Assert.That(breakdown.SelectClause?.Clause, Does.Contain("Name"));
|
|
Assert.That(breakdown.SelectClause?.Clause, Does.Contain("Email"));
|
|
Assert.That(breakdown.WhereClause?.Clause, Does.Contain("Age"));
|
|
Assert.That(breakdown.WhereClause?.Clause, Does.Contain("IsActive"));
|
|
Assert.That(breakdown.OrderByClause?.Clause, Does.Contain("Name"));
|
|
}
|
|
|
|
#region Round-Trip and Query Modification Tests
|
|
|
|
[Test]
|
|
public void AnalyzeAndModify_AddWhereClause_GeneratesUpdatedSql()
|
|
{
|
|
// Arrange - Analyze an existing LINQ query
|
|
var originalQuery = TestDataContext.Users.Where(u => u.Age > 21);
|
|
var breakdown = LinqQueryBreakdown.Analyze(originalQuery);
|
|
|
|
// Act - Add additional filter using breakdown
|
|
breakdown.AddWhereClause("IsActive = 1", "AND");
|
|
var modifiedSql = breakdown.GetSql();
|
|
|
|
// Assert
|
|
Assert.That(modifiedSql, Does.Contain("Age"));
|
|
Assert.That(modifiedSql, Does.Contain("IsActive"));
|
|
Assert.That(modifiedSql, Does.Contain("AND"));
|
|
}
|
|
|
|
[Test]
|
|
public void AnalyzeAndModify_ChangeSelectClause_GeneratesNewProjection()
|
|
{
|
|
// Arrange - Analyze query with projection
|
|
var originalQuery = TestDataContext.Users.Select(u => new { u.Id, u.Name });
|
|
var breakdown = LinqQueryBreakdown.Analyze(originalQuery);
|
|
|
|
// Act - Modify the SELECT clause
|
|
breakdown.SelectClause = new Strata.SqlTools.SqlBreakdown.Classes.SqlExpressionClause
|
|
{
|
|
Clause = "Id, Name, Email, Age"
|
|
};
|
|
var modifiedSql = breakdown.GetSql();
|
|
|
|
// Assert
|
|
Assert.That(modifiedSql, Does.Contain("Email"));
|
|
Assert.That(modifiedSql, Does.Contain("Age"));
|
|
Assert.That(modifiedSql, Does.Contain("Id"));
|
|
Assert.That(modifiedSql, Does.Contain("Name"));
|
|
}
|
|
|
|
[Test]
|
|
public void AnalyzeAndModify_CloneAndExtend_CreatesIndependentQuery()
|
|
{
|
|
// Arrange - Analyze base query
|
|
var baseQuery = TestDataContext.Users.Where(u => u.Age > 18);
|
|
var baseBreakdown = LinqQueryBreakdown.Analyze(baseQuery);
|
|
|
|
// Act - Clone and extend
|
|
var extendedBreakdown = (LinqQueryBreakdown)baseBreakdown.Clone();
|
|
extendedBreakdown.AddWhereClause("IsActive = 1", "AND");
|
|
extendedBreakdown.OrderByClause = new Strata.SqlTools.SqlBreakdown.Classes.SqlExpressionClause(splitOnComma: true)
|
|
{
|
|
Clause = "Name ASC"
|
|
};
|
|
|
|
// Assert - Original unchanged
|
|
Assert.That(baseBreakdown.OrderByClause?.Clause, Is.Null.Or.Empty);
|
|
|
|
// Extended has new conditions
|
|
var extendedSql = extendedBreakdown.GetSql();
|
|
Assert.That(extendedSql, Does.Contain("IsActive"));
|
|
Assert.That(extendedSql, Does.Contain("ORDER BY"));
|
|
}
|
|
|
|
[Test]
|
|
public void AnalyzeAndCompose_MultipleQueries_CreatesUnionScenario()
|
|
{
|
|
// Arrange - Analyze two different queries
|
|
var activeUsersQuery = TestDataContext.Users.Where(u => u.IsActive);
|
|
var activeBreakdown = LinqQueryBreakdown.Analyze(activeUsersQuery);
|
|
|
|
var recentUsersQuery = TestDataContext.Users.Where(u => u.Age < 25);
|
|
var recentBreakdown = LinqQueryBreakdown.Analyze(recentUsersQuery);
|
|
|
|
// Act - Get SQL for both (could be used in UNION scenario)
|
|
var activeSql = activeBreakdown.GetSql();
|
|
var recentSql = recentBreakdown.GetSql();
|
|
|
|
// Assert - Both queries are valid and different
|
|
Assert.That(activeSql, Does.Contain("IsActive"));
|
|
Assert.That(recentSql, Does.Contain("Age"));
|
|
Assert.That(activeSql, Is.Not.EqualTo(recentSql));
|
|
}
|
|
|
|
[Test]
|
|
public void AnalyzeAndBuildFilter_IncrementallyAddConditions_BuildsComplexFilter()
|
|
{
|
|
// Arrange - Start with simple query
|
|
var query = TestDataContext.Users;
|
|
var breakdown = LinqQueryBreakdown.Analyze(query);
|
|
|
|
// Act - Incrementally add filter conditions (simulating filter builder UI)
|
|
breakdown.AddWhereClause("Age >= 18", "AND");
|
|
breakdown.AddWhereClause("Age <= 65", "AND");
|
|
breakdown.AddWhereClause("IsActive = 1", "AND");
|
|
|
|
var filterSql = breakdown.GetSql();
|
|
|
|
// Assert
|
|
Assert.That(filterSql, Does.Contain("Age >= 18"));
|
|
Assert.That(filterSql, Does.Contain("Age <= 65"));
|
|
Assert.That(filterSql, Does.Contain("IsActive = 1"));
|
|
|
|
// Should have multiple AND conditions
|
|
var andCount = filterSql.Split(separator, StringSplitOptions.None).Length - 1;
|
|
Assert.That(andCount, Is.GreaterThanOrEqualTo(2));
|
|
}
|
|
|
|
[Test]
|
|
public void AnalyzeAndPaginate_AddOrderAndLimits_CreatesPaginatedQuery()
|
|
{
|
|
// Arrange - Analyze base query
|
|
var query = TestDataContext.Users.Where(u => u.IsActive);
|
|
var breakdown = LinqQueryBreakdown.Analyze(query);
|
|
|
|
// Act - Add pagination (ORDER BY required for consistent pagination)
|
|
breakdown.OrderByClause = new Strata.SqlTools.SqlBreakdown.Classes.SqlExpressionClause(splitOnComma: true)
|
|
{
|
|
Clause = "Id ASC"
|
|
};
|
|
|
|
// Add pagination metadata as setup clause (simulating OFFSET/FETCH or similar)
|
|
breakdown.SetupClauses.Add("-- Page 1, PageSize 10");
|
|
|
|
var paginatedSql = breakdown.GetSql();
|
|
|
|
// Assert
|
|
Assert.That(paginatedSql, Does.Contain("ORDER BY"));
|
|
Assert.That(paginatedSql, Does.Contain("Id ASC"));
|
|
Assert.That(breakdown.SetupClauses, Has.Count.EqualTo(1));
|
|
}
|
|
|
|
[Test]
|
|
public void AnalyzeAndGenerateReport_ExtractQueryMetrics_ProvidesAnalytics()
|
|
{
|
|
// Arrange - Complex query to analyze
|
|
var query = TestDataContext.Users
|
|
.Where(u => u.Age > 21)
|
|
.Where(u => u.IsActive)
|
|
.OrderBy(u => u.Name)
|
|
.Select(u => new { u.Id, u.Name, u.Email });
|
|
|
|
var breakdown = LinqQueryBreakdown.Analyze(query);
|
|
|
|
// Act - Extract metrics
|
|
var methodCount = breakdown.MethodCallChain.Count;
|
|
var hasProjection = breakdown.SelectClause?.Clause != "*";
|
|
var hasFilters = !string.IsNullOrEmpty(breakdown.WhereClause?.Clause);
|
|
var hasOrdering = !string.IsNullOrEmpty(breakdown.OrderByClause?.Clause);
|
|
var summary = breakdown.GetQuerySummary();
|
|
|
|
// Assert - Verify analytics
|
|
Assert.That(methodCount, Is.GreaterThan(0));
|
|
Assert.That(hasProjection, Is.True);
|
|
Assert.That(hasFilters, Is.True);
|
|
Assert.That(hasOrdering, Is.True);
|
|
Assert.That(summary, Does.Contain("SELECT"));
|
|
Assert.That(summary, Does.Contain("WHERE"));
|
|
Assert.That(summary, Does.Contain("ORDER BY"));
|
|
}
|
|
|
|
[Test]
|
|
public void AnalyzeAndOptimize_RemoveSelectStar_ImprovedProjection()
|
|
{
|
|
// Arrange - Analyze query with SELECT *
|
|
var query = TestDataContext.Users.Where(u => u.IsActive);
|
|
var breakdown = LinqQueryBreakdown.Analyze(query);
|
|
|
|
// Verify it initially has SELECT *
|
|
var originalSql = breakdown.GetSql();
|
|
Assert.That(originalSql, Does.Contain("*").Or.Not.Contain("Id, Name"));
|
|
|
|
// Act - Optimize by specifying only needed columns
|
|
breakdown.SelectClause = new Strata.SqlTools.SqlBreakdown.Classes.SqlExpressionClause
|
|
{
|
|
Clause = "Id, Name, Email"
|
|
};
|
|
|
|
var optimizedSql = breakdown.GetSql();
|
|
|
|
// Assert
|
|
Assert.That(optimizedSql, Does.Contain("Id"));
|
|
Assert.That(optimizedSql, Does.Contain("Name"));
|
|
Assert.That(optimizedSql, Does.Contain("Email"));
|
|
Assert.That(optimizedSql, Does.Not.Contain("SELECT *"));
|
|
}
|
|
|
|
[Test]
|
|
public void AnalyzeMultipleQueries_CompareAndMerge_CreatesCompositeQuery()
|
|
{
|
|
// Arrange - Analyze two related queries
|
|
var usersQuery = TestDataContext.Users.Where(u => u.Age > 21);
|
|
var usersBreakdown = LinqQueryBreakdown.Analyze(usersQuery);
|
|
|
|
var activeQuery = TestDataContext.Users.Where(u => u.IsActive);
|
|
var activeBreakdown = LinqQueryBreakdown.Analyze(activeQuery);
|
|
|
|
// Act - Merge conditions from both queries
|
|
var mergedBreakdown = (LinqQueryBreakdown)usersBreakdown.Clone();
|
|
|
|
// Extract WHERE clause from activeBreakdown and add to merged
|
|
if (!string.IsNullOrEmpty(activeBreakdown.WhereClause?.Clause))
|
|
{
|
|
mergedBreakdown.AddWhereClause(activeBreakdown.WhereClause.Clause, "AND");
|
|
}
|
|
|
|
var mergedSql = mergedBreakdown.GetSql();
|
|
|
|
// Assert - Merged query has both conditions
|
|
Assert.That(mergedSql, Does.Contain("Age"));
|
|
Assert.That(mergedSql, Does.Contain("IsActive"));
|
|
}
|
|
|
|
[Test]
|
|
public void AnalyzeAndDocument_GenerateQueryDocumentation_CreatesReadableOutput()
|
|
{
|
|
// Arrange - Analyze a business query
|
|
var query = TestDataContext.Orders
|
|
.Where(o => o.Amount > 1000)
|
|
.Where(o => o.OrderDate > DateTime.Now.AddDays(-30))
|
|
.OrderBy(o => o.OrderDate);
|
|
|
|
var breakdown = LinqQueryBreakdown.Analyze(query);
|
|
|
|
// Act - Generate documentation
|
|
var documentation = new System.Text.StringBuilder();
|
|
documentation.AppendLine("Query: High-Value Recent Orders");
|
|
documentation.AppendLine($"Entity: {breakdown.EntityType}");
|
|
documentation.AppendLine($"Method Chain: {breakdown.GetMethodChain()}");
|
|
documentation.AppendLine($"Summary: {breakdown.GetQuerySummary()}");
|
|
documentation.AppendLine();
|
|
documentation.AppendLine("Generated SQL:");
|
|
documentation.AppendLine(breakdown.GetSql());
|
|
|
|
var doc = documentation.ToString();
|
|
|
|
// Assert - Documentation contains all key information
|
|
Assert.That(doc, Does.Contain("Query:"));
|
|
Assert.That(doc, Does.Contain("Entity:"));
|
|
Assert.That(doc, Does.Contain("Method Chain:"));
|
|
Assert.That(doc, Does.Contain("Summary:"));
|
|
Assert.That(doc, Does.Contain("Generated SQL:"));
|
|
Assert.That(doc, Does.Contain("Amount"));
|
|
Assert.That(doc, Does.Contain("OrderDate"));
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
// Test data context and entities
|
|
public class TestDataContext
|
|
{
|
|
public static IQueryable<User> Users => new List<User>().AsQueryable();
|
|
public static IQueryable<Order> Orders => new List<Order>().AsQueryable();
|
|
}
|
|
|
|
public class User
|
|
{
|
|
public int Id { get; set; }
|
|
public string Name { get; set; } = string.Empty;
|
|
public string Email { get; set; } = string.Empty;
|
|
public int Age { get; set; }
|
|
public bool IsActive { get; set; }
|
|
}
|
|
|
|
public class Order
|
|
{
|
|
public int Id { get; set; }
|
|
public int UserId { get; set; }
|
|
public decimal Amount { get; set; }
|
|
public DateTime OrderDate { get; set; }
|
|
}
|
|
|
|
#region GetQuery Tests
|
|
|
|
public class GetQueryTests
|
|
{
|
|
private TestDataContext _context = null!;
|
|
|
|
[SetUp]
|
|
public void Setup()
|
|
{
|
|
_context = new TestDataContext();
|
|
}
|
|
|
|
[Test]
|
|
public void GetQuery_LinqBreakdown_ReturnsNullByDefault()
|
|
{
|
|
// Arrange
|
|
var query = TestDataContext.Users.Where(u => u.Age > 18);
|
|
var breakdown = LinqQueryBreakdown.Analyze(query);
|
|
|
|
// Act - GetQuery returns null because LinqQueryBreakdown needs the original provider
|
|
var result = breakdown.GetQuery<User>();
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Null);
|
|
}
|
|
|
|
[Test]
|
|
public void GetQuery_SqlServerQueryBreakdown_ReturnsNull()
|
|
{
|
|
// Arrange
|
|
var breakdown = new QueryBreakdown("Id, Name", "Users", "Age > 18");
|
|
|
|
// Act
|
|
var result = breakdown.GetQuery<User>();
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Null);
|
|
}
|
|
|
|
[Test]
|
|
public void GetQuery_MultipleBreakdownTypes_AllReturnNull()
|
|
{
|
|
// Arrange
|
|
var linqBreakdown = LinqQueryBreakdown.Analyze(TestDataContext.Users);
|
|
var sqlBreakdown = new QueryBreakdown("*", "Users");
|
|
|
|
// Act
|
|
var linqResult = linqBreakdown.GetQuery<User>();
|
|
var sqlResult = sqlBreakdown.GetQuery<User>();
|
|
|
|
// Assert
|
|
Assert.That(linqResult, Is.Null);
|
|
Assert.That(sqlResult, Is.Null);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region INSERT Analysis Tests
|
|
|
|
[Test]
|
|
public void AnalyzeInsert_SingleEntity_CreatesInsertBreakdown()
|
|
{
|
|
// Arrange
|
|
var entity = new User { Id = 1, Name = "John Doe", Email = "john@example.com", Age = 30 };
|
|
|
|
// Act
|
|
var breakdown = LinqQueryBreakdown.AnalyzeInsert(entity);
|
|
|
|
// Assert
|
|
Assert.That(breakdown, Is.Not.Null);
|
|
Assert.That(breakdown.TableName.Clause, Is.EqualTo("User"));
|
|
Assert.That(breakdown.InsertIntoClause.Clause, Is.Not.Empty);
|
|
Assert.That(breakdown.ValuesClause.Clause, Is.Not.Empty);
|
|
Assert.That(breakdown.InsertIntoClause.Clause, Does.Contain("Id"));
|
|
Assert.That(breakdown.InsertIntoClause.Clause, Does.Contain("Name"));
|
|
}
|
|
|
|
[Test]
|
|
public void AnalyzeInsertRange_MultipleEntities_CreatesInsertBreakdown()
|
|
{
|
|
// Arrange
|
|
var entities = new List<User>
|
|
{
|
|
new User { Id = 1, Name = "John Doe", Email = "john@example.com", Age = 30 },
|
|
new User { Id = 2, Name = "Jane Smith", Email = "jane@example.com", Age = 28 }
|
|
};
|
|
|
|
// Act
|
|
var breakdown = LinqQueryBreakdown.AnalyzeInsertRange(entities);
|
|
|
|
// Assert
|
|
Assert.That(breakdown, Is.Not.Null);
|
|
Assert.That(breakdown.TableName.Clause, Is.EqualTo("User"));
|
|
Assert.That(breakdown.InsertIntoClause.Clause, Is.Not.Empty);
|
|
Assert.That(breakdown.ValuesClause.Clause, Does.Contain("("));
|
|
}
|
|
|
|
[Test]
|
|
public void AnalyzeInsert_NullEntity_ThrowsArgumentNullException()
|
|
{
|
|
// Act & Assert
|
|
Assert.Throws<ArgumentNullException>(() => LinqQueryBreakdown.AnalyzeInsert<User>(null!));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region DELETE Analysis Tests
|
|
|
|
[Test]
|
|
public void AnalyzeDelete_WithFilterExpression_CreatesDeleteBreakdown()
|
|
{
|
|
// Arrange
|
|
var filterExpression = System.Linq.Expressions.Expression.Lambda<System.Func<User, bool>>(
|
|
System.Linq.Expressions.Expression.Constant(true),
|
|
System.Linq.Expressions.Expression.Parameter(typeof(User), "u")
|
|
);
|
|
|
|
// Act
|
|
var breakdown = LinqQueryBreakdown.AnalyzeDelete(filterExpression);
|
|
|
|
// Assert
|
|
Assert.That(breakdown, Is.Not.Null);
|
|
Assert.That(breakdown.FromClause.Clause, Is.EqualTo("User"));
|
|
}
|
|
|
|
[Test]
|
|
public void AnalyzeDelete_NullFilter_ThrowsArgumentNullException()
|
|
{
|
|
// Act & Assert
|
|
Assert.Throws<ArgumentNullException>(() =>
|
|
LinqQueryBreakdown.AnalyzeDelete<User>(null!));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region UPDATE Analysis Tests
|
|
|
|
[Test]
|
|
public void AnalyzeUpdate_WithFilterAndUpdateExpressions_CreatesUpdateBreakdown()
|
|
{
|
|
// Arrange
|
|
var filterExpr = System.Linq.Expressions.Expression.Lambda<System.Func<User, bool>>(
|
|
System.Linq.Expressions.Expression.Constant(true),
|
|
System.Linq.Expressions.Expression.Parameter(typeof(User), "u")
|
|
);
|
|
|
|
var updateExpr = System.Linq.Expressions.Expression.Lambda<System.Func<User, User>>(
|
|
System.Linq.Expressions.Expression.Constant(new User()),
|
|
System.Linq.Expressions.Expression.Parameter(typeof(User), "u")
|
|
);
|
|
|
|
// Act
|
|
var breakdown = LinqQueryBreakdown.AnalyzeUpdate(filterExpr, updateExpr);
|
|
|
|
// Assert
|
|
Assert.That(breakdown, Is.Not.Null);
|
|
Assert.That(breakdown.TableName.Clause, Is.EqualTo("User"));
|
|
}
|
|
|
|
[Test]
|
|
public void AnalyzeUpdate_NullFilter_ThrowsArgumentNullException()
|
|
{
|
|
// Arrange
|
|
var updateExpr = System.Linq.Expressions.Expression.Lambda<System.Func<User, User>>(
|
|
System.Linq.Expressions.Expression.Constant(new User()),
|
|
System.Linq.Expressions.Expression.Parameter(typeof(User), "u")
|
|
);
|
|
|
|
// Act & Assert
|
|
Assert.Throws<ArgumentNullException>(() =>
|
|
LinqQueryBreakdown.AnalyzeUpdate<User>(null!, updateExpr));
|
|
}
|
|
|
|
[Test]
|
|
public void AnalyzeUpdate_NullUpdateExpression_ThrowsArgumentNullException()
|
|
{
|
|
// Arrange
|
|
var filterExpr = System.Linq.Expressions.Expression.Lambda<System.Func<User, bool>>(
|
|
System.Linq.Expressions.Expression.Constant(true),
|
|
System.Linq.Expressions.Expression.Parameter(typeof(User), "u")
|
|
);
|
|
|
|
// Act & Assert
|
|
Assert.Throws<ArgumentNullException>(() =>
|
|
LinqQueryBreakdown.AnalyzeUpdate<User>(filterExpr, null!));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region PROCEDURE Analysis Tests
|
|
|
|
[Test]
|
|
public void AnalyzeProcedure_WithName_CreatesProcedureBreakdown()
|
|
{
|
|
// Arrange
|
|
const string procName = "sp_GetUsers";
|
|
|
|
// Act
|
|
var breakdown = LinqQueryBreakdown.AnalyzeProcedure(procName);
|
|
|
|
// Assert
|
|
Assert.That(breakdown, Is.Not.Null);
|
|
Assert.That(breakdown.ProcedureName.Clause, Is.EqualTo(procName));
|
|
}
|
|
|
|
[Test]
|
|
public void AnalyzeProcedure_WithParameters_CreatesProcedureBreakdownWithParams()
|
|
{
|
|
// Arrange
|
|
const string procName = "sp_GetUsersByAge";
|
|
var parameters = new object[] { 18, 65 };
|
|
|
|
// Act
|
|
var breakdown = LinqQueryBreakdown.AnalyzeProcedure(procName, parameters);
|
|
|
|
// Assert
|
|
Assert.That(breakdown, Is.Not.Null);
|
|
Assert.That(breakdown.ProcedureName.Clause, Is.EqualTo(procName));
|
|
Assert.That(breakdown.Parameters, Is.Not.Empty);
|
|
Assert.That(breakdown.Parameters, Has.Count.EqualTo(2));
|
|
}
|
|
|
|
[Test]
|
|
public void AnalyzeProcedure_NullOrEmptyName_ThrowsArgumentException()
|
|
{
|
|
// Act & Assert
|
|
Assert.Throws<ArgumentException>(() => LinqQueryBreakdown.AnalyzeProcedure(null!));
|
|
Assert.Throws<ArgumentException>(() => LinqQueryBreakdown.AnalyzeProcedure(""));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region TRACE Analysis Tests
|
|
|
|
[Test]
|
|
public void AnalyzeTrace_WithValidQuery_ReturnsTraceString()
|
|
{
|
|
// Arrange
|
|
var query = TestDataContext.Users.Where(u => u.Age > 18);
|
|
|
|
// Act
|
|
var trace = LinqQueryBreakdown.AnalyzeTrace(query);
|
|
|
|
// Assert
|
|
Assert.That(trace, Is.Not.Null.And.Not.Empty);
|
|
Assert.That(trace, Does.Contain("User"));
|
|
Assert.That(trace, Does.Contain("Query Provider"));
|
|
Assert.That(trace, Does.Contain("Timestamp"));
|
|
}
|
|
|
|
[Test]
|
|
public void AnalyzeTrace_WithExecutionContext_IncludesContextInTrace()
|
|
{
|
|
// Arrange
|
|
var query = TestDataContext.Users;
|
|
const string context = "Initial Load";
|
|
|
|
// Act
|
|
var trace = LinqQueryBreakdown.AnalyzeTrace(query, context);
|
|
|
|
// Assert
|
|
Assert.That(trace, Is.Not.Null);
|
|
Assert.That(trace, Does.Contain(context));
|
|
Assert.That(trace, Does.Contain("Execution Context"));
|
|
}
|
|
|
|
[Test]
|
|
public void AnalyzeTrace_NullQuery_ThrowsArgumentNullException()
|
|
{
|
|
// Act & Assert
|
|
Assert.Throws<ArgumentNullException>(() =>
|
|
LinqQueryBreakdown.AnalyzeTrace<User>(null!));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Conversion Tests
|
|
|
|
[Test]
|
|
public void ConvertToSqlServerBreakdown_CopiesAllClauses()
|
|
{
|
|
// Arrange
|
|
var query = TestDataContext.Users.Where(u => u.Age > 21).OrderBy(u => u.Name);
|
|
var breakdown = LinqQueryBreakdown.Analyze(query);
|
|
|
|
// Act
|
|
var sqlServerBreakdown = breakdown.ConvertToSqlServerBreakdown();
|
|
|
|
// Assert
|
|
Assert.That(sqlServerBreakdown, Is.Not.Null);
|
|
Assert.That(sqlServerBreakdown.SelectClause?.Clause, Is.EqualTo(breakdown.SelectClause?.Clause));
|
|
Assert.That(sqlServerBreakdown.FromClause?.Clause, Is.EqualTo(breakdown.FromClause?.Clause));
|
|
Assert.That(sqlServerBreakdown.WhereClause?.Clause, Is.EqualTo(breakdown.WhereClause?.Clause));
|
|
Assert.That(sqlServerBreakdown.OrderByClause?.Clause, Is.EqualTo(breakdown.OrderByClause?.Clause));
|
|
}
|
|
|
|
[Test]
|
|
public void ConvertToPostgreSqlBreakdown_CopiesAllClauses()
|
|
{
|
|
// Arrange
|
|
var query = TestDataContext.Users
|
|
.Where(u => u.IsActive)
|
|
.OrderBy(u => u.Name)
|
|
.Select(u => new { u.Id, u.Name });
|
|
var breakdown = LinqQueryBreakdown.Analyze(query);
|
|
|
|
// Act
|
|
var postgresBreakdown = breakdown.ConvertToPostgreSqlBreakdown();
|
|
|
|
// Assert
|
|
Assert.That(postgresBreakdown, Is.Not.Null);
|
|
Assert.That(postgresBreakdown.SelectClause?.Clause, Is.EqualTo(breakdown.SelectClause?.Clause));
|
|
Assert.That(postgresBreakdown.FromClause?.Clause, Is.EqualTo(breakdown.FromClause?.Clause));
|
|
Assert.That(postgresBreakdown.WhereClause?.Clause, Is.EqualTo(breakdown.WhereClause?.Clause));
|
|
Assert.That(postgresBreakdown.OrderByClause?.Clause, Is.EqualTo(breakdown.OrderByClause?.Clause));
|
|
}
|
|
|
|
[Test]
|
|
public void ConvertToSnowflakeBreakdown_CopiesAllClauses()
|
|
{
|
|
// Arrange
|
|
var query = TestDataContext.Users.Where(u => u.Age > 21).OrderBy(u => u.Name);
|
|
var breakdown = LinqQueryBreakdown.Analyze(query);
|
|
|
|
// Act
|
|
var snowflakeBreakdown = breakdown.ConvertToSnowflakeBreakdown();
|
|
|
|
// Assert
|
|
Assert.That(snowflakeBreakdown, Is.Not.Null);
|
|
Assert.That(snowflakeBreakdown.SelectClause?.Clause, Is.EqualTo(breakdown.SelectClause?.Clause));
|
|
Assert.That(snowflakeBreakdown.FromClause?.Clause, Is.EqualTo(breakdown.FromClause?.Clause));
|
|
Assert.That(snowflakeBreakdown.WhereClause?.Clause, Is.EqualTo(breakdown.WhereClause?.Clause));
|
|
Assert.That(snowflakeBreakdown.OrderByClause?.Clause, Is.EqualTo(breakdown.OrderByClause?.Clause));
|
|
}
|
|
|
|
[Test]
|
|
public void ConvertToSqlServerBreakdown_WithComments_PreservesComments()
|
|
{
|
|
// Arrange
|
|
var breakdown = new LinqQueryBreakdown("Id, Name", "Users", "Age > 21");
|
|
breakdown.SelectClause.Comment = "Select user columns";
|
|
breakdown.WhereClause.Comment = "Filter adult users";
|
|
|
|
// Act
|
|
var sqlServerBreakdown = breakdown.ConvertToSqlServerBreakdown();
|
|
|
|
// Assert
|
|
Assert.That(sqlServerBreakdown.SelectClause?.Comment, Is.EqualTo("Select user columns"));
|
|
Assert.That(sqlServerBreakdown.WhereClause?.Comment, Is.EqualTo("Filter adult users"));
|
|
}
|
|
|
|
[Test]
|
|
public void ConvertToPostgreSqlBreakdown_WithComplexQuery_CopieAllClauses()
|
|
{
|
|
// Arrange
|
|
var query = TestDataContext.Users
|
|
.Where(u => u.IsActive && u.Age >= 18)
|
|
.OrderByDescending(u => u.Age);
|
|
|
|
var breakdown = LinqQueryBreakdown.Analyze(query);
|
|
|
|
// Act
|
|
var postgresBreakdown = breakdown.ConvertToPostgreSqlBreakdown();
|
|
|
|
// Assert
|
|
Assert.That(postgresBreakdown.SelectClause?.Clause, Is.Not.Null);
|
|
Assert.That(postgresBreakdown.FromClause?.Clause, Is.Not.Null);
|
|
Assert.That(postgresBreakdown.WhereClause?.Clause, Is.Not.Null);
|
|
Assert.That(postgresBreakdown.GroupByClause?.Clause, Is.Not.Null);
|
|
Assert.That(postgresBreakdown.OrderByClause?.Clause, Is.Not.Null);
|
|
}
|
|
|
|
[Test]
|
|
public void ConvertToSnowflakeBreakdown_PreservesAllClauseInformation()
|
|
{
|
|
// Arrange
|
|
var breakdown = LinqQueryBreakdown.Analyze(TestDataContext.Users.OrderBy(u => u.Name));
|
|
|
|
// Act
|
|
var snowflakeBreakdown = breakdown.ConvertToSnowflakeBreakdown();
|
|
|
|
// Assert
|
|
Assert.That(snowflakeBreakdown.FromClause?.Clause, Is.EqualTo(breakdown.FromClause?.Clause));
|
|
Assert.That(snowflakeBreakdown.OrderByClause?.Clause, Is.EqualTo(breakdown.OrderByClause?.Clause));
|
|
}
|
|
|
|
[Test]
|
|
public void ConvertToSqlServerBreakdown_InstancesAreIndependent()
|
|
{
|
|
// Arrange
|
|
var breakdown = LinqQueryBreakdown.Analyze(TestDataContext.Users.Where(u => u.Age > 21));
|
|
var sqlServerBreakdown = breakdown.ConvertToSqlServerBreakdown();
|
|
|
|
// Act - Modify the SQL Server breakdown
|
|
sqlServerBreakdown.SelectClause.Clause = "Id, Name, Email";
|
|
|
|
// Assert - Original should be unchanged
|
|
Assert.That(breakdown.SelectClause?.Clause, Is.Not.EqualTo(sqlServerBreakdown.SelectClause?.Clause));
|
|
}
|
|
|
|
[Test]
|
|
public void ConvertChainMultipleTimes_EachConversionIndependent()
|
|
{
|
|
// Arrange
|
|
var breakdown = LinqQueryBreakdown.Analyze(TestDataContext.Users.OrderBy(u => u.Name));
|
|
|
|
// Act
|
|
var sqlServer1 = breakdown.ConvertToSqlServerBreakdown();
|
|
var sqlServer2 = breakdown.ConvertToSqlServerBreakdown();
|
|
|
|
// Assert - Both should have same data but be different instances
|
|
Assert.That(sqlServer1, Is.Not.SameAs(sqlServer2));
|
|
Assert.That(sqlServer1.OrderByClause?.Clause, Is.EqualTo(sqlServer2.OrderByClause?.Clause));
|
|
}
|
|
|
|
#endregion
|
|
}
|