Skip to content

Commit 5867b6a

Browse files
committed
fix: Handle body with content-type response
1 parent ecbd009 commit 5867b6a

File tree

5 files changed

+174
-11
lines changed

5 files changed

+174
-11
lines changed

src/MockServerClientNet/MockServerClient.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,7 @@ public async Task<HttpRequest[]> RetrieveRecordedRequestsAsync(HttpRequest httpR
191191
Encoding.UTF8));
192192

193193
var body = await response.Content.ReadAsStringAsync();
194-
195-
return _httpRequestSerializer.DeserializeArray(body, new HttpRequest[0]);
194+
return _httpRequestSerializer.DeserializeArray(body, []);
196195
}
197196

198197
public async Task SendExpectationAsync(Expectation expectation)

src/MockServerClientNet/Model/Body/Body.cs

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System;
2+
using System.Text;
23
using Newtonsoft.Json;
34
using Newtonsoft.Json.Converters;
5+
using Newtonsoft.Json.Linq;
46

57
namespace MockServerClientNet.Model.Body
68
{
@@ -15,10 +17,14 @@ public class Body
1517
[JsonProperty(PropertyName = "base64Bytes", NullValueHandling = NullValueHandling.Ignore)]
1618
public string Base64Bytes { get; set; }
1719

20+
[JsonProperty(PropertyName = "rawBytes", NullValueHandling = NullValueHandling.Ignore)]
21+
public string RawBytes { get; set; }
22+
1823
[JsonProperty(PropertyName = "xml", NullValueHandling = NullValueHandling.Ignore)]
1924
public string XmlValue { get; set; }
2025

2126
[JsonProperty(PropertyName = "json", NullValueHandling = NullValueHandling.Ignore)]
27+
[JsonConverter(typeof(JsonValueConverter))]
2228
public string JsonValue { get; set; }
2329

2430
internal static class Types
@@ -40,9 +46,35 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
4046
JsonSerializer serializer)
4147
{
4248
// this keeps backward compatibility when loading expectations from file
43-
return reader.ValueType == typeof(string)
44-
? DeserializeFromString(reader.Value?.ToString())
45-
: base.ReadJson(reader, objectType, existingValue, serializer);
49+
if (reader.ValueType == typeof(string))
50+
{
51+
return DeserializeFromString(reader.Value?.ToString());
52+
}
53+
54+
var body = (T)base.ReadJson(reader, objectType, existingValue, serializer);
55+
56+
// If RawBytes and Type are present, handle RawBytes instead
57+
if (body == null || string.IsNullOrEmpty(body.RawBytes) || string.IsNullOrEmpty(body.Type))
58+
{
59+
return body;
60+
}
61+
62+
var rawContent = Encoding.UTF8.GetString(Convert.FromBase64String(body.RawBytes));
63+
64+
switch (body.Type?.ToUpperInvariant())
65+
{
66+
case Types.JsonType when string.IsNullOrEmpty(body.JsonValue):
67+
body.JsonValue = rawContent;
68+
break;
69+
case Types.XmlType when string.IsNullOrEmpty(body.XmlValue):
70+
body.XmlValue = rawContent;
71+
break;
72+
case Types.StringType when string.IsNullOrEmpty(body.StringValue):
73+
body.StringValue = rawContent;
74+
break;
75+
}
76+
77+
return body;
4678
}
4779

4880
public override T Create(Type objectType)
@@ -54,5 +86,25 @@ public override T Create(Type objectType)
5486

5587
protected abstract T DeserializeFromString(string value);
5688
}
89+
90+
private class JsonValueConverter : JsonConverter<string>
91+
{
92+
public override void WriteJson(JsonWriter writer, string value, JsonSerializer serializer)
93+
{
94+
writer.WriteValue(value);
95+
}
96+
97+
public override string ReadJson(JsonReader reader, Type objectType, string existingValue, bool hasExistingValue, JsonSerializer serializer)
98+
{
99+
if (reader.TokenType != JsonToken.StartObject)
100+
{
101+
return reader.TokenType == JsonToken.Null ? null : reader.Value?.ToString();
102+
}
103+
104+
// Read the JSON object and serialize it back to a string
105+
var jsonObject = JObject.Load(reader);
106+
return jsonObject.ToString(Formatting.None);
107+
}
108+
}
57109
}
58-
}
110+
}

src/MockServerClientNet/Model/Body/Contents.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,8 @@ public static class Contents
88
{
99
private const string DefaultTextContentType = MediaTypeNames.Text.Plain;
1010
private const string DefaultBinaryContentType = MediaTypeNames.Application.Octet;
11-
12-
// the following are only supported as MediaTypeNames after .netstandard2.1
13-
private const string DefaultXmlContentType = "application/xml";
14-
private const string DefaultJsonContentType = "application/json";
11+
private const string DefaultXmlContentType = MediaTypeNames.Application.Xml;
12+
private const string DefaultJsonContentType = MediaTypeNames.Application.Json;
1513

1614
public static BodyContent EmptyText()
1715
{
@@ -72,4 +70,4 @@ public static BodyContent Json(string json, ContentType contentType = null)
7270
};
7371
}
7472
}
75-
}
73+
}

