Skip to content

Commit cecd80d

Browse files
committed
Sanitize Participant
1 parent df30364 commit cecd80d

File tree

6 files changed

+107
-3
lines changed

6 files changed

+107
-3
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: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ static RegexPatterns()
4343
Cache.TryAdd(Output.CsharpAssemblyAttributeRegex.ToString(), Output.CsharpAssemblyAttributeRegex);
4444
Cache.TryAdd(Output.FsharpAssemblyAttributeRegex.ToString(), Output.FsharpAssemblyAttributeRegex);
4545
Cache.TryAdd(Output.VisualBasicAssemblyAttributeRegex.ToString(), Output.VisualBasicAssemblyAttributeRegex);
46+
Cache.TryAdd(Output.SanitizeParticipantRegex.ToString(), Output.SanitizeParticipantRegex);
4647

4748
Cache.TryAdd(VersionCalculation.DefaultMajorRegex.ToString(), VersionCalculation.DefaultMajorRegex);
4849
Cache.TryAdd(VersionCalculation.DefaultMinorRegex.ToString(), VersionCalculation.DefaultMinorRegex);
@@ -183,12 +184,16 @@ internal static class Output
183184
[StringSyntax(StringSyntaxAttribute.Regex)]
184185
private const string VisualBasicAssemblyAttributeRegexPattern = @"(\s*\<Assembly:\s*(?:.*)\>\s*$(\r?\n)?)";
185186

187+
[StringSyntax(StringSyntaxAttribute.Regex)]
188+
private const string SanitizeParticipantRegexPattern = "[^a-zA-Z0-9]";
189+
186190
public static Regex AssemblyVersionRegex { get; } = new(AssemblyVersionRegexPattern, Options);
187191
public static Regex AssemblyInfoVersionRegex { get; } = new(AssemblyInfoVersionRegexPattern, Options);
188192
public static Regex AssemblyFileVersionRegex { get; } = new(AssemblyFileVersionRegexPattern, Options);
189193
public static Regex CsharpAssemblyAttributeRegex { get; } = new(CsharpAssemblyAttributeRegexPattern, Options | RegexOptions.Multiline);
190194
public static Regex FsharpAssemblyAttributeRegex { get; } = new(FsharpAssemblyAttributeRegexPattern, Options | RegexOptions.Multiline);
191195
public static Regex VisualBasicAssemblyAttributeRegex { get; } = new(VisualBasicAssemblyAttributeRegexPattern, Options | RegexOptions.Multiline);
196+
public static Regex SanitizeParticipantRegex { get; } = new(SanitizeParticipantRegexPattern, Options);
192197
}
193198

194199
internal static 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 & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,7 @@
1919
<InternalsVisibleTo Include="GitVersion.App.Tests" />
2020
<InternalsVisibleTo Include="GitVersion.MsBuild.Tests" />
2121
</ItemGroup>
22+
<ItemGroup>
23+
<ProjectReference Include="..\GitVersion.Core\GitVersion.Core.csproj" />
24+
</ItemGroup>
2225
</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)