Skip to content

Commit 0c13ac0

Browse files
committed
Handle empty response body in delete requests
1 parent 1cc32f6 commit 0c13ac0

3 files changed

Lines changed: 42 additions & 0 deletions

File tree

CloudConvert.API/RestHelper.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ public async Task<T> RequestAsync<T>(HttpRequestMessage request, CancellationTok
2525
var response = await _httpClient.SendAsync(request, cancellationToken);
2626
var responseRaw = await response.Content.ReadAsStringAsync(cancellationToken);
2727

28+
// Handle empty response body (e.g., HTTP 204 No Content)
29+
// System.Text.Json throws when trying to deserialize an empty string
30+
if (string.IsNullOrWhiteSpace(responseRaw) || response.StatusCode == System.Net.HttpStatusCode.NoContent)
31+
{
32+
return default(T);
33+
}
34+
2835
return JsonSerializer.Deserialize<T>(responseRaw, DefaultJsonSerializerOptions.SerializerOptions);
2936
}
3037

CloudConvert.Test/Extensions/MockExtensions.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,19 @@ public static IReturnsResult<HttpMessageHandler> MockResponse(this Mock<HttpMess
2525
});
2626
}
2727

28+
public static IReturnsResult<HttpMessageHandler> MockNoContentResponse(this Mock<HttpMessageHandler> mock, string endpoint)
29+
{
30+
return mock.Protected()
31+
.Setup<Task<HttpResponseMessage>>("SendAsync",
32+
ItExpr.Is<HttpRequestMessage>(message => message.RequestUri.AbsolutePath.EndsWith(endpoint)),
33+
ItExpr.IsAny<CancellationToken>())
34+
.ReturnsAsync(new HttpResponseMessage
35+
{
36+
StatusCode = HttpStatusCode.NoContent,
37+
Content = new StringContent(string.Empty)
38+
});
39+
}
40+
2841
public static void VerifyRequest(this Mock<HttpMessageHandler> mock, string endpoint, Times times)
2942
{
3043
mock.Protected()

CloudConvert.Test/TestJobs.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
using System;
22
using System.IO;
3+
using System.Net.Http;
34
using System.Text.Json;
45
using System.Threading.Tasks;
56
using CloudConvert.API;
67
using CloudConvert.API.Models;
78
using CloudConvert.API.Models.JobModels;
9+
using CloudConvert.Test.Extensions;
810
using Moq;
911
using NUnit.Framework;
1012

@@ -108,5 +110,25 @@ public async Task DeleteJob()
108110

109111
await _cloudConvertAPI.Object.DeleteJobAsync(id);
110112
}
113+
114+
[Test]
115+
public async Task DeleteJob_WithNoContentResponse_ShouldNotThrow()
116+
{
117+
// This test verifies that DeleteJobAsync correctly handles HTTP 204 No Content responses
118+
// with empty response bodies, which was broken in v1.4.0 when switching from Newtonsoft.Json to System.Text.Json
119+
string id = "cd82535b-0614-4b23-bbba-b24ab0e892f7";
120+
121+
var httpMessageHandlerMock = new Mock<HttpMessageHandler>();
122+
httpMessageHandlerMock.MockNoContentResponse($"/jobs/{id}");
123+
124+
var httpClient = new HttpClient(httpMessageHandlerMock.Object);
125+
var restHelper = new RestHelper(httpClient);
126+
var cloudConvertApi = new CloudConvertAPI(restHelper, "API_KEY");
127+
128+
// This should not throw an exception even though the response body is empty
129+
await cloudConvertApi.DeleteJobAsync(id);
130+
131+
httpMessageHandlerMock.VerifyRequest($"/jobs/{id}", Moq.Times.Once());
132+
}
111133
}
112134
}

0 commit comments

Comments
 (0)