Skip to content

Commit 39d1ea9

Browse files
authored
Merge pull request #14 from IvanMurzak/feature/json-schema-enum-types
feat: add enum handling in MethodWrapper and enhance tests
2 parents 52ecd17 + 5a62f35 commit 39d1ea9

7 files changed

Lines changed: 204 additions & 6 deletions

File tree

.claude/settings.local.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
{
22
"permissions": {
33
"allow": [
4-
"Bash(git log:*)"
4+
"Bash(git log:*)",
5+
"Bash(dotnet test:*)",
6+
"Bash(dotnet build:*)"
57
],
68
"deny": [],
79
"ask": []

ReflectorNet.Tests/SchemaTests/MethodWrapperTests.cs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,5 +272,92 @@ public void Async_Method_Invocation_Support()
272272
_output.WriteLine("Async method testing skipped - no suitable method found");
273273
}
274274
}
275+
276+
[Fact]
277+
public async Task MethodWrapper_Enum_Parameter_String_Input()
278+
{
279+
// Arrange
280+
var reflector = new Reflector();
281+
var methodInfo = typeof(com.IvanMurzak.ReflectorNet.Tests.Utils.MethodHelper).GetMethod(nameof(com.IvanMurzak.ReflectorNet.Tests.Utils.MethodHelper.ProcessEnum))!;
282+
var wrapper = MethodWrapper.Create(reflector, null, methodInfo);
283+
284+
// Act & Assert - Test with string representation of enum
285+
var parameters = new Dictionary<string, object?>
286+
{
287+
{ "enumValue", "Option2" }
288+
};
289+
290+
var result = await wrapper.InvokeDict(parameters);
291+
292+
// Assert
293+
Assert.NotNull(result);
294+
Assert.Equal("Processed enum: Option2", result.ToString());
295+
_output.WriteLine($"Enum parameter test with string input: {result}");
296+
}
297+
298+
[Fact]
299+
public async Task MethodWrapper_Enum_Parameter_JsonElement_Input()
300+
{
301+
// Arrange
302+
var reflector = new Reflector();
303+
var methodInfo = typeof(com.IvanMurzak.ReflectorNet.Tests.Utils.MethodHelper).GetMethod(nameof(com.IvanMurzak.ReflectorNet.Tests.Utils.MethodHelper.ProcessEnum))!;
304+
var wrapper = MethodWrapper.Create(reflector, null, methodInfo);
305+
306+
// Act & Assert - Test with JsonElement containing enum string
307+
var jsonDocument = JsonDocument.Parse("\"Option3\"");
308+
var jsonElement = jsonDocument.RootElement;
309+
310+
var parameters = new Dictionary<string, object?>
311+
{
312+
{ "enumValue", jsonElement }
313+
};
314+
315+
var result = await wrapper.InvokeDict(parameters);
316+
317+
// Assert
318+
Assert.NotNull(result);
319+
Assert.Equal("Processed enum: Option3", result.ToString());
320+
_output.WriteLine($"Enum parameter test with JsonElement input: {result}");
321+
}
322+
323+
[Fact]
324+
public async Task MethodWrapper_Enum_Parameter_Default_Value()
325+
{
326+
// Arrange
327+
var reflector = new Reflector();
328+
var methodInfo = typeof(com.IvanMurzak.ReflectorNet.Tests.Utils.MethodHelper).GetMethod(nameof(com.IvanMurzak.ReflectorNet.Tests.Utils.MethodHelper.ProcessEnumWithDefault))!;
329+
var wrapper = MethodWrapper.Create(reflector, null, methodInfo);
330+
331+
// Act & Assert - Test with no parameter (should use default value Option2)
332+
var result = await wrapper.InvokeDict(null);
333+
334+
// Assert
335+
Assert.NotNull(result);
336+
Assert.Equal("Processed enum with default: Option2", result.ToString());
337+
_output.WriteLine($"Enum parameter test with default value: {result}");
338+
}
339+
340+
[Fact]
341+
public async Task MethodWrapper_Enum_Parameter_Mixed_Types()
342+
{
343+
// Arrange
344+
var reflector = new Reflector();
345+
var methodInfo = typeof(com.IvanMurzak.ReflectorNet.Tests.Utils.MethodHelper).GetMethod(nameof(com.IvanMurzak.ReflectorNet.Tests.Utils.MethodHelper.ProcessStringAndEnum))!;
346+
var wrapper = MethodWrapper.Create(reflector, null, methodInfo);
347+
348+
// Act & Assert - Test with string and enum parameters
349+
var parameters = new Dictionary<string, object?>
350+
{
351+
{ "text", "Hello" },
352+
{ "enumValue", "Option4" }
353+
};
354+
355+
var result = await wrapper.InvokeDict(parameters);
356+
357+
// Assert
358+
Assert.NotNull(result);
359+
Assert.Equal("Text: Hello, Enum: Option4", result.ToString());
360+
_output.WriteLine($"Mixed parameters test with enum: {result}");
361+
}
275362
}
276363
}

