Skip to content

Commit 44793e0

Browse files
authored
Merge pull request #4588 from 9swampy/SanitizeParticipant
Sanitize Participant
2 parents 2f439aa + e87314a commit 44793e0

File tree

6 files changed

+109
-6
lines changed

6 files changed

+109
-6
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
using GitVersion.Testing.Helpers;
2+
3+
namespace GitVersion.Core.Tests.Helpers;
4+
5+
[TestFixture]
6+
public class ParticipantSanitizerTests
7+
{
8+
[TestCase("feature/1234-is-id-with-something-kebab", "feature_1234_is_id_with_something_kebab")]
9+
[TestCase("feature/1234-IsSomethingPascalCase", "feature_1234_IsSomethingPascalCase")]
10+
[TestCase("feature/Caps-lower-something-kebab", "feature_Caps_lower_something_kebab")]
11+
[TestCase("feature/Caps-lower-is-kebab", "feature_Caps_lower_is_kebab")]
12+
[TestCase("kebab-folder/1234-is-id-with-something-kebab", "kebab_folder_1234_is_id_with_something_kebab")]
13+
[TestCase("kebab-folder/1234-IsSomethingPascalCase", "kebab_folder_1234_IsSomethingPascalCase")]
14+
[TestCase("kebab-folder/Caps-lower-something-kebab", "kebab_folder_Caps_lower_something_kebab")]
15+
[TestCase("kebab-folder/Caps-lower-is-kebab", "kebab_folder_Caps_lower_is_kebab")]
16+
[TestCase("PascalCaseFolder/1234-is-id-with-something-kebab", "PascalCaseFolder_1234_is_id_with_something_kebab")]
17+
[TestCase("PascalCaseFolder/1234-IsSomethingPascalCase", "PascalCaseFolder_1234_IsSomethingPascalCase")]
18+
[TestCase("PascalCaseFolder/Caps-lower-something-kebab", "PascalCaseFolder_Caps_lower_something_kebab")]
19+
[TestCase("PascalCaseFolder/Caps-lower-is-kebab", "PascalCaseFolder_Caps_lower_is_kebab")]
20+
[TestCase("1234-is-id-with-something-kebab", "1234_is_id_with_something_kebab")]
21+
[TestCase("1234-IsSomethingPascalCase", "1234_IsSomethingPascalCase")]
22+
[TestCase("Caps-lower-something-kebab", "Caps_lower_something_kebab")]
23+
[TestCase("Caps-lower-is-kebab", "Caps_lower_is_kebab")]
24+
[TestCase("feature/all-lower-is-kebab", "feature_all_lower_is_kebab")]
25+
[TestCase("feature/24321-Upperjustoneword", "feature_24321_Upperjustoneword")]
26+
[TestCase("feature/justoneword", "feature_justoneword")]
27+
[TestCase("feature/PascalCase", "feature_PascalCase")]
28+
[TestCase("feature/PascalCase-with-kebab", "feature_PascalCase_with_kebab")]
29+
[TestCase("feature/12414", "feature_12414")]
30+
[TestCase("feature/12414/12342-FeatureStoryTaskWithShortDescription", "feature_12414_12342_FeatureStoryTaskWithShortDescription")]
31+
[TestCase("feature/12414/12342-Short-description", "feature_12414_12342_Short_description")]
32+
[TestCase("feature/12414/12342-short-description", "feature_12414_12342_short_description")]
33+
[TestCase("feature/12414/12342-Short-Description", "feature_12414_12342_Short_Description")]
34+
[TestCase("release/1.0.0", "release_1_0_0")]
35+
[TestCase("releases", "releases")]
36+
[TestCase("feature", "feature")]
37+
[TestCase("feature/tfs1-Short-description", "feature_tfs1_Short_description")]
38+
[TestCase("feature/f2-Short-description", "feature_f2_Short_description")]
39+
[TestCase("feature/bug1", "feature_bug1")]
40+
[TestCase("f2", "f2")]
41+
[TestCase("feature/f2", "feature_f2")]
42+
[TestCase("feature/story2", "feature_story2")]
43+
[TestCase("master", "master")]
44+
[TestCase("develop", "develop")]
45+
[TestCase("main", "main")]
46+
public void SanitizeValidParticipant_ShouldReturnExpectedResult(string input, string expected)
47+
{
48+
var actual = ParticipantSanitizer.SanitizeParticipant(input);
49+
actual.ShouldBe(expected);
50+
}
51+
52+
[TestCase("")]
53+
[TestCase(" ")]
54+
public void SanitizeEmptyOrWhitespaceParticipant_ShouldThrow(string value)
55+
{
56+
var exception = Should.Throw<ArgumentException>(() => ParticipantSanitizer.SanitizeParticipant(value));
57+
exception.Message.ShouldBe("The value cannot be an empty string or composed entirely of whitespace. (Parameter 'participant')");
58+
}
59+
60+
[TestCase("feature/")]
61+
[TestCase("/")]
62+
public void SanitizeInvalidParticipant_ShouldThrow(string value)
63+
{
64+
var exception = Should.Throw<ArgumentException>(() => ParticipantSanitizer.SanitizeParticipant(value));
65+
exception.Message.ShouldBe("The value cannot end with a folder separator ('/'). (Parameter 'participant')");
66+
}
67+
}

