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;
///
/// 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.
///
[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.Multiple(() =>
{
Assert.That(result, Is.InstanceOf());
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.Multiple(() =>
{
Assert.That(result, Is.InstanceOf());
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.Multiple(() =>
{
Assert.That(result, Is.InstanceOf());
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.Multiple(() =>
{
Assert.That(result, Is.InstanceOf());
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());
}
[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());
}
[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());
}
[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());
}
[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());
}
[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());
}
#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());
}
[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());
}
[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());
}
#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.Multiple(() =>
{
Assert.That(result, Is.InstanceOf());
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.Multiple(() =>
{
Assert.That(result, Is.InstanceOf());
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());
}
[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());
}
#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());
}
[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());
}
[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());
}
#endregion
#region Error Handling Tests
[Test]
public void Parse_NullInput_ThrowsArgumentNullException()
{
// Arrange
string? sql = null;
// Act & Assert
Assert.Throws(() => _parser.Parse(sql!));
}
[Test]
public void Parse_EmptyString_ThrowsArgumentNullException()
{
// Arrange
var sql = "";
// Act & Assert
Assert.Throws(() => _parser.Parse(sql));
}
[Test]
public void Parse_Whitespace_ThrowsArgumentNullException()
{
// Arrange
var sql = " ";
// Act & Assert
Assert.Throws(() => _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.Multiple(() =>
{
// Assert
Assert.That(success, Is.False);
Assert.That(result, Is.Null);
});
}
#endregion
}