chore: initial git load of code space
This commit is contained in:
@@ -0,0 +1,584 @@
|
||||
using NUnit.Framework;
|
||||
using Strata.SqlTools.Builders.LinqToSql;
|
||||
using Strata.SqlTools.Validators.LinqToSql;
|
||||
|
||||
namespace Strata.SqlTools.LinqToSql.Tests;
|
||||
|
||||
[TestFixture]
|
||||
public class QueryValidatorTests
|
||||
{
|
||||
#region Basic Validation Tests
|
||||
|
||||
[Test]
|
||||
public void Validate_WithValidQuery_HasNoErrors()
|
||||
{
|
||||
// Arrange
|
||||
var breakdown = LinqQueryBreakdownBuilder
|
||||
.Create()
|
||||
.SelectColumns("Id", "Name")
|
||||
.FromTable("Users")
|
||||
.Where("IsActive = true")
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var validator = QueryValidator.ValidateQuery(breakdown);
|
||||
|
||||
// Assert
|
||||
Assert.That(validator.HasErrors, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Validate_WithNull_ThrowsArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
var validator = QueryValidator.Create();
|
||||
|
||||
// Act & Assert
|
||||
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
Assert.Throws<ArgumentNullException>(() => validator.Validate(null));
|
||||
#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Validate_CanBeCalledMultipleTimes()
|
||||
{
|
||||
// Arrange
|
||||
var validator = QueryValidator.Create();
|
||||
var breakdown1 = LinqQueryBreakdownBuilder
|
||||
.Create()
|
||||
.SelectAll()
|
||||
.FromTable("Users")
|
||||
.Build();
|
||||
var breakdown2 = LinqQueryBreakdownBuilder
|
||||
.Create()
|
||||
.SelectAll()
|
||||
.FromTable("Orders")
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
validator.Validate(breakdown1);
|
||||
var count1 = validator.Issues.Count;
|
||||
validator.Clear();
|
||||
validator.Validate(breakdown2);
|
||||
var count2 = validator.Issues.Count;
|
||||
|
||||
// Assert
|
||||
Assert.That(count1, Is.GreaterThan(0));
|
||||
Assert.That(count2, Is.GreaterThan(0));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region SELECT Clause Validation Tests
|
||||
|
||||
[Test]
|
||||
public void Validate_SelectStarQuery_HasSelectAllColumnsWarning()
|
||||
{
|
||||
// Arrange
|
||||
var breakdown = LinqQueryBreakdownBuilder
|
||||
.Create()
|
||||
.SelectAll()
|
||||
.FromTable("Users")
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var validator = QueryValidator.ValidateQuery(breakdown);
|
||||
|
||||
// Assert
|
||||
var issue = validator.Issues.FirstOrDefault(i => i.Code == "SELECT_ALL_COLUMNS");
|
||||
Assert.That(issue, Is.Not.Null);
|
||||
Assert.That(issue!.Severity, Is.EqualTo(ValidationSeverity.Warning));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Validate_SelectManyColumns_HasSelectTooManyWarning()
|
||||
{
|
||||
// Arrange
|
||||
var columns = Enumerable.Range(1, 25).Select(i => $"Column{i}").ToArray();
|
||||
var breakdown = LinqQueryBreakdownBuilder
|
||||
.Create()
|
||||
.SelectColumns(columns)
|
||||
.FromTable("Users")
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var validator = QueryValidator.ValidateQuery(breakdown);
|
||||
|
||||
// Assert
|
||||
var issue = validator.Issues.FirstOrDefault(i => i.Code == "SELECT_TOO_MANY");
|
||||
Assert.That(issue, Is.Not.Null);
|
||||
Assert.That(issue!.Severity, Is.EqualTo(ValidationSeverity.Warning));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Validate_MissingSelectClause_HasSelectMissingError()
|
||||
{
|
||||
// Arrange
|
||||
var breakdown = LinqQueryBreakdownBuilder
|
||||
.Create()
|
||||
.SelectAll()
|
||||
.FromTable("Users")
|
||||
.Build();
|
||||
breakdown.SelectClause.Clause = string.Empty;
|
||||
|
||||
// Act
|
||||
var validator = QueryValidator.ValidateQuery(breakdown);
|
||||
|
||||
// Assert
|
||||
Assert.That(validator.HasErrors, Is.True);
|
||||
var issue = validator.Issues.FirstOrDefault(i => i.Code == "SELECT_MISSING");
|
||||
Assert.That(issue, Is.Not.Null);
|
||||
Assert.That(issue!.Severity, Is.EqualTo(ValidationSeverity.Error));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region FROM Clause Validation Tests
|
||||
|
||||
[Test]
|
||||
public void Validate_MissingFromClause_HasFromMissingError()
|
||||
{
|
||||
// Arrange
|
||||
var breakdown = LinqQueryBreakdownBuilder
|
||||
.Create()
|
||||
.SelectAll()
|
||||
.FromTable("Users")
|
||||
.Build();
|
||||
breakdown.FromClause.Clause = string.Empty;
|
||||
|
||||
// Act
|
||||
var validator = QueryValidator.ValidateQuery(breakdown);
|
||||
|
||||
// Assert
|
||||
Assert.That(validator.HasErrors, Is.True);
|
||||
var issue = validator.Issues.FirstOrDefault(i => i.Code == "FROM_MISSING");
|
||||
Assert.That(issue, Is.Not.Null);
|
||||
Assert.That(issue!.Severity, Is.EqualTo(ValidationSeverity.Error));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GROUP BY + HAVING Validation Tests
|
||||
|
||||
[Test]
|
||||
public void Validate_HavingWithoutGroupBy_HasHavingWithoutGroupByError()
|
||||
{
|
||||
// Arrange
|
||||
var breakdown = LinqQueryBreakdownBuilder
|
||||
.Create()
|
||||
.SelectAll()
|
||||
.FromTable("Users")
|
||||
.Having("COUNT(*) > 5")
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var validator = QueryValidator.ValidateQuery(breakdown);
|
||||
|
||||
// Assert
|
||||
Assert.That(validator.HasErrors, Is.True);
|
||||
var issue = validator.Issues.FirstOrDefault(i => i.Code == "HAVING_WITHOUT_GROUPBY");
|
||||
Assert.That(issue, Is.Not.Null);
|
||||
Assert.That(issue!.Severity, Is.EqualTo(ValidationSeverity.Error));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Validate_GroupByWithHaving_HasNoGroupByError()
|
||||
{
|
||||
// Arrange
|
||||
var breakdown = LinqQueryBreakdownBuilder
|
||||
.Create()
|
||||
.SelectColumns("Department", "COUNT(*)")
|
||||
.FromTable("Employees")
|
||||
.GroupBy("Department")
|
||||
.Having("COUNT(*) > 5")
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var validator = QueryValidator.ValidateQuery(breakdown);
|
||||
|
||||
// Assert
|
||||
var issue = validator.Issues.FirstOrDefault(i => i.Code == "HAVING_WITHOUT_GROUPBY");
|
||||
Assert.That(issue, Is.Null);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region WHERE Clause Validation Tests
|
||||
|
||||
[Test]
|
||||
public void Validate_NoWhereClause_HasNoWhereClauseInfo()
|
||||
{
|
||||
// Arrange
|
||||
var breakdown = LinqQueryBreakdownBuilder
|
||||
.Create()
|
||||
.SelectAll()
|
||||
.FromTable("Users")
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var validator = QueryValidator.ValidateQuery(breakdown);
|
||||
|
||||
// Assert
|
||||
var issue = validator.Issues.FirstOrDefault(i => i.Code == "NO_WHERE_CLAUSE");
|
||||
Assert.That(issue, Is.Not.Null);
|
||||
Assert.That(issue!.Severity, Is.EqualTo(ValidationSeverity.Warning));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Validate_WithWhereClause_NoWhereWarning()
|
||||
{
|
||||
// Arrange
|
||||
var breakdown = LinqQueryBreakdownBuilder
|
||||
.Create()
|
||||
.SelectAll()
|
||||
.FromTable("Users")
|
||||
.Where("IsActive = true")
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var validator = QueryValidator.ValidateQuery(breakdown);
|
||||
|
||||
// Assert
|
||||
var issue = validator.Issues.FirstOrDefault(i => i.Code == "NO_WHERE_CLAUSE");
|
||||
Assert.That(issue, Is.Null);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ORDER BY Validation Tests
|
||||
|
||||
[Test]
|
||||
public void Validate_NoOrderByOrGroupBy_HasNoOrderByInfo()
|
||||
{
|
||||
// Arrange
|
||||
var breakdown = LinqQueryBreakdownBuilder
|
||||
.Create()
|
||||
.SelectAll()
|
||||
.FromTable("Users")
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var validator = QueryValidator.ValidateQuery(breakdown);
|
||||
|
||||
// Assert
|
||||
var issue = validator.Issues.FirstOrDefault(i => i.Code == "NO_ORDER_BY");
|
||||
Assert.That(issue, Is.Not.Null);
|
||||
Assert.That(issue!.Severity, Is.EqualTo(ValidationSeverity.Info));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Validate_WithOrderBy_NoOrderByInfo()
|
||||
{
|
||||
// Arrange
|
||||
var breakdown = LinqQueryBreakdownBuilder
|
||||
.Create()
|
||||
.SelectAll()
|
||||
.FromTable("Users")
|
||||
.OrderByAscending("Name")
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var validator = QueryValidator.ValidateQuery(breakdown);
|
||||
|
||||
// Assert
|
||||
var issue = validator.Issues.FirstOrDefault(i => i.Code == "NO_ORDER_BY");
|
||||
Assert.That(issue, Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Validate_WithGroupBy_NoOrderByInfo()
|
||||
{
|
||||
// Arrange
|
||||
var breakdown = LinqQueryBreakdownBuilder
|
||||
.Create()
|
||||
.SelectColumns("Department", "COUNT(*)")
|
||||
.FromTable("Employees")
|
||||
.GroupBy("Department")
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var validator = QueryValidator.ValidateQuery(breakdown);
|
||||
|
||||
// Assert
|
||||
var issue = validator.Issues.FirstOrDefault(i => i.Code == "NO_ORDER_BY");
|
||||
Assert.That(issue, Is.Null);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Issue Filtering Tests
|
||||
|
||||
[Test]
|
||||
public void GetIssuesBySeverity_FiltersCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var breakdown = LinqQueryBreakdownBuilder
|
||||
.Create()
|
||||
.SelectAll()
|
||||
.FromTable("Users")
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var validator = QueryValidator.ValidateQuery(breakdown);
|
||||
var warnings = validator.GetIssuesBySeverity(ValidationSeverity.Warning);
|
||||
var infos = validator.GetIssuesBySeverity(ValidationSeverity.Info);
|
||||
var errors = validator.GetIssuesBySeverity(ValidationSeverity.Error);
|
||||
|
||||
// Assert
|
||||
Assert.That(warnings, Is.Not.Empty);
|
||||
Assert.That(infos, Is.Not.Empty);
|
||||
Assert.That(errors, Is.Empty);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Custom Issue Tests
|
||||
|
||||
[Test]
|
||||
public void AddIssue_AddsCustomIssue()
|
||||
{
|
||||
// Arrange
|
||||
var validator = QueryValidator.Create();
|
||||
|
||||
// Act
|
||||
validator.AddIssue(
|
||||
ValidationSeverity.Error,
|
||||
"CUSTOM_001",
|
||||
"Custom validation error",
|
||||
"This is a custom issue");
|
||||
|
||||
// Assert
|
||||
Assert.That(validator.Issues.Count, Is.EqualTo(1));
|
||||
Assert.That(validator.Issues[0].Code, Is.EqualTo("CUSTOM_001"));
|
||||
Assert.That(validator.HasErrors, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddIssue_SupportsMethodChaining()
|
||||
{
|
||||
// Arrange
|
||||
var validator = QueryValidator.Create();
|
||||
|
||||
// Act
|
||||
var result = validator
|
||||
.AddIssue(ValidationSeverity.Error, "ERR_001", "Error 1")
|
||||
.AddIssue(ValidationSeverity.Warning, "WARN_001", "Warning 1")
|
||||
.AddIssue(ValidationSeverity.Info, "INFO_001", "Info 1");
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.SameAs(validator));
|
||||
Assert.That(validator.Issues.Count, Is.EqualTo(3));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Clear Tests
|
||||
|
||||
[Test]
|
||||
public void Clear_RemovesAllIssues()
|
||||
{
|
||||
// Arrange
|
||||
var validator = QueryValidator.Create();
|
||||
validator.AddIssue(ValidationSeverity.Error, "ERR", "Error");
|
||||
validator.AddIssue(ValidationSeverity.Warning, "WARN", "Warning");
|
||||
|
||||
// Act
|
||||
validator.Clear();
|
||||
|
||||
// Assert
|
||||
Assert.That(validator.Issues.Count, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Clear_ReturnsSelf()
|
||||
{
|
||||
// Arrange
|
||||
var validator = QueryValidator.Create();
|
||||
|
||||
// Act
|
||||
var result = validator.Clear();
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.SameAs(validator));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Report Generation Tests
|
||||
|
||||
[Test]
|
||||
public void GetReport_WithNoIssues_ShowsSuccess()
|
||||
{
|
||||
// Arrange
|
||||
var breakdown = LinqQueryBreakdownBuilder
|
||||
.Create()
|
||||
.SelectColumns("Id", "Name")
|
||||
.FromTable("Users")
|
||||
.Where("IsActive = true")
|
||||
.OrderByAscending("Name")
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var validator = QueryValidator.ValidateQuery(breakdown);
|
||||
var report = validator.GetReport();
|
||||
|
||||
// Assert
|
||||
Assert.That(report, Contains.Substring("No validation issues"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetReport_WithIssues_ListsEachIssue()
|
||||
{
|
||||
// Arrange
|
||||
var breakdown = LinqQueryBreakdownBuilder
|
||||
.Create()
|
||||
.SelectAll()
|
||||
.FromTable("Users")
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var validator = QueryValidator.ValidateQuery(breakdown);
|
||||
var report = validator.GetReport();
|
||||
|
||||
// Assert
|
||||
Assert.That(report, Contains.Substring("WARNINGS").IgnoreCase);
|
||||
Assert.That(report, Contains.Substring("Validation Report").IgnoreCase);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetReport_IncludesIssueDetails()
|
||||
{
|
||||
// Arrange
|
||||
var breakdown = LinqQueryBreakdownBuilder
|
||||
.Create()
|
||||
.SelectAll()
|
||||
.FromTable("Users")
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var validator = QueryValidator.ValidateQuery(breakdown);
|
||||
var report = validator.GetReport();
|
||||
|
||||
// Assert
|
||||
Assert.That(report, Contains.Substring("SELECT_ALL_COLUMNS"));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Complex Anti-Pattern Tests
|
||||
|
||||
[Test]
|
||||
public void Validate_ComplexProblematicQuery_FindsMultipleIssues()
|
||||
{
|
||||
// Arrange - Query with SELECT *, no WHERE, no ORDER BY
|
||||
var breakdown = LinqQueryBreakdownBuilder
|
||||
.Create()
|
||||
.SelectAll()
|
||||
.FromTable("LargeTable")
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var validator = QueryValidator.ValidateQuery(breakdown);
|
||||
|
||||
// Assert
|
||||
Assert.That(validator.Issues.Count, Is.GreaterThan(1));
|
||||
Assert.That(validator.HasWarnings, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Validate_WellFormedAggregationQuery_HasMinimalIssues()
|
||||
{
|
||||
// Arrange
|
||||
var breakdown = LinqQueryBreakdownBuilder
|
||||
.Create()
|
||||
.SelectColumns("Category", "COUNT(*) as ProductCount", "SUM(Price) as TotalValue")
|
||||
.FromTable("Products")
|
||||
.Where("IsActive = true")
|
||||
.GroupBy("Category")
|
||||
.OrderByDescending("TotalValue")
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var validator = QueryValidator.ValidateQuery(breakdown);
|
||||
|
||||
// Assert
|
||||
Assert.That(validator.HasErrors, Is.False);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Static Factory Tests
|
||||
|
||||
[Test]
|
||||
public void Create_ReturnsNewValidator()
|
||||
{
|
||||
// Act
|
||||
var validator = QueryValidator.Create();
|
||||
|
||||
// Assert
|
||||
Assert.That(validator, Is.Not.Null);
|
||||
Assert.That(validator, Is.TypeOf<QueryValidator>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ValidateQuery_ReturnsValidatorWithResults()
|
||||
{
|
||||
// Arrange
|
||||
var breakdown = LinqQueryBreakdownBuilder
|
||||
.Create()
|
||||
.SelectAll()
|
||||
.FromTable("Users")
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var validator = QueryValidator.ValidateQuery(breakdown);
|
||||
|
||||
// Assert
|
||||
Assert.That(validator, Is.Not.Null);
|
||||
Assert.That(validator.Issues.Count, Is.GreaterThan(0));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region String Representation Tests
|
||||
|
||||
[Test]
|
||||
public void QueryValidationIssue_ToString_FormatsCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var issue = new QueryValidationIssue(
|
||||
ValidationSeverity.Error,
|
||||
"TEST_001",
|
||||
"Test error message",
|
||||
"Additional details");
|
||||
|
||||
// Act
|
||||
var str = issue.ToString();
|
||||
|
||||
// Assert
|
||||
Assert.That(str, Contains.Substring("Error"));
|
||||
Assert.That(str, Contains.Substring("TEST_001"));
|
||||
Assert.That(str, Contains.Substring("Test error message"));
|
||||
Assert.That(str, Contains.Substring("Additional details"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void QueryValidationIssue_ToStringWithoutDetails_FormatsCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var issue = new QueryValidationIssue(
|
||||
ValidationSeverity.Warning,
|
||||
"WARN_001",
|
||||
"Warning message");
|
||||
|
||||
// Act
|
||||
var str = issue.ToString();
|
||||
|
||||
// Assert
|
||||
Assert.That(str, Contains.Substring("Warning"));
|
||||
Assert.That(str, Contains.Substring("WARN_001"));
|
||||
Assert.That(str, Contains.Substring("Warning message"));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
Reference in New Issue
Block a user