ReflectorNet.Tests/SchemaTests/SchemaTests.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,5 +161,42 @@ private static void ValidatePropertyNotExists(JsonNode properties, string proper
161161
Assert.False(properties.AsObject().ContainsKey(propertyName),
162162
$"Properties should NOT contain '{propertyName}' property (it should be ignored). Available properties: {string.Join(", ", properties.AsObject().Select(x => x.Key))}");
163163
}
164+
165+
[Fact]
166+
public void Enum_Schema_Should_Include_All_Enum_Values()
167+
{
168+
var reflector = new Reflector();
169+
var schema = JsonSchemaValidation(typeof(TestEnumWithDescriptions), reflector);
170+
171+
// Validate that the schema has the correct structure for enums
172+
Assert.NotNull(schema);
173+
Assert.True(schema.AsObject().ContainsKey(JsonSchema.Type),
174+
$"Enum schema should contain '{JsonSchema.Type}' property. Available properties: {string.Join(", ", schema.AsObject().Select(x => x.Key))}");
175+
176+
// Validate that type is "string"
177+
var schemaType = schema[JsonSchema.Type];
178+
Assert.NotNull(schemaType);
179+
Assert.Equal(JsonSchema.String, schemaType.ToString());
180+
181+
// Validate that the schema contains an "enum" property
182+
Assert.True(schema.AsObject().ContainsKey("enum"),
183+
$"Enum schema should contain 'enum' property. Available properties: {string.Join(", ", schema.AsObject().Select(x => x.Key))}");
184+
185+
var enumValues = schema["enum"];
186+
Assert.NotNull(enumValues);
187+
Assert.True(enumValues is JsonArray, "Enum values should be a JSON array");
188+
189+
var enumArray = enumValues.AsArray();
190+
191+
// Validate that all enum values are present
192+
var expectedValues = new[] { "Option1", "Option2", "Option3", "Option4" };
193+
Assert.Equal(expectedValues.Length, enumArray.Count);
194+
195+
foreach (var expectedValue in expectedValues)
196+
{
197+
var hasValue = enumArray.Any(v => v?.ToString() == expectedValue);
198+
Assert.True(hasValue, $"Enum schema should contain value '{expectedValue}'. Actual values: {string.Join(", ", enumArray.Select(v => v?.ToString()))}");
199+
}
200+
}
164201
}
165202
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System.ComponentModel;
2+
using com.IvanMurzak.ReflectorNet.Tests.Model;
3+
4+
namespace com.IvanMurzak.ReflectorNet.Tests.Utils
5+
{
6+
public static partial class MethodHelper
7+
{
8+
[Description("Test method that accepts an enum parameter.")]
9+
public static string ProcessEnum
10+
(
11+
[Description("An enum value to process.")]
12+
TestEnumWithDescriptions enumValue
13+
)
14+
{
15+
return $"Processed enum: {enumValue}";
16+
}
17+
18+
[Description("Test method that accepts an enum parameter with default value.")]
19+
public static string ProcessEnumWithDefault
20+
(
21+
[Description("An enum value to process with default.")]
22+
TestEnumWithDescriptions enumValue = TestEnumWithDescriptions.Option2
23+
)
24+
{
25+
return $"Processed enum with default: {enumValue}";
26+
}
27+
28+
[Description("Test method that accepts both string and enum parameters.")]
29+
public static string ProcessStringAndEnum
30+
(
31+
[Description("A string parameter.")]
32+
string text,
33+
[Description("An enum parameter.")]
34+
TestEnumWithDescriptions enumValue
35+
)
36+
{
37+
return $"Text: {text}, Enum: {enumValue}";
38+
}
39+
}
40+
}

