Skip to content

Commit a2ec951

Browse files
authored
Merge pull request #22 from IvanMurzak/fix/return-schema-required
fix; Method output json-schema required state
2 parents d3556ba + 08b07a5 commit a2ec951

11 files changed

Lines changed: 800 additions & 65 deletions

File tree

.claude/settings.local.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66
"Bash(dotnet build:*)",
77
"Bash(git checkout:*)",
88
"Bash(dotnet clean:*)",
9-
"Bash(gh pr:*)"
9+
"Bash(gh pr:*)",
10+
"Bash(dotnet run:*)"
1011
],
1112
"deny": [],
1213
"ask": []
1314
}
14-
}
15+
}

ConsoleApp/ConsoleApp.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
55
<TargetFramework>net9.0</TargetFramework>
6+
<LangVersion>11.0</LangVersion>
67
<ImplicitUsings>enable</ImplicitUsings>
78
<Nullable>enable</Nullable>
89
</PropertyGroup>

ReflectorNet.Tests.OuterAssembly/ReflectorNet.Tests.OuterAssembly.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net9.0</TargetFramework>
4+
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
55
<ImplicitUsings>disable</ImplicitUsings>
66
<Nullable>enable</Nullable>
77
<IsPackable>false</IsPackable>

ReflectorNet.Tests/ReflectorNet.Tests.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net9.0</TargetFramework>
4+
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
5+
<LangVersion>11.0</LangVersion>
56
<ImplicitUsings>disable</ImplicitUsings>
67
<Nullable>enable</Nullable>
78
<IsPackable>false</IsPackable>

ReflectorNet.Tests/SchemaTests/MethodWrapperTests.cs

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
using com.IvanMurzak.ReflectorNet.Model;
2-
using com.IvanMurzak.ReflectorNet;
32
using com.IvanMurzak.ReflectorNet.Tests.Model;
43
using Xunit.Abstractions;
54
using System;
65
using System.Collections.Generic;
76
using System.Text.Json;
87
using System.Threading.Tasks;
9-
using System.Reflection;
108

