Skip to content

Commit

Permalink
hmm
Browse files Browse the repository at this point in the history
  • Loading branch information
david-driscoll committed Nov 25, 2024
1 parent 12ca746 commit b949408
Show file tree
Hide file tree
Showing 26 changed files with 193 additions and 31 deletions.
16 changes: 14 additions & 2 deletions src/AspNetCore.FluentValidation.OpenApi/Start.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,20 @@ public static class ServiceCollectionExtensions
{
public static IServiceCollection AddFluentValidationOpenApi(this IServiceCollection services)
{
services.AddFluentValidation();
services.TryAddSingleton<IOpenApiSchemaTransformer, FluentValidationOpenApiSchemaTransformer>();
services.AddOpenApi();
services.AddFluentValidationAutoValidation();
services.Configure<OpenApiOptions>(
options =>
{
options.AddSchemaTransformer<FluentValidationOpenApiSchemaTransformer>();
}
);
services.TryAddEnumerable(ServiceDescriptor.Transient<IPropertyRuleHandler, RequiredPropertyRule>());
services.TryAddEnumerable(ServiceDescriptor.Transient<IPropertyRuleHandler, NotEmptyPropertyRule>());
services.TryAddEnumerable(ServiceDescriptor.Transient<IPropertyRuleHandler, LengthPropertyRule>());
services.TryAddEnumerable(ServiceDescriptor.Transient<IPropertyRuleHandler, RegularExpressionPropertyRule>());
services.TryAddEnumerable(ServiceDescriptor.Transient<IPropertyRuleHandler, ComparisonPropertyRule>());
services.TryAddEnumerable(ServiceDescriptor.Transient<IPropertyRuleHandler, BetweenPropertyRule>());
return services;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,54 @@

{
openapi: 3.0.1,
info: {
title: ReSharperTestRunner | v1,
version: 1.0.0
},
paths: {
/default/boolean: {
post: {
tags: [
ReSharperTestRunner
],
requestBody: {
content: {
application/json: {
schema: {
$ref: #/components/schemas/BooleanContainer
}
}
}
},
responses: {
200: {
description: OK,
content: {
application/json: {
schema: {
$ref: #/components/schemas/BooleanContainer
}
}
}
}
}
}
}
},
components: {
schemas: {
BooleanContainer: {
type: object,
properties: {
value: {
type: boolean
}
}
}
}
},
tags: [
{
name: ReSharperTestRunner
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

130 changes: 102 additions & 28 deletions test/AspNetCore.FluentValidation.OpenApi.Tests/RuleTestBase.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Runtime.CompilerServices;
using DiffEngine;
using FluentValidation;
using FluentValidation.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
Expand All @@ -10,57 +11,130 @@

namespace AspNetCore.FluentValidation.OpenApi.Tests;

class StringContainer
{
public required string Value { get; set; }
}
class NullableStringContainer
{
public string? Value { get; set; }
}
class CollectionContainer
{
public List<string> Value { get; set; } = [];
}
class NullableIntegerContainer
{
public int? Value { get; set; }
}
class IntegerContainer
{
public int Value { get; set; }
}
class BooleanContainer
{
public bool Value { get; set; }
}
class NullableBooleanContainer
{
public bool? Value { get; set; }
}
class DecimalContainer
{
public decimal Value { get; set; }
}
class NullableDecimalContainer
{
public decimal? Value { get; set; }
}

[Experimental(Constants.ExperimentalId)]
public class RequiredPropertyRuleTests : RuleTestBase<RequiredPropertyRule>
public class RequiredPropertyRuleTests : RuleTestBase
{
[Fact]
public async Task Should_generate_required_property()
[Theory]
[MemberData(nameof(GetTheoryData))]
public Task Should_generate_required_property(OpenApiResult result) => VerifyJson(result.JsonFactory()).UseParameters(result.Path);

public static IEnumerable<object[]> GetTheoryData()
{
var response = await _client.GetAsync("/openapi/v1.json");
response.EnsureSuccessStatusCode();
await VerifyJson(response.Content.ReadAsStreamAsync());
yield return [GetOpenApiDocument<BooleanContainer>("/default/boolean", x => x.RuleFor(y => y.Value))];
yield return [GetOpenApiDocument<CollectionContainer>("/default/collection", x => x.RuleFor(y => y.Value))];
yield return [GetOpenApiDocument<DecimalContainer>("/default/decimal", x => x.RuleFor(y => y.Value))];
yield return [GetOpenApiDocument<IntegerContainer>("/default/integer", x => x.RuleFor(y => y.Value))];
yield return [GetOpenApiDocument<StringContainer>("/default/string", x => x.RuleFor(z => z.Value))];

yield return [GetOpenApiDocument<BooleanContainer>("/notempty/boolean", x => x.RuleFor(y => y.Value).NotEmpty())];
yield return [GetOpenApiDocument<CollectionContainer>("/notempty/collection", x => x.RuleFor(y => y.Value).NotEmpty())];
yield return [GetOpenApiDocument<DecimalContainer>("/notempty/decimal", x => x.RuleFor(y => y.Value).NotEmpty())];
yield return [GetOpenApiDocument<IntegerContainer>("/notempty/integer", x => x.RuleFor(y => y.Value).NotEmpty())];
yield return [GetOpenApiDocument<StringContainer>("/notempty/string", x => x.RuleFor(y => y.Value).NotEmpty())];
yield return [GetOpenApiDocument<NullableBooleanContainer>("/notempty/nullable/boolean", x => x.RuleFor(y => y.Value).NotEmpty())];
yield return [GetOpenApiDocument<NullableDecimalContainer>("/notempty/nullable/decimal", x => x.RuleFor(y => y.Value).NotEmpty())];
yield return [GetOpenApiDocument<NullableIntegerContainer>("/notempty/nullable/integer", x => x.RuleFor(y => y.Value).NotEmpty())];
yield return [GetOpenApiDocument<NullableStringContainer>("/notempty/nullable/string", x => x.RuleFor(y => y.Value).NotEmpty())];

yield return [GetOpenApiDocument<BooleanContainer>("/notnull/boolean", x => x.RuleFor(y => y.Value).NotNull())];
yield return [GetOpenApiDocument<CollectionContainer>("/notnull/collection", x => x.RuleFor(y => y.Value).NotNull())];
yield return [GetOpenApiDocument<DecimalContainer>("/notnull/decimal", x => x.RuleFor(y => y.Value).NotNull())];
yield return [GetOpenApiDocument<IntegerContainer>("/notnull/integer", x => x.RuleFor(y => y.Value).NotNull())];
yield return [GetOpenApiDocument<StringContainer>("/notnull/string", x => x.RuleFor(y => y.Value).NotNull())];
yield return [GetOpenApiDocument<NullableBooleanContainer>("/notnull/nullable/boolean", x => x.RuleFor(y => y.Value).NotNull())];
yield return [GetOpenApiDocument<NullableDecimalContainer>("/notnull/nullable/decimal", x => x.RuleFor(y => y.Value).NotNull())];
yield return [GetOpenApiDocument<NullableIntegerContainer>("/notnull/nullable/integer", x => x.RuleFor(y => y.Value).NotNull())];
yield return [GetOpenApiDocument<NullableStringContainer>("/notnull/nullable/string", x => x.RuleFor(y => y.Value).NotNull())];

}
}

public record OpenApiResult(string Path, Func<Task<string>> JsonFactory)
{
public override string ToString() => Path;
}

static class ModuleInitializer
{
[ModuleInitializer]
public static void Initialize()
{
DiffRunner.Disabled = true;
DiffTools.UseOrder(DiffTool.Rider, DiffTool.VisualStudioCode, DiffTool.VisualStudio);
// Verify
}
}

#pragma warning disable RSGEXP
public class RuleTestBase<TRule> : IAsyncLifetime where TRule : IPropertyRuleHandler
#pragma warning restore RSGEXP
public abstract class RuleTestBase
#pragma warning restore RSGEXP
{
protected WebApplication _app { get; private set; }
protected HttpClient _client { get; private set; }

public async Task InitializeAsync()
protected static OpenApiResult GetOpenApiDocument<TObject>(string path, Action<InlineValidator<TObject>> configureValidator)
{
var builder = WebApplication.CreateSlimBuilder();
builder.WebHost.UseTestServer();
builder.Services.AddOpenApi();
builder.Services.AddFluentValidationAutoValidation();
builder.Services.AddFluentValidationOpenApi();
return new(
path,
async () =>
{
var builder = WebApplication.CreateSlimBuilder();
builder.WebHost.UseTestServer();
builder.Services.AddOpenApi();
builder.Services.AddFluentValidationAutoValidation();
builder.Services.AddFluentValidationOpenApi();
var validator = new InlineValidator<TObject>();
configureValidator(validator);
builder.Services.AddSingleton<IValidator<TObject>>(validator);

var app = _app = builder.Build();
await using var app = builder.Build();

app.MapOpenApi();
app.MapOpenApi();

app.MapGet("/", () => "Hello world!");
app.MapPost(path, (TObject container) => container);

await app.StartAsync().ConfigureAwait(false);
var testServer = (TestServer)app.Services.GetRequiredService<IServer>();
_client = testServer.CreateClient();
}
await app.StartAsync().ConfigureAwait(false);
var testServer = (TestServer)app.Services.GetRequiredService<IServer>();
var _client = testServer.CreateClient();

public async Task DisposeAsync()
{
await _app.StopAsync().ConfigureAwait(false);
await _app.DisposeAsync().ConfigureAwait(false);
var response = await _client.GetAsync("/openapi/v1.json");
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
);
}
}

0 comments on commit b949408

Please sign in to comment.