diff --git a/OpenApiLINQPadDriver.sln.DotSettings b/OpenApiLINQPadDriver.sln.DotSettings
new file mode 100644
index 0000000..b0e5f9c
--- /dev/null
+++ b/OpenApiLINQPadDriver.sln.DotSettings
@@ -0,0 +1,3 @@
+
+ False
+ True
\ No newline at end of file
diff --git a/OpenApiLINQPadDriver/Extensions/EnumerableExtensions.cs b/OpenApiLINQPadDriver/Extensions/EnumerableExtensions.cs
new file mode 100644
index 0000000..bdb8a8f
--- /dev/null
+++ b/OpenApiLINQPadDriver/Extensions/EnumerableExtensions.cs
@@ -0,0 +1,9 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace OpenApiLINQPadDriver.Extensions;
+internal static class EnumerableExtensions
+{
+ public static IEnumerable AppendIf(this IEnumerable enumerable, bool shouldAppend, T element)
+ => shouldAppend ? enumerable.Append(element) : enumerable;
+}
diff --git a/OpenApiLINQPadDriver/OpenApiContextDriver.cs b/OpenApiLINQPadDriver/OpenApiContextDriver.cs
index a1b36bc..6878ee0 100644
--- a/OpenApiLINQPadDriver/OpenApiContextDriver.cs
+++ b/OpenApiLINQPadDriver/OpenApiContextDriver.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http;
using System.Reflection;
diff --git a/OpenApiLINQPadDriver/OpenApiContextDriverProperties.cs b/OpenApiLINQPadDriver/OpenApiContextDriverProperties.cs
index 2194361..59e916f 100644
--- a/OpenApiLINQPadDriver/OpenApiContextDriverProperties.cs
+++ b/OpenApiLINQPadDriver/OpenApiContextDriverProperties.cs
@@ -112,7 +112,7 @@ private bool GetValue(bool defaultValue, [CallerMemberName] string callerMemberN
private T GetValue(T defaultValue, [CallerMemberName] string callerMemberName = "")
where T : struct, Enum
- => (T)GetValue(v => Enum.TryParse(v, out var val) ? val : defaultValue, defaultValue, callerMemberName);
+ => GetValue(v => Enum.TryParse(v, out var val) ? val : defaultValue, defaultValue, callerMemberName);
private void SetValue(T value, [CallerMemberName] string callerMemberName = "")
{
diff --git a/OpenApiLINQPadDriver/OpenApiLINQPadDriver.csproj b/OpenApiLINQPadDriver/OpenApiLINQPadDriver.csproj
index 7c110a4..7afb576 100644
--- a/OpenApiLINQPadDriver/OpenApiLINQPadDriver.csproj
+++ b/OpenApiLINQPadDriver/OpenApiLINQPadDriver.csproj
@@ -28,6 +28,12 @@
$(AssemblyTitle).
+
+
+ <_Parameter1>OpenApiLINQPadDriverTests
+
+
+
true
true
@@ -70,6 +76,7 @@
+
diff --git a/OpenApiLINQPadDriver/SchemaBuilder.cs b/OpenApiLINQPadDriver/SchemaBuilder.cs
index adfd96c..d6dd5b3 100644
--- a/OpenApiLINQPadDriver/SchemaBuilder.cs
+++ b/OpenApiLINQPadDriver/SchemaBuilder.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using System.IO;
using System.Linq;
using System.Reflection;
using LINQPad.Extensibility.DataContext;
@@ -30,7 +31,6 @@ internal static List GetSchemaAndBuildAssembly(OpenApiContextDrive
#else
var stopWatch = Stopwatch.StartNew();
#endif
-
var document = OpenApiDocumentHelper.GetFromUri(new Uri(openApiContextDriverProperties.OpenApiDocumentUri!));
MeasureTimeAndAddTimeExecutionExplorerItem("Downloading document");
@@ -38,7 +38,8 @@ internal static List GetSchemaAndBuildAssembly(OpenApiContextDrive
document.SetServer(openApiContextDriverProperties.ApiUri!);
var endpointGrouping = openApiContextDriverProperties.EndpointGrouping;
- var settings = CreateCsharpClientGeneratorSettings(endpointGrouping, openApiContextDriverProperties.JsonLibrary, openApiContextDriverProperties.ClassStyle,
+ var classStyle = openApiContextDriverProperties.ClassStyle;
+ var settings = CreateCsharpClientGeneratorSettings(endpointGrouping, openApiContextDriverProperties.JsonLibrary, classStyle,
openApiContextDriverProperties.GenerateSyncMethods, mainContextType);
var generator = new CSharpClientGenerator(document, settings);
@@ -47,6 +48,7 @@ internal static List GetSchemaAndBuildAssembly(OpenApiContextDrive
MeasureTimeAndAddTimeExecutionExplorerItem("Generating NSwag classes");
+ //possibly this switch should be an if based on SupportsMultipleClients?
var clientSourceCode = endpointGrouping switch
{
EndpointGrouping.SingleClientFromOperationIdOperationName => ClientGenerator.SingleClientFromOperationIdOperationNameGenerator(mainContextType),
@@ -58,6 +60,7 @@ internal static List GetSchemaAndBuildAssembly(OpenApiContextDrive
var references = openApiContextDriverProperties.GetCoreFxReferenceAssemblies()
.Append(typeof(JsonConvert).Assembly.Location) //required for code generation, otherwise NSwag will use lowest possible version 10.0.1
+ .AppendIf(classStyle == ClassStyle.Prism, typeof(Prism.IActiveAware).Assembly.Location)
.ToArray();
#pragma warning disable SYSLIB0044 //this is the only way to read this assembly, LINQPad does not give any other reference to it
@@ -130,17 +133,21 @@ void MeasureTimeAndAddTimeExecutionExplorerItem(string name)
var elapsed = stopWatch.Elapsed;
stopWatch.Restart();
#endif
+ File.AppendAllText(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "log.txt"), name + " " + elapsed + Environment.NewLine);
timeExplorerItem.Children.Add(ExplorerItemHelper.CreateForElapsedTime(name, elapsed));
}
}
- private static CSharpClientGeneratorSettings CreateCsharpClientGeneratorSettings(EndpointGrouping endpointGrouping, JsonLibrary jsonLibrary, ClassStyle classStyle, bool generateSyncMethods,
- TypeDescriptor type)
+ private static CSharpClientGeneratorSettings CreateCsharpClientGeneratorSettings(EndpointGrouping endpointGrouping, JsonLibrary jsonLibrary, ClassStyle classStyle,
+ bool generateSyncMethods, TypeDescriptor type)
{
var (operationNameGenerator, className) = endpointGrouping switch
{
- EndpointGrouping.MultipleClientsFromFirstTagAndOperationName => ((IOperationNameGenerator)new MultipleClientsFromFirstTagAndOperationNameGenerator(), "{controller}" + ClientPostFix),
- EndpointGrouping.SingleClientFromOperationIdOperationName => (new SingleClientFromOperationIdOperationNameGenerator(), type.Name),
+ EndpointGrouping.MultipleClientsFromFirstTagAndOperationName
+ => ((IOperationNameGenerator)new MultipleClientsFromFirstTagAndOperationNameGenerator(), "{controller}" + ClientPostFix),
+
+ EndpointGrouping.SingleClientFromOperationIdOperationName
+ => (new SingleClientFromOperationIdOperationNameGenerator(), type.Name),
_ => throw new InvalidOperationException()
};
diff --git a/OpenApiLINQPadDriver/packages.lock.json b/OpenApiLINQPadDriver/packages.lock.json
index 22ff6ed..ba3d9ae 100644
--- a/OpenApiLINQPadDriver/packages.lock.json
+++ b/OpenApiLINQPadDriver/packages.lock.json
@@ -98,6 +98,12 @@
"Newtonsoft.Json": "10.0.1"
}
},
+ "Prism.Core": {
+ "type": "Direct",
+ "requested": "[8.1.97, )",
+ "resolved": "8.1.97",
+ "contentHash": "EP5zrvWddw3eSq25Y7hHnDYdmLZEC2Z/gMrvmHzUuLbitmA1UaS7wQUlSwNr9Km8lzJNCvytFnaGBEFukHgoHg=="
+ },
"Fluid.Core": {
"type": "Transitive",
"resolved": "2.2.15",
@@ -1236,6 +1242,12 @@
"Newtonsoft.Json": "10.0.1"
}
},
+ "Prism.Core": {
+ "type": "Direct",
+ "requested": "[8.1.97, )",
+ "resolved": "8.1.97",
+ "contentHash": "EP5zrvWddw3eSq25Y7hHnDYdmLZEC2Z/gMrvmHzUuLbitmA1UaS7wQUlSwNr9Km8lzJNCvytFnaGBEFukHgoHg=="
+ },
"Fluid.Core": {
"type": "Transitive",
"resolved": "2.2.15",
diff --git a/Tests/OpenApiLINQPadDriverTests/AuthTests.cs b/Tests/OpenApiLINQPadDriverTests/AuthTests.cs
new file mode 100644
index 0000000..436c908
--- /dev/null
+++ b/Tests/OpenApiLINQPadDriverTests/AuthTests.cs
@@ -0,0 +1,52 @@
+namespace OpenApiLINQPadDriverTests;
+public class AuthTests : BaseDriverApiTest
+{
+ [Theory] //todo test both global and local setter
+ [MemberData(nameof(EnumInlineData.Data), MemberType = typeof(EnumInlineData))]
+ public async Task Setting_Headers_In_PrepareRequestFunction_For_MultipleClientsFromFirstTagAndOperationName(JsonLibrary jsonLibrary, ClassStyle classStyle)
+ {
+ MapGet("Header", "/replay", "Replay", static (HttpContext context) => context.Request.Headers);
+ StartApi();
+
+ await ExecuteScriptAsync(
+ """
+ var tokenValue = "fakeBearer";
+ this.PrepareRequestFunction = (a, requestMessage, c) =>
+ {
+ requestMessage.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", tokenValue);
+ };
+
+ var replayedHeaders = await this.HeaderClient.ReplayAsync();
+
+ replayedHeaders.Should()
+ .Contain(h => h.Key == "Authorization" && h.Value.Any(v => v == $"Bearer {tokenValue}"));
+ // return 1;
+ """, EndpointGrouping.MultipleClientsFromFirstTagAndOperationName, jsonLibrary, classStyle);
+ }
+
+ [Theory] //todo test both global and local setter
+ [MemberData(nameof(EnumInlineData.Data), MemberType = typeof(EnumInlineData))]
+ public async Task Setting_Headers_In_PrepareRequestFunction_For_SingleClientFromOperationIdOperationName(JsonLibrary jsonLibrary, ClassStyle classStyle)
+ {
+ MapGet("Header", "/replay", "Replay", static (HttpContext context) => context.Request.Headers);
+ StartApi();
+
+ await ExecuteScriptAsync(
+ """
+ var tokenValue = "fakeBearer";
+ this.PrepareRequestFunction = (a, requestMessage, c) =>
+ {
+ requestMessage.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", tokenValue);
+ };
+
+ var replayedHeaders = await this.ReplayAsync();
+
+ replayedHeaders.Should()
+ .Contain(h => h.Key == "Authorization" && h.Value.Any(v => v == $"Bearer {tokenValue}"));
+
+ //replayedHeaders.Dump();
+ return replayedHeaders;
+ """, EndpointGrouping.SingleClientFromOperationIdOperationName, jsonLibrary, classStyle);
+
+ }
+}
diff --git a/Tests/OpenApiLINQPadDriverTests/CustomResponseBody.cs b/Tests/OpenApiLINQPadDriverTests/CustomResponseBody.cs
new file mode 100644
index 0000000..50d4a64
--- /dev/null
+++ b/Tests/OpenApiLINQPadDriverTests/CustomResponseBody.cs
@@ -0,0 +1,74 @@
+namespace OpenApiLINQPadDriverTests;
+public class CustomResponseBody : BaseDriverApiTest
+{
+ [Theory]
+ [MemberData(nameof(EnumInlineData.Data), MemberType = typeof(EnumInlineData))]
+ public async Task MultipleClientsFromFirstTagAndOperationName(JsonLibrary jsonLibrary, ClassStyle classStyle)
+ {
+ MapGet("CustomResponseAndRequest", "/First", "first", static ([FromBody] CustomRequest request) => new CustomResponse(request.Foo, request.Bar));
+ StartApi();
+
+ var requestConstructor = GetRequestCtor(classStyle);
+ var responseConstructor = GetResponseCtor(classStyle);
+
+ await ExecuteScriptAsync(
+ $"""
+ {nameof(CustomRequest)} request = {requestConstructor};
+
+ var response = await this.CustomResponseAndRequestClient.FirstAsync(request);
+ response.Should().BeEquivalentTo({responseConstructor});
+
+ """, EndpointGrouping.MultipleClientsFromFirstTagAndOperationName, jsonLibrary, classStyle);
+ }
+
+ [Theory]
+ [MemberData(nameof(EnumInlineData.Data), MemberType = typeof(EnumInlineData))]
+ public async Task SingleClientFromOperationIdOperationName(JsonLibrary jsonLibrary, ClassStyle classStyle)
+ {
+ MapGet("CustomResponseAndRequest", "/First", "first", static ([FromBody] CustomRequest request) => new CustomResponse(request.Foo, request.Bar));
+ StartApi();
+
+ var requestConstructor = GetRequestCtor(classStyle);
+ var responseConstructor = GetResponseCtor(classStyle);
+
+ await ExecuteScriptAsync(
+ $"""
+ {nameof(CustomRequest)} request = {requestConstructor};
+
+ var response = await this.FirstAsync(request);
+ response.Should().BeEquivalentTo({responseConstructor});
+
+ """, EndpointGrouping.SingleClientFromOperationIdOperationName, jsonLibrary, classStyle);
+ }
+
+ private record CustomResponse(string Foo, int Bar);
+ private record CustomRequest(string Foo, int Bar);
+
+ private static string GetResponseCtor(ClassStyle classStyle) => classStyle switch
+ {
+ ClassStyle.Poco or ClassStyle.Inpc or ClassStyle.Prism =>
+ """
+ new CustomResponse()
+ {
+ Foo = "string",
+ Bar = 1
+ }
+ """,
+ ClassStyle.Record => "new CustomResponse(1, \"string\")",
+ _ => throw new InvalidOperationException()
+ };
+
+ private static string GetRequestCtor(ClassStyle classStyle) => classStyle switch
+ {
+ ClassStyle.Poco or ClassStyle.Inpc or ClassStyle.Prism =>
+ """
+ new CustomRequest()
+ {
+ Foo = "string",
+ Bar = 1
+ }
+ """,
+ ClassStyle.Record => "new CustomRequest(1, \"string\")",
+ _ => throw new InvalidOperationException()
+ };
+}
diff --git a/Tests/OpenApiLINQPadDriverTests/LPRun/Templates/Test.linq b/Tests/OpenApiLINQPadDriverTests/LPRun/Templates/Test.linq
deleted file mode 100644
index e4e91d2..0000000
--- a/Tests/OpenApiLINQPadDriverTests/LPRun/Templates/Test.linq
+++ /dev/null
@@ -1,11 +0,0 @@
-var number = 2;
-var summary = "test";
-
-var weathers = await this.WeatherClient.GetWeatherForecastAsync(number, summary);
-weathers.Should().HaveCount(number)
-.And.ContainEquivalentOf(new WeatherForecast
-{
- Date = DateTime.Now.Date.AddDays(2),
- TemperatureC = number,
- Summary = summary
-}, Reason());
\ No newline at end of file
diff --git a/Tests/OpenApiLINQPadDriverTests/OpenApiLINQPadDriverTests.csproj b/Tests/OpenApiLINQPadDriverTests/OpenApiLINQPadDriverTests.csproj
index 08b5b68..bbb2c93 100644
--- a/Tests/OpenApiLINQPadDriverTests/OpenApiLINQPadDriverTests.csproj
+++ b/Tests/OpenApiLINQPadDriverTests/OpenApiLINQPadDriverTests.csproj
@@ -3,53 +3,42 @@
net7.0-windows
enable
- true
+
enable
latest
false
- false
true
+
-
+
+
+
+
-
-
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
+
-
-
-
-
-
-
- Always
-
-
-
-
-
-
diff --git a/Tests/OpenApiLINQPadDriverTests/SimpleTests.cs b/Tests/OpenApiLINQPadDriverTests/SimpleTests.cs
new file mode 100644
index 0000000..281a7c7
--- /dev/null
+++ b/Tests/OpenApiLINQPadDriverTests/SimpleTests.cs
@@ -0,0 +1,53 @@
+namespace OpenApiLINQPadDriverTests;
+
+public class SimpleTests : BaseDriverApiTest
+{
+ [Theory] //todo test both global and local setter
+ [MemberData(nameof(EnumInlineData.Data), MemberType = typeof(EnumInlineData))]
+ public async Task MultipleClientsFromFirstTagAndOperationName(JsonLibrary jsonLibrary, ClassStyle classStyle)
+ {
+ MapGet("arithmetic", "/sum", "sum", static (int x, int y) => x + y);
+ MapGet("arithmetic", "/divide", "divide", static (int dividend, int divisor) => dividend / divisor);
+ StartApi();
+
+ await ExecuteScriptAsync(
+ """
+ var sum = await this.ArithmeticClient.SumAsync(x: 1, y: 2);
+ sum.Should().Be(3, Reason());
+
+ var asyncAction = () => this.ArithmeticClient.DivideAsync(dividend: 1, divisor: 0);
+
+ await asyncAction.Should().ThrowAsync().WithMessage("*The HTTP status code of the response was not expected (500)*");
+
+ //this.Dump();
+ """, EndpointGrouping.MultipleClientsFromFirstTagAndOperationName, jsonLibrary, classStyle);
+ }
+
+ [Theory] //todo test both global and local setter
+ [MemberData(nameof(EnumInlineData.Data), MemberType = typeof(EnumInlineData))]
+ public async Task SingleClientFromOperationIdOperationName(JsonLibrary jsonLibrary, ClassStyle classStyle)
+ {
+ MapGet("arithmetic", "/sum", "sum", static (int x, int y) => x + y);
+ MapGet("arithmetic", "/divide", "divide", static (int dividend, int divisor) => dividend / divisor);
+ StartApi();
+
+ await ExecuteScriptAsync(
+ """
+ var sum = await this.SumAsync(x: 1, y: 2);
+ sum.Should().Be(3, Reason());
+
+ var division = await this.DivideAsync(dividend: 10, divisor: 5);
+ division.Should().Be(2, Reason());
+ """, EndpointGrouping.SingleClientFromOperationIdOperationName, jsonLibrary, classStyle);
+ }
+
+
+
+ //todo some form of physical cache based on swagger.json hash? Write to temp and read it or something
+
+ //todo auth tests
+
+ //todo defaults props are set (for both modes)
+
+ //todo test with a lot of endpoints/clients
+}
diff --git a/Tests/OpenApiLINQPadDriverTests/UI.cs b/Tests/OpenApiLINQPadDriverTests/UI.cs
new file mode 100644
index 0000000..02530a0
--- /dev/null
+++ b/Tests/OpenApiLINQPadDriverTests/UI.cs
@@ -0,0 +1,90 @@
+//using LINQPad.Extensibility.DataContext;
+//using Moq;
+//using System.Xml.Linq;
+//using FlaUI.UIA3;
+//using OpenApiLINQPadDriver;
+//using FluentAssertions;
+//using FlaUI.Core.AutomationElements;
+//using FlaUI.Core.Definitions;
+
+//namespace OpenApiLINQPadDriverTests;
+//public class UI
+//{
+
+// [Fact]
+// public async Task Foo()
+// {
+// Action? close = null;
+// var connectionInfoMock = new Mock();
+
+// var xElement = XDocument.Parse(@$"
+// https://localhost/swagger/v1/swagger.json
+// https://localhost/
+//").Root!;
+
+// connectionInfoMock.Setup(s => s.DriverData).Returns(xElement);
+
+// var driverProperties = new OpenApiContextDriverProperties(connectionInfoMock.Object);
+
+// var thread = new Thread(() =>
+// {
+
+
+// var dialog = new ConnectionDialog(driverProperties);
+
+// close = () => dialog.Dispatcher.Invoke(dialog.Close);
+// var result = dialog.ShowDialog();
+
+// result.Should().BeTrue();
+// });
+
+// try
+// {
+// thread.SetApartmentState(ApartmentState.STA);
+// thread.Start();
+
+// var app = FlaUI.Core.Application.Attach(Environment.ProcessId);
+
+// using var automation = new UIA3Automation();
+
+// var window = app.GetMainWindow(automation);
+// window.Title.Should().Be("Open API Connection");
+
+// var okButton = window.FindFirstDescendant(cf => cf.ByText("OK"))?.AsButton();
+// var x = window.FindFirstDescendant(cf => cf.ByText("Open", PropertyConditionFlags.MatchSubstring)).AsLabel();
+
+
+// //var children = window.FindAllDescendants().ToList();
+
+// //children.Should().NotBeNull();
+// x.Should().NotBeNull();
+
+// x.Text.Should().Be("Open Api Json Uri (swagger.json): ");
+
+// var input = window.FindFirstDescendant(cf =>
+// cf.ByAutomationId("OpenApiDocumentUri", PropertyConditionFlags.MatchSubstring))
+// .AsTextBox();
+
+// input.Should().NotBeNull();
+// input.Text.Should().Be("https://localhost/swagger/v1/swagger.json");
+
+
+// input.Enter("https://foobar/swagger/v1/swagger.json");
+
+// await Task.Delay(200);
+
+// okButton.Click();
+// close = null;
+
+// //result = result;
+// }
+// finally
+// {
+// close?.Invoke();
+// thread.Join();
+// }
+
+
+// driverProperties.OpenApiDocumentUri.Should().Be("https://foobar/swagger/v1/swagger.json");
+// }
+//}
diff --git a/Tests/OpenApiLINQPadDriverTests/UnitTest1.cs b/Tests/OpenApiLINQPadDriverTests/UnitTest1.cs
deleted file mode 100644
index f0de723..0000000
--- a/Tests/OpenApiLINQPadDriverTests/UnitTest1.cs
+++ /dev/null
@@ -1,183 +0,0 @@
-using FluentAssertions;
-using LINQPad.Extensibility.DataContext;
-using LPRun;
-using Moq;
-using OpenApiLINQPadDriver;
-using System.Text;
-using System.Xml.Linq;
-using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Hosting;
-using Microsoft.OpenApi.Models;
-using OpenApiServer = Microsoft.OpenApi.Models.OpenApiServer;
-
-namespace OpenApiLINQPadDriverTests;
-
-public class UnitTest1 : IClassFixture
-{
- private readonly ApiFixture _apiFixture;
- public UnitTest1(ApiFixture apiFixture) => _apiFixture = apiFixture;
-
- [Fact]
- public async Task Test1()
- {
- var connectionInfoMock = new Mock();
-
- connectionInfoMock.Setup(s => s.DriverData).Returns(XDocument.Parse(@$"
- {_apiFixture.ApiUrl}swagger/v1/swagger.json
- {_apiFixture.ApiUrl}
-").Root!);
-
- var driverProperties = new OpenApiContextDriverProperties(connectionInfoMock.Object);
- var linqScriptName = "Test";
-
-
- var queryConfig = GetQueryHeaders().Aggregate(new StringBuilder(), static (stringBuilder, header) =>
- {
- if (ShouldRender(header))
- {
- stringBuilder.AppendLine(header);
- stringBuilder.AppendLine();
- }
-
- return stringBuilder;
- }).ToString();
-
- var linqScript = LinqScript.Create(
- $"{linqScriptName}.linq",
- queryConfig,
- linqScriptName);
-
-
- // Act: Execute test LNQPad script.
- var (output, error, exitCode) =
- Runner.Execute(linqScript);
-
- // Assert.
- error.Should().BeNullOrWhiteSpace();
- exitCode.Should().Be(0);
-
- IEnumerable GetQueryHeaders()
- {
- // Connection header.
- var nameSpace = nameof(OpenApiLINQPadDriver);
- var driverTypeName = nameof(OpenApiContextDriver);
- yield return ConnectionHeader.Get(
- nameSpace,
- $"{nameSpace}.{driverTypeName}",
- driverProperties,
- "System.Runtime.CompilerServices");
-
- // FluentAssertions helper.
- yield return
- @"string Reason([CallerLineNumber] int sourceLineNumber = 0) =>" +
- @" $""something went wrong at line #{sourceLineNumber}"";";
-
- // Test context.
- //if (!string.IsNullOrWhiteSpace(context))
- //{
- // yield return $"var context = {context};";
- //}
- }
- }
- static bool ShouldRender(string? str) =>
- !string.IsNullOrWhiteSpace(str);
-
-}
-
-public class ApiFixture : IDisposable
-{
- private readonly CancellationTokenSource _cancellationTokenSource = new();
- public readonly string ApiUrl = "https://localhost:5003/";
- public ApiFixture()
- {
- try
- {
- const string driverName = "OpenApiLINQPadDriver";
- var linqPadFolderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "LINQPad");
- var nugetDriverPath = Path.Combine(linqPadFolderPath, "NuGet.Drivers", driverName);
- var folderDriverPath = Path.Combine(linqPadFolderPath, "Drivers", "DataContext", driverName);
- if (Directory.Exists(nugetDriverPath))
- throw new InvalidOperationException($"Driver already exists inside \"{nugetDriverPath}\", please remove it via LINQPad");
- if (Directory.Exists(folderDriverPath))
- throw new InvalidOperationException($"Driver already exists inside \"{folderDriverPath}\", please remove it via LINQPad");
-
- // Copy driver to LPRun drivers folder.
- Driver.InstallWithDepsJson(
- driverName,
- driverName + ".dll",
- "Tests"
- );
-
-
- var builder = WebApplication.CreateBuilder();
-
- builder.Services.AddControllers();
-
- builder.Services.AddEndpointsApiExplorer();
- builder.Services.AddSwaggerGen(options =>
- {
- options.AddServer(new OpenApiServer
- {
- Url = ApiUrl
- });
- });
- builder.Services.AddMvc();
-
- builder.WebHost
- .UseUrls(ApiUrl);
-
- var app = builder.Build();
-
-
- app.UseSwagger(c =>
- {
- });
- app.UseSwaggerUI();
-
- app.UseHttpsRedirection();
-
- app.MapControllers();
-
- app.MapGet("/weatherforecast", (int numberOf, string summary) =>
- {
- var forecast = Enumerable.Range(1, numberOf).Select(index =>
- new WeatherForecast
- (
- DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
- numberOf,
- summary
- ))
- .ToArray();
- return forecast;
- })
- .WithName("GetWeatherForecast")
- .WithOpenApi(config =>
- {
- config.Tags.Clear();
- config.Tags.Add(new OpenApiTag
- {
- Name = "Weather"
- });
- return config;
- });
-
-
- app.RunAsync(_cancellationTokenSource.Token);
- }
- catch (Exception e)
- {
- Console.WriteLine(e);
- throw;
- }
-
- }
-
- internal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary);
-
- public void Dispose()
- {
- _cancellationTokenSource.Cancel();
- }
-}
\ No newline at end of file
diff --git a/Tests/OpenApiLINQPadDriverTests/Usings.cs b/Tests/OpenApiLINQPadDriverTests/Usings.cs
index 8c927eb..f794039 100644
--- a/Tests/OpenApiLINQPadDriverTests/Usings.cs
+++ b/Tests/OpenApiLINQPadDriverTests/Usings.cs
@@ -1 +1,5 @@
+global using OpenApiLINQPadDriverTests.Utils;
+global using Microsoft.AspNetCore.Http;
+global using Microsoft.AspNetCore.Mvc;
+global using OpenApiLINQPadDriver.Enums;
global using Xunit;
\ No newline at end of file
diff --git a/Tests/OpenApiLINQPadDriverTests/Utils/BaseDriverApiTest.cs b/Tests/OpenApiLINQPadDriverTests/Utils/BaseDriverApiTest.cs
new file mode 100644
index 0000000..47d49de
--- /dev/null
+++ b/Tests/OpenApiLINQPadDriverTests/Utils/BaseDriverApiTest.cs
@@ -0,0 +1,123 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.OpenApi.Models;
+using System.Reflection;
+
+namespace OpenApiLINQPadDriverTests.Utils;
+
+public abstract class BaseDriverApiTest : IAsyncLifetime
+{
+ private const string DriverName = nameof(OpenApiLINQPadDriver);
+ private WebApplication? _webApplication;
+
+ private string? _apiUri;
+ private string ApiUrl => _apiUri ?? throw new ArgumentNullException(nameof(_apiUri));
+ private static readonly string BaseDir = Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().Location).LocalPath)!;
+
+ private readonly List> _endpoints = new();
+ private readonly QueryExecutor? _queryExecutor = new();
+
+ protected void StartApi()
+ {
+ LinqPadHelper.ThrowIfDriverExists(DriverName);
+
+ InstallDriver();
+
+ var builder = WebApplication.CreateBuilder();
+
+ builder.Services.AddEndpointsApiExplorer();
+ builder.Services.AddSwaggerGen(options =>
+ {
+ //options.AddServer(new OpenApiServer
+ //{
+ // Url = ApiUrl
+ //});
+ });
+
+ //by using :0 port an empty one will be assigned
+ builder.WebHost.UseUrls("http://127.0.0.1:0");
+
+ var app = builder.Build();
+
+ app.Lifetime.ApplicationStarted.Register(() => _apiUri = app.Urls.Single());
+
+ app.UseSwagger();
+
+ foreach (var endpoint in _endpoints)
+ {
+ endpoint.Invoke(app);
+ }
+
+ app.RunAsync();
+
+ _webApplication = app;
+ }
+
+ private static void InstallDriver()
+ {
+ var files = new List { DriverName + ".dll", GetDepsJsonRelativePath(DriverName + ".dll", "Tests") };
+
+ var driverOutputPath = Path.Combine("drivers", "DataContext", "NetCore", DriverName);
+
+ Directory.CreateDirectory(driverOutputPath);
+
+ foreach (var file in files)
+ {
+ CopyFile(file);
+ }
+
+ return;
+
+ void ExecIfFileIsNewer(string file, Action action)
+ {
+ var srcFile = Path.GetFullPath(file);
+ var dstFile = Path.Combine(driverOutputPath, Path.GetFileName(file));
+
+ var srcFileInfo = new FileInfo(srcFile);
+ var dstFileInfo = new FileInfo(dstFile);
+
+ if (!dstFileInfo.Exists || dstFileInfo.LastWriteTime < srcFileInfo.LastWriteTime)
+ {
+ action(srcFile, dstFile);
+ }
+ }
+
+ void CopyFile(string file) =>
+ ExecIfFileIsNewer(file, (srcFile, dstFile) => File.Copy(srcFile, dstFile, true));
+ }
+
+ protected Task ExecuteScriptAsync(string script, EndpointGrouping endpointGrouping, JsonLibrary jsonLibrary, ClassStyle classStyle)
+ => _queryExecutor!.RunAsync(ApiUrl, script, endpointGrouping, jsonLibrary, classStyle);
+
+ protected void MapGet(string controllerName, string pattern, string name, Delegate handler)
+ => _endpoints.Add(app =>
+ app.MapGet(pattern, handler)
+ .WithName(name)
+ .WithOpenApi(openApiConfig =>
+ {
+ openApiConfig.Tags.Clear();
+ openApiConfig.Tags.Add(new OpenApiTag
+ {
+ Name = controllerName
+ });
+ return openApiConfig;
+ }));
+
+ private static string GetDepsJsonRelativePath(string driverFileName, string testsFolderPath)
+ => GetDepsJsonRelativePath(driverFileName, baseDir => baseDir.Replace(testsFolderPath, string.Empty, StringComparison.OrdinalIgnoreCase));
+
+ private static string GetDepsJsonRelativePath(string driverFileName, Func getDepsJsonFileFullPath)
+ => Path.Combine(Path.GetRelativePath(BaseDir, Path.Combine(getDepsJsonFileFullPath(BaseDir), Path.ChangeExtension(driverFileName, ".deps.json"))));
+
+ Task IAsyncLifetime.InitializeAsync()
+ => Task.CompletedTask;
+
+ async Task IAsyncLifetime.DisposeAsync()
+ {
+ if (_webApplication != null)
+ await _webApplication.DisposeAsync();
+
+ _queryExecutor?.Dispose();
+ }
+}
diff --git a/Tests/OpenApiLINQPadDriverTests/Utils/ConnectionHeader.cs b/Tests/OpenApiLINQPadDriverTests/Utils/ConnectionHeader.cs
new file mode 100644
index 0000000..e451cf7
--- /dev/null
+++ b/Tests/OpenApiLINQPadDriverTests/Utils/ConnectionHeader.cs
@@ -0,0 +1,44 @@
+using System.Globalization;
+using System.Reflection;
+using System.Security;
+
+namespace OpenApiLINQPadDriverTests.Utils;
+internal static class ConnectionHeader
+{
+ public static string Get(string driverAssemblyName, string driverNamespace, T driverConfig, params string[] additionalNamespaces)
+ where T : notnull
+ =>
+ $"""
+
+
+
+ 2
+ Test
+ {driverNamespace}
+
+ {string.Join(Environment.NewLine, GetKeyValues(driverConfig).Select(keyValuePair => $" <{keyValuePair.Key}>{SecurityElement.Escape(keyValuePair.Value)}{keyValuePair.Key}>"))}
+
+
+ FluentAssertions
+ {string.Join(Environment.NewLine, new[] { "FluentAssertions" }.Concat(additionalNamespaces).Select(additionalNamespace => $" {additionalNamespace}"))}
+
+ """;
+
+ private static IEnumerable<(string Key, string Value)> GetKeyValues(T driverConfig)
+ where T : notnull
+ {
+ return driverConfig.GetType().GetProperties()
+ .Where(propertyInfo => propertyInfo is { CanRead: true, CanWrite: true })
+ .Select(propertyInfo => (propertyInfo.Name, ValueToString(propertyInfo)));
+
+ string ValueToString(PropertyInfo propertyInfo)
+ => propertyInfo.GetValue(driverConfig) switch
+ {
+ null => string.Empty,
+ bool v => v ? "true" : "false",
+ IConvertible v => v.ToString(CultureInfo.InvariantCulture),
+ _ => throw new NotSupportedException($"Could not convert {propertyInfo.Name} of type {propertyInfo.PropertyType} to string")
+ };
+ }
+}
+
diff --git a/Tests/OpenApiLINQPadDriverTests/Utils/EnumInlineData.cs b/Tests/OpenApiLINQPadDriverTests/Utils/EnumInlineData.cs
new file mode 100644
index 0000000..6513c85
--- /dev/null
+++ b/Tests/OpenApiLINQPadDriverTests/Utils/EnumInlineData.cs
@@ -0,0 +1,17 @@
+namespace OpenApiLINQPadDriverTests.Utils;
+internal static class EnumInlineData
+{
+ public static IEnumerable