ReflectorNet/ReflectorNet.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
<!-- NuGet Package Information -->
1111
<PackageId>com.IvanMurzak.ReflectorNet</PackageId>
12-
<Version>1.0.6</Version>
12+
<Version>1.1.0</Version>
1313
<Authors>Ivan Murzak</Authors>
1414
<Copyright>Copyright © Ivan Murzak 2025</Copyright>
1515
<Description>ReflectorNet is an advanced .NET reflection toolkit designed for AI-driven scenarios. Effortlessly search for C# methods using natural language queries, invoke any method by supplying arguments as JSON, and receive results as JSON. The library also provides a powerful API to inspect, modify, and manage in-memory object instances dynamically via JSON data. Ideal for automation, testing, and AI integration workflows.</Description>

ReflectorNet/src/Reflector/MethodWrapper.cs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -286,8 +286,8 @@ public virtual bool VerifyParameters(IReadOnlyDictionary<string, object?>? named
286286
}
287287
else
288288
{
289-
// Use the provided parameter value
290-
return parameter;
289+
// Handle enum conversion for string values and return the provided parameter value
290+
return TryConvertStringToEnum(parameter, methodParameter.ParameterType, methodParameter.Name!);
291291
}
292292
}
293293

@@ -351,8 +351,8 @@ public virtual bool VerifyParameters(IReadOnlyDictionary<string, object?>? named
351351
}
352352
else
353353
{
354-
// Use the provided parameter value
355-
return value;
354+
// Handle enum conversion for string values and return the provided parameter value
355+
return TryConvertStringToEnum(value, parameter.ParameterType, parameter.Name!);
356356
}
357357
}
358358
else if (parameter.HasDefaultValue)
@@ -369,6 +369,22 @@ public virtual bool VerifyParameters(IReadOnlyDictionary<string, object?>? named
369369
}
370370
}
371371

372+
private static object? TryConvertStringToEnum(object? value, Type parameterType, string parameterName)
373+
{
374+
if (value is string stringValue && parameterType.IsEnum)
375+
{
376+
try
377+
{
378+
return Enum.Parse(parameterType, stringValue, ignoreCase: true);
379+
}
380+
catch (ArgumentException)
381+
{
382+
throw new ArgumentException($"Invalid value '{stringValue}' for parameter '{parameterName}'. Valid values are: {string.Join(", ", Enum.GetNames(parameterType))}");
383+
}
384+
}
385+
return value;
386+
}
387+
372388
protected virtual void PrintParameters(object?[]? parameters)
373389
{
374390
if (!(_logger?.IsEnabled(LogLevel.Debug) ?? false))

ReflectorNet/src/Utils/Json/JsonSchema.Internal.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,22 @@ JsonNode GeneratePrimitiveSchema(Type type)
217217
if (underlyingType == typeof(Guid))
218218
return new JsonObject { [Type] = String, ["format"] = "uuid" };
219219

220+
// Handle enum types
221+
if (underlyingType.IsEnum)
222+
{
223+
var enumValues = new JsonArray();
224+
foreach (var enumValue in Enum.GetValues(underlyingType))
225+
{
226+
enumValues.Add(JsonValue.Create(enumValue.ToString()));
227+
}
228+
229+
return new JsonObject
230+
{
231+
[Type] = String,
232+
["enum"] = enumValues
233+
};
234+
}
235+
220236
// Default for unknown primitives
221237
return new JsonObject { [Type] = String };
222238
}

0 commit comments

Comments
 (0)