Skip to content

Commit

Permalink
Merge pull request #149 from icarus-consulting/i148-HttpMock-https
Browse files Browse the repository at this point in the history
Support HTTPS in HttpMock
  • Loading branch information
MSE1188 authored Mar 30, 2023
2 parents a097755 + ab65674 commit f428765
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 11 deletions.
25 changes: 25 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Object oriented http client. C# Port of [Vatavuk's verano-http](https://github.c
* [Yaapii.Http.Mock](#yaapii.http.mock)
* [Mocking different Paths](#mocking-different-paths)
* [HttpMock](#httpmock)
* [HTTPS testing with HttpMock](#https-testing-with-httpmock)
5. [Strong naming and third party libraries](#strong-naming-and-third-party-libraries)

## Creating Requests
Expand Down Expand Up @@ -441,6 +442,30 @@ using( var server =
```HttpMock``` is for receiving requests, not sending them.
Using a wire that sends requests to handle the requests received by ```HttpMock``` would cause each received request to just be sent again, causing an infinite loop.

#### HTTPS testing with HttpMock

```HttpMock``` can optionally be configured to listen for HTTPS requests instead of HTTP requests:
```csharp
using (
new HttpMock(1337,
new FkWire(),
useHttps: true
).Value()
)
{
// ... testing code goes here
}
```

The HTTPS server will use the default developer certificate. If no default certificate is configured, an exception will be thrown.

.NET comes with a [command line tool to manage these certificates](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-dev-certs).
To create and configure a new self-signed default certificate, run the following commands in powershell and click yes on the confirmation prompt:
```
dotnet dev-certs https
dotnet dev-certs https --trust
```

# Strong naming and third party libraries

This package uses [strong naming](https://learn.microsoft.com/en-us/dotnet/standard/library-guidance/strong-naming). There are some downsides to that, but the way we use this library, the benefit outweighs the cost.
Expand Down
62 changes: 51 additions & 11 deletions src/Yaapii.Http.Mock/HttpMock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,21 @@ public sealed class HttpMock : IScalar<IWebHost>, IDisposable
/// </summary>
public HttpMock(int port, params ITemplate[] templates) : this(
port,
new MatchingWire(templates)
new MatchingWire(templates),
useHttps: false
)
{ }

/// <summary>
/// Hosts a local http server for unit testing.
/// Handles incoming requests using the first wire template that matches the request.
/// Always dispose this or the returned <see cref="IWebHost"/> after use.
/// DO NOT use a wire that will send a http request (like AspNetCoreWire), that would just forward incoming requests, potentially causing an infinite loop.
/// </summary>
public HttpMock(int port, bool useHttps, params ITemplate[] templates) : this(
port,
new MatchingWire(templates),
useHttps
)
{ }

Expand All @@ -71,8 +85,9 @@ public HttpMock(int port, params ITemplate[] templates) : this(
/// Always dispose this or the returned <see cref="IWebHost"/> after use.
/// DO NOT use a wire that will send a http request (like AspNetCoreWire), that would just forward incoming requests, potentially causing an infinite loop.
/// </summary>
public HttpMock(int port, string path, IWire wire) : this(
public HttpMock(int port, string path, IWire wire, bool useHttps = false) : this(
port,
useHttps,
new KvpOf<IWire>(path, wire)
)
{ }
Expand All @@ -85,7 +100,21 @@ public HttpMock(int port, string path, IWire wire) : this(
/// </summary>
public HttpMock(int port, params IKvp<IWire>[] pathWirePairs) : this(
port,
new ManyOf<IKvp<IWire>>(pathWirePairs)
new ManyOf<IKvp<IWire>>(pathWirePairs),
useHttps: false
)
{ }

/// <summary>
/// Hosts a local http server for unit testing.
/// Handles incoming requests using the wire specified for that path.
/// Always dispose this or the returned <see cref="IWebHost"/> after use.
/// DO NOT use a wire that will send a http request (like AspNetCoreWire), that would just forward incoming requests, potentially causing an infinite loop.
/// </summary>
public HttpMock(int port, bool useHttps, params IKvp<IWire>[] pathWirePairs) : this(
port,
new ManyOf<IKvp<IWire>>(pathWirePairs),
useHttps
)
{ }

Expand All @@ -95,9 +124,10 @@ public HttpMock(int port, params IKvp<IWire>[] pathWirePairs) : this(
/// Always dispose this or the returned <see cref="IWebHost"/> after use.
/// DO NOT use a wire that will send a http request (like AspNetCoreWire), that would just forward incoming requests, potentially causing an infinite loop.
/// </summary>
public HttpMock(int port, IEnumerable<IKvp<IWire>> pathWirePairs) : this(
public HttpMock(int port, IEnumerable<IKvp<IWire>> pathWirePairs, bool useHttps = false) : this(
port,
new MapOf<IWire>(pathWirePairs)
new MapOf<IWire>(pathWirePairs),
useHttps
)
{ }

Expand All @@ -107,9 +137,10 @@ public HttpMock(int port, IEnumerable<IKvp<IWire>> pathWirePairs) : this(
/// Always dispose this or the returned <see cref="IWebHost"/> after use.
/// DO NOT use a wire that will send a http request (like AspNetCoreWire), that would just forward incoming requests, potentially causing an infinite loop.
/// </summary>
public HttpMock(int port, IDictionary<string, IWire> wires) : this(
public HttpMock(int port, IDictionary<string, IWire> wires, bool useHttps = false) : this(
port,
new MatchingWire(wires)
new MatchingWire(wires),
useHttps
)
{ }

Expand All @@ -119,11 +150,11 @@ public HttpMock(int port, IDictionary<string, IWire> wires) : this(
/// Always dispose this or the returned <see cref="IWebHost"/> after use.
/// DO NOT use a wire that will send a http request (like AspNetCoreWire), that would just forward incoming requests, potentially causing an infinite loop.
/// </summary>
public HttpMock(int port, IWire wire)
public HttpMock(int port, IWire wire, bool useHttps = false)
{
this.server =
new ScalarOf<IWebHost>(() =>
RunServer(port, wire)
RunServer(port, wire, useHttps)
);
}

Expand All @@ -137,13 +168,22 @@ public IWebHost Value()
return this.server.Value();
}

private IWebHost RunServer(int port, IWire wire)
private IWebHost RunServer(int port, IWire wire, bool useHttps)
{
return
WebHost.CreateDefaultBuilder()
.UseKestrel((opt) =>
{
opt.ListenAnyIP(port);
opt.ListenAnyIP(
port,
(listenOptions) =>
{
if (useHttps)
{
listenOptions.UseHttps();
}
}
);
opt.AllowSynchronousIO = true;
})
.Configure((app) =>
Expand Down
30 changes: 30 additions & 0 deletions tests/Test.Yaapii.Http.Mock/HttpMockTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -398,5 +398,35 @@ public void ReturnsBodyMoreThanOnce()
);
}
}

[Fact(Skip = "Appveyor doesn't have a default certificate to use for HTTPS.")]
public void UsesHttps()
{
var port = new AwaitedPort(new TestPort()).Value();
using (var server =
new HttpMock(port,
new FkWire(),
useHttps: true
).Value()
)
{
Assert.Equal(
200,
new Status.Of(
new AspNetCoreWire(
new AspNetCoreClients(),
new TimeSpan(0, 1, 0)
).Response(
new Get(
new Scheme("https"),
new Host("localhost"),
new Port(port),
new Path("test/asdf")
)
)
).AsInt()
);
}
}
}
}

0 comments on commit f428765

Please sign in to comment.