378 lines
9.4 KiB
C#
378 lines
9.4 KiB
C#
using Strata.SqlTools.SqlBreakdown.Expressions;
|
|
using Strata.SqlTools.SqlBreakdown.Expressions.Arithmetic;
|
|
using Strata.SqlTools.SqlBreakdown.Expressions.Functions.Aggregate;
|
|
using Strata.SqlTools.SqlBreakdown.Expressions.Literals;
|
|
|
|
namespace Strata.SqlTools.SqlBreakdown.Tests.Snowflake;
|
|
|
|
/// <summary>
|
|
/// Tests for Snowflake SQL statement expression parser.
|
|
/// Note: The parser currently only supports identifiers enclosed in double quotes ("ID")
|
|
/// or brackets ([ID]). Unquoted identifiers and spaces in identifiers are not supported yet.
|
|
/// </summary>
|
|
[TestFixture]
|
|
public class StatementExpressionParserTests
|
|
{
|
|
private Strata.SqlTools.Statements.Snowflake.StatementExpressionParser _parser = null!;
|
|
|
|
[SetUp]
|
|
public void SetUp()
|
|
{
|
|
_parser = new Strata.SqlTools.Statements.Snowflake.StatementExpressionParser();
|
|
}
|
|
|
|
#region Basic Tests
|
|
|
|
[Test]
|
|
public void Parse_Number_ReturnsNumberExpression()
|
|
{
|
|
// Arrange
|
|
var sql = "42";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<NumberLiteralExpression>());
|
|
Assert.That(((NumberLiteralExpression)result).Value, Is.EqualTo(42));
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_DecimalNumber_ReturnsNumberExpression()
|
|
{
|
|
// Arrange
|
|
var sql = "3.14";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<NumberLiteralExpression>());
|
|
Assert.That(((NumberLiteralExpression)result).Value, Is.EqualTo(3.14m));
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_DoubleQuotedIdentifier_ReturnsColumnExpression()
|
|
{
|
|
// Arrange - Snowflake convention: double-quoted identifiers
|
|
var sql = "\"CUSTOMER_ID\"";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<ColumnExpression>());
|
|
Assert.That(((ColumnExpression)result).ColumnName, Is.EqualTo("CUSTOMER_ID"));
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_BracketedIdentifier_ReturnsColumnExpression()
|
|
{
|
|
// Arrange - Also supports SQL Server bracket style
|
|
var sql = "[CUSTOMER_ID]";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<ColumnExpression>());
|
|
Assert.That(((ColumnExpression)result).ColumnName, Is.EqualTo("CUSTOMER_ID"));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Arithmetic Tests
|
|
|
|
[Test]
|
|
public void Parse_Addition_ReturnsAdditionExpression()
|
|
{
|
|
// Arrange
|
|
var sql = "\"REVENUE\" + \"COST\"";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<AdditionExpression>());
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_Subtraction_ReturnsSubtractionExpression()
|
|
{
|
|
// Arrange
|
|
var sql = "\"REVENUE\" - \"COST\"";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<SubtractionExpression>());
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_Multiplication_ReturnsMultiplicationExpression()
|
|
{
|
|
// Arrange
|
|
var sql = "\"PRICE\" * \"QUANTITY\"";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<MultiplicationExpression>());
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_Division_ReturnsDivisionExpression()
|
|
{
|
|
// Arrange
|
|
var sql = "\"TOTAL\" / \"COUNT\"";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<DivisionExpression>());
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_ColumnWithNumber_ReturnsCorrectExpression()
|
|
{
|
|
// Arrange
|
|
var sql = "\"NET_REVENUE\" * 1.15";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<MultiplicationExpression>());
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_ComplexArithmetic_ReturnsCorrectExpression()
|
|
{
|
|
// Arrange
|
|
var sql = "(\"BASE_PRICE\" + \"TAX_AMOUNT\") * \"QUANTITY\"";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<MultiplicationExpression>());
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Function Tests
|
|
|
|
[Test]
|
|
public void Parse_SumFunction_ReturnsSumFunctionExpression()
|
|
{
|
|
// Arrange
|
|
var sql = "SUM(\"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(\"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(\"NET_REVENUE\" * 1.1)";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<SumFunction>());
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Snowflake-Specific Syntax Tests
|
|
|
|
[Test]
|
|
public void Parse_UppercaseIdentifier_ReturnsColumnExpression()
|
|
{
|
|
// Arrange - Snowflake convention: identifiers typically uppercase
|
|
var sql = "\"ORDER_TOTAL\"";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<ColumnExpression>());
|
|
Assert.That(((ColumnExpression)result).ColumnName, Is.EqualTo("ORDER_TOTAL"));
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_IdentifierWithUnderscores_ReturnsColumnExpression()
|
|
{
|
|
// Arrange - Snowflake naming: underscores common
|
|
var sql = "\"NET_REVENUE_USD_2024\"";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<ColumnExpression>());
|
|
Assert.That(((ColumnExpression)result).ColumnName, Is.EqualTo("NET_REVENUE_USD_2024"));
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_MixedQuotingStyles_ReturnsCorrectExpression()
|
|
{
|
|
// Arrange - Mix of double-quoted and bracketed identifiers
|
|
var sql = "SUM(\"GROSS_REVENUE\") - [COST]";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<SubtractionExpression>());
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_MultipleDoubleQuotedIdentifiers_ReturnsCorrectExpression()
|
|
{
|
|
// Arrange
|
|
var sql = "\"A\" + \"B\" * \"C\"";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<AdditionExpression>());
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Parentheses and Precedence Tests
|
|
|
|
[Test]
|
|
public void Parse_SimpleParentheses_ReturnsCorrectExpression()
|
|
{
|
|
// Arrange
|
|
var sql = "(\"A\" + \"B\")";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<AdditionExpression>());
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_ParenthesesWithMultiplication_ReturnsCorrectPrecedence()
|
|
{
|
|
// Arrange
|
|
var sql = "(1 + 2) * 3";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<MultiplicationExpression>());
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_NestedParentheses_ReturnsCorrectExpression()
|
|
{
|
|
// Arrange
|
|
var sql = "((\"A\" + \"B\") * \"C\") - \"D\"";
|
|
|
|
// Act
|
|
var result = _parser.Parse(sql);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Not.Null);
|
|
Assert.That(result, Is.InstanceOf<SubtractionExpression>());
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Error Handling Tests
|
|
|
|
[Test]
|
|
public void Parse_NullInput_ThrowsArgumentNullException()
|
|
{
|
|
// Arrange
|
|
string? sql = null;
|
|
|
|
// Act & Assert
|
|
Assert.Throws<ArgumentNullException>(() => _parser.Parse(sql!));
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_EmptyString_ThrowsArgumentNullException()
|
|
{
|
|
// Arrange
|
|
var sql = "";
|
|
|
|
// Act & Assert
|
|
Assert.Throws<ArgumentNullException>(() => _parser.Parse(sql));
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_Whitespace_ThrowsArgumentNullException()
|
|
{
|
|
// Arrange
|
|
var sql = " ";
|
|
|
|
// Act & Assert
|
|
Assert.Throws<ArgumentNullException>(() => _parser.Parse(sql));
|
|
}
|
|
|
|
[Test]
|
|
public void TryParse_InvalidSyntax_ReturnsFalse()
|
|
{
|
|
// Arrange
|
|
var sql = "\"A\" +"; // Incomplete expression
|
|
|
|
// Act
|
|
var success = _parser.TryParse(sql, out var result);
|
|
|
|
// Assert
|
|
Assert.That(success, Is.False);
|
|
Assert.That(result, Is.Null);
|
|
}
|
|
|
|
#endregion
|
|
}
|