StatusCode as string (#385)

* StatusCode as string

* fix tests

* fix test

* ReplaceSingleNode

* <!--<DelaySign>true</DelaySign>-->

* Array

* add test

* Response_ProvideResponse_Handlebars_WithBodyAsJson_ResultAsHandlebarsString

* net461

* .

* fix

* target frame

* BodyAsJson

* Response_ProvideResponse_WithStatusCode

* fix build

* fix test
This commit is contained in:
Stef Heyenrath
2019-12-26 13:57:35 +01:00
committed by GitHub
parent b5795713b1
commit 120a808104
17 changed files with 236 additions and 72 deletions

View File

@@ -3,7 +3,8 @@
<Description>Lightweight StandAlone Http Mocking Server for .Net.</Description> <Description>Lightweight StandAlone Http Mocking Server for .Net.</Description>
<AssemblyTitle>WireMock.Net.StandAlone</AssemblyTitle> <AssemblyTitle>WireMock.Net.StandAlone</AssemblyTitle>
<Authors>Stef Heyenrath</Authors> <Authors>Stef Heyenrath</Authors>
<TargetFrameworks>net451;net452;net46;netstandard1.3;netstandard2.0</TargetFrameworks> <!--<TargetFrameworks>net451;net452;net46;netstandard1.3;netstandard2.0</TargetFrameworks>-->
<TargetFrameworks>net451;net452;net46;net461;netstandard1.3;netstandard2.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>WireMock.Net.StandAlone</AssemblyName> <AssemblyName>WireMock.Net.StandAlone</AssemblyName>
<PackageId>WireMock.Net.StandAlone</PackageId> <PackageId>WireMock.Net.StandAlone</PackageId>

View File

@@ -10,7 +10,7 @@ namespace WireMock.Admin.Mappings
/// <summary> /// <summary>
/// Gets or sets the HTTP status. /// Gets or sets the HTTP status.
/// </summary> /// </summary>
public int? StatusCode { get; set; } public object StatusCode { get; set; }
/// <summary> /// <summary>
/// Gets or sets the body destination (SameAsSource, String or Bytes). /// Gets or sets the body destination (SameAsSource, String or Bytes).

View File

@@ -12,7 +12,7 @@ namespace WireMock.Admin.Requests
/// <summary> /// <summary>
/// Gets or sets the status code. /// Gets or sets the status code.
/// </summary> /// </summary>
public int StatusCode { get; set; } = 200; public object StatusCode { get; set; } = 200;
/// <summary> /// <summary>
/// Gets the headers. /// Gets the headers.

View File

@@ -79,7 +79,17 @@ namespace WireMock.Owin.Mappers
break; break;
} }
response.StatusCode = responseMessage.StatusCode; switch (responseMessage.StatusCode)
{
case int statusCodeAsInteger:
response.StatusCode = statusCodeAsInteger;
break;
case string statusCodeAsString:
response.StatusCode = int.Parse(statusCodeAsString);
break;
}
SetResponseHeaders(responseMessage, response); SetResponseHeaders(responseMessage, response);
if (bytes != null) if (bytes != null)

View File

@@ -14,6 +14,13 @@ namespace WireMock.ResponseBuilders
/// <returns>The <see cref="IResponseBuilder"/>.</returns> /// <returns>The <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithStatusCode(int code); IResponseBuilder WithStatusCode(int code);
/// <summary>
/// The with status code.
/// </summary>
/// <param name="code">The code.</param>
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithStatusCode(string code);
/// <summary> /// <summary>
/// The with status code. /// The with status code.
/// </summary> /// </summary>

View File

