908 lines
21 KiB
C#
908 lines
21 KiB
C#
using Strata.SqlTools.SqlBreakdown.Expressions;
|
|
using Strata.SqlTools.SqlBreakdown.Expressions.Functions.Aggregate;
|
|
using Strata.SqlTools.SqlBreakdown.Expressions.Literals;
|
|
using Strata.SqlTools.Statements.SqlServer;
|
|
|
|
namespace Strata.SqlTools.SqlBreakdown.Tests.SqlServer;
|
|
|
|
[TestFixture]
|
|
public class StatementExpressionParserTests
|
|
{
|
|
private StatementExpressionParser _parser = null!;
|
|
|
|
[SetUp]
|
|
public void Setup()
|
|
{
|
|
_parser = new StatementExpressionParser();
|
|
}
|
|
|
|
#region Parse Method Tests
|
|
|
|
[Test]
|
|
public void Parse_SimpleNumber_ReturnsNumberLiteralExpression()
|
|
{
|
|
// Arrange
|
|
var sql = "42";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<NumberLiteralExpression>());
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_SimpleAddition_ReturnsCorrectExpression()
|
|
{
|
|
// Arrange
|
|
var sql = "10 + 20";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<Expression>());
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_SimpleSubtraction_ReturnsCorrectExpression()
|
|
{
|
|
// Arrange
|
|
var sql = "50 - 30";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<Expression>());
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_SimpleMultiplication_ReturnsCorrectExpression()
|
|
{
|
|
// Arrange
|
|
var sql = "5 * 3";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<Expression>());
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_SimpleDivision_ReturnsCorrectExpression()
|
|
{
|
|
// Arrange
|
|
var sql = "100 / 4";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<Expression>());
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_ComplexExpression_ReturnsCorrectExpression()
|
|
{
|
|
// Arrange
|
|
var sql = "10 + 20 * 3 - 5";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<Expression>());
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_ParenthesizedExpression_ReturnsCorrectExpression()
|
|
{
|
|
// Arrange
|
|
var sql = "(10 + 20) * 3";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<Expression>());
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_NestedParentheses_ReturnsCorrectExpression()
|
|
{
|
|
// Arrange
|
|
var sql = "((10 + 5) * 2) / 3";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<Expression>());
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_SumFunction_ReturnsSumFunctionExpression()
|
|
{
|
|
// Arrange
|
|
var sql = "SUM(3_REVENUE)";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<SumFunction>());
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_AvgFunction_ReturnsAverageFunctionExpression()
|
|
{
|
|
// Arrange
|
|
var sql = "AVG(3_REVENUE)";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<AverageFunction>());
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_FunctionWithExpression_ReturnsCorrectExpression()
|
|
{
|
|
// Arrange
|
|
var sql = "SUM(3_REVENUE) + 100";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<Expression>());
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_ColumnIdentifier_ReturnsColumnExpression()
|
|
{
|
|
// Arrange
|
|
var sql = "1_DEPARTMENT_ID";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<RegisteredTableColumnExpression>());
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_ColumnWithArithmetic_ReturnsCorrectExpression()
|
|
{
|
|
// Arrange
|
|
var sql = "3_REVENUE * 2";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<Expression>());
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_DecimalNumber_ReturnsNumberLiteralExpression()
|
|
{
|
|
// Arrange
|
|
var sql = "123.456";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<NumberLiteralExpression>());
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_WithSingleLineComment_ParsesSuccessfully()
|
|
{
|
|
// Arrange
|
|
var sql = "10 + 20 -- This is a comment";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<Expression>());
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_WithMultiLineComment_ParsesSuccessfully()
|
|
{
|
|
// Arrange
|
|
var sql = "10 /* this is a comment */ + 20";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<Expression>());
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_WithExtraWhitespace_ParsesSuccessfully()
|
|
{
|
|
// Arrange
|
|
var sql = " 10 + 20 ";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<Expression>());
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_WithNewlines_ParsesSuccessfully()
|
|
{
|
|
// Arrange
|
|
var sql = @"10
|
|
+
|
|
20";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<Expression>());
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_NullInput_ThrowsArgumentNullException()
|
|
{
|
|
// Arrange
|
|
string sql = null!;
|
|
|
|
// Act & Assert
|
|
var ex = Assert.Throws<ArgumentNullException>(() => _parser.Parse(sql));
|
|
Assert.That(ex.ParamName, Is.EqualTo("sqlStatement"));
|
|
Assert.That(ex.Message, Does.Contain("SQL statement cannot be null or empty"));
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_EmptyString_ThrowsArgumentNullException()
|
|
{
|
|
// Arrange
|
|
var sql = string.Empty;
|
|
|
|
// Act & Assert
|
|
var ex = Assert.Throws<ArgumentNullException>(() => _parser.Parse(sql));
|
|
Assert.That(ex.ParamName, Is.EqualTo("sqlStatement"));
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_WhitespaceOnly_ThrowsArgumentNullException()
|
|
{
|
|
// Arrange
|
|
var sql = " ";
|
|
|
|
// Act & Assert
|
|
var ex = Assert.Throws<ArgumentNullException>(() => _parser.Parse(sql));
|
|
Assert.That(ex.ParamName, Is.EqualTo("sqlStatement"));
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_UnclosedParenthesis_ThrowsFormatException()
|
|
{
|
|
// Arrange
|
|
var sql = "(10 + 20";
|
|
|
|
// Act & Assert
|
|
var ex = Assert.Throws<FormatException>(() => _parser.Parse(sql));
|
|
Assert.That(ex.Message, Does.Contain("Failed to parse SQL statement"));
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_MismatchedParenthesis_ThrowsFormatException()
|
|
{
|
|
// Arrange
|
|
var sql = "10 + 20)";
|
|
|
|
// Act & Assert
|
|
Assert.Throws<FormatException>(() => _parser.Parse(sql));
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_UnrecognizedFunction_ThrowsFormatException()
|
|
{
|
|
// Arrange
|
|
var sql = "UNKNOWN(100)";
|
|
|
|
// Act & Assert
|
|
var ex = Assert.Throws<FormatException>(() => _parser.Parse(sql));
|
|
Assert.That(ex.Message, Does.Contain("Failed to parse SQL statement"));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region TryParse Method Tests
|
|
|
|
[Test]
|
|
public void TryParse_ValidSimpleExpression_ReturnsTrue()
|
|
{
|
|
// Arrange
|
|
var sql = "42";
|
|
|
|
// Act
|
|
var success = _parser.TryParse(sql, out var result);
|
|
|
|
// Assert
|
|
Assert.That(success, Is.True);
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<NumberLiteralExpression>());
|
|
}
|
|
|
|
[Test]
|
|
public void TryParse_ValidComplexExpression_ReturnsTrue()
|
|
{
|
|
// Arrange
|
|
var sql = "(10 + 20) * 3 - 5";
|
|
|
|
// Act
|
|
var success = _parser.TryParse(sql, out var result);
|
|
|
|
// Assert
|
|
Assert.That(success, Is.True);
|
|
Assert.That(result, Is.Not.Null);
|
|
}
|
|
|
|
[Test]
|
|
public void TryParse_ValidFunction_ReturnsTrue()
|
|
{
|
|
// Arrange
|
|
var sql = "SUM(3_REVENUE)";
|
|
|
|
// Act
|
|
var success = _parser.TryParse(sql, out var result);
|
|
|
|
// Assert
|
|
Assert.That(success, Is.True);
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<SumFunction>());
|
|
}
|
|
|
|
[Test]
|
|
public void TryParse_NullInput_ReturnsFalse()
|
|
{
|
|
// Arrange
|
|
string sql = null!;
|
|
|
|
// Act
|
|
var success = _parser.TryParse(sql, out var result);
|
|
|
|
// Assert
|
|
Assert.That(success, Is.False);
|
|
Assert.That(result, Is.Null);
|
|
}
|
|
|
|
[Test]
|
|
public void TryParse_EmptyString_ReturnsFalse()
|
|
{
|
|
// Arrange
|
|
var sql = string.Empty;
|
|
|
|
// Act
|
|
var success = _parser.TryParse(sql, out var result);
|
|
|
|
// Assert
|
|
Assert.That(success, Is.False);
|
|
Assert.That(result, Is.Null);
|
|
}
|
|
|
|
[Test]
|
|
public void TryParse_WhitespaceOnly_ReturnsFalse()
|
|
{
|
|
// Arrange
|
|
var sql = " ";
|
|
|
|
// Act
|
|
var success = _parser.TryParse(sql, out var result);
|
|
|
|
// Assert
|
|
Assert.That(success, Is.False);
|
|
Assert.That(result, Is.Null);
|
|
}
|
|
|
|
[Test]
|
|
public void TryParse_InvalidSyntax_ReturnsFalse()
|
|
{
|
|
// Arrange
|
|
var sql = "(10 + 20";
|
|
|
|
// Act
|
|
var success = _parser.TryParse(sql, out var result);
|
|
|
|
// Assert
|
|
Assert.That(success, Is.False);
|
|
Assert.That(result, Is.Null);
|
|
}
|
|
|
|
[Test]
|
|
public void TryParse_WithErrorMessage_ValidInput_ReturnsTrue()
|
|
{
|
|
// Arrange
|
|
var sql = "100 + 50";
|
|
|
|
// Act
|
|
var success = _parser.TryParse(sql, out var result, out var errorMessage);
|
|
|
|
// Assert
|
|
Assert.That(success, Is.True);
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(errorMessage, Is.Null);
|
|
}
|
|
|
|
[Test]
|
|
public void TryParse_WithErrorMessage_NullInput_ReturnsFalseWithMessage()
|
|
{
|
|
// Arrange
|
|
string sql = null!;
|
|
|
|
// Act
|
|
var success = _parser.TryParse(sql, out var result, out var errorMessage);
|
|
|
|
// Assert
|
|
Assert.That(success, Is.False);
|
|
Assert.That(result, Is.Null);
|
|
Assert.That(errorMessage, Is.Not.Null);
|
|
Assert.That(errorMessage, Does.Contain("SQL statement cannot be null or empty"));
|
|
}
|
|
|
|
[Test]
|
|
public void TryParse_WithErrorMessage_InvalidSyntax_ReturnsFalseWithMessage()
|
|
{
|
|
// Arrange
|
|
var sql = "(10 + 20";
|
|
|
|
// Act
|
|
var success = _parser.TryParse(sql, out var result, out var errorMessage);
|
|
|
|
// Assert
|
|
Assert.That(success, Is.False);
|
|
Assert.That(result, Is.Null);
|
|
Assert.That(errorMessage, Is.Not.Null);
|
|
Assert.That(errorMessage, Does.Contain("Invalid syntax"));
|
|
}
|
|
|
|
[Test]
|
|
public void TryParse_WithErrorMessage_UnsupportedToken_ReturnsFalseWithMessage()
|
|
{
|
|
// Arrange
|
|
var sql = "UNKNOWN(100)";
|
|
|
|
// Act
|
|
var success = _parser.TryParse(sql, out var result, out var errorMessage);
|
|
|
|
// Assert
|
|
Assert.That(success, Is.False);
|
|
Assert.That(result, Is.Null);
|
|
Assert.That(errorMessage, Is.Not.Null);
|
|
Assert.That(errorMessage, Does.Contain("not recognized"));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Order of Operations Tests
|
|
|
|
[Test]
|
|
public void Parse_MultiplicationBeforeAddition_CorrectPrecedence()
|
|
{
|
|
// Arrange
|
|
var sql = "2 + 3 * 4"; // Should be 2 + (3 * 4) = 14, not (2 + 3) * 4 = 20
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
// Note: We can't easily verify the calculation result without evaluating the expression tree
|
|
// But we can verify it parses without error
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_DivisionBeforeSubtraction_CorrectPrecedence()
|
|
{
|
|
// Arrange
|
|
var sql = "20 - 10 / 2"; // Should be 20 - (10 / 2) = 15, not (20 - 10) / 2 = 5
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_ParenthesesOverridePrecedence_CorrectPrecedence()
|
|
{
|
|
// Arrange
|
|
var sql = "(2 + 3) * 4"; // Should be (2 + 3) * 4 = 20
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_MultipleOperationsLeftToRight_CorrectAssociativity()
|
|
{
|
|
// Arrange
|
|
var sql = "10 - 5 - 2"; // Should be (10 - 5) - 2 = 3, not 10 - (5 - 2) = 7
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Comment and Whitespace Normalization Tests
|
|
|
|
[Test]
|
|
public void Parse_SingleLineCommentAtEnd_IgnoresComment()
|
|
{
|
|
// Arrange
|
|
var sql = "10 + 20 -- This should be ignored";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_SingleLineCommentInMiddle_IgnoresComment()
|
|
{
|
|
// Arrange
|
|
var sql = @"10 + -- comment here
|
|
20";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_MultiLineCommentInMiddle_IgnoresComment()
|
|
{
|
|
// Arrange
|
|
var sql = "10 /* this is a\n multi-line\n comment */ + 20";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_MultipleComments_IgnoresAllComments()
|
|
{
|
|
// Arrange
|
|
var sql = "10 /* comment 1 */ + /* comment 2 */ 20 -- comment 3";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_CommentLikeTextInString_PreservesString()
|
|
{
|
|
// Arrange - Although this parser might not handle strings, testing the comment removal logic
|
|
var sql = "10 + 20"; // Simple case without strings as parser may not support them
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_TabsAndSpaces_NormalizesCorrectly()
|
|
{
|
|
// Arrange
|
|
var sql = "10\t+\t 20";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_MultipleNewlines_NormalizesCorrectly()
|
|
{
|
|
// Arrange
|
|
var sql = "10\n\n\n+\n\n20";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Edge Cases and Complex Scenarios
|
|
|
|
[Test]
|
|
public void Parse_VeryLongExpression_ParsesSuccessfully()
|
|
{
|
|
// Arrange
|
|
var sql = "1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_DeeplyNestedParentheses_ParsesSuccessfully()
|
|
{
|
|
// Arrange
|
|
var sql = "((((10 + 5) * 2) - 3) / 4)";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_MixedColumnsNumbersAndFunctions_ParsesSuccessfully()
|
|
{
|
|
// Arrange
|
|
var sql = "SUM(3_REVENUE) + 1_DEPARTMENT_ID * 100";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_FunctionInsideParentheses_ParsesSuccessfully()
|
|
{
|
|
// Arrange
|
|
var sql = "(SUM(3_REVENUE) + 100) * 2";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_LargeDecimalNumber_ParsesSuccessfully()
|
|
{
|
|
// Arrange
|
|
var sql = "123456789.987654321";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<NumberLiteralExpression>());
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_ZeroValue_ParsesSuccessfully()
|
|
{
|
|
// Arrange
|
|
var sql = "0";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<NumberLiteralExpression>());
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_NegativeNumber_WithSubtraction_ParsesSuccessfully()
|
|
{
|
|
// Arrange
|
|
var sql = "0 - 5";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_ConsecutiveOperators_ThrowsFormatException()
|
|
{
|
|
// Arrange
|
|
var sql = "10 + + 20";
|
|
|
|
// Act & Assert
|
|
Assert.Throws<FormatException>(() => _parser.Parse(sql));
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_TrailingOperator_ThrowsFormatException()
|
|
{
|
|
// Arrange
|
|
var sql = "10 + 20 +";
|
|
|
|
// Act & Assert
|
|
Assert.Throws<FormatException>(() => _parser.Parse(sql));
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_LeadingOperator_ThrowsFormatException()
|
|
{
|
|
// Arrange
|
|
var sql = "+ 10 + 20";
|
|
|
|
// Act & Assert
|
|
Assert.Throws<FormatException>(() => _parser.Parse(sql));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Function-Specific Tests
|
|
|
|
[Test]
|
|
public void Parse_SumFunctionLowercase_ParsesSuccessfully()
|
|
{
|
|
// Arrange
|
|
var sql = "sum(3_REVENUE)";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<SumFunction>());
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_AvgFunctionMixedCase_ParsesSuccessfully()
|
|
{
|
|
// Arrange
|
|
var sql = "AvG(3_REVENUE)";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<AverageFunction>());
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_MultipleFunctions_ParsesSuccessfully()
|
|
{
|
|
// Arrange
|
|
var sql = "SUM(3_REVENUE) + AVG(3_REVENUE)";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_FunctionWithNumberArgument_ParsesSuccessfully()
|
|
{
|
|
// Arrange
|
|
var sql = "SUM(100)";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<SumFunction>());
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_FunctionWithComplexExpression_ParsesSuccessfully()
|
|
{
|
|
// Arrange
|
|
var sql = "SUM(3_REVENUE * 2)";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<SumFunction>());
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Column Expression Tests
|
|
|
|
[Test]
|
|
public void Parse_VariousColumnIds_ParsesSuccessfully()
|
|
{
|
|
// Arrange & Act & Assert
|
|
var testCases = new[] { "1_DEPARTMENT_ID", "2_NAME", "3_REVENUE", "4_DISCHARGE_DATE", "586883_FIXED_COST", "586664_VARIABLE_COST" };
|
|
|
|
foreach (var sql in testCases)
|
|
{
|
|
var result = _parser.Parse(sql);
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<RegisteredTableColumnExpression>());
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_ColumnInArithmeticExpression_ParsesSuccessfully()
|
|
{
|
|
// Arrange
|
|
var sql = "(3_REVENUE - 586883_FIXED_COST) / 586664_VARIABLE_COST";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_ColumnWithFunction_ParsesSuccessfully()
|
|
{
|
|
// Arrange
|
|
var sql = "SUM(3_REVENUE) - AVG(586883_FIXED_COST)";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
}
|
|
|
|
#endregion
|
|
}
|