tests/MockServerClientNet.Tests/MockServerClientTest.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ protected HttpRequestMessage BuildRequest(HttpMethod method, string path, string
7070
.WithUri(MockServerClient.ServerAddress(path))
7171
.WithBody(body);
7272
}
73+
74+
protected HttpRequestMessage BuildRequest(HttpMethod method, string path, HttpContent content)
75+
{
76+
return new HttpRequestMessage()
77+
.WithMethod(method)
78+
.WithUri(MockServerClient.ServerAddress(path))
79+
.WithBody(content);
80+
}
7381

7482
protected HttpRequestMessage BuildRequest(HttpMethod method, string path, byte[] bytes)
7583
{
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
using System;
2+
using System.Net.Http;
3+
using System.Net.Http.Headers;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using Xunit;
7+
using static MockServerClientNet.Model.HttpRequest;
8+
using static MockServerClientNet.Model.HttpResponse;
9+
10+
namespace MockServerClientNet.Tests
11+
{
12+
public class RetrieveRecordedEventsTest(MockServerFixture fixture) : MockServerClientTest(fixture: fixture)
13+
{
14+
[Fact]
15+
public async Task ShouldVerifyRetrieveRecordedEventsWorksWithoutContentType()
16+
{
17+
var expectedRequest = Request().WithMethod(HttpMethod.Post);
18+
19+
// set expectation
20+
await MockServerClient
21+
.When(expectedRequest)
22+
.RespondAsync(Response().WithStatusCode(200).WithBody("{}"));
23+
24+
// act
25+
var content = new StringContent("""{ "foo": "bar" }""");
26+
27+
await SendRequestAsync(BuildRequest(HttpMethod.Post, "/test", content));
28+
29+
var response = await MockServerClient.RetrieveRecordedRequestsAsync(expectedRequest);
30+
31+
// assert
32+
Assert.NotEmpty(response);
33+
Assert.Equal("""{ "foo": "bar" }""", response[0].Body.StringValue);
34+
}
35+
36+
[Fact]
37+
public async Task ShouldVerifyRetrieveRecordedEventsWorksWithJsonContentType()
38+
{
39+
// arrange
40+
var expectedRequest = Request().WithMethod(HttpMethod.Post);
41+
42+
await MockServerClient
43+
.When(expectedRequest)
44+
.RespondAsync(Response().WithStatusCode(200).WithBody("{}"));
45+
46+
// act
47+
var content = new StringContent("""{ "foo": "bar" }""");
48+
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
49+
50+
await SendRequestAsync(BuildRequest(HttpMethod.Post, "/test", content));
51+
52+
var response = await MockServerClient.RetrieveRecordedRequestsAsync(expectedRequest);
53+
54+
// assert
55+
Assert.NotEmpty(response);
56+
Assert.Equal("""{"foo":"bar"}""", response[0].Body.JsonValue);
57+
}
58+
59+
[Fact]
60+
public async Task ShouldVerifyRetrieveRecordedEventsWorksWithXmlContentType()
61+
{
62+
// arrange
63+
var expectedRequest = Request().WithMethod(HttpMethod.Patch);
64+
65+
await MockServerClient
66+
.When(expectedRequest)
67+
.RespondAsync(Response().WithStatusCode(200).WithBody("{}"));
68+
69+
// act
70+
var content = new StringContent("<foo>bar</foo>", Encoding.UTF8);
71+
content.Headers.ContentType = new MediaTypeHeaderValue("application/xml");
72+
73+
await SendRequestAsync(BuildRequest(HttpMethod.Patch, "/test", content));
74+
75+
var response = await MockServerClient.RetrieveRecordedRequestsAsync(expectedRequest);
76+
77+
// assert
78+
Assert.NotEmpty(response);
79+
Assert.Equal("<foo>bar</foo>", response[0].Body.XmlValue);
80+
}
81+
82+
[Fact]
83+
public async Task ShouldVerifyRetrieveRecordedEventsWorksWithOctetStreamContentType()
84+
{
85+
// arrange
86+
var expectedRequest = Request().WithMethod(HttpMethod.Put);
87+
88+
await MockServerClient
89+
.When(expectedRequest)
90+
.RespondAsync(Response().WithStatusCode(200).WithBody("{}"));
91+
92+
// act
93+
var binaryData = new byte[] { 0x48, 0x65, 0x6C, 0x6C, 0x6F }; // "Hello" in bytes
94+
var content = new ByteArrayContent(binaryData);
95+
content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
96+
97+
await SendRequestAsync(BuildRequest(HttpMethod.Put, "/test", content));
98+
99+
var response = await MockServerClient.RetrieveRecordedRequestsAsync(expectedRequest);
100+
101+
// assert
102+
Assert.NotEmpty(response);
103+
Assert.Equal(Convert.ToBase64String(binaryData), response[0].Body.Base64Bytes);
104+
}
105+
}
106+
}

0 commit comments

Comments
 (0)