@@ -87,11 +87,7 @@ namespace WireMock.ResponseBuilders
ResponseMessage = responseMessage; ResponseMessage = responseMessage;
} }
/// <summary> /// <inheritdoc cref="IStatusCodeResponseBuilder.WithStatusCode(int)"/>
/// The with status code.
/// </summary>
/// <param name="code">The code.</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>\
[PublicAPI] [PublicAPI]
public IResponseBuilder WithStatusCode(int code) public IResponseBuilder WithStatusCode(int code)
{ {
@@ -99,11 +95,15 @@ namespace WireMock.ResponseBuilders
return this; return this;
} }
/// <summary> /// <inheritdoc cref="IStatusCodeResponseBuilder.WithStatusCode(string)"/>
/// The with status code. [PublicAPI]
/// </summary> public IResponseBuilder WithStatusCode(string code)
/// <param name="code">The code.</param> {
/// <returns>A <see cref="IResponseBuilder"/>.</returns> ResponseMessage.StatusCode = code;
return this;
}
/// <inheritdoc cref="IStatusCodeResponseBuilder.WithStatusCode(HttpStatusCode)"/>
[PublicAPI] [PublicAPI]
public IResponseBuilder WithStatusCode(HttpStatusCode code) public IResponseBuilder WithStatusCode(HttpStatusCode code)
{ {

View File

@@ -19,7 +19,7 @@ namespace WireMock
/// <summary> /// <summary>
/// Gets or sets the status code. /// Gets or sets the status code.
/// </summary> /// </summary>
public int StatusCode { get; set; } = 200; public object StatusCode { get; set; } = 200;
/// <summary> /// <summary>
/// Gets or sets the body. /// Gets or sets the body.

View File

@@ -807,9 +807,15 @@ namespace WireMock.Server
return responseBuilder.WithProxy(proxyAndRecordSettings); return responseBuilder.WithProxy(proxyAndRecordSettings);
} }
if (responseModel.StatusCode.HasValue) switch (responseModel.StatusCode)
{ {
responseBuilder = responseBuilder.WithStatusCode(responseModel.StatusCode.Value); case int statusCodeAsInteger:
responseBuilder = responseBuilder.WithStatusCode(statusCodeAsInteger);
break;
case string statusCodeAsString:
responseBuilder = responseBuilder.WithStatusCode(statusCodeAsString);
break;
} }
if (responseModel.Headers != null) if (responseModel.Headers != null)

View File

