Files
sql-utilities/tests/Strata.SqlTools.Markdown.Tests/Snowflake/QueryBreakdownGeneratorTests.cs
T
Thom LambandClaude Opus 4.7 423108a7dc
SonarQube Analysis / sonarqube (pull_request) Successful in 4m22s
chore(sonar)!: mark Markdown generator classes as 'public static class' (S1118)
After the CA1822 cascade in #15 left every method on the eight
Markdown generator classes static, the classes themselves were
instantiable shells that consumers couldn't usefully `new`. This
commit flips the `class` modifier to `static class` on all eight:

- `Markdown.SqlServer.QueryBreakdownGenerator`
- `Markdown.SqlServer.SqlStatementGenerator`
- `Markdown.LinqToSql.QueryBreakdownGenerator`
- `Markdown.LinqToSql.SqlStatementGenerator`
- `Markdown.PostgreSql.QueryBreakdownGenerator`
- `Markdown.PostgreSql.SqlStatementGenerator`
- `Markdown.Snowflake.QueryBreakdownGenerator`
- `Markdown.Snowflake.SqlStatementGenerator`

Test fixtures drop the now-meaningless `_generator = new …()` field
and `[SetUp]` (kept the existing Setup body where unrelated state was
also initialized — `QueryMarkdownGenerationTests` and
`LinqToSql.QueryBreakdownGeneratorTests`).

BREAKING CHANGE: External NuGet consumers can no longer write
`new Markdown.Snowflake.SqlStatementGenerator()` (or any of the other
seven classes above) — the type is now a static container and may only
be referenced by name, e.g.
`Markdown.Snowflake.SqlStatementGenerator.GenerateSequenceDiagram(…)`.
The call syntax for the static methods is unchanged.

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

244 lines
8.2 KiB
C#

