diff --git a/src/Clients/Goa.Clients.Dynamo.Generator/Attributes/AttributeHandlerRegistry.cs b/src/Clients/Goa.Clients.Dynamo.Generator/Attributes/AttributeHandlerRegistry.cs index 70f43ea..46c85d2 100644 --- a/src/Clients/Goa.Clients.Dynamo.Generator/Attributes/AttributeHandlerRegistry.cs +++ b/src/Clients/Goa.Clients.Dynamo.Generator/Attributes/AttributeHandlerRegistry.cs @@ -61,26 +61,12 @@ private static bool IsAttributeApplicable(AttributeInfo attributeInfo, ISymbol s type = named.TypeArguments[0]; } - return IsDateTimeType(type); + return type.IsDateTimeOrDateTimeOffset(); } return true; } - /// - /// Determines whether the given type is System.DateTime or System.DateTimeOffset - /// using robust symbol identity rather than brittle name comparisons. - /// - private static bool IsDateTimeType(ITypeSymbol type) - { - if (type.SpecialType == SpecialType.System_DateTime) - return true; - - // DateTimeOffset has no SpecialType, so identify it by its fully-qualified name. - return type.Name == nameof(DateTimeOffset) - && type.ContainingNamespace?.ToDisplayString() == "System"; - } - /// /// Validates all attributes on a symbol and reports diagnostics. /// diff --git a/src/Clients/Goa.Clients.Dynamo.Generator/Models/TypeSymbolExtensions.cs b/src/Clients/Goa.Clients.Dynamo.Generator/Models/TypeSymbolExtensions.cs new file mode 100644 index 0000000..213e28c --- /dev/null +++ b/src/Clients/Goa.Clients.Dynamo.Generator/Models/TypeSymbolExtensions.cs @@ -0,0 +1,23 @@ +using Microsoft.CodeAnalysis; + +namespace Goa.Clients.Dynamo.Generator.Models; + +/// +/// Helper extensions for inspecting Roslyn type symbols. +/// +internal static class TypeSymbolExtensions +{ + /// + /// Determines whether the given type is System.DateTime or System.DateTimeOffset + /// using robust symbol identity rather than brittle name comparisons. + /// + public static bool IsDateTimeOrDateTimeOffset(this ITypeSymbol type) + { + if (type.SpecialType == SpecialType.System_DateTime) + return true; + + // DateTimeOffset has no SpecialType, so identify it by its fully-qualified name. + return type.Name == nameof(DateTimeOffset) + && type.ContainingNamespace?.ToDisplayString() == "System"; + } +} diff --git a/src/Clients/Goa.Clients.Dynamo.Generator/TypeHandlers/UnixTimestampTypeHandler.cs b/src/Clients/Goa.Clients.Dynamo.Generator/TypeHandlers/UnixTimestampTypeHandler.cs index b429d80..cfe95d9 100644 --- a/src/Clients/Goa.Clients.Dynamo.Generator/TypeHandlers/UnixTimestampTypeHandler.cs +++ b/src/Clients/Goa.Clients.Dynamo.Generator/TypeHandlers/UnixTimestampTypeHandler.cs @@ -13,9 +13,8 @@ public bool CanHandle(PropertyInfo propertyInfo) { var underlyingType = propertyInfo.UnderlyingType; var hasUnixTimestamp = propertyInfo.Attributes.Any(a => a is UnixTimestampAttributeInfo); - - return hasUnixTimestamp && - (underlyingType.Name == nameof(DateTime) || underlyingType.Name == nameof(DateTimeOffset)); + + return hasUnixTimestamp && underlyingType.IsDateTimeOrDateTimeOffset(); } public string GenerateToAttributeValue(PropertyInfo propertyInfo) diff --git a/tests/Clients/Goa.Clients.Dynamo.Generator.Tests/TypeHandlers/UnixTimestampTypeHandlerTests.cs b/tests/Clients/Goa.Clients.Dynamo.Generator.Tests/TypeHandlers/UnixTimestampTypeHandlerTests.cs index 6ca862c..d85ba4f 100644 --- a/tests/Clients/Goa.Clients.Dynamo.Generator.Tests/TypeHandlers/UnixTimestampTypeHandlerTests.cs +++ b/tests/Clients/Goa.Clients.Dynamo.Generator.Tests/TypeHandlers/UnixTimestampTypeHandlerTests.cs @@ -1,3 +1,4 @@ +using Microsoft.CodeAnalysis; using Goa.Clients.Dynamo.Generator.TypeHandlers; using Goa.Clients.Dynamo.Generator.Tests.Helpers; @@ -92,6 +93,33 @@ await Assert.That(result) .IsFalse(); } + [Test] + public async Task CanHandle_WithUserDefinedTypeNamedDateTime_ShouldReturnFalse() + { + // Arrange: a user-defined type named "DateTime" outside the System namespace + // must not be treated as System.DateTime. The robust symbol-based check relies + // on SpecialType/namespace rather than the type name alone. + var spoofedDateTime = MockSymbolFactory.CreateNamedTypeSymbol( + "DateTime", + "MyApp.DateTime", + namespaceName: "MyApp", + specialType: SpecialType.None).Object; + + var propertyInfo = TestModelBuilders.CreateUnixTimestampPropertyInfo( + "FakeTimestamp", + spoofedDateTime, + isNullable: false, + format: Models.UnixTimestampFormat.Seconds + ); + + // Act + var result = _handler.CanHandle(propertyInfo); + + // Assert + await Assert.That(result) + .IsFalse(); + } + [Test] public async Task GenerateToAttributeValue_NonNullableDateTime_SecondsFormat_ShouldGenerateCorrectCode() {