From 3faaab2eb919e1acb000ea265894065573616e34 Mon Sep 17 00:00:00 2001 From: Kristian Hellang Date: Thu, 2 May 2024 10:50:20 +0200 Subject: [PATCH 1/2] Add test showing exception when nullable parameter is called with null argument --- .../DelegateCountingService.cs | 5 +++++ .../CountingService.cs | 21 ++++++++++++++++++- .../ICountingService.cs | 2 ++ .../ServiceModel/ServiceInvokerFixture.cs | 20 ++++++++++++++++++ 4 files changed, 47 insertions(+), 1 deletion(-) diff --git a/source/Halibut.TestUtils.CompatBinary.Base/DelegateCountingService.cs b/source/Halibut.TestUtils.CompatBinary.Base/DelegateCountingService.cs index 2853c6c40..8c6333332 100644 --- a/source/Halibut.TestUtils.CompatBinary.Base/DelegateCountingService.cs +++ b/source/Halibut.TestUtils.CompatBinary.Base/DelegateCountingService.cs @@ -16,6 +16,11 @@ public int Increment() return countingService.Increment(); } + public int Increment(int? number) + { + return countingService.Increment(number); + } + public int GetCurrentValue() { return countingService.GetCurrentValue(); diff --git a/source/Halibut.TestUtils.Contracts/CountingService.cs b/source/Halibut.TestUtils.Contracts/CountingService.cs index a247d022b..52422e072 100644 --- a/source/Halibut.TestUtils.Contracts/CountingService.cs +++ b/source/Halibut.TestUtils.Contracts/CountingService.cs @@ -8,7 +8,20 @@ public class CountingService : ICountingService int count; public int Increment() { - return Interlocked.Increment(ref count); + return Increment(1); + } + + public int Increment(int? number) + { + var increment = number ?? 1; + var counter = 0; + + for (var i = 0; i < increment; i++) + { + counter = Interlocked.Increment(ref count); + } + + return counter; } public int GetCurrentValue() @@ -26,6 +39,12 @@ public async Task IncrementAsync(CancellationToken cancellationToken) return service.Increment(); } + public async Task IncrementAsync(int? number, CancellationToken cancellationToken) + { + await Task.CompletedTask; + return service.Increment(number); + } + public async Task GetCurrentValueAsync(CancellationToken cancellationToken) { await Task.CompletedTask; diff --git a/source/Halibut.TestUtils.Contracts/ICountingService.cs b/source/Halibut.TestUtils.Contracts/ICountingService.cs index ebe8b6a80..b14885cd5 100644 --- a/source/Halibut.TestUtils.Contracts/ICountingService.cs +++ b/source/Halibut.TestUtils.Contracts/ICountingService.cs @@ -6,12 +6,14 @@ namespace Halibut.TestUtils.Contracts public interface ICountingService { public int Increment(); + public int Increment(int? number); public int GetCurrentValue(); } public interface IAsyncCountingService { public Task IncrementAsync(CancellationToken cancellationToken); + public Task IncrementAsync(int? number, CancellationToken cancellationToken); public Task GetCurrentValueAsync(CancellationToken cancellationToken); } } diff --git a/source/Halibut.Tests/ServiceModel/ServiceInvokerFixture.cs b/source/Halibut.Tests/ServiceModel/ServiceInvokerFixture.cs index e8a2a1727..f838051d0 100644 --- a/source/Halibut.Tests/ServiceModel/ServiceInvokerFixture.cs +++ b/source/Halibut.Tests/ServiceModel/ServiceInvokerFixture.cs @@ -53,6 +53,26 @@ public async Task AsyncInvokeWithNoParamsOnAsyncService() response.Result.Should().Be(1); } + [Test] + public async Task AsyncInvokeWithNullableParamsOnAsyncService() + { + var serviceFactory = new ServiceFactoryBuilder() + .WithConventionVerificationDisabled() + .WithService(() => new AsyncCountingService()) + .Build(); + + var sut = new ServiceInvoker(serviceFactory); + var request = new RequestMessage() + { + ServiceName = nameof(ICountingService), + MethodName = nameof(ICountingService.Increment), + Params = new object[] { null! }, + }; + + var response = await sut.InvokeAsync(request); + response.Result.Should().Be(1); + } + [Test] public async Task AsyncInvokeWithNoParams_AsyncServiceMissingSuffix() { From 9b947e4ac093b72c887b0888356566e5da0920e9 Mon Sep 17 00:00:00 2001 From: Kristian Hellang Date: Thu, 2 May 2024 10:51:16 +0200 Subject: [PATCH 2/2] Nullable values types should be considered a match for null arguments --- source/Halibut/Portability/TypeExtensions.cs | 5 +++++ source/Halibut/ServiceModel/ServiceInvoker.cs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/source/Halibut/Portability/TypeExtensions.cs b/source/Halibut/Portability/TypeExtensions.cs index 8a5eefebf..80f0d373e 100644 --- a/source/Halibut/Portability/TypeExtensions.cs +++ b/source/Halibut/Portability/TypeExtensions.cs @@ -17,5 +17,10 @@ public static bool IsValueType(this Type type) { return type.IsValueType; } + + public static bool IsNullable(this Type type) + { + return !type.IsValueType() || Nullable.GetUnderlyingType(type) != null; + } } } diff --git a/source/Halibut/ServiceModel/ServiceInvoker.cs b/source/Halibut/ServiceModel/ServiceInvoker.cs index 29e6f3556..87339b153 100644 --- a/source/Halibut/ServiceModel/ServiceInvoker.cs +++ b/source/Halibut/ServiceModel/ServiceInvoker.cs @@ -68,7 +68,7 @@ static MethodInfo SelectAsyncMethod(IList methods, RequestMessage re { var paramType = parameters[i].ParameterType; var argType = argumentTypes[i]; - if (argType == null && paramType.IsValueType()) + if (argType == null && !paramType.IsNullable()) { isMatch = false; break;