AutoMapProperty/AutoMapProperty.Tests/NullabilityTests.cs
2026-10-25 23:48:22 +01:00

143 lines
5.9 KiB
C#

using Microsoft.CodeAnalysis;
using Xunit;
namespace AutoMapProperty.Tests;
public class NullabilityTests
{
[Theory]
[InlineData("string", "Nullable", true)]
[InlineData("string?", "NonNullable", false)]
[InlineData("string?", "Preserve", true)]
[InlineData("string", "Preserve", false)]
[InlineData("int", "Nullable", true)]
[InlineData("int?", "Nullable", true)]
[InlineData("int?", "NonNullable", false)]
[InlineData("int", "NonNullable", false)]
[InlineData("int?", "Preserve", true)]
[InlineData("DateTime", "Nullable", true)]
[InlineData("DateTime?", "NonNullable", false)]
[InlineData("string[]", "Nullable", true)]
[InlineData("string[]?", "NonNullable", false)]
[InlineData("ICollection<string?>", "Nullable", true)]
public void OverridesTopLevelNullability(string type, string mode, bool nullable)
{
var compilation = new GeneratorTestCompilation($$"""
namespace Fixtures
{
public class Entity
{
[AutoMapProperty("Response", Nullability = AutoMapNullability.{{mode}})]
public {{type}} Value { get; set; }
}
}
""");
compilation.AssertCompiles();
var generatedType = compilation.Property("Fixtures.EntityResponse", "Value").Type;
Assert.Equal(nullable, generatedType.NullableAnnotation == NullableAnnotation.Annotated ||
generatedType is INamedTypeSymbol { OriginalDefinition.SpecialType: SpecialType.System_Nullable_T });
if (generatedType is INamedTypeSymbol { Name: "ICollection" } collection)
Assert.Equal(NullableAnnotation.Annotated, collection.TypeArguments[0].NullableAnnotation);
}
[Fact]
public void OverridesAreIndependentForEachDtoAndAppliedAfterTypeReplacement()
{
var compilation = new GeneratorTestCompilation("""
namespace Fixtures
{
public class Entity
{
[AutoMapProperty("Request", typeof(long), Nullability = AutoMapNullability.Nullable)]
[AutoMapProperty("Response", typeof(long?), Nullability = AutoMapNullability.NonNullable)]
[AutoMapProperty("Copy")]
public int Value { get; set; }
}
}
""");
compilation.AssertCompiles();
var request = Assert.IsAssignableFrom<INamedTypeSymbol>(compilation.Property("Fixtures.EntityRequest", "Value").Type);
Assert.Equal(SpecialType.System_Nullable_T, request.OriginalDefinition.SpecialType);
Assert.Equal(SpecialType.System_Int64, request.TypeArguments[0].SpecialType);
Assert.Equal(SpecialType.System_Int64, compilation.Property("Fixtures.EntityResponse", "Value").Type.SpecialType);
Assert.Equal(SpecialType.System_Int32, compilation.Property("Fixtures.EntityCopy", "Value").Type.SpecialType);
}
[Fact]
public void NullableValuesMapInBothDirectionsAndThroughProjectors()
{
var compilation = new GeneratorTestCompilation("""
namespace Fixtures
{
public class Entity
{
[AutoMapProperty("Request", Nullability = AutoMapNullability.Nullable)]
public int Value { get; set; }
}
public static class Check
{
public static bool Run()
{
var entity = new Entity { Value = 42 };
var dto = new EntityRequest().ApplyFrom(null, entity);
var projected = dto.GetProjectorFrom(null).Compile()(entity);
dto.Value = 17;
dto.ApplyTo(null, entity);
dto.Value = null;
try { dto.ApplyTo(null, entity); }
catch (InvalidOperationException) { return entity.Value == 17 && projected.Value == 42; }
return false;
}
}
}
""");
Assert.Equal(true, compilation.Evaluate());
}
[Fact]
public void ValueTypeNullabilityOverridesKeepValidInitializers()
{
var compilation = new GeneratorTestCompilation("""
namespace Fixtures
{
public class Entity
{
[AutoMapProperty("Request", Nullability = AutoMapNullability.Nullable)]
public DateTime Date { get; set; } = new DateTime(2026, 1, 2);
[AutoMapProperty("Request", Nullability = AutoMapNullability.NonNullable)]
public int? Count { get; set; } = null;
[AutoMapProperty("Request", Nullability = AutoMapNullability.NonNullable)]
public int? Total { get; set; } = default(int?);
}
public static class Check
{
public static bool Run()
{
var dto = new EntityRequest();
return dto.Date == new DateTime(2026, 1, 2) && dto.Count == 0 && dto.Total == 0;
}
}
}
""");
Assert.Equal(true, compilation.Evaluate());
}
[Fact]
public void InvalidNullabilityReportsPropertyDiagnostic()
{
var compilation = new GeneratorTestCompilation("""
namespace Fixtures
{
public class Entity
{
[AutoMapProperty("Response", Nullability = (AutoMapNullability)99)]
public string Value { get; set; }
}
}
""");
var diagnostic = Assert.Single(compilation.RunResult.Diagnostics);
Assert.Equal("NEJ23", diagnostic.Id);
Assert.True(diagnostic.Location.IsInSource);
}
}