diff --git a/src/OpenAPI.WebApiGenerator/CodeGeneration/ApiConfigurationGenerator.cs b/src/OpenAPI.WebApiGenerator/CodeGeneration/ApiConfigurationGenerator.cs index 946b325..6097593 100644 --- a/src/OpenAPI.WebApiGenerator/CodeGeneration/ApiConfigurationGenerator.cs +++ b/src/OpenAPI.WebApiGenerator/CodeGeneration/ApiConfigurationGenerator.cs @@ -12,8 +12,11 @@ internal SourceCode GenerateClass() => using System; namespace {{@namespace}}; - - public sealed class {{ClassName}} + + /// + /// Configuration for the generated web API. + /// + public sealed class {{ClassName}} { /// /// The uri to the exposed OpenAPI specification used to generate the API. diff --git a/src/OpenAPI.WebApiGenerator/CodeGeneration/AuthGenerator.cs b/src/OpenAPI.WebApiGenerator/CodeGeneration/AuthGenerator.cs index 44500a3..dc18a59 100644 --- a/src/OpenAPI.WebApiGenerator/CodeGeneration/AuthGenerator.cs +++ b/src/OpenAPI.WebApiGenerator/CodeGeneration/AuthGenerator.cs @@ -31,8 +31,9 @@ public AuthGenerator(OpenApiDocument openApiDocument) { return null; } - return new SourceCode("SecuritySchemes.g.cs", + return new SourceCode("SecuritySchemes.g.cs", $$""" +using Microsoft.AspNetCore.Http; using System.Collections.Immutable; namespace {{@namespace}}; @@ -216,7 +217,12 @@ internal static class {{className}} #nullable enable using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using System; +using System.Collections.Generic; +using System.Linq; using System.Security.Claims; +using System.Threading.Tasks; namespace {{@namespace}}; @@ -387,13 +393,15 @@ internal sealed class {{securityRequirementsFilterClassName}}(Operation operatio protected override SecurityRequirements Requirements { get; } = new() {{{string.Join(",", securityRequirementGroups.Select(securityRequirementGroup => - securityRequirementGroup.AggregateToString(securityRequirement => + securityRequirementGroup.Any() ? securityRequirementGroup.AggregateToString(securityRequirement => $$""" new SecurityRequirement { ["{{securityRequirement.Key}}"] = [{{string.Join(", ", securityRequirement.Value.Select(scope => $"\"{scope}\""))}}] } -""")))}} +""") : """ + new SecurityRequirement() + """))}} }; private static Request ResolveRequest(HttpContext context) => (Request) context.Items[RequestItemKey]!; @@ -409,9 +417,11 @@ internal sealed class {{securityRequirementsFilterClassName}}(Operation operatio { return null; } - return new SourceCode("SecuritySchemeOptions.g.cs", + return new SourceCode("SecuritySchemeOptions.g.cs", $$""" #nullable enable +using System; + namespace {{@namespace}}; /// diff --git a/src/OpenAPI.WebApiGenerator/CodeGeneration/HttpRequestExtensionsGenerator.cs b/src/OpenAPI.WebApiGenerator/CodeGeneration/HttpRequestExtensionsGenerator.cs index aeead94..cca4a69 100644 --- a/src/OpenAPI.WebApiGenerator/CodeGeneration/HttpRequestExtensionsGenerator.cs +++ b/src/OpenAPI.WebApiGenerator/CodeGeneration/HttpRequestExtensionsGenerator.cs @@ -37,11 +37,17 @@ internal SourceCode GenerateHttpRequestExtensionsClass() => new($"{HttpRequestExtensionsClassName}.g.cs", $$$"""" #nullable enable + using System; using System.Collections.Concurrent; + using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; + using System.Linq; using System.Text.Json; + using System.Threading; + using System.Threading.Tasks; using Corvus.Json; using Microsoft.AspNetCore.Http; + using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Primitives; using Microsoft.Net.Http.Headers; using OpenAPI.ParameterStyleParsers; diff --git a/src/OpenAPI.WebApiGenerator/CodeGeneration/HttpResponseExtensionsGenerator.cs b/src/OpenAPI.WebApiGenerator/CodeGeneration/HttpResponseExtensionsGenerator.cs index b0d12a7..596050e 100644 --- a/src/OpenAPI.WebApiGenerator/CodeGeneration/HttpResponseExtensionsGenerator.cs +++ b/src/OpenAPI.WebApiGenerator/CodeGeneration/HttpResponseExtensionsGenerator.cs @@ -21,7 +21,10 @@ internal SourceCode GenerateHttpResponseExtensionsClass() => new($"{HttpResponseExtensionsClassName}.g.cs", $$$"""" #nullable enable + using System; using System.Collections.Concurrent; + using System.Collections.Generic; + using System.Linq; using System.Text.Json; using System.Text.Json.Nodes; using Corvus.Json; diff --git a/src/OpenAPI.WebApiGenerator/CodeGeneration/JsonValidationExceptionGenerator.cs b/src/OpenAPI.WebApiGenerator/CodeGeneration/JsonValidationExceptionGenerator.cs index e066b7e..c246048 100644 --- a/src/OpenAPI.WebApiGenerator/CodeGeneration/JsonValidationExceptionGenerator.cs +++ b/src/OpenAPI.WebApiGenerator/CodeGeneration/JsonValidationExceptionGenerator.cs @@ -16,7 +16,9 @@ internal SourceCode GenerateJsonValidationExceptionClass() => #nullable enable using Corvus.Json; using System; + using System.Collections.Generic; using System.Collections.Immutable; + using System.Linq; using System.Text; namespace {{@namespace}}; diff --git a/src/OpenAPI.WebApiGenerator/CodeGeneration/OperationGenerator.cs b/src/OpenAPI.WebApiGenerator/CodeGeneration/OperationGenerator.cs index 7705151..fca5fc8 100644 --- a/src/OpenAPI.WebApiGenerator/CodeGeneration/OperationGenerator.cs +++ b/src/OpenAPI.WebApiGenerator/CodeGeneration/OperationGenerator.cs @@ -31,9 +31,14 @@ internal SourceCode Generate(string @namespace, using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; using System.Collections.Immutable; +using System.Linq; using System.Security.Claims; using System.Threading; +using System.Threading.Tasks; namespace {{@namespace}}; {{ new[] @@ -72,7 +77,7 @@ internal partial class Operation /// Create a request validation error response. /// /// The invalid request - /// The validation context describing the validation errors + /// The validation context describing the validation errors /// private Response CreateRequestValidationErrorResponse(Request request, ValidationContext validationContext) { @@ -272,6 +277,10 @@ private static Diagnostic CreateMissingHandlersDiagnosticMessage(string @namespa private static string GenerateMissingHandler(string @namespace) => $$""" + using System; + using System.Threading; + using System.Threading.Tasks; + namespace {{@namespace}} { internal partial class Operation diff --git a/src/OpenAPI.WebApiGenerator/CodeGeneration/OperationRouterGenerator.cs b/src/OpenAPI.WebApiGenerator/CodeGeneration/OperationRouterGenerator.cs index e452fe9..4c5893e 100644 --- a/src/OpenAPI.WebApiGenerator/CodeGeneration/OperationRouterGenerator.cs +++ b/src/OpenAPI.WebApiGenerator/CodeGeneration/OperationRouterGenerator.cs @@ -12,11 +12,14 @@ internal SourceCode ForMinimalApi(List<(string Namespace, KeyValuePair -/// Configure routes for OpenAPI operations +/// Configure routes for OpenAPI operations /// internal static class OperationRouter { diff --git a/src/OpenAPI.WebApiGenerator/CodeGeneration/RequestGenerator.cs b/src/OpenAPI.WebApiGenerator/CodeGeneration/RequestGenerator.cs index 8322fe4..8b1bc59 100644 --- a/src/OpenAPI.WebApiGenerator/CodeGeneration/RequestGenerator.cs +++ b/src/OpenAPI.WebApiGenerator/CodeGeneration/RequestGenerator.cs @@ -31,6 +31,12 @@ internal SourceCode GenerateRequestClass(string @namespace, string path) $$""" #nullable enable using Corvus.Json; +using Microsoft.AspNetCore.Http; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; namespace {{@namespace}}; diff --git a/src/OpenAPI.WebApiGenerator/CodeGeneration/ResponseGenerator.cs b/src/OpenAPI.WebApiGenerator/CodeGeneration/ResponseGenerator.cs index 984e90e..cae6ff0 100644 --- a/src/OpenAPI.WebApiGenerator/CodeGeneration/ResponseGenerator.cs +++ b/src/OpenAPI.WebApiGenerator/CodeGeneration/ResponseGenerator.cs @@ -14,8 +14,13 @@ public SourceCode GenerateResponseClass(string @namespace, string path) $$""" #nullable enable using Corvus.Json; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Net.Http.Headers; +using System; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Text.Json; using {{httpResponseExtensionsGenerator.Namespace}}; diff --git a/src/OpenAPI.WebApiGenerator/CodeGeneration/SchemaGenerator.cs b/src/OpenAPI.WebApiGenerator/CodeGeneration/SchemaGenerator.cs index bcfdd66..f5ab7aa 100644 --- a/src/OpenAPI.WebApiGenerator/CodeGeneration/SchemaGenerator.cs +++ b/src/OpenAPI.WebApiGenerator/CodeGeneration/SchemaGenerator.cs @@ -152,6 +152,7 @@ private List GenerateCode(SourceGeneratorHelpers.TypesToGenerat optionalAsNullable: typesToGenerate.OptionalAsNullable, disabledNamingHeuristics: [.. typesToGenerate.DisabledNamingHeuristics], fileExtension: ".g.cs", + addExplicitUsings: true, defaultAccessibility: typesToGenerate.DefaultAccessibility); var languageProvider = CSharpLanguageProvider.DefaultWithOptions(options); diff --git a/src/OpenAPI.WebApiGenerator/CodeGeneration/SequentialMediaTypesGenerator.cs b/src/OpenAPI.WebApiGenerator/CodeGeneration/SequentialMediaTypesGenerator.cs index c9c15f5..6a48c6d 100644 --- a/src/OpenAPI.WebApiGenerator/CodeGeneration/SequentialMediaTypesGenerator.cs +++ b/src/OpenAPI.WebApiGenerator/CodeGeneration/SequentialMediaTypesGenerator.cs @@ -27,8 +27,12 @@ internal string GetFullyQualifiedTypeName( using Microsoft.AspNetCore.Authorization; using System; using System.Buffers; +using System.Collections.Generic; +using System.IO; using System.IO.Pipelines; using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; namespace {{@namespace}}; diff --git a/src/OpenAPI.WebApiGenerator/CodeGeneration/ValidationExtensionsGenerator.cs b/src/OpenAPI.WebApiGenerator/CodeGeneration/ValidationExtensionsGenerator.cs index acb99a5..787f8e4 100644 --- a/src/OpenAPI.WebApiGenerator/CodeGeneration/ValidationExtensionsGenerator.cs +++ b/src/OpenAPI.WebApiGenerator/CodeGeneration/ValidationExtensionsGenerator.cs @@ -7,7 +7,10 @@ internal sealed class ValidationExtensionsGenerator(string @namespace) $$""" #nullable enable using Corvus.Json; +using System; +using System.Collections.Generic; using System.Collections.Immutable; +using System.Linq; using System.Text.Json; namespace {{@namespace}}; diff --git a/tests/Example.OpenApi20/Example.OpenApi20.csproj b/tests/Example.OpenApi20/Example.OpenApi20.csproj index f73c2e0..fb54a8e 100644 --- a/tests/Example.OpenApi20/Example.OpenApi20.csproj +++ b/tests/Example.OpenApi20/Example.OpenApi20.csproj @@ -5,6 +5,7 @@ enable enable Example.OpenApi20 + true true LastMajorVersionBinary/$(AssemblyName).dll diff --git a/tests/Example.OpenApi20/Program.cs b/tests/Example.OpenApi20/Program.cs index f33ae56..b5ed162 100644 --- a/tests/Example.OpenApi20/Program.cs +++ b/tests/Example.OpenApi20/Program.cs @@ -37,4 +37,7 @@ app.MapOperations(); app.Run(); +/// +/// Application entry point. +/// public abstract partial class Program; diff --git a/tests/Example.OpenApi30/Example.OpenApi30.csproj b/tests/Example.OpenApi30/Example.OpenApi30.csproj index d38bf49..25e57fe 100644 --- a/tests/Example.OpenApi30/Example.OpenApi30.csproj +++ b/tests/Example.OpenApi30/Example.OpenApi30.csproj @@ -5,6 +5,7 @@ enable enable Example.OpenApi30 + true true LastMajorVersionBinary/$(AssemblyName).dll diff --git a/tests/Example.OpenApi30/Program.cs b/tests/Example.OpenApi30/Program.cs index a54f3e7..2504213 100644 --- a/tests/Example.OpenApi30/Program.cs +++ b/tests/Example.OpenApi30/Program.cs @@ -44,4 +44,7 @@ app.MapOperations(); app.Run(); +/// +/// Application entry point. +/// public abstract partial class Program; diff --git a/tests/Example.OpenApi31/Example.OpenApi31.csproj b/tests/Example.OpenApi31/Example.OpenApi31.csproj index 34288c8..d7cff7f 100644 --- a/tests/Example.OpenApi31/Example.OpenApi31.csproj +++ b/tests/Example.OpenApi31/Example.OpenApi31.csproj @@ -5,6 +5,7 @@ enable enable Example.OpenApi31 + true true LastMajorVersionBinary/$(AssemblyName).dll diff --git a/tests/Example.OpenApi31/Program.cs b/tests/Example.OpenApi31/Program.cs index 6b40083..c5e9686 100644 --- a/tests/Example.OpenApi31/Program.cs +++ b/tests/Example.OpenApi31/Program.cs @@ -49,4 +49,7 @@ app.MapOperations(); app.Run(); +/// +/// Application entry point. +/// public abstract partial class Program; \ No newline at end of file diff --git a/tests/Example.OpenApi32/Example.OpenApi32.csproj b/tests/Example.OpenApi32/Example.OpenApi32.csproj index feb3185..9f7c92f 100644 --- a/tests/Example.OpenApi32/Example.OpenApi32.csproj +++ b/tests/Example.OpenApi32/Example.OpenApi32.csproj @@ -3,8 +3,8 @@ net9.0 enable - enable Example.OpenApi32 + true true LastMajorVersionBinary/$(AssemblyName).dll diff --git a/tests/Example.OpenApi32/Paths/FooFooId/Delete/Operation.Handler.cs b/tests/Example.OpenApi32/Paths/FooFooId/Delete/Operation.Handler.cs index 57a5ad6..8b1bd55 100644 --- a/tests/Example.OpenApi32/Paths/FooFooId/Delete/Operation.Handler.cs +++ b/tests/Example.OpenApi32/Paths/FooFooId/Delete/Operation.Handler.cs @@ -1,3 +1,6 @@ +using System.Threading; +using System.Threading.Tasks; + namespace Example.OpenApi32.Paths.FooFooId.Delete; internal partial class Operation diff --git a/tests/Example.OpenApi32/Paths/FooFooId/Put/Operation.Handler.cs b/tests/Example.OpenApi32/Paths/FooFooId/Put/Operation.Handler.cs index 71f2e86..07ccf49 100644 --- a/tests/Example.OpenApi32/Paths/FooFooId/Put/Operation.Handler.cs +++ b/tests/Example.OpenApi32/Paths/FooFooId/Put/Operation.Handler.cs @@ -1,4 +1,8 @@ +using System; using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; using Corvus.Json; namespace Example.OpenApi32.Paths.FooFooId.Put; diff --git a/tests/Example.OpenApi32/Paths/FooFooIdEvents/Get/Operation.Handler.cs b/tests/Example.OpenApi32/Paths/FooFooIdEvents/Get/Operation.Handler.cs index 06eb312..375e2c4 100644 --- a/tests/Example.OpenApi32/Paths/FooFooIdEvents/Get/Operation.Handler.cs +++ b/tests/Example.OpenApi32/Paths/FooFooIdEvents/Get/Operation.Handler.cs @@ -1,3 +1,6 @@ +using System; +using System.Threading; +using System.Threading.Tasks; using Example.OpenApi32.Components.Schemas; namespace Example.OpenApi32.Paths.FooFooIdEvents.Get; diff --git a/tests/Example.OpenApi32/Paths/FooFooIdEvents/Post/Operation.Handler.cs b/tests/Example.OpenApi32/Paths/FooFooIdEvents/Post/Operation.Handler.cs index 8692acf..83c1955 100644 --- a/tests/Example.OpenApi32/Paths/FooFooIdEvents/Post/Operation.Handler.cs +++ b/tests/Example.OpenApi32/Paths/FooFooIdEvents/Post/Operation.Handler.cs @@ -1,4 +1,7 @@ -using Example.OpenApi32.Components.Schemas; +using System; +using System.Threading; +using System.Threading.Tasks; +using Example.OpenApi32.Components.Schemas; namespace Example.OpenApi32.Paths.FooFooIdEvents.Post; diff --git a/tests/Example.OpenApi32/Program.cs b/tests/Example.OpenApi32/Program.cs index 1599f23..ac9d14d 100644 --- a/tests/Example.OpenApi32/Program.cs +++ b/tests/Example.OpenApi32/Program.cs @@ -1,7 +1,11 @@ +using System; using Corvus.Json; using Example.OpenApi.Auth; using Example.OpenApi32; using Microsoft.AspNetCore.Authentication.Certificate; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; var builder = WebApplication.CreateBuilder(args); @@ -49,4 +53,7 @@ app.MapOperations(); app.Run(); +/// +/// Application entry point. +/// public abstract partial class Program; diff --git a/tests/Example.OpenApi32/openapi.json b/tests/Example.OpenApi32/openapi.json index 1f59ed0..6b2e61b 100644 --- a/tests/Example.OpenApi32/openapi.json +++ b/tests/Example.OpenApi32/openapi.json @@ -73,7 +73,11 @@ "200": { "description": "Successfully deleted" } - } + }, + "security": [ + {}, + {"openIdConnect": []} + ] }, "parameters": [ { diff --git a/tests/OpenAPI.WebApiGenerator.Tests/ApiGeneratorTests.SecurityRequirements.cs b/tests/OpenAPI.WebApiGenerator.Tests/ApiGeneratorTests.SecurityRequirements.cs new file mode 100644 index 0000000..379dad6 --- /dev/null +++ b/tests/OpenAPI.WebApiGenerator.Tests/ApiGeneratorTests.SecurityRequirements.cs @@ -0,0 +1,33 @@ +using System.IO; +using System.Linq; +using AwesomeAssertions; +using Xunit; + +namespace OpenAPI.WebApiGenerator.Tests; + +public partial class ApiGeneratorTests +{ + [Theory] + [MemberData(nameof(AnonymousSecurityRequirementSpecs))] + public void GivenAnonymousSecurityRequirement_WhenGenerating_SecurityRequirementsFilterRepresentsEachRequirementObject( + string _, string openApiSpec) + { + var sourceCode = GetOperationSourceCode(openApiSpec); + + sourceCode.Should().Contain("class SecurityRequirementsFilter"); + sourceCode.Should().Contain("new SecurityRequirement", Exactly.Twice()); + sourceCode.Should().Contain("[\"secret_key\"] = []", Exactly.Once()); + } + + private string GetOperationSourceCode(string openApiSpec) + { + var compilation = SetupGenerator(openApiSpec, out var diagnostics); + HasOnlyMissingHandler(diagnostics); + + var operationSyntaxTree = compilation.SyntaxTrees + .FirstOrDefault(t => Path.GetFileName(t.FilePath).EndsWith("Operation.g.cs")); + operationSyntaxTree.Should().NotBeNull(); + + return operationSyntaxTree.ToString(); + } +} \ No newline at end of file diff --git a/tests/OpenAPI.WebApiGenerator.Tests/ApiGeneratorTests.Setup.cs b/tests/OpenAPI.WebApiGenerator.Tests/ApiGeneratorTests.Setup.cs new file mode 100644 index 0000000..c037a82 --- /dev/null +++ b/tests/OpenAPI.WebApiGenerator.Tests/ApiGeneratorTests.Setup.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Immutable; +using System.IO; +using System.Linq; +using AwesomeAssertions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using OpenAPI.WebApiGenerator.CodeGeneration; +using OpenAPI.WebApiGenerator.Tests.Utils; + +namespace OpenAPI.WebApiGenerator.Tests; + +public partial class ApiGeneratorTests +{ + // Base framework references so generated types resolve; built once and shared. + private static readonly MetadataReference[] RuntimeReferences = + ((string)AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES")!) + .Split(Path.PathSeparator) + .Where(path => !string.IsNullOrEmpty(path)) + .Select(MetadataReference (path) => MetadataReference.CreateFromFile(path)) + .ToArray(); + + private static void HasOnlyMissingHandler(ImmutableArray diagnostics) + { + diagnostics.Should().AllSatisfy(diagnostic => + { + diagnostic.Severity.Should().Be(DiagnosticSeverity.Warning); + diagnostic.Id.Should().Be("AF1001", diagnostic.GetFormattedMessage()); + }); + } + + private Compilation SetupGenerator(string openApiSpec, out ImmutableArray diagnostics) + { + var generator = new ApiGenerator(); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(generator); + + driver = driver.AddAdditionalTexts( + [ + new InMemoryAdditionalText("openapi.json", + openApiSpec) + ] + ); + + const string assemblyName = nameof(ApiGeneratorTests); + var compilation = CSharpCompilation.Create(assemblyName, + references: RuntimeReferences, + options: new CSharpCompilationOptions(outputKind: OutputKind.DynamicallyLinkedLibrary)); + + driver.RunGeneratorsAndUpdateCompilation(compilation, out var newCompilation, out diagnostics, + Cancellation); + + foreach (var tree in newCompilation.SyntaxTrees) + { + tree.GetDiagnostics().Should().NotContain(diagnostic => + diagnostic.Severity == DiagnosticSeverity.Error || + diagnostic.Severity == DiagnosticSeverity.Warning); + } + + var errorsCausedByMissingReferences = new[] + { + "CS0518", // predefined type is not defined or imported + "CS0656", // missing compiler-required member + "CS0012", // type is defined in an assembly that is not referenced + "CS1069", // type could not be found in a namespace, per the using + "CS0234", // type or namespace does not exist in the namespace + "CS0246", // type or namespace could not be found + "CS0400", // The type or namespace name could not be found in the global namespace (are you missing an assembly reference?) + "CS8179", // Predefined type System.ValueTuple is not defined or imported + "CS0103", // name does not exist in the current context + "CS1061", // no definition for member (type unresolved) + "CS0538", // explicit interface declaration is not an interface + "CS1729", // type has no constructor with that many arguments + "CS0314", // type cannot be a type parameter (constraint unresolved) + "CS0305", // wrong number of type arguments (generic unresolved) + "CS0704", // non-virtual member lookup on unresolved type + "CS9174", // collection-expression init on unresolved type + "CS8137", // cannot define a member on an unresolved type + "CS1110", // cannot define an extension on an unresolved type + "CS0229", // ambiguity between members (unresolved base) + "CS0121", // ambiguous call (unresolved overloads) + "CS1955", // non-invocable member used like a method + "CS0161", // not all code paths return a value (unresolved return type) + "CS0315", // no boxing conversion for type parameter (constraint unresolved) + "CS8919" + }; + + var compilationDiagnostics = newCompilation.GetDiagnostics() + .Where(diagnostic => diagnostic.Severity == DiagnosticSeverity.Error) + .Where(diagnostic => !errorsCausedByMissingReferences.Contains(diagnostic.Id)) + .ToArray(); + + compilationDiagnostics.Should().BeEmpty(because: + "the generated code should be valid C#, but found:\n" + + string.Join("\n", compilationDiagnostics.Select(diagnostic => diagnostic.ToString())) + + "\n\n" + + string.Join("\n\n", compilationDiagnostics + .Select(diagnostic => diagnostic.Location.SourceTree) + .Distinct() + .Select(tree => + $""" + // === {tree?.FilePath} === + {tree?.GetText()} + """))); + + return newCompilation; + } +} \ No newline at end of file diff --git a/tests/OpenAPI.WebApiGenerator.Tests/ApiGeneratorTests.TheoryData.AnonymousSecurityRequirementSpecs.cs b/tests/OpenAPI.WebApiGenerator.Tests/ApiGeneratorTests.TheoryData.AnonymousSecurityRequirementSpecs.cs new file mode 100644 index 0000000..c8e403a --- /dev/null +++ b/tests/OpenAPI.WebApiGenerator.Tests/ApiGeneratorTests.TheoryData.AnonymousSecurityRequirementSpecs.cs @@ -0,0 +1,39 @@ +using Xunit; + +namespace OpenAPI.WebApiGenerator.Tests; + +public partial class ApiGeneratorTests +{ + public static TheoryData AnonymousSecurityRequirementSpecs => new() + { + { + "OpenAPI 3.1", + """ + { + "openapi": "3.1.0", + "info": { "title": "foo", "version": "1.0" }, + "paths": { + "/foo": { + "get": { + "operationId": "GetFoo", + "responses": { + "200": { "description": "Success" } + }, + "security": [{}, { "secret_key": [] }] + } + } + }, + "components": { + "securitySchemes": { + "secret_key": { + "type": "apiKey", + "in": "header", + "name": "X-API-Key" + } + } + } + } + """ + } + }; +} \ No newline at end of file diff --git a/tests/OpenAPI.WebApiGenerator.Tests/ApiGeneratorTests.cs b/tests/OpenAPI.WebApiGenerator.Tests/ApiGeneratorTests.cs index 962cc67..6e17569 100644 --- a/tests/OpenAPI.WebApiGenerator.Tests/ApiGeneratorTests.cs +++ b/tests/OpenAPI.WebApiGenerator.Tests/ApiGeneratorTests.cs @@ -1,4 +1,3 @@ -using System.Collections.Immutable; using System.IO; using System.Linq; using System.Threading; @@ -15,7 +14,7 @@ namespace OpenAPI.WebApiGenerator.Tests; public partial class ApiGeneratorTests { private CancellationToken Cancellation => TestContext.Current.CancellationToken; - + [Theory] [InlineData("openapi-v2.json")] [InlineData("openapi-v3.json")] @@ -170,43 +169,4 @@ public void NoResponseContent_Generating_DefaultResponseConstructor(string _, st .And.Subject.First() .Parameters.Should().HaveCount(0); } - - private static void HasOnlyMissingHandler(ImmutableArray diagnostics) - { - diagnostics.Should().AllSatisfy(diagnostic => - { - diagnostic.Severity.Should().Be(DiagnosticSeverity.Warning); - diagnostic.Id.Should().Be("AF1001", diagnostic.GetFormattedMessage()); - }); - } - - private Compilation SetupGenerator(string openApiSpec, out ImmutableArray diagnostics) - { - var generator = new ApiGenerator(); - - GeneratorDriver driver = CSharpGeneratorDriver.Create(generator); - - driver = driver.AddAdditionalTexts( - [ - new InMemoryAdditionalText("openapi.json", - openApiSpec) - ] - ); - - const string assemblyName = nameof(ApiGeneratorTests); - var compilation = CSharpCompilation.Create(assemblyName, - options: new CSharpCompilationOptions(outputKind: OutputKind.DynamicallyLinkedLibrary)); - - driver.RunGeneratorsAndUpdateCompilation(compilation, out var newCompilation, out diagnostics, - Cancellation); - - foreach (var tree in newCompilation.SyntaxTrees) - { - tree.GetDiagnostics().Should().NotContain(diagnostic => - diagnostic.Severity == DiagnosticSeverity.Error || - diagnostic.Severity == DiagnosticSeverity.Warning); - } - - return newCompilation; - } }