Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/nullable query params #53

Merged
merged 19 commits into from
Mar 28, 2022
6 changes: 6 additions & 0 deletions src/Acheve.TestHost/HttpResponseMessageExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ public static async Task IsSuccessStatusCodeOrThrow(this HttpResponseMessage res
throw new Exception($"Response status does not indicate success: {response.StatusCode:D} ({response.StatusCode}); \r\n{content}");
}

/// <summary>
/// Read HttpResponseMessage and convert to T Class
/// </summary>
/// <typeparam name="T">Class</typeparam>
/// <param name="responseMessage">The httpResponseMessage instance</param>
/// <returns>T class object</returns>
public static async Task<T> ReadContentAsAsync<T>(this HttpResponseMessage responseMessage)
{
var json = await responseMessage.Content.ReadAsStringAsync();
Expand Down
56 changes: 35 additions & 21 deletions src/Acheve.TestHost/Routing/TestServerAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,34 +36,48 @@ public void AddArgument(int order, Expression expression, bool activeBodyApiCont

if (!ArgumentValues.ContainsKey(order))
{
switch (expression)
if (IsNullable(argument.ParameterType))
{
case ConstantExpression constant:
{
ArgumentValues.Add(order, new TestServerArgument(constant.Value?.ToString(), isFromBody, isFromForm, isFromHeader, argument.Name));
}
break;
var expressionValue = Expression.Lambda(expression).Compile().DynamicInvoke();

case MemberExpression member when member.NodeType == ExpressionType.MemberAccess:
{
var instance = Expression.Lambda(member)
.Compile()
.DynamicInvoke();
if (expressionValue != null)
{
ArgumentValues.Add(order, new TestServerArgument(expressionValue.ToString(), isFromBody, isFromForm, isFromHeader, argument.Name));
}
}
else
{
switch (expression)
{
case ConstantExpression constant:
{
ArgumentValues.Add(order, new TestServerArgument(constant.Value?.ToString(), isFromBody, isFromForm, isFromHeader, argument.Name));
}
break;

ArgumentValues.Add(order, new TestServerArgument(instance, isFromBody, isFromForm, isFromHeader, argument.Name));
}
break;
case MemberExpression member when member.NodeType == ExpressionType.MemberAccess:
{
var instance = Expression.Lambda(member)
.Compile()
.DynamicInvoke();

case MethodCallExpression method:
{
var instance = Expression.Lambda(method).Compile().DynamicInvoke();
ArgumentValues.Add(order, new TestServerArgument(instance, isFromBody, isFromForm, isFromHeader, argument.Name));
}
break;
ArgumentValues.Add(order, new TestServerArgument(instance, isFromBody, isFromForm, isFromHeader, argument.Name));
}
break;

default: return;
case MethodCallExpression method:
{
var instance = Expression.Lambda(method).Compile().DynamicInvoke();
ArgumentValues.Add(order, new TestServerArgument(instance, isFromBody, isFromForm, isFromHeader, argument.Name));
}
break;

default: return;
}
}
}
}

bool IsNullable(Type type) => Nullable.GetUnderlyingType(type) != null;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using System;
using System.Linq;
using System.Reflection;

namespace Acheve.TestHost.Routing.Tokenizers
Expand All @@ -15,9 +16,9 @@ public void AddTokens<TController>(TestServerAction action, TestServerTokenColle
for (int i = 0; i < parameters.Length; i++)
{
var type = parameters[i].ParameterType;
var instance = action.ArgumentValues[i].Instance;
var instance = action.ArgumentValues.Any(x => x.Key == i) ? action.ArgumentValues[i].Instance : null;

if (instance != null && !(type.IsPrimitive || type == typeof(String) || type == typeof(Decimal) || type == typeof(Guid)))
if (instance != null && !type.IsPrimitiveType())
{
if (!IgnoreBind(parameters[i]))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ public void AddTokens<TController>(TestServerAction action, TestServerTokenColle
{
if (!IgnoreHeader(parameters[i]))
{
if (IsPrimitiveType(parameters[i].ParameterType))
var tokenName = parameters[i].Name.ToLowerInvariant();
serweck marked this conversation as resolved.
Show resolved Hide resolved

if (parameters[i].ParameterType.IsPrimitiveType())
{
var tokenName = parameters[i].Name.ToLowerInvariant();
var tokenValue = action.ArgumentValues[i].Instance;
var tokenValue = action.ArgumentValues.Any(x => x.Key == i) ? action.ArgumentValues[i].Instance : null;

if (tokenValue != null)
{
Expand All @@ -37,7 +38,6 @@ public void AddTokens<TController>(TestServerAction action, TestServerTokenColle
&& arrayValues.Length != 0
)
{
var tokenName = parameters[i].Name.ToLowerInvariant();
var tokenValue = GetTokenValue(arrayValues, tokenName);
tokens.AddToken(tokenName, tokenValue, isConventional: false);
}
Expand Down
15 changes: 15 additions & 0 deletions src/Acheve.TestHost/Routing/Tokenizers/TypeExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace System
{
internal static class TypeExtensions
{
internal static bool IsPrimitiveType(this Type typeToInspect)
{
var type = Nullable.GetUnderlyingType(typeToInspect) ?? typeToInspect;

return type.IsPrimitive
|| type == typeof(string)
|| type == typeof(decimal)
|| type == typeof(Guid);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ public IActionResult UnderDashSupport(Guid param_1, int param_2)
return Ok();
}

[HttpGet("nullableQueryParams")]
public ActionResult<NullableQueryParamsResponse> NullableQueryParams(bool? param1, Guid? param2)
{
return Ok(new NullableQueryParamsResponse { Param1 = param1, Param2 = param2 });
}

[HttpGet("arrayGuid")]
public ActionResult<Guid[]> GuidArraySupport([FromQuery] Guid[] param1)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System;

namespace UnitTests.Acheve.TestHost.Routing.Models
{
public class NullableQueryParamsResponse
{
public bool? Param1 { get; set; }
public Guid? Param2 { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1346,6 +1346,29 @@ public void create_valid_request_supporting_underdash_on_router_params()
.Should().Be($"api/bugs/{guid}/10");
}

[Fact]
public async Task create_valid_request_supporting_nullable_params_on_query()
{
var server = new TestServerBuilder()
.UseDefaultStartup()
.Build();

var guid = Guid.NewGuid();

var request = server.CreateHttpApiRequest<BugsController>(
actionSelector: controller => controller.NullableQueryParams(null, guid),
tokenValues: null,
contentOptions: new NotIncludeContent());

var responseMessage = await request.GetAsync();

responseMessage.EnsureSuccessStatusCode();
var response = await responseMessage.ReadContentAsAsync<NullableQueryParamsResponse>();

response.Param1.Should().Be(null);
response.Param2.Should().Be(guid);
}

[Fact]
public async Task create_request_supporting_guid_array_types_on_parameters()
{
Expand Down