using Strata.SqlTools.Breakdowns.PostgreSql; using Strata.SqlTools.Markdown.PostgreSql; namespace Strata.SqlTools.Markdown.Tests.PostgreSql; [TestFixture] public class QueryBreakdownGeneratorTests { [Test] public void GenerateMermaidDiagram_SimplePostgreSqlQuery_GeneratesValidMermaid() { // Arrange - Using PostgreSQL syntax with lowercase 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_WithPositionalParameters_HandlesParameters() { // Arrange - PostgreSQL uses $1, $2 syntax for positional parameters var query = QueryBreakdown.Parse(@" SELECT user_id, user_name, status FROM users WHERE user_id = $1 AND status = $2 "); // 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_WithNamedParameters_HandlesColonSyntax() { // Arrange - PostgreSQL also supports :parameter syntax var query = QueryBreakdown.Parse(@" SELECT user_id, user_name, email FROM users WHERE user_id = :userId AND status = :status "); // 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_WithPostgreSqlCTE_ShowsWithClause() { // Arrange - PostgreSQL CTE with lowercase 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 "); // Act var result = QueryBreakdownGenerator.GenerateMermaidDiagram(query, "Users with Orders"); // Assert Assert.That(result, Does.Contain("### Users with Orders")); Assert.That(result, Does.Contain("```mermaid")); Assert.That(result, Does.Contain("flowchart TD")); Assert.That(result, Does.Contain("GROUP BY")); } [Test] public void GenerateMermaidDiagram_WithLimit_HandlesLimitClause() { // Arrange - PostgreSQL LIMIT clause var query = QueryBreakdown.Parse(@" SELECT user_id, user_name, created_at FROM users ORDER BY created_at DESC LIMIT 10 "); // Act var result = QueryBreakdownGenerator.GenerateMermaidDiagram(query, "Recent Users"); // Assert Assert.That(result, Does.Contain("SELECT")); Assert.That(result, Does.Contain("FROM")); Assert.That(result, Does.Contain("ORDER BY")); Assert.That(result, Does.Contain("```mermaid")); } [Test] public void GenerateMermaidDiagram_WithLimitOffset_HandlesPagination() { // Arrange - PostgreSQL LIMIT/OFFSET for pagination var query = QueryBreakdown.Parse(@" SELECT product_id, product_name, price FROM products ORDER BY product_name LIMIT 20 OFFSET 40 "); // Act var result = QueryBreakdownGenerator.GenerateMermaidDiagram(query); // Assert Assert.That(result, Does.Contain("SELECT")); Assert.That(result, Does.Contain("ORDER BY")); Assert.That(result, Does.Contain("FROM")); } [Test] public void GenerateMermaidDiagram_WithDoubleQuotedIdentifiers_HandlesPostgreSqlQuoting() { // Arrange - PostgreSQL uses "identifier" for case-sensitive names var query = QueryBreakdown.Parse(@" SELECT ""userId"", ""userName"", ""emailAddress"" FROM ""Users"" WHERE ""status"" = 'ACTIVE' "); // 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_ComplexPostgreSqlQuery_GeneratesCompleteDiagram() { // Arrange - Complex PostgreSQL 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 >= $1 AND sale_date <= $2 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 > $3 ORDER BY sd.total_sales DESC "); // Act var result = QueryBreakdownGenerator.GenerateMermaidDiagram(query, "Regional Sales Analysis"); // Assert Assert.That(result, Does.Contain("### Regional Sales Analysis")); Assert.That(result, Does.Contain("```mermaid")); Assert.That(result, Does.Contain("flowchart TD")); 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")); } [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 "); // 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_PostgreSqlTypeCasting_PreservesSyntax() { // Arrange - PostgreSQL :: 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 "); // 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_PostgreSqlJsonOperators_HandlesJsonNotation() { // Arrange - PostgreSQL JSON operators (-> for JSON object, ->> for text) var query = QueryBreakdown.Parse(@" SELECT json_data->>'name' AS name, json_data->'address'->>'city' AS city, (json_data->>'age')::INTEGER AS age FROM user_json "); // 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")); } [Test] public void GenerateMermaidDiagram_WithDistinct_HandlesDistinctClause() { // Arrange - PostgreSQL DISTINCT var query = QueryBreakdown.Parse(@" SELECT DISTINCT region, product_category FROM sales ORDER BY region, product_category "); // Act var result = QueryBreakdownGenerator.GenerateMermaidDiagram(query); // Assert Assert.That(result, Does.Contain("SELECT")); Assert.That(result, Does.Contain("FROM")); Assert.That(result, Does.Contain("ORDER BY")); } }