using Strata.SqlTools.Breakdowns.Snowflake;
using Strata.SqlTools.Markdown.Snowflake;
namespace Strata.SqlTools.Markdown.Tests.Snowflake;
[TestFixture]
public class QueryBreakdownGeneratorTests
{
[Test]
public void GenerateMermaidDiagram_SimpleSnowflakeQuery_GeneratesValidMermaid()
{
// Arrange - Using Snowflake syntax with uppercase identifiers
var query = new QueryBreakdown("USER_ID, USER_NAME", "USERS");
// Act
var result = QueryBreakdownGenerator.GenerateMermaidDiagram(query, "Simple User Query");
// Assert
Assert.That(result, Does.Contain("```mermaid"));
Assert.That(result, Does.Contain("flowchart TD"));
Assert.That(result, Does.Contain("### Simple User Query"));
Assert.That(result, Does.Contain("SELECT"));
Assert.That(result, Does.Contain("FROM"));
Assert.That(result, Does.Contain("```"));
}
[Test]
public void GenerateMermaidDiagram_WithSnowflakeColonParameters_HandlesParameters()
{
// Arrange - Snowflake uses :parameter syntax
var query = QueryBreakdown.Parse(@"
SELECT USER_ID, USER_NAME, STATUS
FROM USERS
WHERE USER_ID = :userId
AND STATUS = :status
", isMicrosoftSql: false);
// Act
var result = QueryBreakdownGenerator.GenerateMermaidDiagram(query);
// Assert
Assert.That(result, Does.Contain("WHERE"));
Assert.That(result, Does.Contain("```mermaid"));
Assert.That(result, Does.Contain("flowchart TD"));
}
[Test]
public void GenerateMermaidDiagram_WithSnowflakeCTE_ShowsWithClause()
{
// Arrange - Snowflake CTE with uppercase naming
var query = QueryBreakdown.Parse(@"
WITH ACTIVE_USERS AS (
SELECT USER_ID, USER_NAME
FROM USERS
WHERE STATUS = 'ACTIVE'
)
SELECT U.USER_ID, U.USER_NAME, COUNT(O.ORDER_ID) AS ORDER_COUNT
FROM ACTIVE_USERS U
JOIN ORDERS O ON U.USER_ID = O.USER_ID
GROUP BY U.USER_ID, U.USER_NAME
", isMicrosoftSql: false);
// Act
var result = QueryBreakdownGenerator.GenerateMermaidDiagram(query, "Users with Orders");
// Assert
Assert.That(result, Does.Contain("WITH Clause"));
Assert.That(result, Does.Contain("ACTIVE_USERS"));
Assert.That(result, Does.Contain("GROUP BY"));
}
[Test]
public void GenerateMermaidDiagram_WithSnowflakeQualify_HandlesQualifyClause()
{
// Arrange - Snowflake QUALIFY clause (window function filtering)
var query = QueryBreakdown.Parse(@"
SELECT
CUSTOMER_ID,
ORDER_DATE,
ORDER_AMOUNT,
ROW_NUMBER() OVER (PARTITION BY CUSTOMER_ID ORDER BY ORDER_DATE DESC) AS RN
FROM ORDERS
QUALIFY RN = 1
", isMicrosoftSql: false);
// Act
var result = QueryBreakdownGenerator.GenerateMermaidDiagram(query, "Latest Orders per Customer");
// Assert
Assert.That(result, Does.Contain("SELECT"));
Assert.That(result, Does.Contain("FROM"));
Assert.That(result, Does.Contain("```mermaid"));
}
[Test]
public void GenerateMermaidDiagram_WithDoubleQuotedIdentifiers_HandlesSnowflakeQuoting()
{
// Arrange - Snowflake uses "identifier" for case-sensitive names
var query = QueryBreakdown.Parse(@"
SELECT ""userId"", ""userName"", ""emailAddress""
FROM ""Users""
WHERE ""status"" = 'ACTIVE'
", isMicrosoftSql: false);
// Act
var result = QueryBreakdownGenerator.GenerateMermaidDiagram(query);
// Assert
Assert.That(result, Does.Contain("SELECT"));
Assert.That(result, Does.Contain("WHERE"));
Assert.That(result, Does.Contain("FROM"));
}
[Test]
public void GenerateMermaidDiagram_ComplexSnowflakeQuery_GeneratesCompleteDiagram()
{
// Arrange - Complex Snowflake query with multiple clauses
var query = QueryBreakdown.Parse(@"
WITH SALES_DATA AS (
SELECT
REGION,
PRODUCT_ID,
SUM(SALES_AMOUNT) AS TOTAL_SALES,
COUNT(*) AS ORDER_COUNT
FROM SALES
WHERE SALE_DATE >= :startDate AND SALE_DATE <= :endDate
GROUP BY REGION, PRODUCT_ID
)
SELECT
SD.REGION,
P.PRODUCT_NAME,
SD.TOTAL_SALES,
SD.ORDER_COUNT
FROM SALES_DATA SD
JOIN PRODUCTS P ON SD.PRODUCT_ID = P.PRODUCT_ID
WHERE SD.TOTAL_SALES > :minSales
ORDER BY SD.TOTAL_SALES DESC
", isMicrosoftSql: false);
// Act
var result = QueryBreakdownGenerator.GenerateMermaidDiagram(query, "Regional Sales Analysis");
// Assert
Assert.That(result, Does.Contain("### Regional Sales Analysis"));
Assert.That(result, Does.Contain("WITH Clause"));
Assert.That(result, Does.Contain("SALES_DATA"));
Assert.That(result, Does.Contain("SELECT"));
Assert.That(result, Does.Contain("FROM"));
Assert.That(result, Does.Contain("WHERE"));
Assert.That(result, Does.Contain("ORDER BY"));
Assert.That(result, Does.Contain("flowchart TD"));
}
[Test]
public void GenerateMermaidDiagram_WithGroupByAndHaving_IncludesBothClauses()
{
// Arrange
var query = QueryBreakdown.Parse(@"
SELECT CATEGORY, COUNT(*) AS PRODUCT_COUNT, AVG(PRICE) AS AVG_PRICE
FROM PRODUCTS
GROUP BY CATEGORY
HAVING COUNT(*) > 10 AND AVG(PRICE) < 100
", isMicrosoftSql: false);
// Act
var result = QueryBreakdownGenerator.GenerateMermaidDiagram(query);
// Assert
Assert.That(result, Does.Contain("GROUP BY"));
Assert.That(result, Does.Contain("HAVING"));
}
[Test]
public void GenerateMermaidDiagram_NullTitle_GeneratesWithoutTitle()
{
// Arrange
var query = new QueryBreakdown("ID", "ORDERS");
// Act
var result = QueryBreakdownGenerator.GenerateMermaidDiagram(query, null);
// Assert
Assert.That(result, Does.Not.Contain("###"));
Assert.That(result, Does.Contain("```mermaid"));
Assert.That(result, Does.Contain("flowchart TD"));
}
[Test]
public void GenerateMermaidDiagram_EmptyTitle_GeneratesWithoutTitle()
{
// Arrange
var query = new QueryBreakdown("ORDER_ID", "ORDERS");
// Act
var result = QueryBreakdownGenerator.GenerateMermaidDiagram(query, "");
// Assert
Assert.That(result, Does.Not.Contain("###"));
Assert.That(result, Does.Contain("SELECT"));
}
[Test]
public void GenerateMermaidDiagram_SnowflakeTypeCasting_PreservesSyntax()
{
// Arrange - Snowflake :: casting syntax
var query = QueryBreakdown.Parse(@"
SELECT
ORDER_ID::VARCHAR AS ORDER_CODE,
AMOUNT::DECIMAL(10,2) AS FORMATTED_AMOUNT,
CREATE_DATE::TIMESTAMP AS CREATED_AT
FROM ORDERS
", isMicrosoftSql: false);
// Act
var result = QueryBreakdownGenerator.GenerateMermaidDiagram(query, "Type Cast Example");
// Assert
Assert.That(result, Does.Contain("SELECT"));
Assert.That(result, Does.Contain("FROM"));
Assert.That(result, Does.Contain("### Type Cast Example"));
}
[Test]
public void GenerateMermaidDiagram_SnowflakeJsonPath_HandlesJsonNotation()
{
// Arrange - Snowflake JSON path notation (: for path access)
var query = QueryBreakdown.Parse(@"
SELECT
JSON_DATA:name::STRING AS NAME,
JSON_DATA:address.city::STRING AS CITY,
JSON_DATA:age::NUMBER AS AGE
FROM USER_JSON
", isMicrosoftSql: false);
// Act
var result = QueryBreakdownGenerator.GenerateMermaidDiagram(query, "JSON Data");
// Assert
Assert.That(result, Does.Contain("SELECT"));
Assert.That(result, Does.Contain("FROM"));
Assert.That(result, Does.Contain("```mermaid"));
}
}