Skip to content

Commit 8db019f

Browse files
fix: avoid null exception when received body is empty but recorded body isn't (#59)
1 parent 62b5926 commit 8db019f

File tree

4 files changed

+44
-2
lines changed

4 files changed

+44
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
- Add .NET 8.0 support
66
- `AdvancedSettings` uses `MatchRules.Default` instead of a new instance of `MatchRules` if not provided during construction
7+
- Fix `NullReferenceException` when trying to match by body with a null body (`refit` compatibility)
78

89
## v0.9.0 (2023-05-17)
910

EasyVCR.Tests/ClientTest.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,26 @@ public async Task TestMatchNonJsonBody()
513513
Assert.IsNotNull(response);
514514
}
515515

516+
[TestMethod]
517+
public async Task TestMatchEmptyStringBodyToNonEmptyStringBody()
518+
{
519+
var cassette = TestUtils.GetCassette("test_match_empty_body");
520+
cassette.Erase(); // Erase cassette before recording
521+
522+
const string url = "https://httpbin.org/post";
523+
524+
var client = HttpClients.NewHttpClient(cassette, Mode.Record);
525+
var someContent = new ByteArrayContent(Encoding.UTF8.GetBytes("non_empty_string_body"));
526+
_ = await client.PostAsync(url, someContent);
527+
528+
// try to replay the request with match by body enforcement
529+
client = HttpClients.NewHttpClient(cassette, Mode.Replay, new AdvancedSettings
530+
{
531+
MatchRules = new MatchRules().ByBody()
532+
});
533+
var emptyContent = new ByteArrayContent(Encoding.UTF8.GetBytes(string.Empty));
534+
await Assert.ThrowsExceptionAsync<VCRException>(async () => await client.PostAsync(url, emptyContent), $"No interaction found for request POST {url}");
535+
}
516536

517537
[TestMethod]
518538
public async Task TestInteractionElements()
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace EasyVCR.InternalUtilities
2+
{
3+
public static class Extensions
4+
{
5+
public static bool IsEmptyStringOrNull(this string? str) => str == null || string.IsNullOrWhiteSpace(str);
6+
}
7+
}

EasyVCR/MatchRules.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.Linq;
44
using System.Web;
5+
using EasyVCR.InternalUtilities;
56
using EasyVCR.RequestElements;
67
using JsonSerialization = EasyVCR.InternalUtilities.JSON.Serialization;
78
using XmlSerialization = EasyVCR.InternalUtilities.XML.Serialization;
@@ -132,7 +133,7 @@ public MatchRules ByBody(List<CensorElement>? ignoredElements = null)
132133
return true;
133134

134135
if (received.Body == null || recorded.Body == null)
135-
// one has a null body, so they don't match
136+
// one has a null body, the other does not, so they don't match
136137
return false;
137138

138139
var receivedBody = received.Body;
@@ -155,11 +156,24 @@ public MatchRules ByBody(List<CensorElement>? ignoredElements = null)
155156
// not JSON, using the string as it is
156157
}
157158

159+
if (receivedBody.IsEmptyStringOrNull())
160+
// short-cut if the received body is empty
161+
receivedBody = null;
162+
163+
if (recordedBody.IsEmptyStringOrNull())
164+
// short-cut if the recorded body is empty
165+
recordedBody = null;
166+
158167
if (receivedBody == null && recordedBody == null)
159168
// both have empty string bodies, so they match
160169
return true;
161170

162-
return receivedBody!.Equals(recordedBody, StringComparison.OrdinalIgnoreCase);
171+
if (receivedBody == null || recordedBody == null)
172+
// one has a null body, the other does not, so they don't match
173+
return false;
174+
175+
// if the bodies are not null, then we can compare them
176+
return receivedBody.Equals(recordedBody, StringComparison.OrdinalIgnoreCase);
163177
});
164178
return this;
165179
}

0 commit comments

Comments
 (0)