src/GitVersion.Core/Core/RegexPatterns.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ public static Regex GetOrAdd([StringSyntax(StringSyntaxAttribute.Regex)] string
5353
[Output.CsharpAssemblyAttributeRegexPattern] = Output.CsharpAssemblyAttributeRegex,
5454
[Output.FsharpAssemblyAttributeRegexPattern] = Output.FsharpAssemblyAttributeRegex,
5555
[Output.VisualBasicAssemblyAttributeRegexPattern] = Output.VisualBasicAssemblyAttributeRegex,
56+
[Output.SanitizeParticipantRegexPattern] = Output.SanitizeParticipantRegex,
5657
[VersionCalculation.DefaultMajorRegexPattern] = VersionCalculation.DefaultMajorRegex,
5758
[VersionCalculation.DefaultMinorRegexPattern] = VersionCalculation.DefaultMinorRegex,
5859
[VersionCalculation.DefaultPatchRegexPattern] = VersionCalculation.DefaultPatchRegex,
@@ -227,6 +228,9 @@ internal static partial class Output
227228
[StringSyntax(StringSyntaxAttribute.Regex)]
228229
internal const string VisualBasicAssemblyAttributeRegexPattern = @"(\s*\<Assembly:\s*(?:.*)\>\s*$(\r?\n)?)";
229230

231+
[StringSyntax(StringSyntaxAttribute.Regex)]
232+
internal const string SanitizeParticipantRegexPattern = "[^a-zA-Z0-9]";
233+
230234
[GeneratedRegex(AssemblyVersionRegexPattern, Options)]
231235
public static partial Regex AssemblyVersionRegex();
232236

@@ -244,6 +248,9 @@ internal static partial class Output
244248

245249
[GeneratedRegex(VisualBasicAssemblyAttributeRegexPattern, Options | RegexOptions.Multiline)]
246250
public static partial Regex VisualBasicAssemblyAttributeRegex();
251+
252+
[GeneratedRegex(SanitizeParticipantRegexPattern, Options)]
253+
public static partial Regex SanitizeParticipantRegex();
247254
}
248255

249256
internal static partial class VersionCalculation

src/GitVersion.Core/GitVersion.Core.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
<InternalsVisibleTo Include="GitVersion.Output.Tests" />
3535
<InternalsVisibleTo Include="GitVersion.App.Tests" />
3636
<InternalsVisibleTo Include="GitVersion.MsBuild.Tests" />
37+
<InternalsVisibleTo Include="GitVersion.Testing" />
3738
</ItemGroup>
3839

3940
</Project>

src/GitVersion.Testing/Fixtures/SequenceDiagram.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using GitVersion.Testing.Helpers;
12
using GitVersion.Testing.Internal;
23

34
namespace GitVersion.Testing;
@@ -39,11 +40,12 @@ public SequenceDiagram()
3940
/// </summary>
4041
public void Participant(string participant, string? @as = null)
4142
{
42-
this.participants.Add(participant, @as ?? participant);
43-
if (@as == null)
43+
var cleanParticipant = ParticipantSanitizer.SanitizeParticipant(@as ?? participant);
44+
this.participants.Add(participant, cleanParticipant);
45+
if (participant == cleanParticipant)
4446
this.diagramBuilder.AppendLineFormat("participant {0}", participant);
4547
else
46-
this.diagramBuilder.AppendLineFormat("participant \"{0}\" as {1}", participant, @as);
48+
this.diagramBuilder.AppendLineFormat("participant \"{0}\" as {1}", participant, cleanParticipant);
4749
}
4850

4951
/// <summary>

src/GitVersion.Testing/GitVersion.Testing.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,6 @@
88
<PackageReference Include="System.IO.Abstractions" />
99
<PackageReference Include="System.Reflection.Metadata" />
1010
</ItemGroup>
11-
<ItemGroup>
12-
<Compile Include="..\GitVersion.Core\Helpers\FileSystemHelper.cs" Link="Helpers\FileSystemHelper.cs" />
13-
</ItemGroup>
1411
<ItemGroup>
1512
<InternalsVisibleTo Include="GitVersion.BuildAgents.Tests" />
1613
<InternalsVisibleTo Include="GitVersion.Configuration.Tests" />
@@ -19,4 +16,7 @@
1916
<InternalsVisibleTo Include="GitVersion.App.Tests" />
2017
<InternalsVisibleTo Include="GitVersion.MsBuild.Tests" />
2118
</ItemGroup>
19+
<ItemGroup>
20+
<ProjectReference Include="..\GitVersion.Core\GitVersion.Core.csproj" />
21+
</ItemGroup>
2222
</Project>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using GitVersion.Core;
2+
3+
namespace GitVersion.Testing.Helpers;
4+
5+
public static class ParticipantSanitizer
6+
{
7+
/// <summary>
8+
/// Converts a participant identifier to a standardized format that won't break PlantUml.
9+
/// </summary>
10+
/// <param name="participant">The participant identifier to convert. This value cannot be null, empty, or consist only of whitespace.</param>
11+
public static string SanitizeParticipant(string participant)
12+
{
13+
GuardAgainstInvalidParticipants(participant);
14+
15+
return RegexPatterns.Output.SanitizeParticipantRegex().Replace(participant, "_");
16+
}
17+
18+
private static void GuardAgainstInvalidParticipants(string participant)
19+
{
20+
ArgumentException.ThrowIfNullOrWhiteSpace(participant);
21+
if (participant.EndsWith('/'))
22+
{
23+
throw new ArgumentException("The value cannot end with a folder separator ('/').", nameof(participant));
24+
}
25+
}
26+
}

0 commit comments

Comments
 (0)