Files
sql-utilities/tests/Strata.SqlTools.Markdown.Tests/SqlServer/QueryMarkdownGenerationTests.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

149 lines
5.6 KiB
C#

using Strata.SqlTools.Breakdowns.SqlServer;
using Strata.SqlTools.Markdown.SqlServer;
namespace Strata.SqlTools.Markdown.Tests.SqlServer;
[TestFixture]
[Ignore("These tests are designed to generate markdown files from SQL queries in the Queries directory. They are not meant to be run as part of regular unit testing, but can be executed manually when needed.")]
[Category("MarkdownGeneration")]
public class QueryMarkdownGenerationTests
{
private string _queriesSourcePath = null!;
private string _markdownOutputPath = null!;
[SetUp]
public void Setup()
{
// Get the solution root directory
var testDirectory = TestContext.CurrentContext.TestDirectory;
var solutionRoot = Directory.GetParent(testDirectory)?.Parent?.Parent?.Parent?.Parent?.FullName;
if (solutionRoot == null)
throw new InvalidOperationException("Could not determine solution root directory");
_queriesSourcePath = Path.Combine(solutionRoot, "Queries");
_markdownOutputPath = Path.Combine(solutionRoot, "docs", "queries");
// Ensure output directory exists
if (!Directory.Exists(_markdownOutputPath))
{
Directory.CreateDirectory(_markdownOutputPath);
}
}
[Test]
public void GenerateMarkdownForAllQueries_CreatesMarkdownFiles()
{
// Arrange
Assert.That(Directory.Exists(_queriesSourcePath), Is.True,
$"Queries directory not found at: {_queriesSourcePath}");
var sqlFiles = Directory.GetFiles(_queriesSourcePath, "*.sql");
Assert.That(sqlFiles, Is.Not.Empty, "No SQL files found in Queries directory");
int successCount = 0;
int skippedCount = 0;
// Act & Assert for each file
foreach (var sqlFile in sqlFiles)
{
var fileName = Path.GetFileNameWithoutExtension(sqlFile);
var sqlContent = File.ReadAllText(sqlFile);
try
{
// Parse the SQL query
var queryBreakdown = QueryBreakdown.Parse(sqlContent);
// Generate markdown with the filename as title
var markdown = QueryBreakdownGenerator.GenerateMermaidDiagram(queryBreakdown, fileName);
// Verify markdown was generated
Assert.That(markdown, Is.Not.Null);
Assert.That(markdown, Does.Contain("```mermaid"));
Assert.That(markdown, Does.Contain(fileName));
// Write to output file
var outputFile = Path.Combine(_markdownOutputPath, $"{fileName}.md");
File.WriteAllText(outputFile, markdown);
// Verify file was created
Assert.That(File.Exists(outputFile), Is.True,
$"Markdown file was not created: {outputFile}");
TestContext.Out.WriteLine($"Generated: {fileName}.md");
successCount++;
}
catch (FormatException ex)
{
// Skip files that can't be parsed (e.g., partial queries with only WITH clauses)
TestContext.Out.WriteLine($"Skipped: {fileName}.sql - {ex.Message}");
skippedCount++;
}
}
TestContext.Out.WriteLine($"Generated {successCount} markdown files, skipped {skippedCount} files in {_markdownOutputPath}");
Assert.That(successCount, Is.GreaterThan(0), "At least one markdown file should be generated");
}
[Test]
public void GenerateMarkdownForAllQueries_IncludesQuerySource()
{
// Arrange
var sqlFiles = Directory.GetFiles(_queriesSourcePath, "*.sql");
Assert.That(sqlFiles, Is.Not.Empty);
int successCount = 0;
// Act & Assert
foreach (var sqlFile in sqlFiles)
{
var fileName = Path.GetFileNameWithoutExtension(sqlFile);
var sqlContent = File.ReadAllText(sqlFile);
try
{
var queryBreakdown = QueryBreakdown.Parse(sqlContent);
var markdown = QueryBreakdownGenerator.GenerateMermaidDiagram(queryBreakdown, fileName);
// Add source SQL to the markdown
var fullMarkdown = $"{markdown}\n\n## Source SQL\n\n```sql\n{sqlContent}\n```\n";
var outputFile = Path.Combine(_markdownOutputPath, $"{fileName}.md");
File.WriteAllText(outputFile, fullMarkdown);
// Verify the output includes both diagram and source
var writtenContent = File.ReadAllText(outputFile);
Assert.That(writtenContent, Does.Contain("```mermaid"));
Assert.That(writtenContent, Does.Contain("## Source SQL"));
Assert.That(writtenContent, Does.Contain(sqlContent));
successCount++;
}
catch (FormatException)
{
// Skip files that can't be parsed
TestContext.Out.WriteLine($"Skipped: {fileName}.sql (could not parse)");
}
}
Assert.That(successCount, Is.GreaterThan(0), "At least one markdown file should be generated");
}
[Test]
public void AllQueriesDirectory_Exists()
{
// Verify the Queries directory exists
Assert.That(Directory.Exists(_queriesSourcePath), Is.True,
$"Queries directory should exist at: {_queriesSourcePath}");
}
[Test]
public void OutputDirectory_IsCreated()
{
// Verify the output directory was created
Assert.That(Directory.Exists(_markdownOutputPath), Is.True,
$"Output directory should exist at: {_markdownOutputPath}");
}
}