119
namespace com.IvanMurzak.ReflectorNet.Tests.SchemaTests
1210
{
@@ -282,13 +280,13 @@ public async Task MethodWrapper_Enum_Parameter_String_Input()
282280
var wrapper = MethodWrapper.Create(reflector, null, methodInfo);
283281

284282
// Act & Assert - Test with string representation of enum
285-
var parameters = new Dictionary<string, object?>
286-
{
287-
{ "enumValue", "Option2" }
283+
var parameters = new Dictionary<string, object?>
284+
{
285+
{ "enumValue", "Option2" }
288286
};
289287

290288
var result = await wrapper.InvokeDict(parameters);
291-
289+
292290
// Assert
293291
Assert.NotNull(result);
294292
Assert.Equal("Processed enum: Option2", result.ToString());
@@ -306,14 +304,14 @@ public async Task MethodWrapper_Enum_Parameter_JsonElement_Input()
306304
// Act & Assert - Test with JsonElement containing enum string
307305
var jsonDocument = JsonDocument.Parse("\"Option3\"");
308306
var jsonElement = jsonDocument.RootElement;
309-
310-
var parameters = new Dictionary<string, object?>
311-
{
312-
{ "enumValue", jsonElement }
307+
308+
var parameters = new Dictionary<string, object?>
309+
{
310+
{ "enumValue", jsonElement }
313311
};
314312

315313
var result = await wrapper.InvokeDict(parameters);
316-
314+
317315
// Assert
318316
Assert.NotNull(result);
319317
Assert.Equal("Processed enum: Option3", result.ToString());
@@ -330,7 +328,7 @@ public async Task MethodWrapper_Enum_Parameter_Default_Value()
330328

331329
// Act & Assert - Test with no parameter (should use default value Option2)
332330
var result = await wrapper.InvokeDict(null);
333-
331+
334332
// Assert
335333
Assert.NotNull(result);
336334
Assert.Equal("Processed enum with default: Option2", result.ToString());
@@ -346,14 +344,14 @@ public async Task MethodWrapper_Enum_Parameter_Mixed_Types()
346344
var wrapper = MethodWrapper.Create(reflector, null, methodInfo);
347345

348346
// Act & Assert - Test with string and enum parameters
349-
var parameters = new Dictionary<string, object?>
350-
{
347+
var parameters = new Dictionary<string, object?>
348+
{
351349
{ "text", "Hello" },
352-
{ "enumValue", "Option4" }
350+
{ "enumValue", "Option4" }
353351
};
354352

355353
var result = await wrapper.InvokeDict(parameters);
356-
354+
357355
// Assert
358356
Assert.NotNull(result);
359357
Assert.Equal("Text: Hello, Enum: Option4", result.ToString());

ReflectorNet.Tests/SchemaTests/ReturnSchemaTests.cs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -254,11 +254,16 @@ public void GetReturnSchema_TaskNullablePrimitive_UnwrapsWithoutRequired(string
254254
AssertAllRefsDefined(schema!);
255255
}
256256

257-
[Fact]
258-
public void GetReturnSchema_TaskNullableCustomType_UnwrapsToCustomTypeSchemaWithoutRequired()
257+
[Theory]
258+
#if NET5_0_OR_GREATER
259+
[InlineData(false)]
260+
#else
261+
[InlineData(true)]
262+
#endif
263+
public void GetReturnSchema_TaskNullableCustomType_UnwrapsToCustomTypeSchemaWithoutRequired(bool shouldBeRequired)
259264
{
260265
var schema = GetReturnSchemaForMethod(nameof(TaskNullableCustomTypeMethod));
261-
AssertCustomTypeReturnSchema(schema!, new[] { "Name", "Value" }, shouldBeRequired: false);
266+
AssertCustomTypeReturnSchema(schema!, new[] { "Name", "Value" }, shouldBeRequired: shouldBeRequired);
262267
AssertAllRefsDefined(schema!);
263268
}
264269

@@ -602,7 +607,11 @@ public void GetReturnSchema_NullableStringArray_ReturnsArraySchemaWithoutRequire
602607

603608
[Theory]
604609
[InlineData(nameof(ListComplexTypeMethod), true)]
610+
#if NET5_0_OR_GREATER
605611
[InlineData(nameof(NullableListComplexTypeMethod), false)]
612+
#else
613+
[InlineData(nameof(NullableListComplexTypeMethod), true)] // netstandard2.1 cannot detect List<T>? nullability
614+
#endif
606615
public void GetReturnSchema_ListComplexType_ReturnsArraySchemaWithComplexItems(string methodName, bool shouldBeRequired)
607616
{
608617
var schema = GetReturnSchemaForMethod(methodName);
@@ -791,14 +800,14 @@ public void GetReturnSchema_NullMethodInfo_ThrowsArgumentNullException()
791800
}
792801

793802
[Theory]
794-
[InlineData(typeof(string), nameof(WrapperClass<string>.Echo), JsonSchema.String, false)] // string is reference type, T is nullable
803+
[InlineData(typeof(string), nameof(WrapperClass<string>.Echo), JsonSchema.String, true)] // string with Echo (T) is non-nullable due to NullableContextAttribute(1)
795804
[InlineData(typeof(int), nameof(WrapperClass<int>.Echo), JsonSchema.Integer, true)] // int is value type, T is non-nullable
796805
[InlineData(typeof(bool), nameof(WrapperClass<bool>.Echo), JsonSchema.Boolean, true)] // bool is value type, T is non-nullable
797806
[InlineData(typeof(double), nameof(WrapperClass<double>.Echo), JsonSchema.Number, true)] // double is value type, T is non-nullable
798-
[InlineData(typeof(string), nameof(WrapperClass<string>.EchoNullable), JsonSchema.String, false)] // string? is nullable reference
799-
[InlineData(typeof(int), nameof(WrapperClass<int>.EchoNullable), JsonSchema.Integer, true)] // int? (Nullable<int>) is itself non-nullable struct
800-
[InlineData(typeof(bool), nameof(WrapperClass<bool>.EchoNullable), JsonSchema.Boolean, true)] // bool? (Nullable<bool>) is itself non-nullable struct
801-
[InlineData(typeof(double), nameof(WrapperClass<double>.EchoNullable), JsonSchema.Number, true)] // double? (Nullable<double>) is itself non-nullable struct
807+
[InlineData(typeof(string), nameof(WrapperClass<string>.EchoNullable), JsonSchema.String, false)] // T? with reference type is nullable
808+
[InlineData(typeof(int), nameof(WrapperClass<int>.EchoNullable), JsonSchema.Integer, false)] // T? with value type is also nullable (int? becomes int, but T? context is nullable)
809+
[InlineData(typeof(bool), nameof(WrapperClass<bool>.EchoNullable), JsonSchema.Boolean, false)] // T? with value type is also nullable (bool? becomes bool, but T? context is nullable)
810+
[InlineData(typeof(double), nameof(WrapperClass<double>.EchoNullable), JsonSchema.Number, false)] // T? with value type is also nullable (double? becomes double, but T? context is nullable)
802811
public void GetReturnSchema_WrapperEchoPrimitive_ReturnsCorrectSchema(Type genericType, string methodName, string expectedType, bool shouldBeRequired)
803812
{
804813
var wrapperType = typeof(WrapperClass<>).MakeGenericType(genericType);
@@ -855,8 +864,13 @@ public void GetReturnSchema_WrapperEchoArray_ReturnsCorrectSchema(Type genericTy
855864
}
856865

857866
[Theory]
867+
#if NET5_0_OR_GREATER
858868
[InlineData(typeof(string[]), JsonSchema.String, false)]
859869
[InlineData(typeof(int[]), JsonSchema.Integer, false)]
870+
#else
871+
[InlineData(typeof(string[]), JsonSchema.String, true)]
872+
[InlineData(typeof(int[]), JsonSchema.Integer, true)]
873+
#endif
860874
public void GetReturnSchema_WrapperEchoNullableArray_ReturnsCorrectSchema(Type genericType, string expectedItemType, bool shouldBeRequired)
861875
{
862876
var wrapperType = typeof(WrapperClass<>).MakeGenericType(genericType);
@@ -868,6 +882,8 @@ public void GetReturnSchema_WrapperEchoNullableArray_ReturnsCorrectSchema(Type g
868882
[Fact]
869883
public void GetReturnSchema_WrapperEchoListComplex_ReturnsCorrectSchema()
870884
{
885+
// WrapperClass<T>.Echo has NullableContextAttribute(1), meaning T is non-nullable
886+
// Therefore, WrapperClass<List<ComplexReturnType>>.Echo should return non-nullable List<ComplexReturnType>
871887
var wrapperType = typeof(WrapperClass<List<ComplexReturnType>>);
872888
var schema = GetWrapperMethodReturnSchema(wrapperType, nameof(WrapperClass<List<ComplexReturnType>>.Echo));
873889
AssertComplexListReturnSchema(schema!, shouldBeRequired: true);

ReflectorNet.Tests/SchemaTests/SchemaTestBase.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,10 @@ protected void AssertResultRequired(JsonNode schema)
172172
var required = schema[JsonSchema.Required]!.AsArray();
173173
Assert.Contains(required, r => r?.ToString() == JsonSchema.Result);
174174
}
175+
else
176+
{
177+
Assert.Fail("Schema does not contain a required array");
178+
}
175179
}
176180

177181
/// <summary>

0 commit comments

Comments
 (0)