diff --git a/Source/aweXpect.SourceGenerators/ExpectationGenerator.cs b/Source/aweXpect.SourceGenerators/ExpectationGenerator.cs index 54c224873..fb36df99d 100644 --- a/Source/aweXpect.SourceGenerators/ExpectationGenerator.cs +++ b/Source/aweXpect.SourceGenerators/ExpectationGenerator.cs @@ -104,14 +104,23 @@ private static void Execute(ExpectationToGenerate expectationToGenerate, SourceP } string? outcomeMethod = null; - string? name = null; + string? positiveName = null; + string? negativeName = null; if (attributeData.ConstructorArguments.Length == 2) { - name = attributeData.ConstructorArguments[0].Value?.ToString(); + var name = attributeData.ConstructorArguments[0].Value?.ToString(); + positiveName = name?.Replace("{Not}", ""); + negativeName = name?.Replace("{Not}", "Not"); outcomeMethod = attributeData.ConstructorArguments[1].Value?.ToString(); } + else if (attributeData.ConstructorArguments.Length == 3) + { + positiveName = attributeData.ConstructorArguments[0].Value?.ToString(); + negativeName = attributeData.ConstructorArguments[1].Value?.ToString(); + outcomeMethod = attributeData.ConstructorArguments[2].Value?.ToString(); + } - if (outcomeMethod == null || name == null) + if (outcomeMethod == null || positiveName == null) { return null; } @@ -125,7 +134,8 @@ private static void Execute(ExpectationToGenerate expectationToGenerate, SourceP containingNamespace, classSymbol.Name, targetType, - name, + positiveName, + negativeName, outcomeMethod, attributeData); } diff --git a/Source/aweXpect.SourceGenerators/Helpers/ExpectationToGenerate.cs b/Source/aweXpect.SourceGenerators/Helpers/ExpectationToGenerate.cs index b591342fc..5d380bcd1 100644 --- a/Source/aweXpect.SourceGenerators/Helpers/ExpectationToGenerate.cs +++ b/Source/aweXpect.SourceGenerators/Helpers/ExpectationToGenerate.cs @@ -7,25 +7,35 @@ internal readonly record struct ExpectationToGenerate public ExpectationToGenerate(string @namespace, string className, INamedTypeSymbol targetType, - string name, + string positiveName, + string? negativeName, string outcomeMethod, AttributeData attributeData) { Namespace = @namespace; ClassName = className; TargetType = targetType.ToDisplayString(); - Name = name.Replace("{Not}", ""); - NegatedName = name.Replace("{Not}", "Not"); - IncludeNegated = name.Contains("{Not}"); + Name = positiveName; + NegatedName = negativeName; + IncludeNegated = negativeName is not null; OutcomeMethod = outcomeMethod; - string expectationText = outcomeMethod; + string? positiveExpectationText = null; + string? negativeExpectationText = null; foreach (KeyValuePair namedArgument in attributeData.NamedArguments) { switch (namedArgument.Key) { case "ExpectationText": - expectationText = namedArgument.Value.Value?.ToString() ?? expectationText; + string? expectationText = namedArgument.Value.Value?.ToString(); + positiveExpectationText = expectationText?.Replace("{not}", "").Replace(" ", " "); + negativeExpectationText = expectationText?.Replace("{not}", " not ").Replace(" ", " "); + break; + case "PositiveExpectationText": + positiveExpectationText = namedArgument.Value.Value?.ToString(); + break; + case "NegativeExpectationText": + negativeExpectationText = namedArgument.Value.Value?.ToString(); break; case "Remarks": Remarks = namedArgument.Value.Value?.ToString(); @@ -44,8 +54,8 @@ public ExpectationToGenerate(string @namespace, TargetType += "?"; } - ExpectationText = expectationText.Replace("{not}", "").Replace(" ", " "); - NegatedExpectationText = expectationText.Replace("{not}", " not ").Replace(" ", " "); + ExpectationText = positiveExpectationText ?? positiveName; + NegatedExpectationText = negativeExpectationText ?? $"not {positiveName}"; FileName = $"{ClassName}.{Name}.g.cs"; } @@ -53,7 +63,7 @@ public ExpectationToGenerate(string @namespace, public string FileName { get; } public bool IncludeNegated { get; } public bool IsNullable { get; } - public string NegatedName { get; } + public string? NegatedName { get; } public string Namespace { get; } public string ClassName { get; } public string TargetType { get; } diff --git a/Source/aweXpect.SourceGenerators/Helpers/SourceGenerationHelper.cs b/Source/aweXpect.SourceGenerators/Helpers/SourceGenerationHelper.cs index c27eb08fa..19439fc8a 100644 --- a/Source/aweXpect.SourceGenerators/Helpers/SourceGenerationHelper.cs +++ b/Source/aweXpect.SourceGenerators/Helpers/SourceGenerationHelper.cs @@ -16,19 +16,34 @@ namespace aweXpect.SourceGenerators; [System.AttributeUsage(System.AttributeTargets.Class, AllowMultiple = true)] internal class CreateExpectationOnAttribute : System.Attribute { - public CreateExpectationOnAttribute(string methodName, string name) - { - TargetType = typeof(TTarget); - MethodName = methodName; - Name = name; - } - - public Type TargetType { get; } - public string MethodName { get; } - public string Name { get; set; } - public string? ExpectationText { get; set; } - public string? Remarks { get; set; } - public string[] Using { get; set; } = []; + public CreateExpectationOnAttribute(string name, string outcomeMethod) + { + TargetType = typeof(TTarget); + PositiveName = name.Replace("{Not}", ""); + if (name.Contains("{Not}")) + { + NegativeName = name.Replace("{Not}", "Not"); + } + OutcomeMethod = outcomeMethod; + } + + public CreateExpectationOnAttribute(string positiveName, string negativeName, string outcomeMethod) + { + TargetType = typeof(TTarget); + PositiveName = positiveName; + NegativeName = negativeName; + OutcomeMethod = outcomeMethod; + } + + public Type TargetType { get; } + public string PositiveName { get; } + public string? NegativeName { get; } + public string OutcomeMethod { get; set; } + public string? ExpectationText { get; set; } + public string? PositiveExpectationText { get; set; } + public string? NegativeExpectationText { get; set; } + public string? Remarks { get; set; } + public string[] Using { get; set; } = []; } #nullable disable """; @@ -47,19 +62,34 @@ namespace aweXpect.SourceGenerators; [System.AttributeUsage(System.AttributeTargets.Class, AllowMultiple = true)] internal class CreateExpectationOnNullableAttribute : System.Attribute { - public CreateExpectationOnNullableAttribute(string methodName, string name) - { - TargetType = typeof(TTarget); - MethodName = methodName; - Name = name; - } - - public Type TargetType { get; } - public string MethodName { get; } - public string Name { get; set; } - public string? ExpectationText { get; set; } - public string? Remarks { get; set; } - public string[] Using { get; set; } = []; + public CreateExpectationOnNullableAttribute(string name, string outcomeMethod) + { + TargetType = typeof(TTarget); + PositiveName = name.Replace("{Not}", ""); + if (name.Contains("{Not}")) + { + NegativeName = name.Replace("{Not}", "Not"); + } + OutcomeMethod = outcomeMethod; + } + + public CreateExpectationOnNullableAttribute(string positiveName, string negativeName, string outcomeMethod) + { + TargetType = typeof(TTarget); + PositiveName = positiveName; + NegativeName = negativeName; + OutcomeMethod = outcomeMethod; + } + + public Type TargetType { get; } + public string PositiveName { get; } + public string? NegativeName { get; } + public string OutcomeMethod { get; set; } + public string? ExpectationText { get; set; } + public string? PositiveExpectationText { get; set; } + public string? NegativeExpectationText { get; set; } + public string? Remarks { get; set; } + public string[] Using { get; set; } = []; } #nullable disable """; @@ -78,26 +108,26 @@ namespace {{expectationToGenerate.Namespace}}; #nullable enable public static partial class {{expectationToGenerate.ClassName}} { - /// - /// Verifies that the subject {{expectationToGenerate.ExpectationText}}. - /// {{expectationToGenerate.AppendRemarks()}} - public static AndOrResult<{{expectationToGenerate.TargetType}}, IThat<{{expectationToGenerate.TargetType}}>> {{expectationToGenerate.Name}}(this IThat<{{expectationToGenerate.TargetType}}> source) - => new(source.Get().ExpectationBuilder.AddConstraint((it, grammars) => - new {{expectationToGenerate.Name}}Constraint(it, grammars)), - source); + /// + /// Verifies that the subject {{expectationToGenerate.ExpectationText}}. + /// {{expectationToGenerate.AppendRemarks()}} + public static AndOrResult<{{expectationToGenerate.TargetType}}, IThat<{{expectationToGenerate.TargetType}}>> {{expectationToGenerate.Name}}(this IThat<{{expectationToGenerate.TargetType}}> source) + => new(source.Get().ExpectationBuilder.AddConstraint((it, grammars) => + new {{expectationToGenerate.Name}}Constraint(it, grammars)), + source); """; if (expectationToGenerate.IncludeNegated) { result += $$""" - /// - /// Verifies that the subject {{expectationToGenerate.NegatedExpectationText}}. - /// {{expectationToGenerate.AppendRemarks()}} - public static AndOrResult<{{expectationToGenerate.TargetType}}, IThat<{{expectationToGenerate.TargetType}}>> {{expectationToGenerate.NegatedName}}(this IThat<{{expectationToGenerate.TargetType}}> source) - => new(source.Get().ExpectationBuilder.AddConstraint((it, grammars) => - new {{expectationToGenerate.Name}}Constraint(it, grammars).Invert()), - source); + /// + /// Verifies that the subject {{expectationToGenerate.NegatedExpectationText}}. + /// {{expectationToGenerate.AppendRemarks()}} + public static AndOrResult<{{expectationToGenerate.TargetType}}, IThat<{{expectationToGenerate.TargetType}}>> {{expectationToGenerate.NegatedName}}(this IThat<{{expectationToGenerate.TargetType}}> source) + => new(source.Get().ExpectationBuilder.AddConstraint((it, grammars) => + new {{expectationToGenerate.Name}}Constraint(it, grammars).Invert()), + source); """; @@ -113,7 +143,7 @@ private sealed class {{expectationToGenerate.Name}}Constraint(string it, Expecta public ConstraintResult IsMetBy({{expectationToGenerate.TargetType}} actual) { Actual = actual; - Outcome = actual is not null && {{expectationToGenerate.OutcomeMethod.Replace("{value}", "actual.Value")}} ? Outcome.Success : Outcome.Failure; + Outcome = actual is not null && {{expectationToGenerate.OutcomeMethod.Replace("{value}", "actual")}} ? Outcome.Success : Outcome.Failure; return this; } diff --git a/Source/aweXpect/That/Chars/ThatNullableChar.IsALetter.cs b/Source/aweXpect/That/Chars/ThatNullableChar.IsALetter.cs index 2a477bd4f..c7aabbc3f 100644 --- a/Source/aweXpect/That/Chars/ThatNullableChar.IsALetter.cs +++ b/Source/aweXpect/That/Chars/ThatNullableChar.IsALetter.cs @@ -6,7 +6,7 @@ namespace aweXpect; -[CreateExpectationOnNullable("Is{Not}ALetter", "char.IsLetter({value})", +[CreateExpectationOnNullable("Is{Not}ALetter", "char.IsLetter({value}.Value)", ExpectationText = "is {not} a letter", Remarks = """ This means, that the specified Unicode character is categorized as a Unicode letter.
diff --git a/Source/aweXpect/That/Chars/ThatNullableChar.IsANumber.cs b/Source/aweXpect/That/Chars/ThatNullableChar.IsANumber.cs index 78687201e..328a9b6fa 100644 --- a/Source/aweXpect/That/Chars/ThatNullableChar.IsANumber.cs +++ b/Source/aweXpect/That/Chars/ThatNullableChar.IsANumber.cs @@ -6,7 +6,7 @@ namespace aweXpect; -[CreateExpectationOnNullable("Is{Not}ANumber", "char.IsNumber({value})", +[CreateExpectationOnNullable("Is{Not}ANumber", "char.IsNumber({value}.Value)", ExpectationText = "is {not} a number", Remarks = """ This means, that the specified Unicode character is categorized as a number.
diff --git a/Source/aweXpect/That/Chars/ThatNullableChar.IsAnAsciiLetter.cs b/Source/aweXpect/That/Chars/ThatNullableChar.IsAnAsciiLetter.cs index c11ca4364..b51556fe1 100644 --- a/Source/aweXpect/That/Chars/ThatNullableChar.IsAnAsciiLetter.cs +++ b/Source/aweXpect/That/Chars/ThatNullableChar.IsAnAsciiLetter.cs @@ -7,7 +7,7 @@ namespace aweXpect; #if NET8_0_OR_GREATER -[CreateExpectationOnNullable("Is{Not}AnAsciiLetter", "char.IsAsciiLetter({value})", +[CreateExpectationOnNullable("Is{Not}AnAsciiLetter", "char.IsAsciiLetter({value}.Value)", ExpectationText = "is {not} an ASCII letter", Remarks = """ This means, that the specified Unicode character is categorized as an ASCII letter.
@@ -15,7 +15,7 @@ namespace aweXpect; """ )] #else -[CreateExpectationOnNullable("Is{Not}AnAsciiLetter", "{value} is >= 'a' and <= 'z' or >= 'A' and <= 'Z'", +[CreateExpectationOnNullable("Is{Not}AnAsciiLetter", "{value}.Value is >= 'a' and <= 'z' or >= 'A' and <= 'Z'", ExpectationText = "is {not} an ASCII letter" )] #endif diff --git a/Source/aweXpect/That/Chars/ThatNullableChar.IsWhiteSpace.cs b/Source/aweXpect/That/Chars/ThatNullableChar.IsWhiteSpace.cs index f98a91c2f..a758f9b7e 100644 --- a/Source/aweXpect/That/Chars/ThatNullableChar.IsWhiteSpace.cs +++ b/Source/aweXpect/That/Chars/ThatNullableChar.IsWhiteSpace.cs @@ -6,7 +6,7 @@ namespace aweXpect; -[CreateExpectationOnNullable("Is{Not}WhiteSpace", "char.IsWhiteSpace({value})", +[CreateExpectationOnNullable("Is{Not}WhiteSpace", "char.IsWhiteSpace({value}.Value)", ExpectationText = "is {not} white-space", Remarks = """ This means, that the specified Unicode character is categorized as white-space.
diff --git a/Source/aweXpect/That/Uris/ThatUri.HasDefaultPort.cs b/Source/aweXpect/That/Uris/ThatUri.HasDefaultPort.cs new file mode 100644 index 000000000..c87ffc201 --- /dev/null +++ b/Source/aweXpect/That/Uris/ThatUri.HasDefaultPort.cs @@ -0,0 +1,14 @@ +using System; +using aweXpect.SourceGenerators; + +namespace aweXpect; + +[CreateExpectationOn("HasDefaultPort", "DoesNotHaveDefaultPort", "{value}.IsDefaultPort", + PositiveExpectationText = "has the default port for the used scheme", + NegativeExpectationText = "does not have the default port for the used scheme", + Using = ["System",], + Remarks = """ + + """ +)] +public static partial class ThatUri; diff --git a/Source/aweXpect/That/Uris/ThatUri.IsAbsolute.cs b/Source/aweXpect/That/Uris/ThatUri.IsAbsolute.cs new file mode 100644 index 000000000..8ed7ef050 --- /dev/null +++ b/Source/aweXpect/That/Uris/ThatUri.IsAbsolute.cs @@ -0,0 +1,13 @@ +using System; +using aweXpect.SourceGenerators; + +namespace aweXpect; + +[CreateExpectationOn("Is{Not}Absolute", "{value}.IsAbsoluteUri", + ExpectationText = "is {not} an absolute URI", + Using = ["System",], + Remarks = """ + + """ +)] +public static partial class ThatUri; diff --git a/Source/aweXpect/That/Uris/ThatUri.IsFile.cs b/Source/aweXpect/That/Uris/ThatUri.IsFile.cs new file mode 100644 index 000000000..acc4987fa --- /dev/null +++ b/Source/aweXpect/That/Uris/ThatUri.IsFile.cs @@ -0,0 +1,13 @@ +using System; +using aweXpect.SourceGenerators; + +namespace aweXpect; + +[CreateExpectationOn("Is{Not}File", "{value}.IsFile", + ExpectationText = "is {not} a file URI", + Using = ["System",], + Remarks = """ + + """ +)] +public static partial class ThatUri; diff --git a/Source/aweXpect/That/Uris/ThatUri.IsLoopback.cs b/Source/aweXpect/That/Uris/ThatUri.IsLoopback.cs new file mode 100644 index 000000000..4f0031ed9 --- /dev/null +++ b/Source/aweXpect/That/Uris/ThatUri.IsLoopback.cs @@ -0,0 +1,14 @@ +using System; +using aweXpect.SourceGenerators; + +namespace aweXpect; + +[CreateExpectationOn("Is{Not}Loopback", "{value}.IsLoopback", + PositiveExpectationText = "references the local host", + NegativeExpectationText = "does not reference the local host", + Using = ["System",], + Remarks = """ + + """ +)] +public static partial class ThatUri; diff --git a/Source/aweXpect/That/Uris/ThatUri.IsUnc.cs b/Source/aweXpect/That/Uris/ThatUri.IsUnc.cs new file mode 100644 index 000000000..4f5ba2c44 --- /dev/null +++ b/Source/aweXpect/That/Uris/ThatUri.IsUnc.cs @@ -0,0 +1,13 @@ +using System; +using aweXpect.SourceGenerators; + +namespace aweXpect; + +[CreateExpectationOn("Is{Not}Unc", "{value}.IsUnc", + ExpectationText = "is {not} an UNC path", + Using = ["System",], + Remarks = """ + + """ +)] +public static partial class ThatUri; diff --git a/Source/aweXpect/That/Uris/ThatUri.cs b/Source/aweXpect/That/Uris/ThatUri.cs new file mode 100644 index 000000000..38e88bcb4 --- /dev/null +++ b/Source/aweXpect/That/Uris/ThatUri.cs @@ -0,0 +1,8 @@ +using System; + +namespace aweXpect; + +/// +/// Expectations on values. +/// +public static partial class ThatUri; diff --git a/Source/aweXpect/aweXpect.csproj.DotSettings b/Source/aweXpect/aweXpect.csproj.DotSettings index 138aaf1ff..057d7d4fb 100644 --- a/Source/aweXpect/aweXpect.csproj.DotSettings +++ b/Source/aweXpect/aweXpect.csproj.DotSettings @@ -21,4 +21,5 @@ True True True - True + True + True diff --git a/Tests/aweXpect.Api.Tests/Expected/aweXpect_net8.0.txt b/Tests/aweXpect.Api.Tests/Expected/aweXpect_net8.0.txt index 23f05b64d..6cccc978e 100644 --- a/Tests/aweXpect.Api.Tests/Expected/aweXpect_net8.0.txt +++ b/Tests/aweXpect.Api.Tests/Expected/aweXpect_net8.0.txt @@ -1232,6 +1232,19 @@ namespace aweXpect public static aweXpect.Results.TimeToleranceResult> IsOneOf(this aweXpect.Core.IThat source, params System.TimeSpan?[] expected) { } public static aweXpect.Results.AndOrResult> IsPositive(this aweXpect.Core.IThat source) { } } + public static class ThatUri + { + public static aweXpect.Results.AndOrResult> DoesNotHaveDefaultPort(this aweXpect.Core.IThat source) { } + public static aweXpect.Results.AndOrResult> HasDefaultPort(this aweXpect.Core.IThat source) { } + public static aweXpect.Results.AndOrResult> IsAbsolute(this aweXpect.Core.IThat source) { } + public static aweXpect.Results.AndOrResult> IsFile(this aweXpect.Core.IThat source) { } + public static aweXpect.Results.AndOrResult> IsLoopback(this aweXpect.Core.IThat source) { } + public static aweXpect.Results.AndOrResult> IsNotAbsolute(this aweXpect.Core.IThat source) { } + public static aweXpect.Results.AndOrResult> IsNotFile(this aweXpect.Core.IThat source) { } + public static aweXpect.Results.AndOrResult> IsNotLoopback(this aweXpect.Core.IThat source) { } + public static aweXpect.Results.AndOrResult> IsNotUnc(this aweXpect.Core.IThat source) { } + public static aweXpect.Results.AndOrResult> IsUnc(this aweXpect.Core.IThat source) { } + } } namespace aweXpect.Equivalency { diff --git a/Tests/aweXpect.Api.Tests/Expected/aweXpect_netstandard2.0.txt b/Tests/aweXpect.Api.Tests/Expected/aweXpect_netstandard2.0.txt index 341c17493..cdbc04eea 100644 --- a/Tests/aweXpect.Api.Tests/Expected/aweXpect_netstandard2.0.txt +++ b/Tests/aweXpect.Api.Tests/Expected/aweXpect_netstandard2.0.txt @@ -1178,6 +1178,19 @@ namespace aweXpect public static aweXpect.Results.TimeToleranceResult> IsOneOf(this aweXpect.Core.IThat source, params System.TimeSpan?[] expected) { } public static aweXpect.Results.AndOrResult> IsPositive(this aweXpect.Core.IThat source) { } } + public static class ThatUri + { + public static aweXpect.Results.AndOrResult> DoesNotHaveDefaultPort(this aweXpect.Core.IThat source) { } + public static aweXpect.Results.AndOrResult> HasDefaultPort(this aweXpect.Core.IThat source) { } + public static aweXpect.Results.AndOrResult> IsAbsolute(this aweXpect.Core.IThat source) { } + public static aweXpect.Results.AndOrResult> IsFile(this aweXpect.Core.IThat source) { } + public static aweXpect.Results.AndOrResult> IsLoopback(this aweXpect.Core.IThat source) { } + public static aweXpect.Results.AndOrResult> IsNotAbsolute(this aweXpect.Core.IThat source) { } + public static aweXpect.Results.AndOrResult> IsNotFile(this aweXpect.Core.IThat source) { } + public static aweXpect.Results.AndOrResult> IsNotLoopback(this aweXpect.Core.IThat source) { } + public static aweXpect.Results.AndOrResult> IsNotUnc(this aweXpect.Core.IThat source) { } + public static aweXpect.Results.AndOrResult> IsUnc(this aweXpect.Core.IThat source) { } + } } namespace aweXpect.Equivalency { diff --git a/Tests/aweXpect.Tests/Uris/ThatUri.DoesNotHaveDefaultPort.Tests.cs b/Tests/aweXpect.Tests/Uris/ThatUri.DoesNotHaveDefaultPort.Tests.cs new file mode 100644 index 000000000..29c822838 --- /dev/null +++ b/Tests/aweXpect.Tests/Uris/ThatUri.DoesNotHaveDefaultPort.Tests.cs @@ -0,0 +1,77 @@ +namespace aweXpect.Tests; + +public sealed partial class ThatUri +{ + public sealed class DoesNotHaveDefaultPort + { + public sealed class Tests + { + [Theory] + [InlineData("https://www.awexpect.com:80")] + [InlineData("http://www.example.com:443")] + public async Task WhenSubjectDoesNotHaveTheDefaultPort_ShouldSucceed(string uriString) + { + Uri subject = new(uriString); + + async Task Act() + => await That(subject).DoesNotHaveDefaultPort(); + + await That(Act).DoesNotThrow(); + } + + [Theory] + [InlineData("https://www.awexpect.com")] + [InlineData("https://www.awexpect.com:443")] + [InlineData("http://www.example.com:80")] + public async Task WhenSubjectHasTheDefaultPort_ShouldFail(string uriString) + { + Uri subject = new(uriString); + + async Task Act() + => await That(subject).DoesNotHaveDefaultPort(); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + does not have the default port for the used scheme, + but it was {subject} + """); + } + } + + public sealed class NegatedTests + { + [Theory] + [InlineData("https://www.awexpect.com:80")] + [InlineData("http://www.example.com:443")] + public async Task WhenSubjectDoesNotHaveTheDefaultPort_ShouldFail(string uriString) + { + Uri subject = new(uriString); + + async Task Act() + => await That(subject).DoesNotComplyWith(it => it.DoesNotHaveDefaultPort()); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + has the default port for the used scheme, + but it was {subject} + """); + } + + [Theory] + [InlineData("https://www.awexpect.com")] + [InlineData("https://www.awexpect.com:443")] + [InlineData("http://www.example.com:80")] + public async Task WhenSubjectHasTheDefaultPort_ShouldSucceed(string uriString) + { + Uri subject = new(uriString); + + async Task Act() + => await That(subject).DoesNotComplyWith(it => it.DoesNotHaveDefaultPort()); + + await That(Act).DoesNotThrow(); + } + } + } +} diff --git a/Tests/aweXpect.Tests/Uris/ThatUri.HasDefaultPort.Tests.cs b/Tests/aweXpect.Tests/Uris/ThatUri.HasDefaultPort.Tests.cs new file mode 100644 index 000000000..5db5e5ac5 --- /dev/null +++ b/Tests/aweXpect.Tests/Uris/ThatUri.HasDefaultPort.Tests.cs @@ -0,0 +1,77 @@ +namespace aweXpect.Tests; + +public sealed partial class ThatUri +{ + public sealed class HasDefaultPort + { + public sealed class Tests + { + [Theory] + [InlineData("https://www.awexpect.com:80")] + [InlineData("http://www.example.com:443")] + public async Task WhenSubjectDoesNotHaveTheDefaultPort_ShouldFail(string uriString) + { + Uri subject = new(uriString); + + async Task Act() + => await That(subject).HasDefaultPort(); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + has the default port for the used scheme, + but it was {subject} + """); + } + + [Theory] + [InlineData("https://www.awexpect.com")] + [InlineData("https://www.awexpect.com:443")] + [InlineData("http://www.example.com:80")] + public async Task WhenSubjectHasTheDefaultPort_ShouldSucceed(string uriString) + { + Uri subject = new(uriString); + + async Task Act() + => await That(subject).HasDefaultPort(); + + await That(Act).DoesNotThrow(); + } + } + + public sealed class NegatedTests + { + [Theory] + [InlineData("https://www.awexpect.com:80")] + [InlineData("http://www.example.com:443")] + public async Task WhenSubjectDoesNotHaveTheDefaultPort_ShouldSucceed(string uriString) + { + Uri subject = new(uriString); + + async Task Act() + => await That(subject).DoesNotComplyWith(it => it.HasDefaultPort()); + + await That(Act).DoesNotThrow(); + } + + [Theory] + [InlineData("https://www.awexpect.com")] + [InlineData("https://www.awexpect.com:443")] + [InlineData("http://www.example.com:80")] + public async Task WhenSubjectHasTheDefaultPort_ShouldFail(string uriString) + { + Uri subject = new(uriString); + + async Task Act() + => await That(subject).DoesNotComplyWith(it => it.HasDefaultPort()); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + does not have the default port for the used scheme, + but it was {subject} + """); + } + } + } +} diff --git a/Tests/aweXpect.Tests/Uris/ThatUri.IsAbsolute.Tests.cs b/Tests/aweXpect.Tests/Uris/ThatUri.IsAbsolute.Tests.cs new file mode 100644 index 000000000..7d95f4600 --- /dev/null +++ b/Tests/aweXpect.Tests/Uris/ThatUri.IsAbsolute.Tests.cs @@ -0,0 +1,69 @@ +namespace aweXpect.Tests; + +public sealed partial class ThatUri +{ + public sealed class IsAbsolute + { + public sealed class Tests + { + [Fact] + public async Task WhenSubjectIsAbsolute_ShouldSucceed() + { + Uri subject = new("https://www.awexpect.com"); + + async Task Act() + => await That(subject).IsAbsolute(); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenSubjectIsNotAbsolute_ShouldFail() + { + Uri subject = new Uri("https://www.awexpect.com") + .MakeRelativeUri(new Uri("https://www.awexpect.com/foo/bar")); + + async Task Act() + => await That(subject).IsAbsolute(); + + await That(Act).Throws() + .WithMessage(""" + Expected that subject + is an absolute URI, + but it was foo/bar + """); + } + } + + public sealed class NegatedTests + { + [Fact] + public async Task WhenSubjectIsAbsolute_ShouldFail() + { + Uri subject = new("https://www.awexpect.com"); + + async Task Act() + => await That(subject).DoesNotComplyWith(it => it.IsAbsolute()); + + await That(Act).Throws() + .WithMessage(""" + Expected that subject + is not an absolute URI, + but it was https://www.awexpect.com/ + """); + } + + [Fact] + public async Task WhenSubjectIsNotAbsolute_ShouldSucceed() + { + Uri subject = new Uri("https://www.awexpect.com") + .MakeRelativeUri(new Uri("https://www.awexpect.com/foo/bar")); + + async Task Act() + => await That(subject).DoesNotComplyWith(it => it.IsAbsolute()); + + await That(Act).DoesNotThrow(); + } + } + } +} diff --git a/Tests/aweXpect.Tests/Uris/ThatUri.IsFile.Tests.cs b/Tests/aweXpect.Tests/Uris/ThatUri.IsFile.Tests.cs new file mode 100644 index 000000000..ac4a575fa --- /dev/null +++ b/Tests/aweXpect.Tests/Uris/ThatUri.IsFile.Tests.cs @@ -0,0 +1,67 @@ +namespace aweXpect.Tests; + +public sealed partial class ThatUri +{ + public sealed class IsFile + { + public sealed class Tests + { + [Fact] + public async Task WhenSubjectIsAFileUri_ShouldSucceed() + { + Uri subject = new("file://server/filename.ext"); + + async Task Act() + => await That(subject).IsFile(); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenSubjectIsNotAFileUri_ShouldFail() + { + Uri subject = new("https://www.awexpect.com"); + + async Task Act() + => await That(subject).IsFile(); + + await That(Act).Throws() + .WithMessage(""" + Expected that subject + is a file URI, + but it was https://www.awexpect.com/ + """); + } + } + + public sealed class NegatedTests + { + [Fact] + public async Task WhenSubjectIsAFileUri_ShouldFail() + { + Uri subject = new("file://server/filename.ext"); + + async Task Act() + => await That(subject).DoesNotComplyWith(it => it.IsFile()); + + await That(Act).Throws() + .WithMessage(""" + Expected that subject + is not a file URI, + but it was file://server/filename.ext + """); + } + + [Fact] + public async Task WhenSubjectIsNotAFileUri_ShouldSucceed() + { + Uri subject = new("https://www.awexpect.com"); + + async Task Act() + => await That(subject).DoesNotComplyWith(it => it.IsFile()); + + await That(Act).DoesNotThrow(); + } + } + } +} diff --git a/Tests/aweXpect.Tests/Uris/ThatUri.IsLoopback.Tests.cs b/Tests/aweXpect.Tests/Uris/ThatUri.IsLoopback.Tests.cs new file mode 100644 index 000000000..dbdf7ad58 --- /dev/null +++ b/Tests/aweXpect.Tests/Uris/ThatUri.IsLoopback.Tests.cs @@ -0,0 +1,73 @@ +namespace aweXpect.Tests; + +public sealed partial class ThatUri +{ + public sealed class IsLoopback + { + public sealed class Tests + { + [Theory] + [InlineData("127.0.0.1")] + [InlineData("loopback")] + [InlineData("localhost")] + public async Task WhenSubjectIsLoopback_ShouldSucceed(string uriString) + { + Uri subject = new UriBuilder(uriString).Uri; + + async Task Act() + => await That(subject).IsLoopback(); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenSubjectIsNotLoopback_ShouldFail() + { + Uri subject = new("https://www.awexpect.com"); + + async Task Act() + => await That(subject).IsLoopback(); + + await That(Act).Throws() + .WithMessage(""" + Expected that subject + references the local host, + but it was https://www.awexpect.com/ + """); + } + } + + public sealed class NegatedTests + { + [Theory] + [InlineData("127.0.0.1")] + [InlineData("loopback")] + [InlineData("localhost")] + public async Task WhenSubjectIsLoopback_ShouldFail(string uriString) + { + Uri subject = new UriBuilder(uriString).Uri; + + async Task Act() + => await That(subject).DoesNotComplyWith(it => it.IsLoopback()); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + does not reference the local host, + but it was {subject} + """); + } + + [Fact] + public async Task WhenSubjectIsNotLoopback_ShouldSucceed() + { + Uri subject = new("https://www.awexpect.com"); + + async Task Act() + => await That(subject).DoesNotComplyWith(it => it.IsLoopback()); + + await That(Act).DoesNotThrow(); + } + } + } +} diff --git a/Tests/aweXpect.Tests/Uris/ThatUri.IsNotAbsolute.Tests.cs b/Tests/aweXpect.Tests/Uris/ThatUri.IsNotAbsolute.Tests.cs new file mode 100644 index 000000000..e3fd08db4 --- /dev/null +++ b/Tests/aweXpect.Tests/Uris/ThatUri.IsNotAbsolute.Tests.cs @@ -0,0 +1,69 @@ +namespace aweXpect.Tests; + +public sealed partial class ThatUri +{ + public sealed class IsNotAbsolute + { + public sealed class Tests + { + [Fact] + public async Task WhenSubjectIsAbsolute_ShouldFail() + { + Uri subject = new("https://www.awexpect.com"); + + async Task Act() + => await That(subject).IsNotAbsolute(); + + await That(Act).Throws() + .WithMessage(""" + Expected that subject + is not an absolute URI, + but it was https://www.awexpect.com/ + """); + } + + [Fact] + public async Task WhenSubjectIsNotAbsolute_ShouldSucceed() + { + Uri subject = new Uri("https://www.awexpect.com") + .MakeRelativeUri(new Uri("https://www.awexpect.com/foo/bar")); + + async Task Act() + => await That(subject).IsNotAbsolute(); + + await That(Act).DoesNotThrow(); + } + } + + public sealed class NegatedTests + { + [Fact] + public async Task WhenSubjectIsAbsolute_ShouldSucceed() + { + Uri subject = new("https://www.awexpect.com"); + + async Task Act() + => await That(subject).DoesNotComplyWith(it => it.IsNotAbsolute()); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenSubjectIsNotAbsolute_ShouldFail() + { + Uri subject = new Uri("https://www.awexpect.com") + .MakeRelativeUri(new Uri("https://www.awexpect.com/foo/bar")); + + async Task Act() + => await That(subject).DoesNotComplyWith(it => it.IsNotAbsolute()); + + await That(Act).Throws() + .WithMessage(""" + Expected that subject + is an absolute URI, + but it was foo/bar + """); + } + } + } +} diff --git a/Tests/aweXpect.Tests/Uris/ThatUri.IsNotFile.Tests.cs b/Tests/aweXpect.Tests/Uris/ThatUri.IsNotFile.Tests.cs new file mode 100644 index 000000000..dc6e66551 --- /dev/null +++ b/Tests/aweXpect.Tests/Uris/ThatUri.IsNotFile.Tests.cs @@ -0,0 +1,67 @@ +namespace aweXpect.Tests; + +public sealed partial class ThatUri +{ + public sealed class IsNotFile + { + public sealed class Tests + { + [Fact] + public async Task WhenSubjectIsAFileUri_ShouldFail() + { + Uri subject = new("file://server/filename.ext"); + + async Task Act() + => await That(subject).IsNotFile(); + + await That(Act).Throws() + .WithMessage(""" + Expected that subject + is not a file URI, + but it was file://server/filename.ext + """); + } + + [Fact] + public async Task WhenSubjectIsNotAFileUri_ShouldSucceed() + { + Uri subject = new("https://www.awexpect.com"); + + async Task Act() + => await That(subject).IsNotFile(); + + await That(Act).DoesNotThrow(); + } + } + + public sealed class NegatedTests + { + [Fact] + public async Task WhenSubjectIsAFileUri_ShouldSucceed() + { + Uri subject = new("file://server/filename.ext"); + + async Task Act() + => await That(subject).DoesNotComplyWith(it => it.IsNotFile()); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenSubjectIsNotAFileUri_ShouldFail() + { + Uri subject = new("https://www.awexpect.com"); + + async Task Act() + => await That(subject).DoesNotComplyWith(it => it.IsNotFile()); + + await That(Act).Throws() + .WithMessage(""" + Expected that subject + is a file URI, + but it was https://www.awexpect.com/ + """); + } + } + } +} diff --git a/Tests/aweXpect.Tests/Uris/ThatUri.IsNotLoopback.Tests.cs b/Tests/aweXpect.Tests/Uris/ThatUri.IsNotLoopback.Tests.cs new file mode 100644 index 000000000..0d1964df0 --- /dev/null +++ b/Tests/aweXpect.Tests/Uris/ThatUri.IsNotLoopback.Tests.cs @@ -0,0 +1,73 @@ +namespace aweXpect.Tests; + +public sealed partial class ThatUri +{ + public sealed class IsNotLoopback + { + public sealed class Tests + { + [Theory] + [InlineData("127.0.0.1")] + [InlineData("loopback")] + [InlineData("localhost")] + public async Task WhenSubjectIsNotLoopback_ShouldFail(string uriString) + { + Uri subject = new UriBuilder(uriString).Uri; + + async Task Act() + => await That(subject).IsNotLoopback(); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + does not reference the local host, + but it was {subject} + """); + } + + [Fact] + public async Task WhenSubjectIsNotLoopback_ShouldSucceed() + { + Uri subject = new("https://www.awexpect.com"); + + async Task Act() + => await That(subject).IsNotLoopback(); + + await That(Act).DoesNotThrow(); + } + } + + public sealed class NegatedTests + { + [Fact] + public async Task WhenSubjectIsNotLoopback_ShouldFail() + { + Uri subject = new("https://www.awexpect.com"); + + async Task Act() + => await That(subject).DoesNotComplyWith(it => it.IsNotLoopback()); + + await That(Act).Throws() + .WithMessage(""" + Expected that subject + references the local host, + but it was https://www.awexpect.com/ + """); + } + + [Theory] + [InlineData("127.0.0.1")] + [InlineData("loopback")] + [InlineData("localhost")] + public async Task WhenSubjectIsNotLoopback_ShouldSucceed(string uriString) + { + Uri subject = new UriBuilder(uriString).Uri; + + async Task Act() + => await That(subject).DoesNotComplyWith(it => it.IsNotLoopback()); + + await That(Act).DoesNotThrow(); + } + } + } +} diff --git a/Tests/aweXpect.Tests/Uris/ThatUri.IsNotUnc.Tests.cs b/Tests/aweXpect.Tests/Uris/ThatUri.IsNotUnc.Tests.cs new file mode 100644 index 000000000..1e1e93c7c --- /dev/null +++ b/Tests/aweXpect.Tests/Uris/ThatUri.IsNotUnc.Tests.cs @@ -0,0 +1,67 @@ +namespace aweXpect.Tests; + +public sealed partial class ThatUri +{ + public sealed class IsNotUnc + { + public sealed class Tests + { + [Fact] + public async Task WhenSubjectIsNotUnc_ShouldFail() + { + Uri subject = new("file://server/filename.ext"); + + async Task Act() + => await That(subject).IsNotUnc(); + + await That(Act).Throws() + .WithMessage(""" + Expected that subject + is not an UNC path, + but it was file://server/filename.ext + """); + } + + [Fact] + public async Task WhenSubjectIsNotUnc_ShouldSucceed() + { + Uri subject = new("https://www.awexpect.com"); + + async Task Act() + => await That(subject).IsNotUnc(); + + await That(Act).DoesNotThrow(); + } + } + + public sealed class NegatedTests + { + [Fact] + public async Task WhenSubjectIsNotUnc_ShouldFail() + { + Uri subject = new("https://www.awexpect.com"); + + async Task Act() + => await That(subject).DoesNotComplyWith(it => it.IsNotUnc()); + + await That(Act).Throws() + .WithMessage(""" + Expected that subject + is an UNC path, + but it was https://www.awexpect.com/ + """); + } + + [Fact] + public async Task WhenSubjectIsNotUnc_ShouldSucceed() + { + Uri subject = new("file://server/filename.ext"); + + async Task Act() + => await That(subject).DoesNotComplyWith(it => it.IsNotUnc()); + + await That(Act).DoesNotThrow(); + } + } + } +} diff --git a/Tests/aweXpect.Tests/Uris/ThatUri.IsUnc.Tests.cs b/Tests/aweXpect.Tests/Uris/ThatUri.IsUnc.Tests.cs new file mode 100644 index 000000000..451e56d80 --- /dev/null +++ b/Tests/aweXpect.Tests/Uris/ThatUri.IsUnc.Tests.cs @@ -0,0 +1,67 @@ +namespace aweXpect.Tests; + +public sealed partial class ThatUri +{ + public sealed class IsUnc + { + public sealed class Tests + { + [Fact] + public async Task WhenSubjectIsNotUnc_ShouldFail() + { + Uri subject = new("https://www.awexpect.com"); + + async Task Act() + => await That(subject).IsUnc(); + + await That(Act).Throws() + .WithMessage(""" + Expected that subject + is an UNC path, + but it was https://www.awexpect.com/ + """); + } + + [Fact] + public async Task WhenSubjectIsUnc_ShouldSucceed() + { + Uri subject = new("file://server/filename.ext"); + + async Task Act() + => await That(subject).IsUnc(); + + await That(Act).DoesNotThrow(); + } + } + + public sealed class NegatedTests + { + [Fact] + public async Task WhenSubjectIsNotUnc_ShouldSucceed() + { + Uri subject = new("https://www.awexpect.com"); + + async Task Act() + => await That(subject).DoesNotComplyWith(it => it.IsUnc()); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenSubjectIsUnc_ShouldFail() + { + Uri subject = new("file://server/filename.ext"); + + async Task Act() + => await That(subject).DoesNotComplyWith(it => it.IsUnc()); + + await That(Act).Throws() + .WithMessage(""" + Expected that subject + is not an UNC path, + but it was file://server/filename.ext + """); + } + } + } +} diff --git a/Tests/aweXpect.Tests/Uris/ThatUri.cs b/Tests/aweXpect.Tests/Uris/ThatUri.cs new file mode 100644 index 000000000..ce35534cf --- /dev/null +++ b/Tests/aweXpect.Tests/Uris/ThatUri.cs @@ -0,0 +1,4 @@ +namespace aweXpect.Tests; + +// ReSharper disable once ClassNeverInstantiated.Global +public sealed partial class ThatUri; diff --git a/Tests/aweXpect.Tests/aweXpect.Tests.csproj.DotSettings b/Tests/aweXpect.Tests/aweXpect.Tests.csproj.DotSettings index 73ecc708b..787f5e33e 100644 --- a/Tests/aweXpect.Tests/aweXpect.Tests.csproj.DotSettings +++ b/Tests/aweXpect.Tests/aweXpect.Tests.csproj.DotSettings @@ -20,4 +20,5 @@ True True True - True + True + True