diff --git a/Saunter.sln b/Saunter.sln index 43c713c..9d8fe9d 100644 --- a/Saunter.sln +++ b/Saunter.sln @@ -15,6 +15,9 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Saunter.Tests", "test\Saunter.Tests\Saunter.Tests.csproj", "{3ADB27EF-7C80-40EB-AFC6-5D06D415FFAB}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{6ABD4842-47AF-49A5-B057-0EBA64416789}" + ProjectSection(SolutionItems) = preProject + examples\.gitignore = examples\.gitignore + EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StreetlightsAPI", "examples\StreetlightsAPI\StreetlightsAPI.csproj", "{F188D4A7-BBCB-464F-A370-2BD84D18EA79}" ProjectSection(ProjectDependencies) = postProject @@ -57,6 +60,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AsyncAPI.Saunter.Generator. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AsyncAPI.Saunter.Generator.Cli.Tests", "test\AsyncAPI.Saunter.Generator.Cli.Tests\AsyncAPI.Saunter.Generator.Cli.Tests.csproj", "{18AD0249-0436-4A26-9972-B97BA6905A54}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StreetlightsAPI.TopLevelStatement", "examples\StreetlightsAPI.TopLevelStatement\StreetlightsAPI.TopLevelStatement.csproj", "{6F6B8B03-9045-46EC-AE12-E7ADA492F9FA}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AsyncAPI.Saunter.Generator.Build", "src\AsyncAPI.Saunter.Generator.Build\AsyncAPI.Saunter.Generator.Build.csproj", "{A320E670-5CB0-4815-AF67-D8D09FC92A2A}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AsyncAPI.Saunter.Generator.Build.Tests", "test\AsyncAPI.Saunter.Generator.Build.Tests\AsyncAPI.Saunter.Generator.Build.Tests.csproj", "{61142B10-7B49-436E-AE32-2737658BD1E5}" @@ -155,6 +160,18 @@ Global {18AD0249-0436-4A26-9972-B97BA6905A54}.Release|x64.Build.0 = Release|Any CPU {18AD0249-0436-4A26-9972-B97BA6905A54}.Release|x86.ActiveCfg = Release|Any CPU {18AD0249-0436-4A26-9972-B97BA6905A54}.Release|x86.Build.0 = Release|Any CPU + {6F6B8B03-9045-46EC-AE12-E7ADA492F9FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6F6B8B03-9045-46EC-AE12-E7ADA492F9FA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6F6B8B03-9045-46EC-AE12-E7ADA492F9FA}.Debug|x64.ActiveCfg = Debug|Any CPU + {6F6B8B03-9045-46EC-AE12-E7ADA492F9FA}.Debug|x64.Build.0 = Debug|Any CPU + {6F6B8B03-9045-46EC-AE12-E7ADA492F9FA}.Debug|x86.ActiveCfg = Debug|Any CPU + {6F6B8B03-9045-46EC-AE12-E7ADA492F9FA}.Debug|x86.Build.0 = Debug|Any CPU + {6F6B8B03-9045-46EC-AE12-E7ADA492F9FA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6F6B8B03-9045-46EC-AE12-E7ADA492F9FA}.Release|Any CPU.Build.0 = Release|Any CPU + {6F6B8B03-9045-46EC-AE12-E7ADA492F9FA}.Release|x64.ActiveCfg = Release|Any CPU + {6F6B8B03-9045-46EC-AE12-E7ADA492F9FA}.Release|x64.Build.0 = Release|Any CPU + {6F6B8B03-9045-46EC-AE12-E7ADA492F9FA}.Release|x86.ActiveCfg = Release|Any CPU + {6F6B8B03-9045-46EC-AE12-E7ADA492F9FA}.Release|x86.Build.0 = Release|Any CPU {A320E670-5CB0-4815-AF67-D8D09FC92A2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A320E670-5CB0-4815-AF67-D8D09FC92A2A}.Debug|Any CPU.Build.0 = Debug|Any CPU {A320E670-5CB0-4815-AF67-D8D09FC92A2A}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -193,6 +210,7 @@ Global {E8FACA22-CFED-4710-89E4-D55F31BF96B3} = {D8CB9C0D-9605-457B-979F-C8994B20A926} {6C102D4D-3DA4-4763-B75E-C15E33E7E94A} = {28D4C365-FDED-49AE-A97D-36202E24A55A} {18AD0249-0436-4A26-9972-B97BA6905A54} = {6491E321-2D02-44AB-9116-D722FE169595} + {6F6B8B03-9045-46EC-AE12-E7ADA492F9FA} = {6ABD4842-47AF-49A5-B057-0EBA64416789} {A320E670-5CB0-4815-AF67-D8D09FC92A2A} = {28D4C365-FDED-49AE-A97D-36202E24A55A} {61142B10-7B49-436E-AE32-2737658BD1E5} = {6491E321-2D02-44AB-9116-D722FE169595} EndGlobalSection diff --git a/examples/.gitignore b/examples/.gitignore new file mode 100644 index 0000000..f91a116 --- /dev/null +++ b/examples/.gitignore @@ -0,0 +1,4 @@ +specs/ +streetlights.json +streetlights.yml +streetlights.yaml \ No newline at end of file diff --git a/examples/StreetlightsAPI.TopLevelStatement/Program.cs b/examples/StreetlightsAPI.TopLevelStatement/Program.cs new file mode 100644 index 0000000..2e59f1c --- /dev/null +++ b/examples/StreetlightsAPI.TopLevelStatement/Program.cs @@ -0,0 +1,79 @@ +using System.Linq; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using NLog; +using NLog.Web; +using Saunter; +using Saunter.AsyncApiSchema.v2; +using StreetlightsAPI; + +LogManager.Setup().LoadConfigurationFromAppSettings(); + +var builder = WebApplication.CreateBuilder(args); +builder.Logging.AddSimpleConsole(console => console.SingleLine = true); +builder.Host.UseNLog(); + +// Add Saunter to the application services. +builder.Services.AddAsyncApiSchemaGeneration(options => +{ + options.AssemblyMarkerTypes = [typeof(StreetlightMessageBus)]; + + options.Middleware.UiTitle = "Streetlights API"; + + options.AsyncApi = new AsyncApiDocument + { + Info = new Info("Streetlights API", "1.0.0") + { + Description = "The Smartylighting Streetlights API allows you to remotely manage the city lights.", + License = new License("Apache 2.0") + { + Url = "https://www.apache.org/licenses/LICENSE-2.0" + } + }, + Servers = + { + ["mosquitto"] = new Server("test.mosquitto.org", "mqtt"), + ["webapi"] = new Server("localhost:5000", "http"), + }, + }; +}); + +builder.Services.AddScoped(); +builder.Services.AddControllers(); + +var app = builder.Build(); + +app.UseDeveloperExceptionPage(); + +app.UseRouting(); +app.UseCors(configure => configure.AllowAnyOrigin().AllowAnyMethod()); + +// to be fixed with issue #173 +#pragma warning disable ASP0014 // Suggest using top level route registrations instead of UseEndpoints +app.UseEndpoints(endpoints => +{ + endpoints.MapAsyncApiDocuments(); + endpoints.MapAsyncApiUi(); + + endpoints.MapControllers(); +}); +#pragma warning restore ASP0014 // Suggest using top level route registrations instead of UseEndpoints + +await app.StartAsync(); + +// Print the AsyncAPI doc location +var logger = app.Services.GetService().CreateLogger(); +var options = app.Services.GetService>(); +var addresses = app.Urls; +logger.LogInformation("AsyncAPI doc available at: {URL}", $"{addresses.FirstOrDefault()}{options.Value.Middleware.Route}"); +logger.LogInformation("AsyncAPI UI available at: {URL}", $"{addresses.FirstOrDefault()}{options.Value.Middleware.UiBaseRoute}"); + +// Redirect base url to AsyncAPI UI +app.Map("/", () => Results.Redirect("index.html")); +app.Map("/index.html", () => Results.Redirect(options.Value.Middleware.UiBaseRoute)); + +await app.WaitForShutdownAsync(); diff --git a/examples/StreetlightsAPI.TopLevelStatement/StreetlightsAPI.TopLevelStatement.csproj b/examples/StreetlightsAPI.TopLevelStatement/StreetlightsAPI.TopLevelStatement.csproj new file mode 100644 index 0000000..f3bb8e3 --- /dev/null +++ b/examples/StreetlightsAPI.TopLevelStatement/StreetlightsAPI.TopLevelStatement.csproj @@ -0,0 +1,63 @@ + + + + + net8.0 + false + + + true + json,yml + streetlights.{extension} + specs + + + + bin\Debug\StreetlightsAPI.TopLevelStatement.xml + 1701;1702;1591 + + + + bin\Release\StreetlightsAPI.TopLevelStatement.xml + 1701;1702;1591 + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + PreserveNewest + + + + + + + + + diff --git a/examples/StreetlightsAPI.TopLevelStatement/appsettings.json b/examples/StreetlightsAPI.TopLevelStatement/appsettings.json new file mode 100644 index 0000000..b4f6487 --- /dev/null +++ b/examples/StreetlightsAPI.TopLevelStatement/appsettings.json @@ -0,0 +1,19 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + + "AllowedHosts": "*", + + "Kestrel": { + "EndPoints": { + "Http": { + "Url": "http://localhost:5001" + } + } + } +} diff --git a/examples/StreetlightsAPI/Program.cs b/examples/StreetlightsAPI/Program.cs index aa3f77a..9afd805 100644 --- a/examples/StreetlightsAPI/Program.cs +++ b/examples/StreetlightsAPI/Program.cs @@ -6,6 +6,8 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using NLog; +using NLog.Web; using Saunter; using Saunter.AsyncApiSchema.v2; @@ -15,6 +17,8 @@ public class Program { public static void Main(string[] args) { + LogManager.Setup().LoadConfigurationFromAppSettings(); + CreateHostBuilder(args).Build().Run(); } @@ -22,10 +26,11 @@ public static IHostBuilder CreateHostBuilder(string[] args) { return Host.CreateDefaultBuilder(args) .ConfigureLogging(logging => logging.AddSimpleConsole(console => console.SingleLine = true)) + .UseNLog() .ConfigureWebHostDefaults(web => { web.UseStartup(); - web.UseUrls("http://localhost:5000"); + web.UseUrls("http://localhost:5001"); }); } } diff --git a/examples/StreetlightsAPI/StreetlightsAPI.csproj b/examples/StreetlightsAPI/StreetlightsAPI.csproj index 59dc6fd..a0896d4 100644 --- a/examples/StreetlightsAPI/StreetlightsAPI.csproj +++ b/examples/StreetlightsAPI/StreetlightsAPI.csproj @@ -1,7 +1,9 @@  - net8.0 + + net6.0 false @@ -44,6 +46,15 @@ + + + PreserveNewest + - + + + + + + diff --git a/examples/StreetlightsAPI/nlog.config b/examples/StreetlightsAPI/nlog.config new file mode 100644 index 0000000..dd85514 --- /dev/null +++ b/examples/StreetlightsAPI/nlog.config @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/AsyncAPI.Saunter.Generator.Cli/AsyncAPI.Saunter.Generator.Cli.csproj b/src/AsyncAPI.Saunter.Generator.Cli/AsyncAPI.Saunter.Generator.Cli.csproj index d03d9a8..f2483a8 100644 --- a/src/AsyncAPI.Saunter.Generator.Cli/AsyncAPI.Saunter.Generator.Cli.csproj +++ b/src/AsyncAPI.Saunter.Generator.Cli/AsyncAPI.Saunter.Generator.Cli.csproj @@ -8,6 +8,7 @@ enable 12 AsyncAPI.Saunter.Generator.Cli + $(NoWarn);EF1001 AsyncAPI Command Line Tools: Dotnet tool to generate AsyncAPI spec file(s) from dotnet startup assembly. AsyncAPI Initiative @@ -42,8 +43,8 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + - diff --git a/src/AsyncAPI.Saunter.Generator.Cli/Program.cs b/src/AsyncAPI.Saunter.Generator.Cli/Program.cs index 3502ea7..58bfef2 100644 --- a/src/AsyncAPI.Saunter.Generator.Cli/Program.cs +++ b/src/AsyncAPI.Saunter.Generator.Cli/Program.cs @@ -15,3 +15,5 @@ var app = ConsoleApp.Create(); app.Add(); app.Run(args); + +Environment.ExitCode = 0; diff --git a/src/AsyncAPI.Saunter.Generator.Cli/ToFile/DependencyResolver.cs b/src/AsyncAPI.Saunter.Generator.Cli/ToFile/DependencyResolver.cs new file mode 100644 index 0000000..3d6d010 --- /dev/null +++ b/src/AsyncAPI.Saunter.Generator.Cli/ToFile/DependencyResolver.cs @@ -0,0 +1,22 @@ +using System.Reflection; + +namespace AsyncAPI.Saunter.Generator.Cli.ToFile; + +internal static class DependencyResolver +{ + public static void Init(string startupAssemblyBasePath) + { + AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => + { + var requestedAssembly = new AssemblyName(args.Name); + var fullPath = Path.Combine(startupAssemblyBasePath, $"{requestedAssembly.Name}.dll"); + if (File.Exists(fullPath)) + { + var assembly = Assembly.LoadFile(fullPath); + return assembly; + } + Console.WriteLine($"Could not resolve assembly: {args.Name}, requested by {args.RequestingAssembly?.FullName}"); + return default; + }; + } +} diff --git a/src/AsyncAPI.Saunter.Generator.Cli/ToFile/ServiceProviderBuilder.cs b/src/AsyncAPI.Saunter.Generator.Cli/ToFile/ServiceProviderBuilder.cs index 5773b5f..eed4c58 100644 --- a/src/AsyncAPI.Saunter.Generator.Cli/ToFile/ServiceProviderBuilder.cs +++ b/src/AsyncAPI.Saunter.Generator.Cli/ToFile/ServiceProviderBuilder.cs @@ -1,5 +1,6 @@ -using System.Reflection; -using System.Runtime.Loader; +using System.Runtime.Loader; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.EntityFrameworkCore.Design.Internal; using Microsoft.Extensions.Logging; namespace AsyncAPI.Saunter.Generator.Cli.ToFile; @@ -13,12 +14,20 @@ internal class ServiceProviderBuilder(ILogger logger) : { public IServiceProvider BuildServiceProvider(string startupAssembly) { - var fullPath = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), startupAssembly)); + var fullPath = Path.GetFullPath(startupAssembly); + var basePath = Path.GetDirectoryName(fullPath); + DependencyResolver.Init(basePath); + logger.LogInformation($"Loading startup assembly: {fullPath}"); var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(fullPath); - var nswagCommandsAssembly = Assembly.LoadFrom(Path.GetFullPath(Path.Combine(Path.GetDirectoryName(typeof(ServiceProviderBuilder).Assembly.Location), "NSwag.Commands.dll"))); - var nswagServiceProvider = nswagCommandsAssembly.GetType("NSwag.Commands.ServiceProviderResolver"); - var serviceProvider = (IServiceProvider)nswagServiceProvider.InvokeMember("GetServiceProvider", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static, null, null, [assembly]); + var reporter = new OperationReporter(new OperationReportHandler( + m => logger.LogError(m), + m => logger.LogWarning(m), + m => logger.LogInformation(m), + m => logger.LogDebug(m))); + var appServiceProvider = new AppServiceProviderFactory(assembly, reporter); + var serviceProvider = appServiceProvider.Create([]); + return serviceProvider; } } diff --git a/src/AsyncAPI.Saunter.Generator.Cli/ToFile/StreamProvider.cs b/src/AsyncAPI.Saunter.Generator.Cli/ToFile/StreamProvider.cs index 8af5dbd..7ada619 100644 --- a/src/AsyncAPI.Saunter.Generator.Cli/ToFile/StreamProvider.cs +++ b/src/AsyncAPI.Saunter.Generator.Cli/ToFile/StreamProvider.cs @@ -1,14 +1,19 @@ -namespace AsyncAPI.Saunter.Generator.Cli.ToFile; +using System.Diagnostics; +using Microsoft.Extensions.Logging; + +namespace AsyncAPI.Saunter.Generator.Cli.ToFile; internal interface IStreamProvider { Stream GetStreamFor(string path); } -internal class StreamProvider : IStreamProvider +internal class StreamProvider(ILogger logger) : IStreamProvider { public Stream GetStreamFor(string path) { + logger.LogDebug($"GetStreamFor(path: {path})"); + if (!string.IsNullOrEmpty(path)) { Directory.CreateDirectory(Path.GetDirectoryName(path)); diff --git a/src/AsyncAPI.Saunter.Generator.Cli/ToFile/ToFileCommand.cs b/src/AsyncAPI.Saunter.Generator.Cli/ToFile/ToFileCommand.cs index c801645..91055b4 100644 --- a/src/AsyncAPI.Saunter.Generator.Cli/ToFile/ToFileCommand.cs +++ b/src/AsyncAPI.Saunter.Generator.Cli/ToFile/ToFileCommand.cs @@ -13,11 +13,11 @@ internal class ToFileCommand(ILogger logger, IEnvironmentBuilder /// Retrieves AsyncAPI spec from a startup assembly and writes to file. /// /// relative path to the application's startup assembly - /// -o,relative path where the AsyncAPI will be output [defaults to stdout] - /// -d,name(s) of the AsyncAPI documents you want to retrieve, as configured in your startup class [defaults to all documents] - /// exports AsyncAPI in json and/or yml format [defaults to json] - /// defines the file name template, {document} and {extension} template variables can be used [defaults to "{document}_asyncapi.{extension}\"] - /// define environment variable(s) for the application. Formatted as a comma separated list of key=value pairs or just key for flags + /// -o,relative path where the AsyncAPI documents will be exported to + /// -d,name(s) of the AsyncAPI documents you want to export as configured in your startup class. To export all documents using null. + /// exports AsyncAPI in json and/or yml format + /// defines the file name template, {document} and {extension} template variables can be used + /// define environment variable(s) for the application. Formatted as a comma separated list of _key=value_ pairs [Command("tofile")] public int ToFile([Argument] string startupassembly, string output = "./", string doc = null, string format = "json", string filename = DEFAULT_FILENAME, string env = "") { diff --git a/src/AsyncAPI.Saunter.Generator.Cli/readme.md b/src/AsyncAPI.Saunter.Generator.Cli/readme.md index 648b570..7f41dda 100644 --- a/src/AsyncAPI.Saunter.Generator.Cli/readme.md +++ b/src/AsyncAPI.Saunter.Generator.Cli/readme.md @@ -12,9 +12,9 @@ dotnet asyncapi tofile [startup-assembly] --output [output-path] --format [json, ## Tool options - _--doc_: The name of the AsyncAPI document as defined in the startup class by the ```.ConfigureNamedAsyncApi()```-method. If only ```.AddAsyncApiSchemaGeneration()``` is used, the document is unnamed and will always be exported. If not specified, all documents will be exported. -- _--output_: relative path where the AsyncAPI will be output (default is the csproj root path: ./) -- _--filename_: the template for the outputted file names. Default: "{document}_asyncapi.{extension}" -- _--format_: the output formats to generate, can be a combination of json, yml and/or yaml. +- _--output_: relative path where the AsyncAPI documents will be exported to (Default: the csproj root "./"). +- _--filename_: the template for the outputted file names (Default: "{document}_asyncapi.{extension}"). +- _--format_: the output formats to generate, can be a combination of json, yml and/or yaml (Default: "json"). - _--env_: define environment variable(s) for the application. Formatted as a comma separated list of _key=value_ pairs, example: ```ASPNETCORE_ENVIRONMENT=AsyncAPI,CONNECT_TO_DATABASE=false```. ## Install the Generator.Cli dotnet Tool @@ -27,4 +27,4 @@ Want to learn more about .NET tools? Or want to install it local using a manifes [Check out this Microsoft page on how to manage .NET tools](https://learn.microsoft.com/en-us/dotnet/core/tools/global-tools) ## Internals -How does the tool work internally? It tries to exact an ```IServiceProvider``` from the provided _startup-assembly_ and exports AsyncApiDocument(s) as registered in the services provider. \ No newline at end of file +How does the tool work internally? It tries to exact an ```IServiceProvider``` from the provided _startup-assembly_ and exports AsyncApiDocument(s) as registered with the services provider. \ No newline at end of file diff --git a/test/AsyncAPI.Saunter.Generator.Build.Tests/StreetlightsApiBuildTests.cs b/test/AsyncAPI.Saunter.Generator.Build.Tests/StreetlightsApiBuildTests.cs index 7f04344..c7aa79e 100644 --- a/test/AsyncAPI.Saunter.Generator.Build.Tests/StreetlightsApiBuildTests.cs +++ b/test/AsyncAPI.Saunter.Generator.Build.Tests/StreetlightsApiBuildTests.cs @@ -9,7 +9,7 @@ namespace AsyncAPI.Saunter.Generator.Build.Tests; -public class StreetlightsApiBuildTests(ITestOutputHelper output) +public class IntegrationTests(ITestOutputHelper output) { private string Run(string file, string args, string workingDirectory, int expectedExitCode = 0) { @@ -32,18 +32,18 @@ private string Run(string file, string args, string workingDirectory, int expect return stdOut; } - private const string csprojPath = "../../../../../examples/StreetlightsAPI/StreetlightsAPI.csproj"; - - [Fact] - public void BuildingCsprojGeneratesSpecFilesTest() + [Theory] + [InlineData("StreetlightsAPI")] + [InlineData("StreetlightsAPI.TopLevelStatement")] + public void BuildingCsprojGeneratesSpecFilesTest(string csproj) { var pwd = Directory.GetCurrentDirectory(); - var csproj = Path.GetFullPath(Path.Combine(pwd, csprojPath)); + var csprojFullPath = Path.GetFullPath(Path.Combine(pwd, $"../../../../../examples/{csproj}/{csproj}.csproj")); output.WriteLine($"Current working directory: {pwd}"); - output.WriteLine($"Csproj under test: {csproj}"); - File.Exists(csproj).ShouldBeTrue(); + output.WriteLine($"Csproj under test: {csprojFullPath}"); + File.Exists(csprojFullPath).ShouldBeTrue(); - var csprojDir = Path.GetDirectoryName(csproj); + var csprojDir = Path.GetDirectoryName(csprojFullPath); var specDir = Path.Combine(csprojDir, "specs"); // Spec files should have been generated during the builds of the solution diff --git a/test/AsyncAPI.Saunter.Generator.Cli.Tests/AsyncAPI.Saunter.Generator.Cli.Tests.csproj b/test/AsyncAPI.Saunter.Generator.Cli.Tests/AsyncAPI.Saunter.Generator.Cli.Tests.csproj index 85684c5..08d9da7 100644 --- a/test/AsyncAPI.Saunter.Generator.Cli.Tests/AsyncAPI.Saunter.Generator.Cli.Tests.csproj +++ b/test/AsyncAPI.Saunter.Generator.Cli.Tests/AsyncAPI.Saunter.Generator.Cli.Tests.csproj @@ -43,6 +43,7 @@ + @@ -52,6 +53,9 @@ + + PreserveNewest + PreserveNewest diff --git a/test/AsyncAPI.Saunter.Generator.Cli.Tests/IntegrationTests.cs b/test/AsyncAPI.Saunter.Generator.Cli.Tests/IntegrationTests.cs index 2fe3d56..c607991 100644 --- a/test/AsyncAPI.Saunter.Generator.Cli.Tests/IntegrationTests.cs +++ b/test/AsyncAPI.Saunter.Generator.Cli.Tests/IntegrationTests.cs @@ -42,37 +42,44 @@ Retrieves AsyncAPI spec from a startup assembly and writes to file. [0] relative path to the application's startup assembly Options: - -o|--output relative path where the AsyncAPI will be output [defaults to stdout] (Default: "./") - -d|--doc name(s) of the AsyncAPI documents you want to retrieve as configured in your startup class [defaults to all documents] (Default: null) - --format exports AsyncAPI in json and/or yml format [defaults to json] (Default: "json") - --filename defines the file name template, {document} and {extension} template variables can be used [defaults to "{document}_asyncapi.{extension}\"] (Default: "{document}_asyncapi.{extension}") - --env define environment variable(s) for the application. Formatted as a comma separated list of key=value pairs or just key for flags (Default: "") + -o|--output relative path where the AsyncAPI documents will be exported to (Default: "./") + -d|--doc name(s) of the AsyncAPI documents you want to export as configured in your startup class. To export all documents using null. (Default: null) + --format exports AsyncAPI in json and/or yml format (Default: "json") + --filename defines the file name template, {document} and {extension} template variables can be used (Default: "{document}_asyncapi.{extension}") + --env define environment variable(s) for the application. Formatted as a comma separated list of _key=value_ pairs (Default: "") """, StringCompareShould.IgnoreLineEndings); } - [Fact] - public void StreetlightsAPIExportSpecTest() + /// + /// Both example projects are used to check whether AsyncAPI spec generation is working because they are targeting different .NET versions and are using different hosting strategies. + /// - StreetlightsAPI project is targeting NET6 using the 'old school' Startup-class hosting mechanism. + /// - StreetlightsAPI.TopLevelStatement project is targeting NET8 using the new Top Level Statement hosting mechanism. + /// + [Theory] + [InlineData("StreetlightsAPI", "net6.0")] + [InlineData("StreetlightsAPI.TopLevelStatement", "net8.0")] + public void Streetlights_ExportSpecTest(string csprojName, string targetFramework) { - var path = Directory.GetCurrentDirectory(); + var path = Path.Combine(Directory.GetCurrentDirectory()); output.WriteLine($"Output path: {path}"); - var stdOut = RunTool($"tofile ../../../../../examples/StreetlightsAPI/bin/Debug/net8.0/StreetlightsAPI.dll --output {path} --format json,yml,yaml"); + var stdOut = RunTool($"tofile ../../../../../examples/{csprojName}/bin/Debug/{targetFramework}/{csprojName}.dll --output {path} --filename {csprojName}.{{extension}} --format json,yml,yaml"); stdOut.ShouldNotBeEmpty(); - stdOut.ShouldContain($"AsyncAPI yaml successfully written to {Path.Combine(path, "asyncapi.yaml")}"); - stdOut.ShouldContain($"AsyncAPI yml successfully written to {Path.Combine(path, "asyncapi.yml")}"); - stdOut.ShouldContain($"AsyncAPI json successfully written to {Path.Combine(path, "asyncapi.json")}"); + stdOut.ShouldContain($"AsyncAPI yaml successfully written to {Path.Combine(path, $"{csprojName}.yaml")}"); + stdOut.ShouldContain($"AsyncAPI yml successfully written to {Path.Combine(path, $"{csprojName}.yml")}"); + stdOut.ShouldContain($"AsyncAPI json successfully written to {Path.Combine(path, $"{csprojName}.json")}"); - File.Exists("asyncapi.yml").ShouldBeTrue("asyncapi.yml"); - File.Exists("asyncapi.yaml").ShouldBeTrue("asyncapi.yaml"); - File.Exists("asyncapi.json").ShouldBeTrue("asyncapi.json"); + File.Exists(Path.Combine(path, $"{csprojName}.yml")).ShouldBeTrue(); + File.Exists(Path.Combine(path, $"{csprojName}.yaml")).ShouldBeTrue(); + File.Exists(Path.Combine(path, $"{csprojName}.json")).ShouldBeTrue(); - var yml = File.ReadAllText("asyncapi.yml"); - yml.ShouldBe(ExpectedSpecFiles.Yml_v2_6, "yaml"); + var yml = File.ReadAllText(Path.Combine(path, $"{csprojName}.yml")); + yml.ShouldBe(ExpectedSpecFiles.Yml_v2_6, "yml"); - var yaml = File.ReadAllText("asyncapi.yaml"); - yaml.ShouldBe(yml, "yml"); + var yaml = File.ReadAllText(Path.Combine(path, $"{csprojName}.yaml")); + yaml.ShouldBe(yml, "yaml"); - var json = File.ReadAllText("asyncapi.json"); + var json = File.ReadAllText(Path.Combine(path, $"{csprojName}.json")); json.ShouldBe(ExpectedSpecFiles.Json_v2_6, "json"); } } diff --git a/test/AsyncAPI.Saunter.Generator.Cli.Tests/ToFile/StreamProviderTests.cs b/test/AsyncAPI.Saunter.Generator.Cli.Tests/ToFile/StreamProviderTests.cs index a340b6d..27051eb 100644 --- a/test/AsyncAPI.Saunter.Generator.Cli.Tests/ToFile/StreamProviderTests.cs +++ b/test/AsyncAPI.Saunter.Generator.Cli.Tests/ToFile/StreamProviderTests.cs @@ -1,11 +1,21 @@ using AsyncAPI.Saunter.Generator.Cli.ToFile; +using Microsoft.Extensions.Logging; +using NSubstitute; +using NSubstitute.Community.Logging; using Shouldly; namespace AsyncAPI.Saunter.Generator.Cli.Tests.ToFile; public class StreamProviderTests { - private readonly IStreamProvider _streamProvider = new StreamProvider(); + private readonly IStreamProvider _streamProvider; + private readonly ILogger _logger; + + public StreamProviderTests() + { + this._logger = Substitute.For>(); + this._streamProvider = new StreamProvider(this._logger); + } [Fact] public void NullPathIsStdOut() @@ -14,6 +24,7 @@ public void NullPathIsStdOut() stream.ShouldNotBeNull(); Assert.False(stream is FileStream); + this._logger.Received(1).CallToLog(LogLevel.Debug); } [Fact] @@ -33,5 +44,7 @@ public void StringPathIsFileStream() { File.Delete(path); } + + this._logger.Received(1).CallToLog(LogLevel.Debug); } }