diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/ObjectFieldDescriptor.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/ObjectFieldDescriptor.cs index b99c4166d65..35b0487e699 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/ObjectFieldDescriptor.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/ObjectFieldDescriptor.cs @@ -551,6 +551,9 @@ private static string ProcessExpression(Expression? expression) switch (expression) { + case UnaryExpression { NodeType: ExpressionType.Convert } unaryExpr: + return ProcessExpression(unaryExpr.Operand); + case MemberExpression memberExpr: var parent = ProcessExpression(memberExpr.Expression); return string.IsNullOrEmpty(parent) diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/ObjectFieldDescriptorTests.cs b/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/ObjectFieldDescriptorTests.cs index 9bfdc1e5b6e..ffa7c9d0d28 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/ObjectFieldDescriptorTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/ObjectFieldDescriptorTests.cs @@ -1,6 +1,7 @@ using HotChocolate.Internal; using HotChocolate.Resolvers; using HotChocolate.Types.Descriptors; +using HotChocolate.Types.Descriptors.Configurations; using HotChocolate.Types.Introspection; using Moq; @@ -219,4 +220,100 @@ public void ResolverTypeIsSet() var description = descriptor.CreateConfiguration(); Assert.Equal(typeof(string), description.ResolverType); } + + [Fact] + public void ExpressionSelectionSetFormatter_Format_PrimitiveProperty() + { + // arrange & act + // When the property type is a value type (int), the compiler wraps + // the member access in a Convert expression to box it to object. + // The formatter must unwrap this to extract the member name. + var result = ObjectFieldDescriptor.ExpressionSelectionSetFormatter + .Format(e => e.Type); + + // assert + Assert.Equal("Type", result); + } + + [Fact] + public void ExpressionSelectionSetFormatter_Format_EnumProperty() + { + // arrange & act + var result = ObjectFieldDescriptor.ExpressionSelectionSetFormatter + .Format(e => e.Provider); + + // assert + Assert.Equal("Provider", result); + } + + [Fact] + public void ExpressionSelectionSetFormatter_Format_StringProperty() + { + // arrange & act + // String is a reference type, so no Convert wrapping occurs. + var result = ObjectFieldDescriptor.ExpressionSelectionSetFormatter + .Format(e => e.Name); + + // assert + Assert.Equal("Name", result); + } + + [Fact] + public void ExpressionSelectionSetFormatter_Format_AnonymousObject() + { + // arrange & act + var result = ObjectFieldDescriptor.ExpressionSelectionSetFormatter + .Format(e => new { e.Type, e.Provider }); + + // assert + Assert.Equal("Type Provider", result); + } + + [Fact] + public void ParentRequires_WithPrimitiveProperty_SetsRequirements() + { + // arrange + var descriptor = ObjectFieldDescriptor.New(Context, "field"); + + // act + descriptor.ParentRequires(e => e.Type); + + // assert + var config = descriptor.CreateConfiguration(); + Assert.True(config.Flags.HasFlag(CoreFieldFlags.WithRequirements)); + var feature = config.Features.Get(); + Assert.NotNull(feature); + Assert.Equal("Type", feature.Requirements); + } + + [Fact] + public void ParentRequires_WithEnumProperty_SetsRequirements() + { + // arrange + var descriptor = ObjectFieldDescriptor.New(Context, "field"); + + // act + descriptor.ParentRequires(e => e.Provider); + + // assert + var config = descriptor.CreateConfiguration(); + Assert.True(config.Flags.HasFlag(CoreFieldFlags.WithRequirements)); + var feature = config.Features.Get(); + Assert.NotNull(feature); + Assert.Equal("Provider", feature.Requirements); + } + + private enum TestProvider + { + None, + Google, + Facebook + } + + private sealed class ParentRequiresTestEntity + { + public int Type { get; set; } + public TestProvider Provider { get; set; } + public string Name { get; set; } = default!; + } }