diff --git a/src/HotChocolate/Core/src/Types.NodaTime/DurationType.cs b/src/HotChocolate/Core/src/Types.NodaTime/DurationType.cs index 90f38c3369f..716be41bdf3 100644 --- a/src/HotChocolate/Core/src/Types.NodaTime/DurationType.cs +++ b/src/HotChocolate/Core/src/Types.NodaTime/DurationType.cs @@ -1,3 +1,4 @@ +using System; using System.Diagnostics.CodeAnalysis; using System.Globalization; using HotChocolate.Types.NodaTime.Properties; @@ -11,25 +12,39 @@ namespace HotChocolate.Types.NodaTime; /// public class DurationType : StringToStructBaseType { + private readonly IPattern[] _allowedPatterns; + private readonly IPattern _serializationPattern; + + /// + /// Initializes a new instance of . + /// + public DurationType() : this(DurationPattern.Roundtrip) + { + } + /// /// Initializes a new instance of . /// - public DurationType() : base("Duration") + public DurationType(params IPattern[] allowedPatterns) : base("Duration") { + if (allowedPatterns.Length == 0) + { + throw ThrowHelper.PatternCannotBeEmpty(this); + } + + _allowedPatterns = allowedPatterns; + _serializationPattern = allowedPatterns[0]; Description = NodaTimeResources.DurationType_Description; } /// protected override string Serialize(Duration runtimeValue) - => DurationPattern.Roundtrip - .WithCulture(CultureInfo.InvariantCulture) + => _serializationPattern .Format(runtimeValue); /// protected override bool TryDeserialize( string resultValue, [NotNullWhen(true)] out Duration? runtimeValue) - => DurationPattern.Roundtrip - .WithCulture(CultureInfo.InvariantCulture) - .TryParse(resultValue, out runtimeValue); + => _allowedPatterns.TryParse(resultValue, out runtimeValue); } diff --git a/src/HotChocolate/Core/src/Types.NodaTime/Extensions/PatternExtensions.cs b/src/HotChocolate/Core/src/Types.NodaTime/Extensions/PatternExtensions.cs index 96a02519cf3..19116fa15aa 100644 --- a/src/HotChocolate/Core/src/Types.NodaTime/Extensions/PatternExtensions.cs +++ b/src/HotChocolate/Core/src/Types.NodaTime/Extensions/PatternExtensions.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using NodaTime.Text; namespace HotChocolate.Types.NodaTime; @@ -7,7 +8,7 @@ internal static class PatternExtensions public static bool TryParse( this IPattern pattern, string text, - out NodaTimeType? output) + [NotNullWhen(true)] out NodaTimeType? output) where NodaTimeType : struct { ParseResult result = pattern.Parse(text); @@ -25,7 +26,7 @@ internal static class PatternExtensions public static bool TryParse( this IPattern pattern, string text, - out NodaTimeType? output) + [NotNullWhen(true)] out NodaTimeType? output) where NodaTimeType : class { ParseResult result = pattern.Parse(text); @@ -39,4 +40,40 @@ internal static class PatternExtensions output = null; return false; } + + public static bool TryParse( + this IPattern[] patterns, + string text, + [NotNullWhen(true)] out NodaTimeType? output) + where NodaTimeType : struct + { + foreach (IPattern pattern in patterns) + { + if (pattern.TryParse(text, out output)) + { + return true; + } + } + + output = default; + return false; + } + + public static bool TryParse( + this IPattern[] patterns, + string text, + [NotNullWhen(true)] out NodaTimeType? output) + where NodaTimeType : class + { + foreach (IPattern pattern in patterns) + { + if (pattern.TryParse(text, out output)) + { + return true; + } + } + + output = default; + return false; + } } diff --git a/src/HotChocolate/Core/src/Types.NodaTime/InstantType.cs b/src/HotChocolate/Core/src/Types.NodaTime/InstantType.cs index 4204838f5c0..0aa032a9d83 100644 --- a/src/HotChocolate/Core/src/Types.NodaTime/InstantType.cs +++ b/src/HotChocolate/Core/src/Types.NodaTime/InstantType.cs @@ -11,25 +11,39 @@ namespace HotChocolate.Types.NodaTime; /// public class InstantType : StringToStructBaseType { + private readonly IPattern[] _allowedPatterns; + private readonly IPattern _serializationPattern; + + /// + /// Initializes a new instance of . + /// + public InstantType() : this(InstantPattern.ExtendedIso) + { + } + /// /// Initializes a new instance of . /// - public InstantType() : base("Instant") + public InstantType(params IPattern[] allowedPatterns) : base("Instant") { + if (allowedPatterns.Length == 0) + { + throw ThrowHelper.PatternCannotBeEmpty(this); + } + + _allowedPatterns = allowedPatterns; + _serializationPattern = allowedPatterns[0]; Description = NodaTimeResources.InstantType_Description; } /// protected override string Serialize(Instant runtimeValue) - => InstantPattern.ExtendedIso - .WithCulture(CultureInfo.InvariantCulture) + => _serializationPattern .Format(runtimeValue); /// protected override bool TryDeserialize( string resultValue, [NotNullWhen(true)] out Instant? runtimeValue) - => InstantPattern.ExtendedIso - .WithCulture(CultureInfo.InvariantCulture) - .TryParse(resultValue, out runtimeValue); + => _allowedPatterns.TryParse(resultValue, out runtimeValue); } diff --git a/src/HotChocolate/Core/src/Types.NodaTime/LocalDateTimeType.cs b/src/HotChocolate/Core/src/Types.NodaTime/LocalDateTimeType.cs index 1100caa187d..f3342ac8e63 100644 --- a/src/HotChocolate/Core/src/Types.NodaTime/LocalDateTimeType.cs +++ b/src/HotChocolate/Core/src/Types.NodaTime/LocalDateTimeType.cs @@ -11,25 +11,39 @@ namespace HotChocolate.Types.NodaTime; /// public class LocalDateTimeType : StringToStructBaseType { + private readonly IPattern[] _allowedPatterns; + private readonly IPattern _serializationPattern; + + /// + /// Initializes a new instance of . + /// + public LocalDateTimeType() : this(LocalDateTimePattern.ExtendedIso) + { + } + /// /// Initializes a new instance of . /// - public LocalDateTimeType() : base("LocalDateTime") + public LocalDateTimeType(params IPattern[] allowedPatterns) : base("LocalDateTime") { + if (allowedPatterns.Length == 0) + { + throw ThrowHelper.PatternCannotBeEmpty(this); + } + + _allowedPatterns = allowedPatterns; + _serializationPattern = allowedPatterns[0]; Description = NodaTimeResources.LocalDateTimeType_Description; } /// protected override string Serialize(LocalDateTime runtimeValue) - => LocalDateTimePattern.ExtendedIso - .WithCulture(CultureInfo.InvariantCulture) + => _serializationPattern .Format(runtimeValue); /// protected override bool TryDeserialize( string resultValue, [NotNullWhen(true)] out LocalDateTime? runtimeValue) - => LocalDateTimePattern.ExtendedIso - .WithCulture(CultureInfo.InvariantCulture) - .TryParse(resultValue, out runtimeValue); + => _allowedPatterns.TryParse(resultValue, out runtimeValue); } diff --git a/src/HotChocolate/Core/src/Types.NodaTime/LocalDateType.cs b/src/HotChocolate/Core/src/Types.NodaTime/LocalDateType.cs index 4e1ed25edc1..baf72777c2d 100644 --- a/src/HotChocolate/Core/src/Types.NodaTime/LocalDateType.cs +++ b/src/HotChocolate/Core/src/Types.NodaTime/LocalDateType.cs @@ -12,25 +12,39 @@ namespace HotChocolate.Types.NodaTime; /// public class LocalDateType : StringToStructBaseType { + private readonly IPattern[] _allowedPatterns; + private readonly IPattern _serializationPattern; + + /// + /// Initializes a new instance of . + /// + public LocalDateType() : this(LocalDatePattern.Iso) + { + } + /// /// Initializes a new instance of . /// - public LocalDateType() : base("LocalDate") + public LocalDateType(params IPattern[] allowedPatterns) : base("LocalDate") { + if (allowedPatterns.Length == 0) + { + throw ThrowHelper.PatternCannotBeEmpty(this); + } + + _allowedPatterns = allowedPatterns; + _serializationPattern = allowedPatterns[0]; Description = NodaTimeResources.LocalDateType_Description; } /// protected override string Serialize(LocalDate runtimeValue) - => LocalDatePattern.Iso - .WithCulture(CultureInfo.InvariantCulture) + => _serializationPattern .Format(runtimeValue); /// protected override bool TryDeserialize( string resultValue, [NotNullWhen(true)] out LocalDate? runtimeValue) - => LocalDatePattern.Iso - .WithCulture(CultureInfo.InvariantCulture) - .TryParse(resultValue, out runtimeValue); + => _allowedPatterns.TryParse(resultValue, out runtimeValue); } diff --git a/src/HotChocolate/Core/src/Types.NodaTime/LocalTimeType.cs b/src/HotChocolate/Core/src/Types.NodaTime/LocalTimeType.cs index ba94b8d3a14..8e8a09b2e2d 100644 --- a/src/HotChocolate/Core/src/Types.NodaTime/LocalTimeType.cs +++ b/src/HotChocolate/Core/src/Types.NodaTime/LocalTimeType.cs @@ -12,25 +12,39 @@ namespace HotChocolate.Types.NodaTime; /// public class LocalTimeType : StringToStructBaseType { + private readonly IPattern[] _allowedPatterns; + private readonly IPattern _serializationPattern; + + /// + /// Initializes a new instance of . + /// + public LocalTimeType() : this(LocalTimePattern.ExtendedIso) + { + } + /// /// Initializes a new instance of . /// - public LocalTimeType() : base("LocalTime") + public LocalTimeType(params IPattern[] allowedPatterns) : base("LocalTime") { + if (allowedPatterns.Length == 0) + { + throw ThrowHelper.PatternCannotBeEmpty(this); + } + + _allowedPatterns = allowedPatterns; + _serializationPattern = allowedPatterns[0]; Description = NodaTimeResources.LocalTimeType_Description; } /// protected override string Serialize(LocalTime runtimeValue) - => LocalTimePattern.ExtendedIso - .WithCulture(CultureInfo.InvariantCulture) + => _serializationPattern .Format(runtimeValue); /// protected override bool TryDeserialize( string resultValue, [NotNullWhen(true)] out LocalTime? runtimeValue) - => LocalTimePattern.ExtendedIso - .WithCulture(CultureInfo.InvariantCulture) - .TryParse(resultValue, out runtimeValue); + => _allowedPatterns.TryParse(resultValue, out runtimeValue); } diff --git a/src/HotChocolate/Core/src/Types.NodaTime/OffsetDateTimeType.cs b/src/HotChocolate/Core/src/Types.NodaTime/OffsetDateTimeType.cs index 1623b58fe7b..885e30a6211 100644 --- a/src/HotChocolate/Core/src/Types.NodaTime/OffsetDateTimeType.cs +++ b/src/HotChocolate/Core/src/Types.NodaTime/OffsetDateTimeType.cs @@ -11,25 +11,43 @@ namespace HotChocolate.Types.NodaTime; /// public class OffsetDateTimeType : StringToStructBaseType { + private readonly IPattern[] _allowedPatterns; + private readonly IPattern _serializationPattern; + + /// + /// Initializes a new instance of . + /// + public OffsetDateTimeType() : this(OffsetDateTimePattern.ExtendedIso) + { + // Backwards compatibility with the original code's behavior + _serializationPattern = OffsetDateTimePattern.GeneralIso; + _allowedPatterns = new IPattern[] { OffsetDateTimePattern.ExtendedIso }; + } + /// /// Initializes a new instance of . /// - public OffsetDateTimeType() : base("OffsetDateTime") + public OffsetDateTimeType(params IPattern[] allowedPatterns) + : base("OffsetDateTime") { + if (allowedPatterns.Length == 0) + { + throw ThrowHelper.PatternCannotBeEmpty(this); + } + + _allowedPatterns = allowedPatterns; + _serializationPattern = _allowedPatterns[0]; Description = NodaTimeResources.OffsetDateTimeType_Description; } /// protected override string Serialize(OffsetDateTime runtimeValue) - => OffsetDateTimePattern.GeneralIso - .WithCulture(CultureInfo.InvariantCulture) + => _serializationPattern .Format(runtimeValue); /// protected override bool TryDeserialize( string resultValue, [NotNullWhen(true)] out OffsetDateTime? runtimeValue) - => OffsetDateTimePattern.ExtendedIso - .WithCulture(CultureInfo.InvariantCulture) - .TryParse(resultValue, out runtimeValue); + => _allowedPatterns.TryParse(resultValue, out runtimeValue); } diff --git a/src/HotChocolate/Core/src/Types.NodaTime/OffsetDateType.cs b/src/HotChocolate/Core/src/Types.NodaTime/OffsetDateType.cs index e209a5d824e..ebbb8c32a4b 100644 --- a/src/HotChocolate/Core/src/Types.NodaTime/OffsetDateType.cs +++ b/src/HotChocolate/Core/src/Types.NodaTime/OffsetDateType.cs @@ -13,25 +13,39 @@ namespace HotChocolate.Types.NodaTime; /// public class OffsetDateType : StringToStructBaseType { + private readonly IPattern[] _allowedPatterns; + private readonly IPattern _serializationPattern; + + /// + /// Initializes a new instance of . + /// + public OffsetDateType() : this(OffsetDatePattern.GeneralIso) + { + } + /// /// Initializes a new instance of . /// - public OffsetDateType() : base("OffsetDate") + public OffsetDateType(params IPattern[] allowedPatterns) : base("OffsetDate") { + if (allowedPatterns.Length == 0) + { + throw ThrowHelper.PatternCannotBeEmpty(this); + } + + _allowedPatterns = allowedPatterns; + _serializationPattern = allowedPatterns[0]; Description = NodaTimeResources.OffsetDateType_Description; } /// protected override string Serialize(OffsetDate runtimeValue) - => OffsetDatePattern.GeneralIso - .WithCulture(CultureInfo.InvariantCulture) + => _serializationPattern .Format(runtimeValue); /// protected override bool TryDeserialize( string resultValue, [NotNullWhen(true)] out OffsetDate? runtimeValue) - => OffsetDatePattern.GeneralIso - .WithCulture(CultureInfo.InvariantCulture) - .TryParse(resultValue, out runtimeValue); + => _allowedPatterns.TryParse(resultValue, out runtimeValue); } diff --git a/src/HotChocolate/Core/src/Types.NodaTime/OffsetTimeType.cs b/src/HotChocolate/Core/src/Types.NodaTime/OffsetTimeType.cs index ac3cf956ada..cde4ba5cbe2 100644 --- a/src/HotChocolate/Core/src/Types.NodaTime/OffsetTimeType.cs +++ b/src/HotChocolate/Core/src/Types.NodaTime/OffsetTimeType.cs @@ -12,25 +12,39 @@ namespace HotChocolate.Types.NodaTime; /// public class OffsetTimeType : StringToStructBaseType { + private readonly IPattern[] _allowedPatterns; + private readonly IPattern _serializationPattern; + + /// + /// Initializes a new instance of . + /// + public OffsetTimeType() : this(OffsetTimePattern.GeneralIso) + { + } + /// /// Initializes a new instance of . /// - public OffsetTimeType() : base("OffsetTime") + public OffsetTimeType(params IPattern[] allowedPatterns) : base("OffsetTime") { + if (allowedPatterns.Length == 0) + { + throw ThrowHelper.PatternCannotBeEmpty(this); + } + + _allowedPatterns = allowedPatterns; + _serializationPattern = _allowedPatterns[0]; Description = NodaTimeResources.OffsetTimeType_Description; } /// protected override string Serialize(OffsetTime runtimeValue) - => OffsetTimePattern.GeneralIso - .WithCulture(CultureInfo.InvariantCulture) + => _serializationPattern .Format(runtimeValue); /// protected override bool TryDeserialize( string resultValue, [NotNullWhen(true)] out OffsetTime? runtimeValue) - => OffsetTimePattern.GeneralIso - .WithCulture(CultureInfo.InvariantCulture) - .TryParse(resultValue, out runtimeValue); + => _allowedPatterns.TryParse(resultValue, out runtimeValue); } diff --git a/src/HotChocolate/Core/src/Types.NodaTime/OffsetType.cs b/src/HotChocolate/Core/src/Types.NodaTime/OffsetType.cs index b9acdb34032..be0f0c090e2 100644 --- a/src/HotChocolate/Core/src/Types.NodaTime/OffsetType.cs +++ b/src/HotChocolate/Core/src/Types.NodaTime/OffsetType.cs @@ -13,25 +13,39 @@ namespace HotChocolate.Types.NodaTime; /// public class OffsetType : StringToStructBaseType { + private readonly IPattern[] _allowedPatterns; + private readonly IPattern _serializationPattern; + + /// + /// Initializes a new instance of . + /// + public OffsetType() : this(OffsetPattern.GeneralInvariantWithZ) + { + } + /// /// Initializes a new instance of . /// - public OffsetType() : base("Offset") + public OffsetType(params IPattern[] allowedPatterns) : base("Offset") { + if (allowedPatterns.Length == 0) + { + throw ThrowHelper.PatternCannotBeEmpty(this); + } + + _allowedPatterns = allowedPatterns; + _serializationPattern = allowedPatterns[0]; Description = NodaTimeResources.OffsetType_Description; } /// protected override string Serialize(Offset runtimeValue) - => OffsetPattern.GeneralInvariantWithZ - .WithCulture(CultureInfo.InvariantCulture) + => _serializationPattern .Format(runtimeValue); /// protected override bool TryDeserialize( string resultValue, [NotNullWhen(true)] out Offset? runtimeValue) - => OffsetPattern.GeneralInvariantWithZ - .WithCulture(CultureInfo.InvariantCulture) - .TryParse(resultValue, out runtimeValue); + => _allowedPatterns.TryParse(resultValue, out runtimeValue); } diff --git a/src/HotChocolate/Core/src/Types.NodaTime/PeriodType.cs b/src/HotChocolate/Core/src/Types.NodaTime/PeriodType.cs index 694f60c94c6..7c7248d0814 100644 --- a/src/HotChocolate/Core/src/Types.NodaTime/PeriodType.cs +++ b/src/HotChocolate/Core/src/Types.NodaTime/PeriodType.cs @@ -11,21 +11,38 @@ namespace HotChocolate.Types.NodaTime; /// public class PeriodType : StringToClassBaseType { + private readonly IPattern[] _allowedPatterns; + private readonly IPattern _serializationPattern; + + /// + /// Initializes a new instance of . + /// + public PeriodType() : this(PeriodPattern.Roundtrip) + { + } + /// /// Initializes a new instance of . /// - public PeriodType() : base("Period") + public PeriodType(params IPattern[] allowedPatterns) : base("Period") { + if (allowedPatterns.Length == 0) + { + throw ThrowHelper.PatternCannotBeEmpty(this); + } + + _allowedPatterns = allowedPatterns; + _serializationPattern = allowedPatterns[0]; Description = NodaTimeResources.PeriodType_Description; } /// protected override string Serialize(Period runtimeValue) - => PeriodPattern.Roundtrip.Format(runtimeValue); + => _serializationPattern.Format(runtimeValue); /// protected override bool TryDeserialize( string resultValue, [NotNullWhen(true)] out Period? runtimeValue) - => PeriodPattern.Roundtrip.TryParse(resultValue, out runtimeValue); + => _allowedPatterns.TryParse(resultValue, out runtimeValue); } diff --git a/src/HotChocolate/Core/src/Types.NodaTime/Properties/NodaTimeResources.Designer.cs b/src/HotChocolate/Core/src/Types.NodaTime/Properties/NodaTimeResources.Designer.cs index 484ebfda87b..dd95e57c574 100644 --- a/src/HotChocolate/Core/src/Types.NodaTime/Properties/NodaTimeResources.Designer.cs +++ b/src/HotChocolate/Core/src/Types.NodaTime/Properties/NodaTimeResources.Designer.cs @@ -9,21 +9,21 @@ namespace HotChocolate.Types.NodaTime.Properties { using System; - - + + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [System.Diagnostics.DebuggerNonUserCodeAttribute()] [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class NodaTimeResources { - + private static System.Resources.ResourceManager resourceMan; - + private static System.Globalization.CultureInfo resourceCulture; - + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal NodaTimeResources() { } - + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] internal static System.Resources.ResourceManager ResourceManager { get { @@ -34,7 +34,7 @@ internal class NodaTimeResources { return resourceMan; } } - + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] internal static System.Globalization.CultureInfo Culture { get { @@ -44,101 +44,107 @@ internal class NodaTimeResources { resourceCulture = value; } } - + internal static string IntToStructBaseType_ParseLiteral_UnableToDeserializeInt { get { return ResourceManager.GetString("IntToStructBaseType_ParseLiteral_UnableToDeserializeInt", resourceCulture); } } - + internal static string StringToClassBaseType_ParseLiteral_UnableToDeserializeString { get { return ResourceManager.GetString("StringToClassBaseType_ParseLiteral_UnableToDeserializeString", resourceCulture); } } - + internal static string StringToStructBaseType_ParseLiteral_UnableToDeserializeString { get { return ResourceManager.GetString("StringToStructBaseType_ParseLiteral_UnableToDeserializeString", resourceCulture); } } - + internal static string DateTimeZoneType_Description { get { return ResourceManager.GetString("DateTimeZoneType_Description", resourceCulture); } } - + internal static string DurationType_Description { get { return ResourceManager.GetString("DurationType_Description", resourceCulture); } } - + internal static string InstantType_Description { get { return ResourceManager.GetString("InstantType_Description", resourceCulture); } } - + internal static string IsoDayOfWeekType_Description { get { return ResourceManager.GetString("IsoDayOfWeekType_Description", resourceCulture); } } - + internal static string LocalDateTimeType_Description { get { return ResourceManager.GetString("LocalDateTimeType_Description", resourceCulture); } } - + internal static string LocalDateType_Description { get { return ResourceManager.GetString("LocalDateType_Description", resourceCulture); } } - + internal static string LocalTimeType_Description { get { return ResourceManager.GetString("LocalTimeType_Description", resourceCulture); } } - + internal static string OffsetDateTimeType_Description { get { return ResourceManager.GetString("OffsetDateTimeType_Description", resourceCulture); } } - + internal static string OffsetDateType_Description { get { return ResourceManager.GetString("OffsetDateType_Description", resourceCulture); } } - + internal static string OffsetTimeType_Description { get { return ResourceManager.GetString("OffsetTimeType_Description", resourceCulture); } } - + internal static string OffsetType_Description { get { return ResourceManager.GetString("OffsetType_Description", resourceCulture); } } - + internal static string PeriodType_Description { get { return ResourceManager.GetString("PeriodType_Description", resourceCulture); } } - + internal static string ZonedDateTimeType_Description { get { return ResourceManager.GetString("ZonedDateTimeType_Description", resourceCulture); } } + + internal static string NodaTime_NoPatternProvided { + get { + return ResourceManager.GetString("NodaTime_NoPatternProvided", resourceCulture); + } + } } } diff --git a/src/HotChocolate/Core/src/Types.NodaTime/Properties/NodaTimeResources.resx b/src/HotChocolate/Core/src/Types.NodaTime/Properties/NodaTimeResources.resx index 5b3c434deef..5cb744599e9 100644 --- a/src/HotChocolate/Core/src/Types.NodaTime/Properties/NodaTimeResources.resx +++ b/src/HotChocolate/Core/src/Types.NodaTime/Properties/NodaTimeResources.resx @@ -70,4 +70,7 @@ A time zone maps UTC instants to local times - or, equivalently, to the offset f A LocalDateTime in a specific time zone and with a particular offset to distinguish between otherwise-ambiguous instants. A ZonedDateTime is global, in that it maps to a single Instant. + + The provided patterns are empty. You must provide at least one pattern for type {0}. + diff --git a/src/HotChocolate/Core/src/Types.NodaTime/ThrowHelper.cs b/src/HotChocolate/Core/src/Types.NodaTime/ThrowHelper.cs new file mode 100644 index 00000000000..8944e5e6ba3 --- /dev/null +++ b/src/HotChocolate/Core/src/Types.NodaTime/ThrowHelper.cs @@ -0,0 +1,13 @@ +using HotChocolate.Types.NodaTime.Properties; + +namespace HotChocolate.Types.NodaTime; + +internal static class ThrowHelper +{ + public static SchemaException PatternCannotBeEmpty(ITypeSystemObject type) => + new(SchemaErrorBuilder + .New() + .SetTypeSystemObject(type) + .SetMessage(NodaTimeResources.NodaTime_NoPatternProvided, type.Name) + .Build()); +} diff --git a/src/HotChocolate/Core/src/Types.NodaTime/ZonedDateTimeType.cs b/src/HotChocolate/Core/src/Types.NodaTime/ZonedDateTimeType.cs index c7f725a9eef..1528be7b511 100644 --- a/src/HotChocolate/Core/src/Types.NodaTime/ZonedDateTimeType.cs +++ b/src/HotChocolate/Core/src/Types.NodaTime/ZonedDateTimeType.cs @@ -13,27 +13,43 @@ namespace HotChocolate.Types.NodaTime; public class ZonedDateTimeType : StringToStructBaseType { private static readonly string formatString = "uuuu'-'MM'-'dd'T'HH':'mm':'ss' 'z' 'o"; + private static readonly ZonedDateTimePattern _default = + ZonedDateTimePattern.CreateWithInvariantCulture(formatString, DateTimeZoneProviders.Tzdb); + + private readonly IPattern[] _allowedPatterns; + private readonly IPattern _serializationPattern; + + /// + /// Initializes a new instance of . + /// + public ZonedDateTimeType() : this(_default) + { + } /// /// Initializes a new instance of . /// - public ZonedDateTimeType() + public ZonedDateTimeType(params IPattern[] allowedPatterns) : base("ZonedDateTime") { + if (allowedPatterns.Length == 0) + { + throw ThrowHelper.PatternCannotBeEmpty(this); + } + + _allowedPatterns = allowedPatterns; + _serializationPattern = allowedPatterns[0]; Description = NodaTimeResources.ZonedDateTimeType_Description; } /// protected override string Serialize(ZonedDateTime runtimeValue) - => ZonedDateTimePattern - .CreateWithInvariantCulture(formatString, DateTimeZoneProviders.Tzdb) + => _serializationPattern .Format(runtimeValue); /// protected override bool TryDeserialize( string resultValue, [NotNullWhen(true)] out ZonedDateTime? runtimeValue) - => ZonedDateTimePattern - .CreateWithInvariantCulture(formatString, DateTimeZoneProviders.Tzdb) - .TryParse(resultValue, out runtimeValue); + => _allowedPatterns.TryParse(resultValue, out runtimeValue); } diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/DateTimeZoneTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/DateTimeZoneTypeTests.cs index 5a863c27cbe..d32ead2cbb2 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/DateTimeZoneTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/DateTimeZoneTypeTests.cs @@ -27,6 +27,7 @@ public string Test(DateTimeZone arg) } private readonly IRequestExecutor testExecutor; + public DateTimeZoneTypeIntegrationTests() { testExecutor = SchemaBuilder.New() diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/DurationTypeJsonRoundtripIntegrationTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/DurationTypeJsonRoundtripIntegrationTests.cs new file mode 100644 index 00000000000..efbd05866c5 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/DurationTypeJsonRoundtripIntegrationTests.cs @@ -0,0 +1,218 @@ +using System; +using HotChocolate.Execution; +using NodaTime; +using NodaTime.Text; +using Xunit; + +namespace HotChocolate.Types.NodaTime.Tests; + +public class DurationTypeJsonRoundtripIntegrationTests +{ + public static class Schema + { + public class Query + { + public Duration PositiveWithDecimals => + Duration.FromTimeSpan(new TimeSpan(123, 7, 53, 10, 19)); + + public Duration NegativeWithDecimals => + -Duration.FromTimeSpan(new TimeSpan(123, 7, 53, 10, 19)); + + public Duration PositiveWithoutDecimals => + Duration.FromTimeSpan(new TimeSpan(123, 7, 53, 10)); + + public Duration PositiveWithoutSeconds => + Duration.FromTimeSpan(new TimeSpan(123, 7, 53, 0)); + + public Duration PositiveWithoutMinutes => + Duration.FromTimeSpan(new TimeSpan(123, 7, 0, 0)); + + public Duration PositiveWithRoundtrip => + Duration.FromTimeSpan(new TimeSpan(123, 26, 0, 70)); + } + + public class Mutation + { + public Duration Test(Duration arg) + => arg + Duration.FromMinutes(10); + } + } + + private readonly IRequestExecutor testExecutor; + + public DurationTypeJsonRoundtripIntegrationTests() + { + testExecutor = SchemaBuilder.New() + .AddQueryType() + .AddMutationType() + .AddNodaTime(excludeTypes: typeof(DurationType)) + .AddType(new DurationType(DurationPattern.JsonRoundtrip)) + .Create() + .MakeExecutable(); + } + + [Fact] + public void QueryReturnsSerializedDataWithDecimals() + { + IExecutionResult? result = testExecutor.Execute("query { test: positiveWithDecimals }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2959:53:10.019", queryResult!.Data!["test"]); + } + + [Fact] + public void QueryReturnsSerializedDataWithNegativeValue() + { + IExecutionResult? result = testExecutor.Execute("query { test: negativeWithDecimals }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("-2959:53:10.019", queryResult!.Data!["test"]); + } + + [Fact] + public void QueryReturnsSerializedDataWithoutDecimals() + { + IExecutionResult? result = testExecutor.Execute("query { test: positiveWithoutDecimals }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2959:53:10", queryResult!.Data!["test"]); + } + + [Fact] + public void QueryReturnsSerializedDataWithoutSeconds() + { + IExecutionResult? result = testExecutor.Execute("query { test: positiveWithoutSeconds }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2959:53:00", queryResult!.Data!["test"]); + } + + [Fact] + public void QueryReturnsSerializedDataWithoutMinutes() + { + IExecutionResult? result = testExecutor.Execute("query { test: positiveWithoutMinutes }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2959:00:00", queryResult!.Data!["test"]); + } + + [Fact] + public void QueryReturnsSerializedDataWithRoundtrip() + { + IExecutionResult? result = testExecutor.Execute("query { test: positiveWithRoundtrip }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2978:01:10", queryResult!.Data!["test"]); + } + + [Fact] + public void MutationParsesInputWithDecimals() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: Duration!) { test(arg: $arg) }") + .SetVariableValue("arg", "238:01:00.019") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("238:11:00.019", queryResult!.Data!["test"]); + } + + [Fact] + public void MutationParsesInputWithoutDecimals() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: Duration!) { test(arg: $arg) }") + .SetVariableValue("arg", "238:01:00") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("238:11:00", queryResult!.Data!["test"]); + } + + [Fact] + public void MutationParsesInputWithoutLeadingZero() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: Duration!) { test(arg: $arg) }") + .SetVariableValue("arg", "238:01:00") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("238:11:00", queryResult!.Data!["test"]); + } + + [Fact] + public void MutationParsesInputWithNegativeValue() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: Duration!) { test(arg: $arg) }") + .SetVariableValue("arg", "-238:01:00") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("-237:51:00", queryResult!.Data!["test"]); + } + + [Fact] + public void MutationDoesntParseInputWithPlusSign() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: Duration!) { test(arg: $arg) }") + .SetVariableValue("arg", "+09:22:01:00") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + + [Fact] + public void MutationParsesLiteralWithDecimals() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"238:01:00.019\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("238:11:00.019", queryResult!.Data!["test"]); + } + + [Fact] + public void MutationParsesLiteralWithoutDecimals() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"238:01:00\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("238:11:00", queryResult!.Data!["test"]); + } + + [Fact] + public void MutationParsesLiteralWithoutLeadingZero() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"238:01:00\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("238:11:00", queryResult!.Data!["test"]); + } + + [Fact] + public void MutationParsesLiteralWithNegativeValue() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"-238:01:00\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("-237:51:00", queryResult!.Data!["test"]); + } + + [Fact] + public void MutationDoesntParseLiteralWithPlusSign() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"+238:01:00\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } +} diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/DurationTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/DurationTypeTests.cs index 2846785ce1c..28d8d17c74e 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/DurationTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/DurationTypeTests.cs @@ -3,6 +3,7 @@ using HotChocolate.Execution; using Microsoft.Extensions.DependencyInjection; using NodaTime; +using NodaTime.Text; using Xunit; namespace HotChocolate.Types.NodaTime.Tests @@ -29,6 +30,7 @@ public Duration Test(Duration arg) } private readonly IRequestExecutor testExecutor; + public DurationTypeIntegrationTests() { testExecutor = SchemaBuilder.New() @@ -228,5 +230,12 @@ public void MutationDoesntParseLiteralWithOverflownHours() Assert.Null(queryResult!.Data); Assert.Equal(1, queryResult!.Errors!.Count); } + + [Fact] + public void PatternEmpty_ThrowSchemaException() + { + static object Call() => new DurationType(Array.Empty>()); + Assert.Throws(Call); + } } } diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/InstantTypeDateTimeOffsetIntegrationTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/InstantTypeDateTimeOffsetIntegrationTests.cs new file mode 100644 index 00000000000..c77130c3621 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/InstantTypeDateTimeOffsetIntegrationTests.cs @@ -0,0 +1,107 @@ +using System; +using System.Globalization; +using System.Text; +using HotChocolate.Execution; +using NodaTime; +using NodaTime.Extensions; +using NodaTime.Text; +using Xunit; + +namespace HotChocolate.Types.NodaTime.Tests +{ + public class InstantTypeDateTimeOffsetIntegrationTests + { + class InstantDateTimeOffsetPattern : IPattern + { + public ParseResult Parse(string text) + { + return DateTimeOffset.TryParse( + text, + DateTimeFormatInfo.InvariantInfo, + DateTimeStyles.AssumeUniversal, + out var value) + ? ParseResult.ForValue(value.ToInstant()) + : ParseResult + .ForException(() => new FormatException("Could not parse DateTimeOffset")); + } + + public string Format(Instant value) + { + return InstantPattern.General.Format(value); + } + + public StringBuilder AppendFormat(Instant value, StringBuilder builder) + { + return InstantPattern.General.AppendFormat(value, builder); + } + } + + private readonly IRequestExecutor testExecutor; + + public InstantTypeDateTimeOffsetIntegrationTests() + { + testExecutor = SchemaBuilder.New() + .AddQueryType() + .AddMutationType() + .AddNodaTime(typeof(InstantType)) + .AddType( + new InstantType(InstantPattern.ExtendedIso, new InstantDateTimeOffsetPattern())) + .Create() + .MakeExecutable(); + } + + [Fact] + public void QueryReturnsUtc() + { + IExecutionResult? result = testExecutor.Execute("query { test: one }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-02-20T17:42:59.000001234Z", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: Instant!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-02-21T17:42:59.000001234Z") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-02-21T17:52:59.000001234Z", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesParseAnIncorrectExtendedVariableAsDateTimeOffset() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: Instant!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-02-20T17:42:59") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-02-20T17:52:59Z", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-02-20T17:42:59.000001234Z\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-02-20T17:52:59.000001234Z", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesParseIncorrectExtendedLiteralAsDateTimeOffset() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-02-20T17:42:59\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-02-20T17:52:59Z", queryResult!.Data!["test"]); + } + } +} diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/InstantTypeGeneralIntegrationTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/InstantTypeGeneralIntegrationTests.cs new file mode 100644 index 00000000000..a99b09ac1e7 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/InstantTypeGeneralIntegrationTests.cs @@ -0,0 +1,83 @@ +using System.Linq; +using HotChocolate.Execution; +using NodaTime.Text; +using Xunit; + +namespace HotChocolate.Types.NodaTime.Tests +{ + public class InstantTypeGeneralIntegrationTests + { + private readonly IRequestExecutor testExecutor; + + public InstantTypeGeneralIntegrationTests() + { + testExecutor = SchemaBuilder.New() + .AddQueryType() + .AddMutationType() + .AddNodaTime(typeof(InstantType)) + .AddType(new InstantType(InstantPattern.General)) + .Create() + .MakeExecutable(); + } + + [Fact] + public void QueryReturnsUtc() + { + IExecutionResult? result = testExecutor.Execute("query { test: one }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-02-20T17:42:59Z", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: Instant!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-02-21T17:42:59Z") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-02-21T17:52:59Z", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseAnIncorrectVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: Instant!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-02-20T17:42:59") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + + [Fact] + public void ParsesLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-02-20T17:42:59Z\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-02-20T17:52:59Z", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseIncorrectLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-02-20T17:42:59\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + Assert.Null(queryResult.Errors.First().Code); + Assert.Equal( + "Unable to deserialize string to Instant", + queryResult.Errors.First().Message); + } + } +} diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/InstantTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/InstantTypeTests.cs index f64c56a9b55..1af9eba67da 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/InstantTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/InstantTypeTests.cs @@ -1,7 +1,8 @@ +using System; using System.Linq; using HotChocolate.Execution; -using Microsoft.Extensions.DependencyInjection; using NodaTime; +using NodaTime.Text; using Xunit; namespace HotChocolate.Types.NodaTime.Tests @@ -12,7 +13,8 @@ public static class Schema { public class Query { - public Instant One => Instant.FromUtc(2020, 02, 20, 17, 42, 59); + public Instant One => + Instant.FromUtc(2020, 02, 20, 17, 42, 59).PlusNanoseconds(1234); } public class Mutation @@ -25,6 +27,7 @@ public Instant Test(Instant arg) } private readonly IRequestExecutor testExecutor; + public InstantTypeIntegrationTests() { testExecutor = SchemaBuilder.New() @@ -40,7 +43,7 @@ public void QueryReturnsUtc() { IExecutionResult? result = testExecutor.Execute("query { test: one }"); var queryResult = result as IReadOnlyQueryResult; - Assert.Equal("2020-02-20T17:42:59Z", queryResult!.Data!["test"]); + Assert.Equal("2020-02-20T17:42:59.000001234Z", queryResult!.Data!["test"]); } [Fact] @@ -49,10 +52,10 @@ public void ParsesVariable() IExecutionResult? result = testExecutor .Execute(QueryRequestBuilder.New() .SetQuery("mutation($arg: Instant!) { test(arg: $arg) }") - .SetVariableValue("arg", "2020-02-21T17:42:59Z") + .SetVariableValue("arg", "2020-02-21T17:42:59.000001234Z") .Create()); var queryResult = result as IReadOnlyQueryResult; - Assert.Equal("2020-02-21T17:52:59Z", queryResult!.Data!["test"]); + Assert.Equal("2020-02-21T17:52:59.000001234Z", queryResult!.Data!["test"]); } [Fact] @@ -73,10 +76,10 @@ public void ParsesLiteral() { IExecutionResult? result = testExecutor .Execute(QueryRequestBuilder.New() - .SetQuery("mutation { test(arg: \"2020-02-20T17:42:59Z\") }") + .SetQuery("mutation { test(arg: \"2020-02-20T17:42:59.000001234Z\") }") .Create()); var queryResult = result as IReadOnlyQueryResult; - Assert.Equal("2020-02-20T17:52:59Z", queryResult!.Data!["test"]); + Assert.Equal("2020-02-20T17:52:59.000001234Z", queryResult!.Data!["test"]); } [Fact] @@ -90,7 +93,16 @@ public void DoesntParseIncorrectLiteral() Assert.Null(queryResult!.Data); Assert.Equal(1, queryResult!.Errors!.Count); Assert.Null(queryResult.Errors.First().Code); - Assert.Equal("Unable to deserialize string to Instant", queryResult.Errors.First().Message); + Assert.Equal( + "Unable to deserialize string to Instant", + queryResult.Errors.First().Message); + } + + [Fact] + public void PatternEmpty_ThrowSchemaException() + { + static object Call() => new InstantType(Array.Empty>()); + Assert.Throws(Call); } } } diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/IsoDayOfWeekTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/IsoDayOfWeekTypeTests.cs index f81f80db203..53a09604f8b 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/IsoDayOfWeekTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/IsoDayOfWeekTypeTests.cs @@ -30,6 +30,7 @@ public IsoDayOfWeek Test(IsoDayOfWeek arg) } private readonly IRequestExecutor testExecutor; + public IsoDayOfWeekTypeIntegrationTests() { testExecutor = SchemaBuilder.New() diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTimeTypeFullRoundtripIntegrationTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTimeTypeFullRoundtripIntegrationTests.cs new file mode 100644 index 00000000000..148f2f1e410 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTimeTypeFullRoundtripIntegrationTests.cs @@ -0,0 +1,83 @@ +using System.Linq; +using HotChocolate.Execution; +using NodaTime.Text; +using Xunit; + +namespace HotChocolate.Types.NodaTime.Tests +{ + public class LocalDateTimeTypeFullRoundtripIntegrationTests + { + private readonly IRequestExecutor testExecutor; + + public LocalDateTimeTypeFullRoundtripIntegrationTests() + { + testExecutor = SchemaBuilder.New() + .AddQueryType() + .AddMutationType() + .AddNodaTime(typeof(LocalDateTimeType)) + .AddType(new LocalDateTimeType(LocalDateTimePattern.FullRoundtrip)) + .Create() + .MakeExecutable(); + } + + [Fact] + public void QueryReturns() + { + IExecutionResult? result = testExecutor.Execute("query { test: one }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-02-07T17:42:59.000001234 (Julian)", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: LocalDateTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-02-21T17:42:59.000001234 (Julian)") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-02-21T17:52:59.000001234 (Julian)", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseAnIncorrectVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: LocalDateTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-02-20T17:42:59Z") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + + [Fact] + public void ParsesLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-02-20T17:42:59.000001234 (Julian)\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-02-20T17:52:59.000001234 (Julian)", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseIncorrectLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-02-20T17:42:59Z\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + Assert.Null(queryResult.Errors[0].Code); + Assert.Equal( + "Unable to deserialize string to LocalDateTime", + queryResult.Errors[0].Message); + } + } +} diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTimeTypeGeneralIsoIntegrationTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTimeTypeGeneralIsoIntegrationTests.cs new file mode 100644 index 00000000000..fd9aaa99ec6 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTimeTypeGeneralIsoIntegrationTests.cs @@ -0,0 +1,83 @@ +using System.Linq; +using HotChocolate.Execution; +using NodaTime.Text; +using Xunit; + +namespace HotChocolate.Types.NodaTime.Tests +{ + public class LocalDateTimeTypeGeneralIsoIntegrationTests + { + private readonly IRequestExecutor testExecutor; + + public LocalDateTimeTypeGeneralIsoIntegrationTests() + { + testExecutor = SchemaBuilder.New() + .AddQueryType() + .AddMutationType() + .AddNodaTime(typeof(LocalDateTimeType)) + .AddType(new LocalDateTimeType(LocalDateTimePattern.GeneralIso)) + .Create() + .MakeExecutable(); + } + + [Fact] + public void QueryReturns() + { + IExecutionResult? result = testExecutor.Execute("query { test: one }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-02-07T17:42:59", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: LocalDateTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-02-21T17:42:59") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-02-21T17:52:59", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseAnIncorrectVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: LocalDateTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-02-20T17:42:59Z") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + + [Fact] + public void ParsesLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-02-20T17:42:59\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-02-20T17:52:59", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseIncorrectLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-02-20T17:42:59Z\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + Assert.Null(queryResult.Errors[0].Code); + Assert.Equal( + "Unable to deserialize string to LocalDateTime", + queryResult.Errors[0].Message); + } + } +} diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTimeTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTimeTypeTests.cs index b128ad4e8ce..90b0a5d1310 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTimeTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTimeTypeTests.cs @@ -3,6 +3,7 @@ using HotChocolate.Execution; using Microsoft.Extensions.DependencyInjection; using NodaTime; +using NodaTime.Text; using Xunit; namespace HotChocolate.Types.NodaTime.Tests @@ -13,7 +14,11 @@ public static class Schema { public class Query { - public LocalDateTime One => LocalDateTime.FromDateTime(new DateTime(2020, 02, 20, 17, 42, 59)); + public LocalDateTime One => + LocalDateTime.FromDateTime( + new DateTime(2020, 02, 20, 17, 42, 59)) + .PlusNanoseconds(1234) + .WithCalendar(CalendarSystem.Julian); } public class Mutation @@ -26,6 +31,7 @@ public LocalDateTime Test(LocalDateTime arg) } private readonly IRequestExecutor testExecutor; + public LocalDateTimeTypeIntegrationTests() { testExecutor = SchemaBuilder.New() @@ -41,7 +47,7 @@ public void QueryReturns() { IExecutionResult? result = testExecutor.Execute("query { test: one }"); var queryResult = result as IReadOnlyQueryResult; - Assert.Equal("2020-02-20T17:42:59", queryResult!.Data!["test"]); + Assert.Equal("2020-02-07T17:42:59.000001234", queryResult!.Data!["test"]); } [Fact] @@ -50,10 +56,10 @@ public void ParsesVariable() IExecutionResult? result = testExecutor .Execute(QueryRequestBuilder.New() .SetQuery("mutation($arg: LocalDateTime!) { test(arg: $arg) }") - .SetVariableValue("arg", "2020-02-21T17:42:59") + .SetVariableValue("arg", "2020-02-21T17:42:59.000001234") .Create()); var queryResult = result as IReadOnlyQueryResult; - Assert.Equal("2020-02-21T17:52:59", queryResult!.Data!["test"]); + Assert.Equal("2020-02-21T17:52:59.000001234", queryResult!.Data!["test"]); } [Fact] @@ -62,7 +68,7 @@ public void DoesntParseAnIncorrectVariable() IExecutionResult? result = testExecutor .Execute(QueryRequestBuilder.New() .SetQuery("mutation($arg: LocalDateTime!) { test(arg: $arg) }") - .SetVariableValue("arg", "2020-02-20T17:42:59Z") + .SetVariableValue("arg", "2020-02-20T17:42:59.000001234Z") .Create()); var queryResult = result as IReadOnlyQueryResult; Assert.Null(queryResult!.Data); @@ -74,10 +80,10 @@ public void ParsesLiteral() { IExecutionResult? result = testExecutor .Execute(QueryRequestBuilder.New() - .SetQuery("mutation { test(arg: \"2020-02-20T17:42:59\") }") + .SetQuery("mutation { test(arg: \"2020-02-20T17:42:59.000001234\") }") .Create()); var queryResult = result as IReadOnlyQueryResult; - Assert.Equal("2020-02-20T17:52:59", queryResult!.Data!["test"]); + Assert.Equal("2020-02-20T17:52:59.000001234", queryResult!.Data!["test"]); } [Fact] @@ -85,13 +91,22 @@ public void DoesntParseIncorrectLiteral() { IExecutionResult? result = testExecutor .Execute(QueryRequestBuilder.New() - .SetQuery("mutation { test(arg: \"2020-02-20T17:42:59Z\") }") + .SetQuery("mutation { test(arg: \"2020-02-20T17:42:59.000001234Z\") }") .Create()); var queryResult = result as IReadOnlyQueryResult; Assert.Null(queryResult!.Data); Assert.Equal(1, queryResult!.Errors!.Count); - Assert.Null(queryResult.Errors.First().Code); - Assert.Equal("Unable to deserialize string to LocalDateTime", queryResult.Errors.First().Message); + Assert.Null(queryResult.Errors[0].Code); + Assert.Equal( + "Unable to deserialize string to LocalDateTime", + queryResult.Errors[0].Message); + } + + [Fact] + public void PatternEmpty_ThrowSchemaException() + { + static object Call() => new LocalDateTimeType(Array.Empty>()); + Assert.Throws(Call); } } } diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTypeFullRoundtripIntegrationTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTypeFullRoundtripIntegrationTests.cs new file mode 100644 index 00000000000..8b93f9c9e2b --- /dev/null +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTypeFullRoundtripIntegrationTests.cs @@ -0,0 +1,83 @@ +using System.Linq; +using HotChocolate.Execution; +using NodaTime.Text; +using Xunit; + +namespace HotChocolate.Types.NodaTime.Tests +{ + public class LocalDateTypeFullRoundtripIntegrationTests + { + private readonly IRequestExecutor testExecutor; + + public LocalDateTypeFullRoundtripIntegrationTests() + { + testExecutor = SchemaBuilder.New() + .AddQueryType() + .AddMutationType() + .AddNodaTime(typeof(LocalDateType)) + .AddType(new LocalDateType(LocalDatePattern.FullRoundtrip)) + .Create() + .MakeExecutable(); + } + + [Fact] + public void QueryReturns() + { + IExecutionResult? result = testExecutor.Execute("query { test: one }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("5780-05-25 (Hebrew Civil)", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: LocalDate!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-02-21 (Hebrew Civil)") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-02-24 (Hebrew Civil)", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseAnIncorrectVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: LocalDate!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-02-20T17:42:59 (Hebrew Civil)") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + + [Fact] + public void ParsesLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-02-20 (Hebrew Civil)\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-02-23 (Hebrew Civil)", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseIncorrectLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-02-20T17:42:59 (Hebrew Civil)\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + Assert.Null(queryResult.Errors[0].Code); + Assert.Equal( + "Unable to deserialize string to LocalDate", + queryResult.Errors[0].Message); + } + } +} diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTypeTests.cs index 5671a5706d5..5097d5463ac 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTypeTests.cs @@ -3,6 +3,7 @@ using HotChocolate.Execution; using Microsoft.Extensions.DependencyInjection; using NodaTime; +using NodaTime.Text; using Xunit; namespace HotChocolate.Types.NodaTime.Tests @@ -13,7 +14,9 @@ public static class Schema { public class Query { - public LocalDate One => LocalDate.FromDateTime(new DateTime(2020, 02, 20, 17, 42, 59)); + public LocalDate One => LocalDate.FromDateTime( + new DateTime(2020, 02, 20, 17, 42, 59)) + .WithCalendar(CalendarSystem.HebrewCivil); } public class Mutation @@ -26,6 +29,7 @@ public LocalDate Test(LocalDate arg) } private readonly IRequestExecutor testExecutor; + public LocalDateTypeIntegrationTests() { testExecutor = SchemaBuilder.New() @@ -41,7 +45,7 @@ public void QueryReturns() { IExecutionResult? result = testExecutor.Execute("query { test: one }"); var queryResult = result as IReadOnlyQueryResult; - Assert.Equal("2020-02-20", queryResult!.Data!["test"]); + Assert.Equal("5780-05-25", queryResult!.Data!["test"]); } [Fact] @@ -90,8 +94,17 @@ public void DoesntParseIncorrectLiteral() var queryResult = result as IReadOnlyQueryResult; Assert.Null(queryResult!.Data); Assert.Equal(1, queryResult!.Errors!.Count); - Assert.Null(queryResult.Errors.First().Code); - Assert.Equal("Unable to deserialize string to LocalDate", queryResult.Errors.First().Message); + Assert.Null(queryResult.Errors[0].Code); + Assert.Equal( + "Unable to deserialize string to LocalDate", + queryResult.Errors[0].Message); + } + + [Fact] + public void PatternEmpty_ThrowSchemaException() + { + static object Call() => new LocalDateType(Array.Empty>()); + Assert.Throws(Call); } } } diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalTimeTypeGeneralIsoIntegrationTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalTimeTypeGeneralIsoIntegrationTests.cs new file mode 100644 index 00000000000..979df328e97 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalTimeTypeGeneralIsoIntegrationTests.cs @@ -0,0 +1,82 @@ +using System.Linq; +using HotChocolate.Execution; +using NodaTime.Text; +using Xunit; + +namespace HotChocolate.Types.NodaTime.Tests +{ + public class LocalTimeTypeGeneralIsoIntegrationTests + { + private readonly IRequestExecutor testExecutor; + public LocalTimeTypeGeneralIsoIntegrationTests() + { + testExecutor = SchemaBuilder.New() + .AddQueryType() + .AddMutationType() + .AddNodaTime(typeof(LocalTimeType)) + .AddType(new LocalTimeType(LocalTimePattern.GeneralIso)) + .Create() + .MakeExecutable(); + } + + [Fact] + public void QueryReturns() + { + IExecutionResult? result = testExecutor.Execute("query { test: one }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("12:42:13", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: LocalTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "12:42:13") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("12:52:13", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseAnIncorrectVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: LocalTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "12:42") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + + [Fact] + public void ParsesLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"12:42:13\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("12:52:13", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseIncorrectLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"12:42\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + Assert.Null(queryResult.Errors[0].Code); + Assert.Equal( + "Unable to deserialize string to LocalTime", + queryResult.Errors[0].Message); + } + } +} diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalTimeTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalTimeTypeTests.cs index 74fca6eb1f9..3c10164d725 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalTimeTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalTimeTypeTests.cs @@ -1,7 +1,9 @@ +using System; using System.Linq; using HotChocolate.Execution; using Microsoft.Extensions.DependencyInjection; using NodaTime; +using NodaTime.Text; using Xunit; namespace HotChocolate.Types.NodaTime.Tests @@ -12,7 +14,9 @@ public static class Schema { public class Query { - public LocalTime One => LocalTime.FromHourMinuteSecondMillisecondTick(12, 42, 13, 31, 100); + public LocalTime One => LocalTime + .FromHourMinuteSecondMillisecondTick(12, 42, 13, 31, 100) + .PlusNanoseconds(1234); } public class Mutation @@ -25,6 +29,7 @@ public LocalTime Test(LocalTime arg) } private readonly IRequestExecutor testExecutor; + public LocalTimeTypeIntegrationTests() { testExecutor = SchemaBuilder.New() @@ -40,7 +45,7 @@ public void QueryReturns() { IExecutionResult? result = testExecutor.Execute("query { test: one }"); var queryResult = result as IReadOnlyQueryResult; - Assert.Equal("12:42:13.03101", queryResult!.Data!["test"]); + Assert.Equal("12:42:13.031011234", queryResult!.Data!["test"]); } [Fact] @@ -49,10 +54,10 @@ public void ParsesVariable() IExecutionResult? result = testExecutor .Execute(QueryRequestBuilder.New() .SetQuery("mutation($arg: LocalTime!) { test(arg: $arg) }") - .SetVariableValue("arg", "12:42:13.03101") + .SetVariableValue("arg", "12:42:13.031011234") .Create()); var queryResult = result as IReadOnlyQueryResult; - Assert.Equal("12:52:13.03101", queryResult!.Data!["test"]); + Assert.Equal("12:52:13.031011234", queryResult!.Data!["test"]); } [Fact] @@ -85,10 +90,10 @@ public void ParsesLiteral() { IExecutionResult? result = testExecutor .Execute(QueryRequestBuilder.New() - .SetQuery("mutation { test(arg: \"12:42:13.03101\") }") + .SetQuery("mutation { test(arg: \"12:42:13.031011234\") }") .Create()); var queryResult = result as IReadOnlyQueryResult; - Assert.Equal("12:52:13.03101", queryResult!.Data!["test"]); + Assert.Equal("12:52:13.031011234", queryResult!.Data!["test"]); } [Fact] @@ -112,8 +117,17 @@ public void DoesntParseIncorrectLiteral() var queryResult = result as IReadOnlyQueryResult; Assert.Null(queryResult!.Data); Assert.Equal(1, queryResult!.Errors!.Count); - Assert.Null(queryResult.Errors.First().Code); - Assert.Equal("Unable to deserialize string to LocalTime", queryResult.Errors.First().Message); + Assert.Null(queryResult.Errors[0].Code); + Assert.Equal( + "Unable to deserialize string to LocalTime", + queryResult.Errors[0].Message); + } + + [Fact] + public void PatternEmpty_ThrowSchemaException() + { + static object Call() => new LocalTimeType(Array.Empty>()); + Assert.Throws(Call); } } } diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/NodaTimeRequestExecutorBuilderExtensions.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/NodaTimeRequestExecutorBuilderExtensions.cs index 46eb18a82fb..84cff437e2c 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/NodaTimeRequestExecutorBuilderExtensions.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/NodaTimeRequestExecutorBuilderExtensions.cs @@ -1,23 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; + namespace HotChocolate.Types.NodaTime.Tests { public static class NodaTimeRequestExecutorBuilderExtensions { - public static ISchemaBuilder AddNodaTime(this ISchemaBuilder schemaBuilder) + public static ISchemaBuilder AddNodaTime( + this ISchemaBuilder schemaBuilder, + params Type[] excludeTypes) { - return schemaBuilder - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType(); + foreach (var type in nodaTimeTypes.Except(excludeTypes)) + { + schemaBuilder = schemaBuilder.AddType(type); + } + + return schemaBuilder; } + + private static readonly IReadOnlyList nodaTimeTypes = new[] + { + typeof(DateTimeZoneType), typeof(DurationType), typeof(InstantType), + typeof(IsoDayOfWeekType), typeof(LocalDateTimeType), typeof(LocalDateType), + typeof(LocalTimeType), typeof(OffsetDateTimeType), typeof(OffsetDateType), + typeof(OffsetTimeType), typeof(OffsetType), typeof(PeriodType), + typeof(ZonedDateTimeType), + }; } } diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTimeTypeExtendedIntegrationTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTimeTypeExtendedIntegrationTests.cs new file mode 100644 index 00000000000..4919853a168 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTimeTypeExtendedIntegrationTests.cs @@ -0,0 +1,114 @@ +using System.Linq; +using HotChocolate.Execution; +using NodaTime.Text; +using Xunit; + +namespace HotChocolate.Types.NodaTime.Tests +{ + public class OffsetDateTimeTypeExtendedIntegrationTests + { + private readonly IRequestExecutor testExecutor; + + public OffsetDateTimeTypeExtendedIntegrationTests() + { + testExecutor = SchemaBuilder.New() + .AddQueryType() + .AddMutationType() + .AddNodaTime(typeof(OffsetDateTimeType)) + .AddType(new OffsetDateTimeType(OffsetDateTimePattern.ExtendedIso)) + .Create() + .MakeExecutable(); + } + + [Fact] + public void QueryReturns() + { + IExecutionResult? result = testExecutor.Execute("query { test: hours }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T18:30:13.000001234+02", queryResult!.Data!["test"]); + } + + [Fact] + public void QueryReturnsWithMinutes() + { + IExecutionResult? result = testExecutor.Execute("query { test: hoursAndMinutes }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T18:30:13.000001234+02:30", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: OffsetDateTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-12-31T18:30:13+02") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T18:40:13+02", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariableWithMinutes() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: OffsetDateTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-12-31T18:30:13+02:35") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T18:40:13+02:35", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseAnIncorrectVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: OffsetDateTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-12-31T18:30:13") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + + [Fact] + public void ParsesLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-12-31T18:30:13+02\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T18:40:13+02", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesLiteralWithMinutes() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-12-31T18:30:13+02:35\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T18:40:13+02:35", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseIncorrectLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-12-31T18:30:13\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + Assert.Null(queryResult.Errors[0].Code); + Assert.Equal( + "Unable to deserialize string to OffsetDateTime", + queryResult.Errors[0].Message); + } + } +} diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTimeTypeGeneralIntegrationTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTimeTypeGeneralIntegrationTests.cs new file mode 100644 index 00000000000..f80a55a87aa --- /dev/null +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTimeTypeGeneralIntegrationTests.cs @@ -0,0 +1,114 @@ +using System.Linq; +using HotChocolate.Execution; +using NodaTime.Text; +using Xunit; + +namespace HotChocolate.Types.NodaTime.Tests +{ + public class OffsetDateTimeTypeGeneralIntegrationTests + { + private readonly IRequestExecutor testExecutor; + + public OffsetDateTimeTypeGeneralIntegrationTests() + { + testExecutor = SchemaBuilder.New() + .AddQueryType() + .AddMutationType() + .AddNodaTime(typeof(OffsetDateTimeType)) + .AddType(new OffsetDateTimeType(OffsetDateTimePattern.GeneralIso)) + .Create() + .MakeExecutable(); + } + + [Fact] + public void QueryReturns() + { + IExecutionResult? result = testExecutor.Execute("query { test: hours }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T18:30:13+02", queryResult!.Data!["test"]); + } + + [Fact] + public void QueryReturnsWithMinutes() + { + IExecutionResult? result = testExecutor.Execute("query { test: hoursAndMinutes }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T18:30:13+02:30", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: OffsetDateTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-12-31T18:30:13+02") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T18:40:13+02", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariableWithMinutes() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: OffsetDateTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-12-31T18:30:13+02:35") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T18:40:13+02:35", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseAnIncorrectVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: OffsetDateTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-12-31T18:30:13") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + + [Fact] + public void ParsesLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-12-31T18:30:13+02\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T18:40:13+02", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesLiteralWithMinutes() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-12-31T18:30:13+02:35\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T18:40:13+02:35", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseIncorrectLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-12-31T18:30:13\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + Assert.Null(queryResult.Errors[0].Code); + Assert.Equal( + "Unable to deserialize string to OffsetDateTime", + queryResult.Errors[0].Message); + } + } +} diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTimeTypeRfc3339IntegrationTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTimeTypeRfc3339IntegrationTests.cs new file mode 100644 index 00000000000..3ea6025e9e4 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTimeTypeRfc3339IntegrationTests.cs @@ -0,0 +1,114 @@ +using System.Linq; +using HotChocolate.Execution; +using NodaTime.Text; +using Xunit; + +namespace HotChocolate.Types.NodaTime.Tests +{ + public class OffsetDateTimeTypeRfc3339IntegrationTests + { + private readonly IRequestExecutor testExecutor; + + public OffsetDateTimeTypeRfc3339IntegrationTests() + { + testExecutor = SchemaBuilder.New() + .AddQueryType() + .AddMutationType() + .AddNodaTime(typeof(OffsetDateTimeType)) + .AddType(new OffsetDateTimeType(OffsetDateTimePattern.Rfc3339)) + .Create() + .MakeExecutable(); + } + + [Fact] + public void QueryReturns() + { + IExecutionResult? result = testExecutor.Execute("query { test: hours }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T18:30:13.000001234+02:00", queryResult!.Data!["test"]); + } + + [Fact] + public void QueryReturnsWithMinutes() + { + IExecutionResult? result = testExecutor.Execute("query { test: hoursAndMinutes }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T18:30:13.000001234+02:30", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: OffsetDateTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-12-31T18:30:13.000001234+02:00") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T18:40:13.000001234+02:00", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariableWithMinutes() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: OffsetDateTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-12-31T18:30:13+02:35") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T18:40:13+02:35", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseAnIncorrectVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: OffsetDateTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-12-31T18:30:13") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + + [Fact] + public void ParsesLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-12-31T18:30:13.000001234+02:00\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T18:40:13.000001234+02:00", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesLiteralWithMinutes() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-12-31T18:30:13+02:35\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T18:40:13+02:35", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseIncorrectLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-12-31T18:30:13\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + Assert.Null(queryResult.Errors[0].Code); + Assert.Equal( + "Unable to deserialize string to OffsetDateTime", + queryResult.Errors[0].Message); + } + } +} diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTimeTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTimeTypeTests.cs index 2ed669eeab5..29276bd1214 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTimeTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTimeTypeTests.cs @@ -3,6 +3,7 @@ using HotChocolate.Execution; using Microsoft.Extensions.DependencyInjection; using NodaTime; +using NodaTime.Text; using Xunit; namespace HotChocolate.Types.NodaTime.Tests @@ -13,13 +14,22 @@ public static class Schema { public class Query { - public OffsetDateTime Hours => - OffsetDateTime.FromDateTimeOffset( - new DateTimeOffset(2020, 12, 31, 18, 30, 13, TimeSpan.FromHours(2))); + public OffsetDateTime Hours => OffsetDateTime + .FromDateTimeOffset( + new DateTimeOffset(2020, 12, 31, 18, 30, 13, TimeSpan.FromHours(2))) + .PlusNanoseconds(1234); - public OffsetDateTime HoursAndMinutes => - OffsetDateTime.FromDateTimeOffset( - new DateTimeOffset(2020, 12, 31, 18, 30, 13, TimeSpan.FromHours(2) + TimeSpan.FromMinutes(30))); + public OffsetDateTime HoursAndMinutes => OffsetDateTime + .FromDateTimeOffset( + new DateTimeOffset( + 2020, + 12, + 31, + 18, + 30, + 13, + TimeSpan.FromHours(2) + TimeSpan.FromMinutes(30))) + .PlusNanoseconds(1234); } public class Mutation @@ -32,6 +42,7 @@ public OffsetDateTime Test(OffsetDateTime arg) } private readonly IRequestExecutor testExecutor; + public OffsetDateTimeTypeIntegrationTests() { testExecutor = SchemaBuilder.New() @@ -130,5 +141,12 @@ public void DoesntParseIncorrectLiteral() Assert.Null(queryResult.Errors.First().Code); Assert.Equal("Unable to deserialize string to OffsetDateTime", queryResult.Errors.First().Message); } + + [Fact] + public void PatternEmpty_ThrowSchemaException() + { + static object Call() => new OffsetDateTimeType(Array.Empty>()); + Assert.Throws(Call); + } } } diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTypeFullRoundtripIntegrationTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTypeFullRoundtripIntegrationTests.cs new file mode 100644 index 00000000000..af41693410d --- /dev/null +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTypeFullRoundtripIntegrationTests.cs @@ -0,0 +1,114 @@ +using System.Linq; +using HotChocolate.Execution; +using NodaTime.Text; +using Xunit; + +namespace HotChocolate.Types.NodaTime.Tests +{ + public class OffsetDateTypeFullRoundtripIntegrationTests + { + private readonly IRequestExecutor testExecutor; + + public OffsetDateTypeFullRoundtripIntegrationTests() + { + testExecutor = SchemaBuilder.New() + .AddQueryType() + .AddMutationType() + .AddNodaTime(typeof(OffsetDateType)) + .AddType(new OffsetDateType(OffsetDatePattern.FullRoundtrip)) + .Create() + .MakeExecutable(); + } + + [Fact] + public void QueryReturns() + { + IExecutionResult? result = testExecutor.Execute("query { test: hours }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31+02 (Gregorian)", queryResult!.Data!["test"]); + } + + [Fact] + public void QueryReturnsWithMinutes() + { + IExecutionResult? result = testExecutor.Execute("query { test: hoursAndMinutes }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31+02:35 (Gregorian)", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: OffsetDate!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-12-31+02 (Gregorian)") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31+02 (Gregorian)", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariableWithMinutes() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: OffsetDate!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-12-31+02:35 (Gregorian)") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31+02:35 (Gregorian)", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseAnIncorrectVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: OffsetDate!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-12-31 (Gregorian)") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + + [Fact] + public void ParsesLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-12-31+02 (Gregorian)\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31+02 (Gregorian)", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesLiteralWithMinutes() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-12-31+02:35 (Gregorian)\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31+02:35 (Gregorian)", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseIncorrectLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-12-31 (Gregorian)\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + Assert.Null(queryResult.Errors[0].Code); + Assert.Equal( + "Unable to deserialize string to OffsetDate", + queryResult.Errors[0].Message); + } + } +} diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTypeTests.cs index 1789f6c91e6..00a13e4956d 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTypeTests.cs @@ -3,6 +3,7 @@ using HotChocolate.Execution; using Microsoft.Extensions.DependencyInjection; using NodaTime; +using NodaTime.Text; using Xunit; namespace HotChocolate.Types.NodaTime.Tests @@ -16,12 +17,12 @@ public class Query public OffsetDate Hours => new OffsetDate( LocalDate.FromDateTime(new DateTime(2020, 12, 31, 18, 30, 13)), - Offset.FromHours(2)); + Offset.FromHours(2)).WithCalendar(CalendarSystem.Gregorian); public OffsetDate HoursAndMinutes => new OffsetDate( LocalDate.FromDateTime(new DateTime(2020, 12, 31, 18, 30, 13)), - Offset.FromHoursAndMinutes(2, 35)); + Offset.FromHoursAndMinutes(2, 35)).WithCalendar(CalendarSystem.Gregorian); } public class Mutation @@ -31,6 +32,7 @@ public class Mutation } private readonly IRequestExecutor testExecutor; + public OffsetDateTypeIntegrationTests() { testExecutor = SchemaBuilder.New() @@ -126,8 +128,17 @@ public void DoesntParseIncorrectLiteral() var queryResult = result as IReadOnlyQueryResult; Assert.Null(queryResult!.Data); Assert.Equal(1, queryResult!.Errors!.Count); - Assert.Null(queryResult.Errors.First().Code); - Assert.Equal("Unable to deserialize string to OffsetDate", queryResult.Errors.First().Message); + Assert.Null(queryResult.Errors[0].Code); + Assert.Equal( + "Unable to deserialize string to OffsetDate", + queryResult.Errors[0].Message); + } + + [Fact] + public void PatternEmpty_ThrowSchemaException() + { + static object Call() => new OffsetDateType(Array.Empty>()); + Assert.Throws(Call); } } } diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTimeTypeExtendedIntegrationTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTimeTypeExtendedIntegrationTests.cs new file mode 100644 index 00000000000..1e077206545 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTimeTypeExtendedIntegrationTests.cs @@ -0,0 +1,114 @@ +using System.Linq; +using HotChocolate.Execution; +using NodaTime.Text; +using Xunit; + +namespace HotChocolate.Types.NodaTime.Tests +{ + public class OffsetTimeTypeExtendedIntegrationTests + { + private readonly IRequestExecutor testExecutor; + + public OffsetTimeTypeExtendedIntegrationTests() + { + testExecutor = SchemaBuilder.New() + .AddQueryType() + .AddMutationType() + .AddNodaTime(typeof(OffsetTimeType)) + .AddType(new OffsetTimeType(OffsetTimePattern.ExtendedIso)) + .Create() + .MakeExecutable(); + } + + [Fact] + public void QueryReturns() + { + IExecutionResult? result = testExecutor.Execute("query { test: hours }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("18:30:13.010011234+02", queryResult!.Data!["test"]); + } + + [Fact] + public void QueryReturnsWithMinutes() + { + IExecutionResult? result = testExecutor.Execute("query { test: hoursAndMinutes }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("18:30:13.010011234+02:35", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: OffsetTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "18:30:13.010011234+02") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("18:30:13.010011234+02", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariableWithMinutes() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: OffsetTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "18:30:13.010011234+02:35") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("18:30:13.010011234+02:35", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseAnIncorrectVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: OffsetTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "18:30:13.010011234") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + + [Fact] + public void ParsesLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"18:30:13.010011234+02\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("18:30:13.010011234+02", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesLiteralWithMinutes() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"18:30:13.010011234+02:35\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("18:30:13.010011234+02:35", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseIncorrectLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"18:30:13.010011234\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + Assert.Null(queryResult.Errors[0].Code); + Assert.Equal( + "Unable to deserialize string to OffsetTime", + queryResult.Errors[0].Message); + } + } +} diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTimeTypeRfc3339IntegrationTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTimeTypeRfc3339IntegrationTests.cs new file mode 100644 index 00000000000..4055ced6493 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTimeTypeRfc3339IntegrationTests.cs @@ -0,0 +1,114 @@ +using System.Linq; +using HotChocolate.Execution; +using NodaTime.Text; +using Xunit; + +namespace HotChocolate.Types.NodaTime.Tests +{ + public class OffsetTimeTypeRfc3339IntegrationTests + { + private readonly IRequestExecutor testExecutor; + + public OffsetTimeTypeRfc3339IntegrationTests() + { + testExecutor = SchemaBuilder.New() + .AddQueryType() + .AddMutationType() + .AddNodaTime(typeof(OffsetTimeType)) + .AddType(new OffsetTimeType(OffsetTimePattern.Rfc3339)) + .Create() + .MakeExecutable(); + } + + [Fact] + public void QueryReturns() + { + IExecutionResult? result = testExecutor.Execute("query { test: hours }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("18:30:13.010011234+02:00", queryResult!.Data!["test"]); + } + + [Fact] + public void QueryReturnsWithMinutes() + { + IExecutionResult? result = testExecutor.Execute("query { test: hoursAndMinutes }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("18:30:13.010011234+02:35", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: OffsetTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "18:30:13.010011234+02:00") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("18:30:13.010011234+02:00", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariableWithMinutes() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: OffsetTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "18:30:13.010011234+02:35") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("18:30:13.010011234+02:35", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseAnIncorrectVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: OffsetTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "18:30:13.010011234+02") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + + [Fact] + public void ParsesLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"18:30:13.010011234+02:00\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("18:30:13.010011234+02:00", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesLiteralWithMinutes() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"18:30:13.010011234+02:35\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("18:30:13.010011234+02:35", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseIncorrectLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"18:30:13.010011234+02\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + Assert.Null(queryResult.Errors[0].Code); + Assert.Equal( + "Unable to deserialize string to OffsetTime", + queryResult.Errors[0].Message); + } + } +} diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTimeTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTimeTypeTests.cs index 0a617f38a18..62470ed79b7 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTimeTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTimeTypeTests.cs @@ -1,7 +1,9 @@ +using System; using System.Linq; using HotChocolate.Execution; using Microsoft.Extensions.DependencyInjection; using NodaTime; +using NodaTime.Text; using Xunit; namespace HotChocolate.Types.NodaTime.Tests @@ -14,12 +16,16 @@ public class Query { public OffsetTime Hours => new OffsetTime( - LocalTime.FromHourMinuteSecondMillisecondTick(18, 30, 13, 10, 100), + LocalTime + .FromHourMinuteSecondMillisecondTick(18, 30, 13, 10, 100) + .PlusNanoseconds(1234), Offset.FromHours(2)); public OffsetTime HoursAndMinutes => new OffsetTime( - LocalTime.FromHourMinuteSecondMillisecondTick(18, 30, 13, 10, 100), + LocalTime + .FromHourMinuteSecondMillisecondTick(18, 30, 13, 10, 100) + .PlusNanoseconds(1234), Offset.FromHoursAndMinutes(2, 35)); } @@ -30,6 +36,7 @@ public class Mutation } private readonly IRequestExecutor testExecutor; + public OffsetTimeTypeIntegrationTests() { testExecutor = SchemaBuilder.New() @@ -125,8 +132,17 @@ public void DoesntParseIncorrectLiteral() var queryResult = result as IReadOnlyQueryResult; Assert.Null(queryResult!.Data); Assert.Equal(1, queryResult!.Errors!.Count); - Assert.Null(queryResult.Errors.First().Code); - Assert.Equal("Unable to deserialize string to OffsetTime", queryResult.Errors.First().Message); + Assert.Null(queryResult.Errors[0].Code); + Assert.Equal( + "Unable to deserialize string to OffsetTime", + queryResult.Errors[0].Message); + } + + [Fact] + public void PatternEmpty_ThrowSchemaException() + { + static object Call() => new OffsetTimeType(Array.Empty>()); + Assert.Throws(Call); } } } diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTypeGeneralInvariantWithoutZIntegrationTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTypeGeneralInvariantWithoutZIntegrationTests.cs new file mode 100644 index 00000000000..8de7cda1579 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTypeGeneralInvariantWithoutZIntegrationTests.cs @@ -0,0 +1,145 @@ +using System.Linq; +using HotChocolate.Execution; +using NodaTime.Text; +using Xunit; + +namespace HotChocolate.Types.NodaTime.Tests +{ + public class OffsetTypeGeneralInvariantWithoutZIntegrationTests + { + private readonly IRequestExecutor testExecutor; + + public OffsetTypeGeneralInvariantWithoutZIntegrationTests() + { + testExecutor = SchemaBuilder.New() + .AddQueryType() + .AddMutationType() + .AddNodaTime(typeof(OffsetType)) + .AddType(new OffsetType(OffsetPattern.GeneralInvariant)) + .Create() + .MakeExecutable(); + } + + [Fact] + public void QueryReturns() + { + IExecutionResult? result = testExecutor.Execute("query { test: hours }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("+02", queryResult!.Data!["test"]); + } + + [Fact] + public void QueryReturnsWithMinutes() + { + IExecutionResult? result = testExecutor.Execute("query { test: hoursAndMinutes }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("+02:35", queryResult!.Data!["test"]); + } + + [Fact] + public void QueryReturnsWithZ() + { + IExecutionResult? result = testExecutor.Execute("query { test: zOffset }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("+00", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: Offset!) { test(arg: $arg) }") + .SetVariableValue("arg", "+02") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("+03:05", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariableWithMinutes() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: Offset!) { test(arg: $arg) }") + .SetVariableValue("arg", "+02:35") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("+03:40", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseAnIncorrectVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: Offset!) { test(arg: $arg) }") + .SetVariableValue("arg", "18:30:13+02") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + + [Fact] + public void ParsesLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"+02\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("+03:05", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesLiteralWithMinutes() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"+02:35\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("+03:40", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesLiteralWithZero() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"+00\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("+01:05", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseLiteralWithZ() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"Z\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + Assert.Null(queryResult.Errors[0].Code); + Assert.Equal("Unable to deserialize string to Offset", queryResult.Errors[0].Message); + } + + [Fact] + public void DoesntParseIncorrectLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"18:30:13+02\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + Assert.Null(queryResult.Errors[0].Code); + Assert.Equal("Unable to deserialize string to Offset", queryResult.Errors[0].Message); + } + } +} diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTypeTests.cs index 7337936a45c..536a03fb559 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTypeTests.cs @@ -1,7 +1,8 @@ -using System.Linq; +using System; using HotChocolate.Execution; using Microsoft.Extensions.DependencyInjection; using NodaTime; +using NodaTime.Text; using Xunit; namespace HotChocolate.Types.NodaTime.Tests @@ -14,6 +15,7 @@ public class Query { public Offset Hours => Offset.FromHours(2); public Offset HoursAndMinutes => Offset.FromHoursAndMinutes(2, 35); + public Offset ZOffset => Offset.Zero; } public class Mutation @@ -24,6 +26,7 @@ public Offset Test(Offset arg) } private readonly IRequestExecutor testExecutor; + public OffsetTypeIntegrationTests() { testExecutor = SchemaBuilder.New() @@ -50,6 +53,14 @@ public void QueryReturnsWithMinutes() Assert.Equal("+02:35", queryResult!.Data!["test"]); } + [Fact] + public void QueryReturnsWithZ() + { + IExecutionResult? result = testExecutor.Execute("query { test: zOffset }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("Z", queryResult!.Data!["test"]); + } + [Fact] public void ParsesVariable() { @@ -109,6 +120,17 @@ public void ParsesLiteralWithMinutes() Assert.Equal("+03:40", queryResult!.Data!["test"]); } + [Fact] + public void ParsesLiteralWithZ() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"Z\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("+01:05", queryResult!.Data!["test"]); + } + [Fact] public void DoesntParseIncorrectLiteral() { @@ -119,8 +141,15 @@ public void DoesntParseIncorrectLiteral() var queryResult = result as IReadOnlyQueryResult; Assert.Null(queryResult!.Data); Assert.Equal(1, queryResult!.Errors!.Count); - Assert.Null(queryResult.Errors.First().Code); - Assert.Equal("Unable to deserialize string to Offset", queryResult.Errors.First().Message); + Assert.Null(queryResult.Errors[0].Code); + Assert.Equal("Unable to deserialize string to Offset", queryResult.Errors[0].Message); + } + + [Fact] + public void PatternEmpty_ThrowSchemaException() + { + static object Call() => new OffsetType(Array.Empty>()); + Assert.Throws(Call); } } } diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/PeriodTypeNormalizingIsoIntegrationTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/PeriodTypeNormalizingIsoIntegrationTests.cs new file mode 100644 index 00000000000..b167ba154d7 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/PeriodTypeNormalizingIsoIntegrationTests.cs @@ -0,0 +1,81 @@ +using System.Linq; +using HotChocolate.Execution; +using NodaTime.Text; +using Xunit; + +namespace HotChocolate.Types.NodaTime.Tests +{ + public class PeriodTypeNormalizingIsoIntegrationTests + { + private readonly IRequestExecutor testExecutor; + + public PeriodTypeNormalizingIsoIntegrationTests() + { + testExecutor = SchemaBuilder.New() + .AddQueryType() + .AddMutationType() + .AddNodaTime(typeof(PeriodType)) + .AddType(new PeriodType(PeriodPattern.NormalizingIso)) + .Create() + .MakeExecutable(); + } + + [Fact] + public void QueryReturns() + { + IExecutionResult? result = testExecutor.Execute("query { test: one }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("P-17DT-23H-59M-59.9999861S", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: Period!) { test(arg: $arg) }") + .SetVariableValue("arg", "P-17DT-23H-59M-59.9999861S") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("P-18DT-9M-59.9999861S", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseAnIncorrectVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: Period!) { test(arg: $arg) }") + .SetVariableValue("arg", "-P-17DT-23H-59M-59.9999861S") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + + [Fact] + public void ParsesLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"P-17DT-23H-59M-59.9999861S\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("P-18DT-9M-59.9999861S", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseIncorrectLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"-P-17DT-23H-59M-59.9999861S\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + Assert.Null(queryResult.Errors[0].Code); + Assert.Equal("Unable to deserialize string to Period", queryResult.Errors[0].Message); + } + } +} diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/PeriodTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/PeriodTypeTests.cs index a9e25bd8226..076db5c1347 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/PeriodTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/PeriodTypeTests.cs @@ -1,7 +1,9 @@ +using System; using System.Linq; using HotChocolate.Execution; using Microsoft.Extensions.DependencyInjection; using NodaTime; +using NodaTime.Text; using Xunit; namespace HotChocolate.Types.NodaTime.Tests @@ -12,7 +14,8 @@ public static class Schema { public class Query { - public Period One => Period.FromWeeks(-3) + Period.FromDays(3) + Period.FromTicks(139); + public Period One => + Period.FromWeeks(-3) + Period.FromDays(3) + Period.FromTicks(139); } public class Mutation @@ -23,6 +26,7 @@ public Period Test(Period arg) } private readonly IRequestExecutor testExecutor; + public PeriodTypeIntegrationTests() { testExecutor = SchemaBuilder.New() @@ -87,8 +91,15 @@ public void DoesntParseIncorrectLiteral() var queryResult = result as IReadOnlyQueryResult; Assert.Null(queryResult!.Data); Assert.Equal(1, queryResult!.Errors!.Count); - Assert.Null(queryResult.Errors.First().Code); - Assert.Equal("Unable to deserialize string to Period", queryResult.Errors.First().Message); + Assert.Null(queryResult.Errors[0].Code); + Assert.Equal("Unable to deserialize string to Period", queryResult.Errors[0].Message); + } + + [Fact] + public void PatternEmpty_ThrowSchemaException() + { + static object Call() => new PeriodType(Array.Empty>()); + Assert.Throws(Call); } } } diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/ZonedDateTimeTypeCustomIntegrationTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/ZonedDateTimeTypeCustomIntegrationTests.cs new file mode 100644 index 00000000000..6c2947363ce --- /dev/null +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/ZonedDateTimeTypeCustomIntegrationTests.cs @@ -0,0 +1,118 @@ +using System.Linq; +using HotChocolate.Execution; +using NodaTime; +using NodaTime.Text; +using Xunit; + +namespace HotChocolate.Types.NodaTime.Tests; + +public class ZonedDateTimeTypeCustomIntegrationTests +{ + private readonly IRequestExecutor testExecutor; + + public ZonedDateTimeTypeCustomIntegrationTests() + { + var pattern = ZonedDateTimePattern.CreateWithInvariantCulture( + "uuuu'-'MM'-'dd'T'HH':'mm':'ss' 'z' '(o)", + DateTimeZoneProviders.Tzdb); + + testExecutor = SchemaBuilder.New() + .AddQueryType() + .AddMutationType() + .AddNodaTime(typeof(ZonedDateTimeType)) + .AddType(new ZonedDateTimeType(pattern)) + .Create() + .MakeExecutable(); + } + + [Fact] + public void QueryReturns() + { + IExecutionResult? result = testExecutor.Execute("query { test: rome }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T18:30:13 Asia/Kathmandu (+05:45)", queryResult!.Data!["test"]); + } + + [Fact] + public void QueryReturnsUtc() + { + IExecutionResult? result = testExecutor.Execute("query { test: utc }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T18:30:13 UTC (+00)", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: ZonedDateTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-12-31T19:30:13 Asia/Kathmandu (+05:45)") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T19:40:13 Asia/Kathmandu (+05:45)", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariableWithUTC() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: ZonedDateTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-12-31T19:30:13 UTC (+00)") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T19:40:13 UTC (+00)", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseAnIncorrectVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: ZonedDateTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-12-31T19:30:13 (UTC)") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + + [Fact] + public void ParsesLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-12-31T19:30:13 Asia/Kathmandu (+05:45)\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T19:40:13 Asia/Kathmandu (+05:45)", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesLiteralWithUTC() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-12-31T19:30:13 UTC (+00)\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T19:40:13 UTC (+00)", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseIncorrectLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-12-31T19:30:13 (UTC)\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + Assert.Null(queryResult.Errors[0].Code); + Assert.Equal( + "Unable to deserialize string to ZonedDateTime", + queryResult.Errors[0].Message); + } +} diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/ZonedDateTimeTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/ZonedDateTimeTypeTests.cs index f028026b074..af7f20f90d4 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/ZonedDateTimeTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/ZonedDateTimeTypeTests.cs @@ -3,6 +3,7 @@ using HotChocolate.Execution; using Microsoft.Extensions.DependencyInjection; using NodaTime; +using NodaTime.Text; using Xunit; namespace HotChocolate.Types.NodaTime.Tests @@ -36,6 +37,7 @@ public ZonedDateTime Test(ZonedDateTime arg) } private readonly IRequestExecutor testExecutor; + public ZonedDateTimeTypeIntegrationTests() { testExecutor = SchemaBuilder.New() @@ -131,8 +133,18 @@ public void DoesntParseIncorrectLiteral() var queryResult = result as IReadOnlyQueryResult; Assert.Null(queryResult!.Data); Assert.Equal(1, queryResult!.Errors!.Count); - Assert.Null(queryResult.Errors.First().Code); - Assert.Equal("Unable to deserialize string to ZonedDateTime", queryResult.Errors.First().Message); + Assert.Null( + queryResult.Errors[0].Code); + Assert.Equal( + "Unable to deserialize string to ZonedDateTime", + queryResult.Errors[0].Message); + } + + [Fact] + public void PatternEmpty_ThrowSchemaException() + { + static object Call() => new ZonedDateTimeType(Array.Empty>()); + Assert.Throws(Call); } } }