diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 040d192b..12d8d7f4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,6 +22,9 @@ jobs: - name: 'WireMock.Net.Tests' run: dotnet test './test/WireMock.Net.Tests/WireMock.Net.Tests.csproj' -c Release --framework net8.0 + - name: 'WireMock.Net.Tests.UsingNuGet' + run: dotnet test './test/WireMock.Net.Tests.UsingNuGet/WireMock.Net.Tests.UsingNuGet.csproj' -c Release + - name: 'WireMock.Net.TUnitTests' run: dotnet test './test/WireMock.Net.TUnitTests/WireMock.Net.TUnitTests.csproj' -c Release --framework net8.0 @@ -46,6 +49,9 @@ jobs: - name: 'WireMock.Net.Tests' run: dotnet test './test/WireMock.Net.Tests/WireMock.Net.Tests.csproj' -c Release --framework net8.0 + - name: 'WireMock.Net.Tests.UsingNuGet' + run: dotnet test './test/WireMock.Net.Tests.UsingNuGet/WireMock.Net.Tests.UsingNuGet.csproj' -c Release + - name: 'WireMock.Net.TUnitTests' run: dotnet test './test/WireMock.Net.TUnitTests/WireMock.Net.TUnitTests.csproj' -c Release --framework net8.0 diff --git a/WireMock.Net Solution.sln b/WireMock.Net Solution.sln index 05339c42..db8ff663 100644 --- a/WireMock.Net Solution.sln +++ b/WireMock.Net Solution.sln @@ -132,6 +132,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Shared", "src\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Minimal", "src\WireMock.Net.Minimal\WireMock.Net.Minimal.csproj", "{BFEF8990-65B3-4274-310F-7355F0B84035}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.ConsoleApp.UsingNuGet", "examples\WireMock.Net.ConsoleApp.UsingNuGet\WireMock.Net.ConsoleApp.UsingNuGet.csproj", "{1F80A6E6-D146-4E40-9EA8-49DB8494239F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Tests.UsingNuGet", "test\WireMock.Net.Tests.UsingNuGet\WireMock.Net.Tests.UsingNuGet.csproj", "{BBA332C6-28A9-42E7-9C4D-A0816E52A198}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -314,6 +318,14 @@ Global {BFEF8990-65B3-4274-310F-7355F0B84035}.Debug|Any CPU.Build.0 = Debug|Any CPU {BFEF8990-65B3-4274-310F-7355F0B84035}.Release|Any CPU.ActiveCfg = Release|Any CPU {BFEF8990-65B3-4274-310F-7355F0B84035}.Release|Any CPU.Build.0 = Release|Any CPU + {1F80A6E6-D146-4E40-9EA8-49DB8494239F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1F80A6E6-D146-4E40-9EA8-49DB8494239F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1F80A6E6-D146-4E40-9EA8-49DB8494239F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1F80A6E6-D146-4E40-9EA8-49DB8494239F}.Release|Any CPU.Build.0 = Release|Any CPU + {BBA332C6-28A9-42E7-9C4D-A0816E52A198}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BBA332C6-28A9-42E7-9C4D-A0816E52A198}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BBA332C6-28A9-42E7-9C4D-A0816E52A198}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BBA332C6-28A9-42E7-9C4D-A0816E52A198}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -365,6 +377,8 @@ Global {F8B4A93E-46EF-4237-88FE-15FDAB7635D4} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2} {D3804228-91F4-4502-9595-39584E5A0177} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2} {BFEF8990-65B3-4274-310F-7355F0B84035} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2} + {1F80A6E6-D146-4E40-9EA8-49DB8494239F} = {985E0ADB-D4B4-473A-AA40-567E279B7946} + {BBA332C6-28A9-42E7-9C4D-A0816E52A198} = {0BB8B634-407A-4610-A91F-11586990767A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458} diff --git a/azure-pipelines-ci.yml b/azure-pipelines-ci.yml index a20a72d0..35d671b7 100644 --- a/azure-pipelines-ci.yml +++ b/azure-pipelines-ci.yml @@ -109,6 +109,13 @@ jobs: packageType: 'sdk' version: '8.0.x' + - task: DotNetCoreCLI@2 + displayName: 'WireMock.Net.Tests.UsingNuGet' + inputs: + command: 'test' + projects: './test/WireMock.Net.Tests.UsingNuGet/WireMock.Net.Tests.UsingNuGet.csproj' + arguments: '--configuration Release' + - task: DotNetCoreCLI@2 displayName: 'WireMock.Net.Tests with Coverage' inputs: diff --git a/examples/WireMock.Net.ConsoleApp.UsingNuGet/Program.cs b/examples/WireMock.Net.ConsoleApp.UsingNuGet/Program.cs new file mode 100644 index 00000000..51217211 --- /dev/null +++ b/examples/WireMock.Net.ConsoleApp.UsingNuGet/Program.cs @@ -0,0 +1,88 @@ +// Copyright © WireMock.Net + +using System.Net.Http.Headers; +using System.Text; +using WireMock.Matchers; +using WireMock.RequestBuilders; +using WireMock.ResponseBuilders; +using WireMock.Server; + +Directory.SetCurrentDirectory(Path.GetTempPath()); + +using var server = WireMockServer.Start(); + +var textPlainContent = "This is some plain text"; +var textPlainContentType = "text/plain"; +var textPlainContentTypeMatcher = new ContentTypeMatcher(textPlainContentType); +var textPlainContentMatcher = new ExactMatcher(textPlainContent); +var textPlainMatcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, textPlainContentTypeMatcher, null, null, textPlainContentMatcher); + +var textJson = "{ \"Key\" : \"Value\" }"; +var textJsonContentType = "text/json"; +var textJsonContentTypeMatcher = new ContentTypeMatcher(textJsonContentType); +var textJsonContentMatcher = new JsonMatcher(new { Key = "Value" }, true); +var jsonMatcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, textJsonContentTypeMatcher, null, null, textJsonContentMatcher); + +var imagePngBytes = Convert.FromBase64String("iVBORw0KGgoAAAANSUhEUgAAAAIAAAACAgMAAAAP2OW3AAAADFBMVEX/tID/vpH/pWX/sHidUyjlAAAADElEQVR4XmMQYNgAAADkAMHebX3mAAAAAElFTkSuQmCC"); +var imagePngContentMatcher = new ExactObjectMatcher(imagePngBytes); +var imagePngMatcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, null, null, null, imagePngContentMatcher); + +var matchers = new IMatcher[] +{ + textPlainMatcher, + jsonMatcher, + imagePngMatcher +}; + +server + .Given(Request.Create() + .UsingPost() + .WithPath("/multipart") + .WithMultiPart(matchers) + ) + .RespondWith(Response.Create() + .WithBodyAsJson(new + { + Method = "{{request.Method}}", + BodyAsMimeMessage = "{{request.BodyAsMimeMessage.TextBody}}" + }) + .WithTransformer() + ); + +server + .Given(Request.Create() + .UsingPost() + .WithPath("/multipart2") + .WithMultiPart(matchers) + ) + .RespondWith(Response.Create() + .WithBody(request => + { + if (request.BodyAsMimeMessage == null) + { + throw new InvalidProgramException("Not expected"); + } + return "OK"; + }) + .WithTransformer() + ); + +var formDataContent = new MultipartFormDataContent +{ + { new StringContent(textPlainContent, Encoding.UTF8, textPlainContentType), "text" }, + { new StringContent(textJson, Encoding.UTF8, textJsonContentType), "json" } +}; + +var fileContent = new ByteArrayContent(imagePngBytes); +fileContent.Headers.ContentType = new MediaTypeHeaderValue("image/png"); +formDataContent.Add(fileContent, "somefile", "image.png"); + +var client = server.CreateClient(); + +var response = await client.PostAsync("/multipart", formDataContent); +var content = await response.Content.ReadAsStringAsync(); +Console.WriteLine(content); + +var response2 = await client.PostAsync("/multipart2", formDataContent); +var content2 = await response2.Content.ReadAsStringAsync(); +Console.WriteLine(content2); \ No newline at end of file diff --git a/examples/WireMock.Net.ConsoleApp.UsingNuGet/WireMock.Net.ConsoleApp.UsingNuGet.csproj b/examples/WireMock.Net.ConsoleApp.UsingNuGet/WireMock.Net.ConsoleApp.UsingNuGet.csproj new file mode 100644 index 00000000..ba3f5902 --- /dev/null +++ b/examples/WireMock.Net.ConsoleApp.UsingNuGet/WireMock.Net.ConsoleApp.UsingNuGet.csproj @@ -0,0 +1,18 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + \ No newline at end of file diff --git a/src/WireMock.Net.MimePart/Util/MimeKitUtils.cs b/src/WireMock.Net.MimePart/Util/MimeKitUtils.cs index bd05f1e0..e0bd93ca 100644 --- a/src/WireMock.Net.MimePart/Util/MimeKitUtils.cs +++ b/src/WireMock.Net.MimePart/Util/MimeKitUtils.cs @@ -28,7 +28,7 @@ internal class MimeKitUtils : IMimeKitUtils if (requestMessage.BodyData != null && requestMessage.Headers?.TryGetValue(HttpKnownHeaderNames.ContentType, out var contentTypeHeader) == true && - StartsWithMultiPart(contentTypeHeader) // Only parse when "multipart/mixed" + StartsWithMultiPart(contentTypeHeader) ) { var bytes = requestMessage.BodyData?.DetectedBodyType switch diff --git a/src/WireMock.Net.MimePart/WireMock.Net.MimePart.csproj b/src/WireMock.Net.MimePart/WireMock.Net.MimePart.csproj index edbfad31..26251d62 100644 --- a/src/WireMock.Net.MimePart/WireMock.Net.MimePart.csproj +++ b/src/WireMock.Net.MimePart/WireMock.Net.MimePart.csproj @@ -36,9 +36,9 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive + + all + runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/WireMock.Net.Minimal/Util/TypeLoader.cs b/src/WireMock.Net.Minimal/Util/TypeLoader.cs index 35ac0272..60ba7fdd 100644 --- a/src/WireMock.Net.Minimal/Util/TypeLoader.cs +++ b/src/WireMock.Net.Minimal/Util/TypeLoader.cs @@ -51,7 +51,7 @@ internal static class TypeLoader { var key = typeof(TInterface).FullName!; - var pluginType = Assemblies.GetOrAdd(key, _ => + return Assemblies.GetOrAdd(key, _ => { if (TryFindTypeInDlls(null, out var foundType)) { @@ -60,7 +60,6 @@ internal static class TypeLoader throw new DllNotFoundException($"No dll found which implements interface '{key}'."); }); - return pluginType; } private static Type GetPluginTypeByFullName(string implementationTypeFullName) where TInterface : class @@ -68,7 +67,7 @@ internal static class TypeLoader var @interface = typeof(TInterface).FullName; var key = $"{@interface}_{implementationTypeFullName}"; - var pluginType = Assemblies.GetOrAdd(key, _ => + return Assemblies.GetOrAdd(key, _ => { if (TryFindTypeInDlls(implementationTypeFullName, out var foundType)) { @@ -77,28 +76,40 @@ internal static class TypeLoader throw new DllNotFoundException($"No dll found which implements Interface '{@interface}' and has FullName '{implementationTypeFullName}'."); }); - return pluginType; } private static bool TryFindTypeInDlls(string? implementationTypeFullName, [NotNullWhen(true)] out Type? pluginType) where TInterface : class { - foreach (var file in Directory.GetFiles(Directory.GetCurrentDirectory(), "*.dll")) +#if NETSTANDARD1_3 + var directoriesToSearch = new[] { AppContext.BaseDirectory }; +#else + var processDirectory = Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule?.FileName); + var assemblyDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + var directoriesToSearch = new[] { processDirectory, assemblyDirectory } + .Where(d => !string.IsNullOrEmpty(d)) + .Distinct() + .ToArray(); +#endif + foreach (var directory in directoriesToSearch) { - try + foreach (var file in Directory.GetFiles(directory!, "*.dll")) { - var assembly = Assembly.Load(new AssemblyName + try { - Name = Path.GetFileNameWithoutExtension(file) - }); + var assembly = Assembly.Load(new AssemblyName + { + Name = Path.GetFileNameWithoutExtension(file) + }); - if (TryGetImplementationTypeByInterfaceAndOptionalFullName(assembly, implementationTypeFullName, out pluginType)) - { - return true; + if (TryGetImplementationTypeByInterfaceAndOptionalFullName(assembly, implementationTypeFullName, out pluginType)) + { + return true; + } + } + catch + { + // no-op: just try next .dll } - } - catch - { - // no-op: just try next .dll } } diff --git a/src/WireMock.Net.Minimal/WireMock.Net.Minimal.csproj b/src/WireMock.Net.Minimal/WireMock.Net.Minimal.csproj index e2320ff0..bfd7f6c8 100644 --- a/src/WireMock.Net.Minimal/WireMock.Net.Minimal.csproj +++ b/src/WireMock.Net.Minimal/WireMock.Net.Minimal.csproj @@ -65,7 +65,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/WireMock.Net.Tests.UsingNuGet/WireMock.Net.Tests.UsingNuGet.csproj b/test/WireMock.Net.Tests.UsingNuGet/WireMock.Net.Tests.UsingNuGet.csproj new file mode 100644 index 00000000..4facbda3 --- /dev/null +++ b/test/WireMock.Net.Tests.UsingNuGet/WireMock.Net.Tests.UsingNuGet.csproj @@ -0,0 +1,39 @@ + + + + net8.0 + enable + enable + + false + true + + + + $(DefineConstants);GRAPHQL;MIMEKIT;PROTOBUF + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + diff --git a/test/WireMock.Net.Tests.UsingNuGet/WireMockServerTests.WithMultiPart.cs b/test/WireMock.Net.Tests.UsingNuGet/WireMockServerTests.WithMultiPart.cs new file mode 100644 index 00000000..4237df8e --- /dev/null +++ b/test/WireMock.Net.Tests.UsingNuGet/WireMockServerTests.WithMultiPart.cs @@ -0,0 +1,103 @@ +// Copyright © WireMock.Net + +using System.Net; +using System.Net.Http.Headers; +using System.Text; +using AwesomeAssertions; +using WireMock.Matchers; +using WireMock.RequestBuilders; +using WireMock.ResponseBuilders; +using WireMock.Server; + +// ReSharper disable once CheckNamespace +namespace WireMock.Net.Tests; + +public partial class WireMockServerTests +{ + [Fact] + public async Task WireMockServer_WithMultiPartBody_Using_MimePartMatchers() + { + // Arrange + using var server = WireMockServer.Start(); + + var textPlainContent = "This is some plain text"; + var textPlainContentType = "text/plain"; + var textPlainContentTypeMatcher = new ContentTypeMatcher(textPlainContentType); + var textPlainContentMatcher = new ExactMatcher(textPlainContent); + var textPlainMatcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, textPlainContentTypeMatcher, null, null, textPlainContentMatcher); + + var textJson = "{ \"Key\" : \"Value\" }"; + var textJsonContentType = "text/json"; + var textJsonContentTypeMatcher = new ContentTypeMatcher(textJsonContentType); + var textJsonContentMatcher = new JsonMatcher(new { Key = "Value" }, true); + var jsonMatcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, textJsonContentTypeMatcher, null, null, textJsonContentMatcher); + + var imagePngBytes = Convert.FromBase64String("iVBORw0KGgoAAAANSUhEUgAAAAIAAAACAgMAAAAP2OW3AAAADFBMVEX/tID/vpH/pWX/sHidUyjlAAAADElEQVR4XmMQYNgAAADkAMHebX3mAAAAAElFTkSuQmCC"); + var imagePngContentMatcher = new ExactObjectMatcher(imagePngBytes); + var imagePngMatcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, null, null, null, imagePngContentMatcher); + + var matchers = new IMatcher[] + { + textPlainMatcher, + jsonMatcher, + imagePngMatcher + }; + + server + .Given(Request.Create() + .UsingPost() + .WithPath("/multipart") + .WithMultiPart(matchers) + ) + .RespondWith(Response.Create() + .WithBody("{{request.Method}};{{request.BodyAsMimeMessage.TextBody}}") + .WithTransformer() + ); + + server + .Given(Request.Create() + .UsingPost() + .WithPath("/multipart2") + .WithMultiPart(matchers) + ) + .RespondWith(Response.Create() + .WithBody(request => + { + if (request.BodyAsMimeMessage == null) + { + throw new InvalidProgramException("Not expected"); + } + return "OK"; + }) + .WithTransformer() + ); + + var formDataContent = new MultipartFormDataContent + { + { new StringContent(textPlainContent, Encoding.UTF8, textPlainContentType), "text" }, + { new StringContent(textJson, Encoding.UTF8, textJsonContentType), "json" } + }; + + var fileContent = new ByteArrayContent(imagePngBytes); + fileContent.Headers.ContentType = new MediaTypeHeaderValue("image/png"); + formDataContent.Add(fileContent, "somefile", "image.png"); + + var client = server.CreateClient(); + + // Act 1 + var response1 = await client.PostAsync("/multipart", formDataContent); + + // Assert 1 + response1.StatusCode.Should().Be(HttpStatusCode.OK); + var content1 = await response1.Content.ReadAsStringAsync(); + content1.Should().Be("POST;This is some plain text"); + + // Act 2 + var response2 = await client.PostAsync("/multipart2", formDataContent); + + // Assert 1 + response2.StatusCode.Should().Be(HttpStatusCode.OK); + var content2 = await response2.Content.ReadAsStringAsync(); + content2.Should().Be("OK"); + } +} \ No newline at end of file diff --git a/test/WireMock.Net.Tests/Util/TypeLoaderTests.cs b/test/WireMock.Net.Tests/Util/TypeLoaderTests.cs index 7d1accb7..6b591f6e 100644 --- a/test/WireMock.Net.Tests/Util/TypeLoaderTests.cs +++ b/test/WireMock.Net.Tests/Util/TypeLoaderTests.cs @@ -1,6 +1,7 @@ // Copyright © WireMock.Net using System; +using System.IO; using AnyOfTypes; using FluentAssertions; using WireMock.Matchers; @@ -57,12 +58,22 @@ public class TypeLoaderTests [Fact] public void LoadNewInstance() { - // Act - AnyOf pattern = "x"; - var result = TypeLoader.LoadNewInstance(MatchBehaviour.AcceptOnMatch, MatchOperator.Or, pattern); + var current = Directory.GetCurrentDirectory(); + try + { + Directory.SetCurrentDirectory(Path.GetTempPath()); - // Assert - result.Should().NotBeNull(); + // Act + AnyOf pattern = "x"; + var result = TypeLoader.LoadNewInstance(MatchBehaviour.AcceptOnMatch, MatchOperator.Or, pattern); + + // Assert + result.Should().NotBeNull(); + } + finally + { + Directory.SetCurrentDirectory(current); + } } [Fact]