using Microsoft.CodeAnalysis; using Xunit; namespace AutoMapProperty.Tests; public class MappingRegressionTests { [Fact] public void NullableNestedDtosMapNullAndNonNullValues() { var compilation = new GeneratorTestCompilation(""" namespace Fixtures { public class Child { [AutoMapProperty("Response")] public int Value { get; set; } } public class Parent { [AutoMapProperty("Response", typeof(ChildResponse), Nullability = AutoMapNullability.Nullable)] public Child Child { get; set; } = new Child(); } public static class Check { public static bool Run() { var parent = new Parent { Child = new Child { Value = 42 } }; var dto = new ParentResponse().ApplyFrom(null, parent); if (dto.Child.Value != 42 || dto.GetProjectorFrom(null).Compile()(parent).Child.Value != 42) return false; dto.Child = null; dto.ApplyTo(null, parent); if (parent.Child != null || new ParentResponse().ApplyFrom(null, parent).Child != null || dto.GetProjectorFrom(null).Compile()(parent).Child != null) return false; if (!ParentResponse.EqualExpression(null, parent).Compile()(dto) || !dto.Equals(new ParentResponse { Child = null })) return false; dto.Child = new ChildResponse { Value = 17 }; dto.ApplyTo(null, parent); return parent.Child.Value == 17; } } } """); Assert.Equal(NullableAnnotation.Annotated, compilation.Property("Fixtures.ParentResponse", "Child").Type.NullableAnnotation); Assert.Equal(true, compilation.Evaluate()); } [Fact] public void InheritedGeneratedPropertiesParticipateInMappings() { var compilation = new GeneratorTestCompilation(""" namespace Fixtures { public class Base { [AutoMapProperty("Request", Nullability = AutoMapNullability.NonNullable)] public string? Name { get; set; } } public class Derived : Base { [AutoMapProperty("Request")] public int Value { get; set; } } public partial class DerivedRequest : BaseRequest { } public static class Check { public static bool Run() { var entity = new Derived { Name = "name", Value = 42 }; var dto = new DerivedRequest().ApplyFrom(null, entity); var projected = dto.GetProjectorFrom(null).Compile()(entity); dto.Name = "changed"; dto.ApplyTo(null, entity); return entity.Name == "changed" && projected.Name == "name" && projected.Value == 42; } } } """); Assert.Equal(true, compilation.Evaluate()); } [Fact] public void CustomConvertersReceiveOverriddenNullableValues() { var compilation = new GeneratorTestCompilation(""" namespace Fixtures { public class Entity { [AutoMapProperty("Request", typeof(string), nameof(Parse), nameof(Format), Nullability = AutoMapNullability.Nullable)] public int Value { get; set; } public static int Parse(IServiceProvider providers, Entity entity, string? value) => value == null ? 0 : int.Parse(value); public static string Format(IServiceProvider providers, Entity entity, int value) => value.ToString(); } public static class Check { public static bool Run() { var entity = new Entity { Value = 42 }; var dto = new EntityRequest().ApplyFrom(null, entity); if (dto.Value != "42" || dto.GetProjectorFrom(null).Compile()(entity).Value != "42") return false; dto.Value = null; dto.ApplyTo(null, entity); return entity.Value == 0; } } } """); Assert.Equal(true, compilation.Evaluate()); } [Fact] public void FiltersMappingAttributesFromSharedListsAndKeepsValidationAttributes() { var compilation = new GeneratorTestCompilation(""" namespace Fixtures { public class Entity { [System.ComponentModel.DataAnnotations.Required, AutoMapProperty("Response", Nullability = AutoMapNullability.Nullable)] public string Value { get; set; } = "initial"; } } """); compilation.AssertCompiles(); var attribute = Assert.Single(compilation.Property("Fixtures.EntityResponse", "Value").GetAttributes()); Assert.Equal("RequiredAttribute", attribute.AttributeClass!.Name); } [Fact] public void NestedCollectionsAndArrayTypeReplacementsKeepMappingAndInitializerBehavior() { var compilation = new GeneratorTestCompilation(""" namespace Fixtures { public class Child { [AutoMapProperty("Response")] public int Value { get; set; } } public class Parent { [AutoMapProperty("Response", typeof(ChildResponse[]), Nullability = AutoMapNullability.Nullable)] public Child[] Children { get; set; } = Array.Empty(); } public static class Check { public static bool Run() { var parent = new Parent { Children = new[] { new Child { Value = 42 } } }; var dto = new ParentResponse().ApplyFrom(null, parent); if (dto.Children[0].Value != 42 || dto.GetProjectorFrom(null).Compile()(parent).Children[0].Value != 42) return false; dto.Children[0].Value = 17; dto.ApplyTo(null, parent); if (parent.Children[0].Value != 17) return false; dto.Children = null; dto.ApplyTo(null, parent); return parent.Children.Length == 0; } } } """); Assert.Equal(true, compilation.Evaluate()); } [Fact] public void SupportsInterfacesComputedPropertiesAndNullTypeArguments() { var compilation = new GeneratorTestCompilation(""" namespace Fixtures { public interface ISource { [AutoMapProperty("Request", Nullability = AutoMapNullability.Nullable)] string Name { get; } } public class Entity { [AutoMapProperty("Response", null, Nullability = AutoMapNullability.NonNullable)] public string? Name => "name"; } public static class Check { public static bool Run() => new EntityResponse().ApplyFrom(null, new Entity()).Name == "name"; } } """); Assert.Equal(NullableAnnotation.Annotated, compilation.Property("Fixtures.ISourceRequest", "Name").Type.NullableAnnotation); Assert.Equal(NullableAnnotation.NotAnnotated, compilation.Property("Fixtures.EntityResponse", "Name").Type.NullableAnnotation); Assert.Equal(true, compilation.Evaluate()); } }