Skip to content

Commit f8c2005

Browse files
authored
feat: adds handling for missing content with unit tests (#73)
This commit adds complete support for returning nullable response types using the nullability check of the generic ResponseType to set the response types as nullable when content is missing.
1 parent b60302b commit f8c2005

File tree

5 files changed

+162
-3
lines changed

5 files changed

+162
-3
lines changed

APIMatic.Core.Test/Api/HttpGet/ApiCallGetTest.cs

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,8 +597,119 @@ public void ApiCall_GetError_NullOn404()
597597

598598
// Act and Assert
599599
var actual = CoreHelper.RunTask(apiCall);
600+
Assert.Null(actual);
601+
}
602+
603+
[Test]
604+
public void ApiCall_GetModel_MissingContent()
605+
{
606+
//Arrange
607+
var url = "/apicall/get/model/missingContent";
608+
609+
handlerMock.When(GetCompleteUrl(url))
610+
.Respond(HttpStatusCode.NoContent, new ByteArrayContent(Array.Empty<byte>()));
611+
612+
var apiCall = CreateSimpleApiCall<ServerResponse>()
613+
.RequestBuilder(requestBuilderAction => requestBuilderAction.Setup(HttpMethod.Get, url))
614+
.ExecuteAsync();
615+
616+
// Act and Assert
617+
var actual = CoreHelper.RunTask(apiCall);
618+
Assert.Null(actual);
619+
}
620+
621+
[Test]
622+
public void ApiCall_GetString_MissingContent()
623+
{
624+
//Arrange
625+
var url = "/apicall/get/string/missingContent";
626+
627+
handlerMock.When(GetCompleteUrl(url))
628+
.Respond(HttpStatusCode.NoContent, new ByteArrayContent(Array.Empty<byte>()));
629+
630+
var apiCall = CreateSimpleApiCall<string>()
631+
.RequestBuilder(requestBuilderAction => requestBuilderAction.Setup(HttpMethod.Get, url))
632+
.ExecuteAsync();
633+
634+
// Act and Assert
635+
var actual = CoreHelper.RunTask(apiCall);
636+
Assert.Null(actual);
637+
}
638+
639+
[Test]
640+
public void ApiCall_GetNumber_MissingContent()
641+
{
642+
//Arrange
643+
var url = "/apicall/get/number/missingContent";
644+
645+
handlerMock.When(GetCompleteUrl(url))
646+
.Respond(HttpStatusCode.NoContent, new ByteArrayContent(Array.Empty<byte>()));
647+
648+
var apiCall = CreateSimpleApiCall<int>()
649+
.RequestBuilder(requestBuilderAction => requestBuilderAction.Setup(HttpMethod.Get, url))
650+
.ResponseHandler(resHandlerAction => resHandlerAction.Deserializer(res => int.Parse(res)))
651+
.ExecuteAsync();
600652

653+
// Act and Assert
654+
var exp = Assert.Throws<FormatException>(() => CoreHelper.RunTask(apiCall));
655+
Assert.AreEqual("Input string was not in a correct format.", exp.Message);
656+
}
657+
658+
[Test]
659+
public void ApiCall_GetNullableNumber_MissingContent()
660+
{
661+
//Arrange
662+
var url = "/apicall/get/nullableNumber/missingContent";
663+
664+
handlerMock.When(GetCompleteUrl(url))
665+
.Respond(HttpStatusCode.NoContent, new ByteArrayContent(Array.Empty<byte>()));
666+
667+
var apiCall = CreateSimpleApiCall<int?>()
668+
.RequestBuilder(requestBuilderAction => requestBuilderAction.Setup(HttpMethod.Get, url))
669+
.ResponseHandler(resHandlerAction => resHandlerAction.Deserializer(res => int.Parse(res)))
670+
.ExecuteAsync();
671+
672+
// Act and Assert
673+
var actual = CoreHelper.RunTask(apiCall);
601674
Assert.Null(actual);
602675
}
676+
677+
[Test]
678+
public void ApiCall_GetNullableNumber_WhiteSpaceContent()
679+
{
680+
//Arrange
681+
var url = "/apicall/get/nullableNumber/whiteSpacedContent";
682+
683+
handlerMock.When(GetCompleteUrl(url))
684+
.Respond(HttpStatusCode.NoContent, new StringContent(" "));
685+
686+
var apiCall = CreateSimpleApiCall<int?>()
687+
.RequestBuilder(requestBuilderAction => requestBuilderAction.Setup(HttpMethod.Get, url))
688+
.ResponseHandler(resHandlerAction => resHandlerAction.Deserializer(res => int.Parse(res)))
689+
.ExecuteAsync();
690+
691+
// Act and Assert
692+
var actual = CoreHelper.RunTask(apiCall);
693+
Assert.Null(actual);
694+
}
695+
696+
[Test]
697+
public void ApiCall_GetNullableNumber_WithContent()
698+
{
699+
//Arrange
700+
var url = "/apicall/get/nullableNumber/withContent";
701+
702+
handlerMock.When(GetCompleteUrl(url))
703+
.Respond(HttpStatusCode.OK, new StringContent("123"));
704+
705+
var apiCall = CreateSimpleApiCall<int?>()
706+
.RequestBuilder(requestBuilderAction => requestBuilderAction.Setup(HttpMethod.Get, url))
707+
.ResponseHandler(resHandlerAction => resHandlerAction.Deserializer(res => int.Parse(res)))
708+
.ExecuteAsync();
709+
710+
// Act and Assert
711+
var actual = CoreHelper.RunTask(apiCall);
712+
Assert.AreEqual(123, actual);
713+
}
603714
}
604715
}

