Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions src/DemaConsulting.BuildMark/BuildInformation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,105 @@ public static async Task<BuildInformation> CreateAsync(IRepoConnector connector,
knownIssues);
}

/// <summary>
/// Generates a Markdown build report from this build information.
/// </summary>
/// <param name="headingDepth">Root markdown heading depth (default 1).</param>
/// <param name="includeKnownIssues">Flag for whether to include known issues (default false).</param>
/// <returns>Markdown-formatted build report.</returns>
public string ToMarkdown(int headingDepth = 1, bool includeKnownIssues = false)
{
// Build heading prefix based on requested depth
var heading = new string('#', headingDepth);
var subHeading = new string('#', headingDepth + 1);

// Start building the markdown report
var markdown = new System.Text.StringBuilder();

// Add title section
markdown.AppendLine($"{heading} Build Report");
markdown.AppendLine();

// Add version information section
markdown.AppendLine($"{subHeading} Version Information");
markdown.AppendLine();
markdown.AppendLine("| Field | Value |");
markdown.AppendLine("|-------|-------|");
markdown.AppendLine($"| **Version** | {ToVersion.Tag} |");
markdown.AppendLine($"| **Commit Hash** | {ToHash} |");
if (FromVersion != null)
{
markdown.AppendLine($"| **Previous Version** | {FromVersion.Tag} |");
markdown.AppendLine($"| **Previous Commit Hash** | {FromHash} |");
}
else
{
markdown.AppendLine("| **Previous Version** | N/A |");
markdown.AppendLine("| **Previous Commit Hash** | N/A |");
}
markdown.AppendLine();

// Add changes section
markdown.AppendLine($"{subHeading} Changes");
markdown.AppendLine();
markdown.AppendLine("| Issue | Title |");
markdown.AppendLine("|-------|-------|");
if (ChangeIssues.Count > 0)
{
foreach (var issue in ChangeIssues)
{
markdown.AppendLine($"| [{issue.Id}]({issue.Url}) | {issue.Title} |");
}
}
else
{
markdown.AppendLine("| N/A | N/A |");
}
markdown.AppendLine();

// Add bugs fixed section
markdown.AppendLine($"{subHeading} Bugs Fixed");
markdown.AppendLine();
markdown.AppendLine("| Issue | Title |");
markdown.AppendLine("|-------|-------|");
if (BugIssues.Count > 0)
{
foreach (var issue in BugIssues)
{
markdown.AppendLine($"| [{issue.Id}]({issue.Url}) | {issue.Title} |");
}
}
else
{
markdown.AppendLine("| N/A | N/A |");
}
markdown.AppendLine();

// Add known issues section if requested
if (includeKnownIssues)
{
markdown.AppendLine($"{subHeading} Known Issues");
markdown.AppendLine();
markdown.AppendLine("| Issue | Title |");
markdown.AppendLine("|-------|-------|");
if (KnownIssues.Count > 0)
{
foreach (var issue in KnownIssues)
{
markdown.AppendLine($"| [{issue.Id}]({issue.Url}) | {issue.Title} |");
}
}
else
{
markdown.AppendLine("| N/A | N/A |");
}
markdown.AppendLine();
}

// Return the complete markdown report
return markdown.ToString();
}

/// <summary>
/// Finds the index of a tag in the tag history by normalized version.
/// </summary>
Expand Down
152 changes: 152 additions & 0 deletions test/DemaConsulting.BuildMark.Tests/BuildInformationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,158 @@ public async Task BuildInformation_CreateAsync_HandlesFirstReleaseCorrectly()
Assert.AreEqual("v1.0.0", buildInfo.ToVersion.Tag);
}

/// <summary>
/// Test that ToMarkdown generates correct markdown with default parameters.
/// </summary>
[TestMethod]
public async Task BuildInformation_ToMarkdown_GeneratesCorrectMarkdownWithDefaults()
{
// Arrange
var connector = new MockRepoConnector();
var buildInfo = await BuildInformation.CreateAsync(connector, Version.Create("v2.0.0"));

// Act
var markdown = buildInfo.ToMarkdown();

// Assert
Assert.Contains("# Build Report", markdown);
Assert.Contains("## Version Information", markdown);
Assert.Contains("## Changes", markdown);
Assert.Contains("## Bugs Fixed", markdown);
Assert.DoesNotContain("## Known Issues", markdown);
Assert.Contains("v2.0.0", markdown);
Assert.Contains("ver-1.1.0", markdown);
}

