Files
sql-utilities/tests/Strata.SqlTools.LinqToSql.Tests/LinqQueryBreakdownTests.cs
T
Thom LambandClaude Opus 4.7 f5d539b906 chore(sonar): hoist constant array literals to static readonly fields (CA1861)
Applied via `dotnet format analyzers --diagnostics CA1861 --severity info`,
plus manual cleanup:

- Renamed two cryptic fixer-generated field names:
  - QueryBreakdownCollection.stringArray -> SnowflakeFunctionNames (and
    inlined the now-redundant local alias)
  - ExpressionObjectTests.arg2 -> NotInValues
- Deduped three identical `separator = ['\r','\n']` fields the fixer
  emitted in the same test class (kept the first declaration; the other
  two test methods now reuse it).

8 files touched.

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

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.Count, Is.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
}