APIMatic.Core.Test/TestBase.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
using APIMatic.Core.Http.Configuration;
66
using APIMatic.Core.Test.MockTypes.Authentication;
77
using APIMatic.Core.Types;
8-
using APIMatic.Core.Utilities.Logger.Configuration;
9-
using Microsoft.Extensions.Logging.Abstractions;
108
using RichardSzalay.MockHttp;
119

1210
namespace APIMatic.Core.Test

APIMatic.Core.Test/Utilities/CoreHelperTest.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using APIMatic.Core.Http.Configuration;
77
using APIMatic.Core.Test.MockTypes.Models;
88
using APIMatic.Core.Test.MockTypes.Utilities;
9+
using APIMatic.Core.Types;
910
using APIMatic.Core.Utilities;
1011
using APIMatic.Core.Utilities.Converters;
1112
using APIMatic.Core.Utilities.Date;
@@ -989,5 +990,38 @@ public void UnknownEnumConverter_CanConvertEnumWithUnknownValue()
989990
}
990991

991992
#endregion
993+
994+
#region Others
995+
996+
[Test]
997+
public void IsNullableType_AllTypes()
998+
{
999+
Assert.That(CoreHelper.IsNullableType(typeof(WorkingDays)), Is.False);
1000+
Assert.That(CoreHelper.IsNullableType(typeof(WorkingDays?)), Is.True);
1001+
1002+
Assert.That(CoreHelper.IsNullableType(typeof(DateTime)), Is.False);
1003+
Assert.That(CoreHelper.IsNullableType(typeof(DateTime?)), Is.True);
1004+
1005+
Assert.That(CoreHelper.IsNullableType(typeof(void)), Is.False);
1006+
Assert.That(CoreHelper.IsNullableType(typeof(VoidType)), Is.True);
1007+
1008+
Assert.That(CoreHelper.IsNullableType(typeof(int)), Is.False);
1009+
Assert.That(CoreHelper.IsNullableType(typeof(int?)), Is.True);
1010+
Assert.That(CoreHelper.IsNullableType(typeof(Nullable<int>)), Is.True);
1011+
1012+
Assert.That(CoreHelper.IsNullableType(typeof(double)), Is.False);
1013+
Assert.That(CoreHelper.IsNullableType(typeof(double?)), Is.True);
1014+
Assert.That(CoreHelper.IsNullableType(typeof(Nullable<double>)), Is.True);
1015+
1016+
Assert.That(CoreHelper.IsNullableType(typeof(bool)), Is.False);
1017+
Assert.That(CoreHelper.IsNullableType(typeof(bool?)), Is.True);
1018+
Assert.That(CoreHelper.IsNullableType(typeof(Nullable<bool>)), Is.True);
1019+
1020+
Assert.That(CoreHelper.IsNullableType(typeof(string)), Is.True);
1021+
Assert.That(CoreHelper.IsNullableType(typeof(ServerResponse)), Is.True);
1022+
Assert.That(CoreHelper.IsNullableType(typeof(List<object>)), Is.True);
1023+
Assert.That(CoreHelper.IsNullableType(typeof(Dictionary<string, object>)), Is.True);
1024+
}
1025+
#endregion
9921026
}
9931027
}

APIMatic.Core/Response/ResponseHandler.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ internal ReturnType Result<ReturnType>(CoreContext<CoreRequest, CoreResponse> co
121121
}
122122
throw ResponseError(context);
123123
}
124-
if (typeof(ResponseType) == typeof(VoidType))
124+
if (HasEmptyResponse(context.Response))
125125
{
126126
return default;
127127
}
@@ -138,6 +138,16 @@ internal ReturnType Result<ReturnType>(CoreContext<CoreRequest, CoreResponse> co
138138
throw new InvalidOperationException($"Unable to transform {typeof(ResponseType)} into {typeof(ReturnType)}. ReturnTypeCreator is not provided.");
139139
}
140140

141+
private bool HasEmptyResponse(CoreResponse response)
142+
{
143+
var resType = typeof(ResponseType);
144+
if (resType == typeof(VoidType))
145+
{
146+
return true;
147+
}
148+
return string.Equals(response.Body?.Trim(), string.Empty) && CoreHelper.IsNullableType(resType);
149+
}
150+
141151
private ApiException ResponseError(CoreContext<CoreRequest, CoreResponse> context)
142152
{
143153
Context httpContext = compatibilityFactory.CreateHttpContext(context.Request, context.Response);

APIMatic.Core/Utilities/CoreHelper.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,12 @@ internal static bool IsScalarType(Type type)
172172
return Type.GetTypeCode(type) != TypeCode.Object || type == typeof(VoidType);
173173
}
174174

175+
internal static bool IsNullableType(Type type)
176+
{
177+
// Check if it's a reference type or a nullable value type
178+
return !type.IsValueType || Nullable.GetUnderlyingType(type) != null;
179+
}
180+
175181
/// <summary>
176182
/// Prepares parameters for serialization as a form encoded string by flattening complex Types such as Collections and Models to a list of KeyValuePairs, where each value is a string representation of the original Type.
177183
/// </summary>

0 commit comments

Comments
 (0)