-
Notifications
You must be signed in to change notification settings - Fork 197
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Guy Fankam <[email protected]>
- Loading branch information
1 parent
7796fef
commit fedbca2
Showing
13 changed files
with
499 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
[*.cs] | ||
|
||
# CA1062: Validate arguments of public methods | ||
dotnet_diagnostic.CA1062.severity = suggestion | ||
|
||
# IDE0022: Use expression body for method | ||
csharp_style_expression_bodied_methods = when_on_single_line | ||
|
||
# IDE0022: Use expression body for method | ||
dotnet_diagnostic.IDE0022.severity = suggestion | ||
|
||
# IDE0058: Expression value is never used | ||
dotnet_diagnostic.IDE0058.severity = suggestion | ||
|
||
# CA2007: Consider calling ConfigureAwait on the awaited task | ||
dotnet_diagnostic.CA2007.severity = suggestion | ||
|
||
# CA1859: Use concrete types when possible for improved performance | ||
dotnet_diagnostic.CA1859.severity = suggestion | ||
|
||
# CA1034: Nested types should not be visible | ||
dotnet_diagnostic.CA1034.severity = suggestion | ||
|
||
# CA1848: Use the LoggerMessage delegates | ||
dotnet_diagnostic.CA1848.severity = suggestion | ||
|
||
# IDE0200: Remove unnecessary lambda expression | ||
dotnet_diagnostic.IDE0200.severity = suggestion | ||
|
||
# CA1707: Identifiers should not contain underscores | ||
dotnet_diagnostic.CA1707.severity = suggestion | ||
|
||
# IDE0008: Use explicit type | ||
csharp_style_var_elsewhere = true | ||
csharp_style_var_for_built_in_types = true | ||
dotnet_diagnostic.IDE0008.severity = suggestion | ||
|
||
# IDE0039: Use local function | ||
dotnet_diagnostic.IDE0039.severity = warning | ||
|
||
# IDE0320: Make anonymous function static | ||
dotnet_diagnostic.IDE0320.severity = suggestion |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
using FluentAssertions; | ||
using FluentAssertions.Execution; | ||
using FluentAssertions.Primitives; | ||
using LanguageExt; | ||
using LanguageExt.UnsafeValueAccess; | ||
|
||
namespace common.tests; | ||
|
||
public static class OptionExtensions | ||
{ | ||
public static OptionAssertions<T> Should<T>(this Option<T> instance) where T : notnull => | ||
new OptionAssertions<T>(instance); | ||
} | ||
|
||
public sealed class OptionAssertions<T>(Option<T> subject) : ReferenceTypeAssertions<Option<T>, OptionAssertions<T>>(subject) where T : notnull | ||
{ | ||
protected override string Identifier { get; } = "option"; | ||
|
||
[CustomAssertion] | ||
public AndConstraint<OptionAssertions<T>> BeSome(string because = "", params object[] becauseArgs) | ||
{ | ||
Execute.Assertion | ||
.BecauseOf(because, becauseArgs) | ||
.ForCondition(Subject.IsSome) | ||
.FailWith("Expected {context:option} to be Some{reason}, but it is None."); | ||
|
||
return new AndConstraint<OptionAssertions<T>>(this); | ||
} | ||
|
||
[CustomAssertion] | ||
public AndConstraint<OptionAssertions<T>> BeSome(T expected, string because = "", params object[] becauseArgs) | ||
{ | ||
Execute.Assertion | ||
.BecauseOf(because, becauseArgs) | ||
.WithExpectation("Expected {context:option} to be Some {0}{reason}, ", expected) | ||
.ForCondition(Subject.IsSome) | ||
.FailWith("but it is None.") | ||
.Then | ||
.Given(() => Subject.ValueUnsafe()) | ||
.ForCondition(actual => expected.Equals(actual)) | ||
.FailWith("but it is {0}.", t => new[] { t }); | ||
|
||
return new AndConstraint<OptionAssertions<T>>(this); | ||
} | ||
|
||
[CustomAssertion] | ||
public AndConstraint<OptionAssertions<T>> BeNone(string because = "", params object[] becauseArgs) | ||
{ | ||
Execute.Assertion | ||
.BecauseOf(because, becauseArgs) | ||
.ForCondition(Subject.IsNone) | ||
.FailWith("Expected {context:option} to be None{reason}, but it is Some."); | ||
|
||
return new AndConstraint<OptionAssertions<T>>(this); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
using common; | ||
using common.tests; | ||
using CsCheck; | ||
using LanguageExt; | ||
using LanguageExt.UnsafeValueAccess; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using System; | ||
using System.Linq; | ||
using System.Text.Json.Nodes; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Xunit; | ||
|
||
namespace publisher.unit.tests; | ||
|
||
public class FindApiDiagnosticDtoTests | ||
{ | ||
[Fact] | ||
public async Task Returns_none_if_the_dto_does_not_exist() | ||
{ | ||
var generator = from fixture in Fixture.Generate() | ||
where fixture.OriginalDto.IsNone | ||
select fixture; | ||
|
||
await generator.SampleAsync(async fixture => | ||
{ | ||
var dtoOption = await fixture.Run(CancellationToken.None); | ||
|
||
dtoOption.Should().BeNone(); | ||
}); | ||
} | ||
|
||
[Fact] | ||
public async Task Returns_the_original_dto_if_there_is_no_override() | ||
{ | ||
var generator = from fixture in Fixture.Generate() | ||
where fixture.OriginalDto.IsSome | ||
where fixture.DtoOverride.IsNone | ||
select fixture; | ||
|
||
await generator.SampleAsync(async fixture => | ||
{ | ||
var dtoOption = await fixture.Run(CancellationToken.None); | ||
|
||
var expectedDto = fixture.OriginalDto.ValueUnsafe() ?? throw new InvalidOperationException("Expected dto should not be null."); | ||
dtoOption.Should().BeSome(expectedDto); | ||
}); | ||
} | ||
|
||
[Fact] | ||
public async Task Returns_the_overridden_dto_if_there_is_an_override() | ||
{ | ||
var generator = from fixture in Fixture.Generate() | ||
where fixture.OriginalDto.IsSome | ||
where fixture.DtoOverride.IsSome | ||
select fixture; | ||
|
||
await generator.SampleAsync(async fixture => | ||
{ | ||
var dtoOption = await fixture.Run(CancellationToken.None); | ||
|
||
// Assert | ||
var originalDto = fixture.OriginalDto.ValueUnsafe() ?? throw new InvalidOperationException("Original dto should not be null."); | ||
var dtoOverride = fixture.DtoOverride.ValueUnsafe() ?? throw new InvalidOperationException("Override should not be null."); | ||
var expectedDto = OverrideDtoFactory.Override(originalDto, dtoOverride); | ||
dtoOption.Should().BeSome(expectedDto); | ||
}); | ||
} | ||
|
||
private sealed record Fixture | ||
{ | ||
public required ManagementServiceDirectory ServiceDirectory { get; init; } | ||
public required ApiName ApiName { get; init; } | ||
public required ApiDiagnosticName Name { get; init; } | ||
public required Option<ApiDiagnosticDto> OriginalDto { get; init; } | ||
public required Option<JsonObject> DtoOverride { get; init; } | ||
|
||
public async ValueTask<Option<ApiDiagnosticDto>> Run(CancellationToken cancellationToken) | ||
{ | ||
var provider = GetServiceProvider(); | ||
|
||
var findDto = ApiDiagnosticModule.GetFindApiDiagnosticDto(provider); | ||
|
||
return await findDto(Name, ApiName, cancellationToken); | ||
} | ||
|
||
private IServiceProvider GetServiceProvider() | ||
{ | ||
var services = new ServiceCollection(); | ||
|
||
services.AddSingleton(ServiceDirectory); | ||
|
||
services.AddSingleton<TryGetFileContents>(async (file, cancellationToken) => | ||
{ | ||
await ValueTask.CompletedTask; | ||
|
||
return OriginalDto.Map(dto => BinaryData.FromObjectAsJson(dto)); | ||
}); | ||
|
||
services.AddSingleton(new ConfigurationJson | ||
{ | ||
Value = DtoOverride.Map(@override => new JsonObject | ||
{ | ||
["apis"] = new JsonObject | ||
{ | ||
[ApiName.Value] = new JsonObject | ||
{ | ||
["diagnostics"] = new JsonObject | ||
{ | ||
[Name.Value] = @override | ||
} | ||
} | ||
} | ||
}).IfNone([]) | ||
}); | ||
|
||
services.AddSingleton(ConfigurationJsonModule.GetFindConfigurationSection); | ||
|
||
return services.BuildServiceProvider(); | ||
} | ||
|
||
public static Gen<Fixture> Generate() => | ||
from serviceDirectory in from directoryInfo in Generator.DirectoryInfo | ||
select ManagementServiceDirectory.From(directoryInfo) | ||
from apiName in from apiType in ApiType.Generate() | ||
from apiName in ApiModel.GenerateName(apiType) | ||
select apiName | ||
from name in ApiDiagnosticModel.GenerateName() | ||
from originalDto in from modelOption in ApiDiagnosticModel.Generate().OptionOf() | ||
select modelOption.Map(ModelToDto) | ||
from dtoOverride in from modelOption in ApiDiagnosticModel.Generate().OptionOf() | ||
select from model in modelOption | ||
let dto = ModelToDto(model) | ||
select JsonObjectExtensions.Parse(dto) | ||
select new Fixture | ||
{ | ||
ServiceDirectory = serviceDirectory, | ||
ApiName = apiName, | ||
Name = name, | ||
OriginalDto = originalDto, | ||
DtoOverride = dtoOverride | ||
}; | ||
|
||
private static ApiDiagnosticDto ModelToDto(ApiDiagnosticModel model) => | ||
new() | ||
{ | ||
Properties = new ApiDiagnosticDto.DiagnosticContract | ||
{ | ||
LoggerId = $"/loggers/{model.LoggerName}", | ||
AlwaysLog = model.AlwaysLog.ValueUnsafe(), | ||
Sampling = model.Sampling.Map(sampling => new ApiDiagnosticDto.SamplingSettings | ||
{ | ||
SamplingType = sampling.Type, | ||
Percentage = sampling.Percentage | ||
}).ValueUnsafe() | ||
} | ||
}; | ||
} | ||
} |
Oops, something went wrong.