@@ -1,4 +1,5 @@
using JetBrains.Annotations; using HandlebarsDotNet;
using JetBrains.Annotations;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using System; using System;
@@ -24,14 +25,14 @@ namespace WireMock.Transformers
{ {
var handlebarsContext = _factory.Create(); var handlebarsContext = _factory.Create();
var responseMessage = new ResponseMessage { StatusCode = original.StatusCode }; var responseMessage = new ResponseMessage();
var template = new { request = requestMessage }; var template = new { request = requestMessage };
switch (original.BodyData.DetectedBodyType) switch (original.BodyData.DetectedBodyType)
{ {
case BodyType.Json: case BodyType.Json:
TransformBodyAsJson(handlebarsContext, template, original, responseMessage); TransformBodyAsJson(handlebarsContext.Handlebars, template, original, responseMessage);
break; break;
case BodyType.File: case BodyType.File:
@@ -40,7 +41,7 @@ namespace WireMock.Transformers
case BodyType.String: case BodyType.String:
responseMessage.BodyOriginal = original.BodyData.BodyAsString; responseMessage.BodyOriginal = original.BodyData.BodyAsString;
TransformBodyAsString(handlebarsContext, template, original, responseMessage); TransformBodyAsString(handlebarsContext.Handlebars, template, original, responseMessage);
break; break;
} }
@@ -62,29 +63,46 @@ namespace WireMock.Transformers
responseMessage.Headers = newHeaders; responseMessage.Headers = newHeaders;
switch (original.StatusCode)
{
case int statusCodeAsInteger:
responseMessage.StatusCode = statusCodeAsInteger;
break;
case string statusCodeAsString:
var templateForStatusCode = handlebarsContext.Handlebars.Compile(statusCodeAsString);
responseMessage.StatusCode = templateForStatusCode(template);
break;
}
return responseMessage; return responseMessage;
} }
private static void TransformBodyAsJson(IHandlebarsContext handlebarsContext, object template, ResponseMessage original, ResponseMessage responseMessage) private static void TransformBodyAsJson(IHandlebars handlebarsContext, object template, ResponseMessage original, ResponseMessage responseMessage)
{ {
JToken jToken; JToken jToken;
switch (original.BodyData.BodyAsJson) switch (original.BodyData.BodyAsJson)
{ {
case JObject bodyAsJObject: case JObject bodyAsJObject:
jToken = bodyAsJObject.DeepClone(); jToken = bodyAsJObject.DeepClone();
WalkNode(handlebarsContext, jToken, template);
break; break;
case Array bodyAsArray: case Array bodyAsArray:
jToken = JArray.FromObject(bodyAsArray); jToken = JArray.FromObject(bodyAsArray);
WalkNode(handlebarsContext, jToken, template);
break;
case string bodyAsString:
jToken = ReplaceSingleNode(handlebarsContext, bodyAsString, template);
break; break;
default: default:
jToken = JObject.FromObject(original.BodyData.BodyAsJson); jToken = JObject.FromObject(original.BodyData.BodyAsJson);
WalkNode(handlebarsContext, jToken, template);
break; break;
} }
WalkNode(handlebarsContext, jToken, template);
responseMessage.BodyData = new BodyData responseMessage.BodyData = new BodyData
{ {
DetectedBodyType = original.BodyData.DetectedBodyType, DetectedBodyType = original.BodyData.DetectedBodyType,
@@ -93,7 +111,25 @@ namespace WireMock.Transformers
}; };
} }
private static void WalkNode(IHandlebarsContext handlebarsContext, JToken node, object context) private static JToken ReplaceSingleNode(IHandlebars handlebarsContext, string stringValue, object context)
{
var templateForStringValue = handlebarsContext.Compile(stringValue);
string transformedString = templateForStringValue(context);
if (!string.Equals(stringValue, transformedString))
{
const string property = "_";
JObject dummy = JObject.Parse($"{{ \"{property}\": null }}");
JToken node = dummy[property];
ReplaceNodeValue(node, transformedString);
return dummy[property];
}
return stringValue;
}
private static void WalkNode(IHandlebars handlebarsContext, JToken node, object context)
{ {
if (node.Type == JTokenType.Object) if (node.Type == JTokenType.Object)
{ {
@@ -120,7 +156,7 @@ namespace WireMock.Transformers
return; return;
} }
var templateForStringValue = handlebarsContext.Handlebars.Compile(stringValue); var templateForStringValue = handlebarsContext.Compile(stringValue);
string transformedString = templateForStringValue(context); string transformedString = templateForStringValue(context);
if (!string.Equals(stringValue, transformedString)) if (!string.Equals(stringValue, transformedString))
{ {
@@ -152,9 +188,9 @@ namespace WireMock.Transformers
node.Replace(value); node.Replace(value);
} }
private static void TransformBodyAsString(IHandlebarsContext handlebarsContext, object template, ResponseMessage original, ResponseMessage responseMessage) private static void TransformBodyAsString(IHandlebars handlebarsContext, object template, ResponseMessage original, ResponseMessage responseMessage)
{ {
var templateBodyAsString = handlebarsContext.Handlebars.Compile(original.BodyData.BodyAsString); var templateBodyAsString = handlebarsContext.Compile(original.BodyData.BodyAsString);
responseMessage.BodyData = new BodyData responseMessage.BodyData = new BodyData
{ {

View File

@@ -10,6 +10,26 @@ namespace WireMock.Util
/// </summary> /// </summary>
internal static class HttpStatusRangeParser internal static class HttpStatusRangeParser
{ {
/// <summary>
/// Determines whether the specified pattern is match.
/// </summary>
/// <param name="pattern">The pattern. (Can be null, in that case it's allowed.)</param>
/// <param name="httpStatusCode">The value.</param>
/// <exception cref="ArgumentException"><paramref name="pattern"/> is invalid.</exception>
public static bool IsMatch(string pattern, object httpStatusCode)
{
switch (httpStatusCode)
{
case int statusCodeAsInteger:
return IsMatch(pattern, statusCodeAsInteger);
case string statusCodeAsString:
return IsMatch(pattern, int.Parse(statusCodeAsString));
}
return false;
}
/// <summary> /// <summary>
/// Determines whether the specified pattern is match. /// Determines whether the specified pattern is match.
/// </summary> /// </summary>

View File

@@ -4,6 +4,7 @@
<AssemblyTitle>WireMock.Net</AssemblyTitle> <AssemblyTitle>WireMock.Net</AssemblyTitle>
<Authors>Stef Heyenrath</Authors> <Authors>Stef Heyenrath</Authors>
<!--<TargetFrameworks>net451;net452;net46;net461;netstandard1.3;netstandard2.0;netcoreapp2.1</TargetFrameworks>--> <!--<TargetFrameworks>net451;net452;net46;net461;netstandard1.3;netstandard2.0;netcoreapp2.1</TargetFrameworks>-->
<!--<TargetFrameworks>net451;net452;net46;netstandard1.3;netstandard2.0</TargetFrameworks>-->
<TargetFrameworks>net451;net452;net46;net461;netstandard1.3;netstandard2.0</TargetFrameworks> <TargetFrameworks>net451;net452;net46;net461;netstandard1.3;netstandard2.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>WireMock.Net</AssemblyName> <AssemblyName>WireMock.Net</AssemblyName>

View File

@@ -78,7 +78,7 @@ namespace WireMock.Net.Tests.Owin
// Assert and Verify // Assert and Verify
_optionsMock.Verify(o => o.Logger.Warn(It.IsAny<string>(), It.IsAny<object[]>()), Times.Once); _optionsMock.Verify(o => o.Logger.Warn(It.IsAny<string>(), It.IsAny<object[]>()), Times.Once);
Expression<Func<ResponseMessage, bool>> match = r => r.StatusCode == 404 && ((StatusModel)r.BodyData.BodyAsJson).Status == "No matching mapping found"; Expression<Func<ResponseMessage, bool>> match = r => (int) r.StatusCode == 404 && ((StatusModel)r.BodyData.BodyAsJson).Status == "No matching mapping found";
_responseMapperMock.Verify(m => m.MapAsync(It.Is(match), It.IsAny<IResponse>()), Times.Once); _responseMapperMock.Verify(m => m.MapAsync(It.Is(match), It.IsAny<IResponse>()), Times.Once);
} }
@@ -99,7 +99,7 @@ namespace WireMock.Net.Tests.Owin
// Assert and Verify // Assert and Verify
_optionsMock.Verify(o => o.Logger.Error(It.IsAny<string>(), It.IsAny<object[]>()), Times.Once); _optionsMock.Verify(o => o.Logger.Error(It.IsAny<string>(), It.IsAny<object[]>()), Times.Once);
Expression<Func<ResponseMessage, bool>> match = r => r.StatusCode == 401; Expression<Func<ResponseMessage, bool>> match = r => (int) r.StatusCode == 401;
_responseMapperMock.Verify(m => m.MapAsync(It.Is(match), It.IsAny<IResponse>()), Times.Once); _responseMapperMock.Verify(m => m.MapAsync(It.Is(match), It.IsAny<IResponse>()), Times.Once);
} }
@@ -120,7 +120,7 @@ namespace WireMock.Net.Tests.Owin
// Assert and Verify // Assert and Verify
_optionsMock.Verify(o => o.Logger.Error(It.IsAny<string>(), It.IsAny<object[]>()), Times.Once); _optionsMock.Verify(o => o.Logger.Error(It.IsAny<string>(), It.IsAny<object[]>()), Times.Once);
Expression<Func<ResponseMessage, bool>> match = r => r.StatusCode == 401; Expression<Func<ResponseMessage, bool>> match = r => (int) r.StatusCode == 401;
_responseMapperMock.Verify(m => m.MapAsync(It.Is(match), It.IsAny<IResponse>()), Times.Once); _responseMapperMock.Verify(m => m.MapAsync(It.Is(match), It.IsAny<IResponse>()), Times.Once);
} }

View File

@@ -13,21 +13,6 @@ namespace WireMock.Net.Tests
{ {
private const string ClientIp = "::1"; private const string ClientIp = "::1";
// [Fact] : TODO : this test fails???
public void Request_WithPath_EncodedSpaces()
{
// Assign
var spec = Request.Create().WithPath("/path/a%20b").UsingAnyMethod();
// when
var body = new BodyData();
var request = new RequestMessage(new UrlDetails("http://localhost/path/a%20b"), "GET", ClientIp, body);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Fact] [Fact]
public void Request_WithPath_Spaces() public void Request_WithPath_Spaces()
{ {

View File

@@ -8,7 +8,7 @@ using Xunit;
namespace WireMock.Net.Tests.ResponseBuilders namespace WireMock.Net.Tests.ResponseBuilders
{ {
public class ResponseWithWithFaultTests public class ResponseWithFaultTests
{ {
private readonly Mock<IFluentMockServerSettings> _settingsMock = new Mock<IFluentMockServerSettings>(); private readonly Mock<IFluentMockServerSettings> _settingsMock = new Mock<IFluentMockServerSettings>();
private const string ClientIp = "::1"; private const string ClientIp = "::1";

View File

@@ -24,30 +24,6 @@ namespace WireMock.Net.Tests.ResponseBuilders
private readonly Mock<IFluentMockServerSettings> _settingsMock = new Mock<IFluentMockServerSettings>(); private readonly Mock<IFluentMockServerSettings> _settingsMock = new Mock<IFluentMockServerSettings>();
private const string ClientIp = "::1"; private const string ClientIp = "::1";
[Fact]
public async Task Response_ProvideResponse_Handlebars_WithBodyAsJson_ResultAsObject()
{
// Assign
string jsonString = "{ \"things\": [ { \"name\": \"RequiredThing\" }, { \"name\": \"Wiremock\" } ] }";
var bodyData = new BodyData
{
BodyAsJson = JsonConvert.DeserializeObject(jsonString),
DetectedBodyType = BodyType.Json,
Encoding = Encoding.UTF8
};
var request = new RequestMessage(new UrlDetails("http://localhost/foo_object"), "POST", ClientIp, bodyData);
var response = Response.Create()
.WithBodyAsJson(new { x = "test {{request.path}}" })
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
Check.That(JsonConvert.SerializeObject(responseMessage.BodyData.BodyAsJson)).Equals("{\"x\":\"test /foo_object\"}");
}
[Fact] [Fact]
public async Task Response_ProvideResponse_Handlebars_UrlPathVerb() public async Task Response_ProvideResponse_Handlebars_UrlPathVerb()
{ {
@@ -195,6 +171,30 @@ namespace WireMock.Net.Tests.ResponseBuilders
Check.That(responseMessage.BodyData.BodyAsString).Equals("test http://localhost:1234 1234 http localhost"); Check.That(responseMessage.BodyData.BodyAsString).Equals("test http://localhost:1234 1234 http localhost");
} }
[Fact]
public async Task Response_ProvideResponse_Handlebars_WithBodyAsJson_ResultAsObject()
{
// Assign
string jsonString = "{ \"things\": [ { \"name\": \"RequiredThing\" }, { \"name\": \"Wiremock\" } ] }";
var bodyData = new BodyData
{
BodyAsJson = JsonConvert.DeserializeObject(jsonString),
DetectedBodyType = BodyType.Json,
Encoding = Encoding.UTF8
};
var request = new RequestMessage(new UrlDetails("http://localhost/foo_object"), "POST", ClientIp, bodyData);
var response = Response.Create()
.WithBodyAsJson(new { x = "test {{request.path}}" })
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
Check.That(JsonConvert.SerializeObject(responseMessage.BodyData.BodyAsJson)).Equals("{\"x\":\"test /foo_object\"}");
}
[Fact] [Fact]
public async Task Response_ProvideResponse_Handlebars_WithBodyAsJson_ResultAsArray() public async Task Response_ProvideResponse_Handlebars_WithBodyAsJson_ResultAsArray()
{ {
@@ -286,5 +286,53 @@ namespace WireMock.Net.Tests.ResponseBuilders
// Assert // Assert
Check.That(responseMessage.BodyData.BodyAsFile).Equals(@"c:\1\test.json"); Check.That(responseMessage.BodyData.BodyAsFile).Equals(@"c:\1\test.json");
} }
[Fact]
public async Task Response_ProvideResponse_Handlebars_WithBodyAsJson_ResultAsNormalString()
{
// Assign
string jsonString = "{ \"name\": \"WireMock\" }";
var bodyData = new BodyData
{
BodyAsJson = JsonConvert.DeserializeObject(jsonString),
DetectedBodyType = BodyType.Json,
Encoding = Encoding.UTF8
};
var request = new RequestMessage(new UrlDetails("http://localhost/foo_object"), "POST", ClientIp, bodyData);
var response = Response.Create()
.WithBodyAsJson("test")
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
Check.That(JsonConvert.SerializeObject(responseMessage.BodyData.BodyAsJson)).Equals("\"test\"");
}
[Fact]
public async Task Response_ProvideResponse_Handlebars_WithBodyAsJson_ResultAsHandlebarsString()
{
// Assign
string jsonString = "{ \"name\": \"WireMock\" }";
var bodyData = new BodyData
{
BodyAsJson = JsonConvert.DeserializeObject(jsonString),
DetectedBodyType = BodyType.Json,
Encoding = Encoding.UTF8
};
var request = new RequestMessage(new UrlDetails("http://localhost/foo_object"), "POST", ClientIp, bodyData);
var response = Response.Create()
.WithBodyAsJson("{{{request.bodyAsJson}}}")
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
Check.That(JsonConvert.SerializeObject(responseMessage.BodyData.BodyAsJson)).Equals("{\"name\":\"WireMock\"}");
}
} }
} }

View File

@@ -0,0 +1,49 @@
using FluentAssertions;
using Moq;
using System.Net;
using System.Threading.Tasks;
using WireMock.Models;
using WireMock.ResponseBuilders;
using WireMock.Settings;
using Xunit;
namespace WireMock.Net.Tests.ResponseBuilders
{
public class ResponseWithStatusCodeTests
{
private readonly Mock<IFluentMockServerSettings> _settingsMock = new Mock<IFluentMockServerSettings>();
private const string ClientIp = "::1";
[Theory]
[InlineData("201", "201")]
[InlineData(201, 201)]
[InlineData(HttpStatusCode.Created, 201)]
public async Task Response_ProvideResponse_WithStatusCode(object statusCode, object expectedStatusCode)
{
// Arrange
var request = new RequestMessage(new UrlDetails("http://localhost/fault"), "GET", ClientIp);
// Act
var response = Response.Create();
switch (statusCode)
{
case string statusCodeAsString:
response = response.WithStatusCode(statusCodeAsString);
break;
case int statusCodeAInteger:
response = response.WithStatusCode(statusCodeAInteger);
break;
case HttpStatusCode statusCodeAsEnum:
response = response.WithStatusCode(statusCodeAsEnum);
break;
}
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
responseMessage.StatusCode.Should().Be(expectedStatusCode);
}
}
}

View File

@@ -3,6 +3,7 @@
<PropertyGroup> <PropertyGroup>
<Authors>Stef Heyenrath</Authors> <Authors>Stef Heyenrath</Authors>
<TargetFrameworks>net452;netcoreapp2.1</TargetFrameworks> <TargetFrameworks>net452;netcoreapp2.1</TargetFrameworks>
<!--<TargetFramework>netcoreapp2.1</TargetFramework>-->
<DebugType>full</DebugType> <DebugType>full</DebugType>
<AssemblyName>WireMock.Net.Tests</AssemblyName> <AssemblyName>WireMock.Net.Tests</AssemblyName>
<PackageId>WireMock.Net.Tests</PackageId> <PackageId>WireMock.Net.Tests</PackageId>
@@ -42,7 +43,7 @@
<PackageReference Include="System.Threading" Version="4.3.0" /> <PackageReference Include="System.Threading" Version="4.3.0" />
<PackageReference Include="RestEase" Version="1.4.7" /> <PackageReference Include="RestEase" Version="1.4.7" />
<PackageReference Include="RandomDataGenerator.Net" Version="1.0.10" /> <PackageReference Include="RandomDataGenerator.Net" Version="1.0.10" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="Moq" Version="4.10.1" /> <PackageReference Include="Moq" Version="4.10.1" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="NFluent" Version="2.5.0" /> <PackageReference Include="NFluent" Version="2.5.0" />