/// <summary>
/// Test that ToMarkdown includes known issues when requested.
/// </summary>
[TestMethod]
public async Task BuildInformation_ToMarkdown_IncludesKnownIssuesWhenRequested()
{
// Arrange
var connector = new MockRepoConnector();
var buildInfo = await BuildInformation.CreateAsync(connector, Version.Create("v2.0.0"));

// Act
var markdown = buildInfo.ToMarkdown(includeKnownIssues: true);

// Assert
Assert.Contains("## Known Issues", markdown);
Assert.Contains("Known bug A", markdown);
Assert.Contains("Known bug B", markdown);
}

/// <summary>
/// Test that ToMarkdown respects custom heading depth.
/// </summary>
[TestMethod]
public async Task BuildInformation_ToMarkdown_RespectsCustomHeadingDepth()
{
// Arrange
var connector = new MockRepoConnector();
var buildInfo = await BuildInformation.CreateAsync(connector, Version.Create("v2.0.0"));

// Act
var markdown = buildInfo.ToMarkdown(headingDepth: 3);

// Assert
Assert.Contains("### Build Report", markdown);
Assert.Contains("#### Version Information", markdown);
Assert.Contains("#### Changes", markdown);
Assert.Contains("#### Bugs Fixed", markdown);
}

/// <summary>
/// Test that ToMarkdown displays N/A for empty changes table.
/// </summary>
[TestMethod]
public void BuildInformation_ToMarkdown_DisplaysNAForEmptyChanges()
{
// Arrange - Create build info with no change issues
var buildInfo = new BuildInformation(
Version.Create("v1.0.0"),
Version.Create("v1.1.0"),
"abc123",
"def456",
new List<IssueInfo>(), // No changes
new List<IssueInfo> { new IssueInfo("2", "Bug fix", "https://example.com/2") },
new List<IssueInfo>());

// Act
var markdown = buildInfo.ToMarkdown();

// Assert - Check that Changes section contains N/A
var changesSectionStart = markdown.IndexOf("## Changes", StringComparison.Ordinal);
var bugsSectionStart = markdown.IndexOf("## Bugs Fixed", StringComparison.Ordinal);
var changesSection = markdown.Substring(changesSectionStart, bugsSectionStart - changesSectionStart);
Assert.Contains("| N/A | N/A |", changesSection);
}

/// <summary>
/// Test that ToMarkdown displays N/A for empty bugs table.
/// </summary>
[TestMethod]
public void BuildInformation_ToMarkdown_DisplaysNAForEmptyBugs()
{
// Arrange - Create build info with no bug issues
var buildInfo = new BuildInformation(
Version.Create("v1.0.0"),
Version.Create("v1.1.0"),
"abc123",
"def456",
new List<IssueInfo> { new IssueInfo("1", "Feature", "https://example.com/1") },
new List<IssueInfo>(), // No bugs
new List<IssueInfo>());

// Act
var markdown = buildInfo.ToMarkdown();

// Assert - Check that Bugs Fixed section contains N/A
var bugsSectionStart = markdown.IndexOf("## Bugs Fixed", StringComparison.Ordinal);
var bugsSection = markdown.Substring(bugsSectionStart);
Assert.Contains("| N/A | N/A |", bugsSection);
}

/// <summary>
/// Test that ToMarkdown includes issue links in tables.
/// </summary>
[TestMethod]
public async Task BuildInformation_ToMarkdown_IncludesIssueLinks()
{
// Arrange
var connector = new MockRepoConnector();
var buildInfo = await BuildInformation.CreateAsync(connector, Version.Create("v2.0.0"));

// Act
var markdown = buildInfo.ToMarkdown();

// Assert
Assert.Contains("[3](https://github.com/example/repo/issues/3)", markdown);
Assert.Contains("[2](https://github.com/example/repo/issues/2)", markdown);
}

/// <summary>
/// Test that ToMarkdown handles first release with N/A for previous version.
/// </summary>
[TestMethod]
public async Task BuildInformation_ToMarkdown_HandlesFirstReleaseWithNA()
{
// Arrange
var connector = new MockRepoConnector();
var buildInfo = await BuildInformation.CreateAsync(connector, Version.Create("v1.0.0"));

// Act
var markdown = buildInfo.ToMarkdown();

// Assert
var versionInfoStart = markdown.IndexOf("## Version Information", StringComparison.Ordinal);
var changesStart = markdown.IndexOf("## Changes", StringComparison.Ordinal);
var versionInfo = markdown.Substring(versionInfoStart, changesStart - versionInfoStart);
Assert.Contains("| **Previous Version** | N/A |", versionInfo);
Assert.Contains("| **Previous Commit Hash** | N/A |", versionInfo);
}

/// <summary>
/// Mock connector with no tags.
/// </summary>
Expand Down