From c7ac4c52d22ed0c7f81e1c4e643f5fdab8ebe8d8 Mon Sep 17 00:00:00 2001 From: Denis Vieira dos Anjos Date: Wed, 28 Jun 2023 07:03:22 -0300 Subject: [PATCH 1/5] Percentagem implementation --- .../Types/Percentage/PercentageConverter.cs | 13 +++++ src/Nox.Types/Types/Percentage/Percentage.cs | 36 +++++++++++- .../TypeOptions/PercentageTypeOptions.cs | 10 ++++ .../Types/Percentage/PercentageTests.cs | 56 ++++++++++++++++++- 4 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 src/Nox.Types.EntityFramework/Types/Percentage/PercentageConverter.cs create mode 100644 src/Nox.Types/Types/Percentage/TypeOptions/PercentageTypeOptions.cs diff --git a/src/Nox.Types.EntityFramework/Types/Percentage/PercentageConverter.cs b/src/Nox.Types.EntityFramework/Types/Percentage/PercentageConverter.cs new file mode 100644 index 0000000..fadb7ee --- /dev/null +++ b/src/Nox.Types.EntityFramework/Types/Percentage/PercentageConverter.cs @@ -0,0 +1,13 @@ +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace Nox.Types.EntityFramework.Types; + +public class PercentageConverter : ValueConverter +{ + /// + /// Initializes a new instance of the class. + /// + public PercentageConverter() : base(percentage => percentage.Value, percentageValue => Percentage.From(percentageValue)) + { + } +} diff --git a/src/Nox.Types/Types/Percentage/Percentage.cs b/src/Nox.Types/Types/Percentage/Percentage.cs index 3b856b6..c8d3e39 100644 --- a/src/Nox.Types/Types/Percentage/Percentage.cs +++ b/src/Nox.Types/Types/Percentage/Percentage.cs @@ -1,9 +1,39 @@ +using System; + namespace Nox.Types; +/// +/// Represents a Nox type and value object. +/// +public sealed class Percentage : ValueObject +{ + private readonly PercentageTypeOptions _percentageTypeOptions = new(); + /// - /// Represents a Nox type and value object. + /// Validates a object. /// - /// Placeholder, needs to be implemented - public sealed class Percentage : ValueObject + /// true if the value is valid. + internal override ValidationResult Validate() + { + var result = base.Validate(); + + if (Value < _percentageTypeOptions.MinValue) + { + result.Errors.Add(new ValidationFailure(nameof(Value), $"Could not create a Nox Percentage type as value {Value} is less than than the minimum specified value of {_percentageTypeOptions.MinValue}")); + } + + if (Value > _percentageTypeOptions.MaxValue) + { + result.Errors.Add(new ValidationFailure(nameof(Value), $"Could not create a Nox Percentage type a value {Value} is greater than than the maximum specified value of {_percentageTypeOptions.MaxValue}")); + } + + Value = (float)Math.Round(Value, _percentageTypeOptions.Digits); + + return result; + } + + public override string ToString() { + return $"{Value * 100}%"; } +} diff --git a/src/Nox.Types/Types/Percentage/TypeOptions/PercentageTypeOptions.cs b/src/Nox.Types/Types/Percentage/TypeOptions/PercentageTypeOptions.cs new file mode 100644 index 0000000..99d2d70 --- /dev/null +++ b/src/Nox.Types/Types/Percentage/TypeOptions/PercentageTypeOptions.cs @@ -0,0 +1,10 @@ +namespace Nox.Types; +public class PercentageTypeOptions +{ + public static readonly float DefaultMinValue = 0; + public static readonly float DefaultMaxValue = 1; + public float MinValue { get; set; } = DefaultMinValue; + public float MaxValue { get; set; } = DefaultMaxValue; + + public int Digits { get; set; } = 2; +} diff --git a/tests/Nox.Types.Tests/Types/Percentage/PercentageTests.cs b/tests/Nox.Types.Tests/Types/Percentage/PercentageTests.cs index f4f8ce3..0c086a8 100644 --- a/tests/Nox.Types.Tests/Types/Percentage/PercentageTests.cs +++ b/tests/Nox.Types.Tests/Types/Percentage/PercentageTests.cs @@ -1,11 +1,61 @@ -// ReSharper disable once CheckNamespace -namespace Nox.Types.Tests.Types; +namespace Nox.Types.Tests.Types; public class PercentageTests { [Fact] - public void When_Create_Should() + public void Percentage_Constructor_ReturnsSameValue() { + var testPercentage = 0.5f; + var number = Percentage.From(testPercentage); + + Assert.Equal(testPercentage, number.Value); + } + + [Fact] + public void Percentage_Constructor_ThrowsException_WhenValueExceedsMaxAllowed() + { + var testPercentage = 3.2f; + + Assert.Throws(() => _ = + Percentage.From(testPercentage) + ); + } + + [Fact] + public void Percentage_Constructor_ThrowsException_WhenValueIsLessThanMinAllowed() + { + var testPercentage = -0.3f; + + Assert.Throws(() => _ = + Percentage.From(testPercentage) + ); + } + + [Fact] + public void Percentage_Constructor_RoundsFloatValues_WhenConstructedWithFloatInput() + { + var testPercentage = 0.4f; + + var percentage = Number.From(testPercentage); + + Assert.Equal(40, percentage.Value); + } + + [Fact] + public void Percentage_ToString_Returns_Value() + { + void Test() + { + var pecentageValue = 0.45f; + + var percentage = Number.From(pecentageValue); + + var percentageAsString = percentage.ToString(); + + Assert.Equal("45%", percentageAsString); + } + + TestUtility.RunInInvariantCulture(Test); } } \ No newline at end of file From 6212021d4f3835a2fa5f4a5f75f11597c2eb1154 Mon Sep 17 00:00:00 2001 From: Denis Vieira dos Anjos Date: Wed, 28 Jun 2023 10:28:11 -0300 Subject: [PATCH 2/5] Percentage tests --- .../Types/Percentage/PercentageConverter.cs | 2 +- tests/Nox.Types.Tests/Types/Percentage/PercentageTests.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Nox.Types.EntityFramework/Types/Percentage/PercentageConverter.cs b/src/Nox.Types.EntityFramework/Types/Percentage/PercentageConverter.cs index fadb7ee..fa8530f 100644 --- a/src/Nox.Types.EntityFramework/Types/Percentage/PercentageConverter.cs +++ b/src/Nox.Types.EntityFramework/Types/Percentage/PercentageConverter.cs @@ -2,7 +2,7 @@ namespace Nox.Types.EntityFramework.Types; -public class PercentageConverter : ValueConverter +public class PercentageConverter : ValueConverter { /// /// Initializes a new instance of the class. diff --git a/tests/Nox.Types.Tests/Types/Percentage/PercentageTests.cs b/tests/Nox.Types.Tests/Types/Percentage/PercentageTests.cs index 0c086a8..fae2e3e 100644 --- a/tests/Nox.Types.Tests/Types/Percentage/PercentageTests.cs +++ b/tests/Nox.Types.Tests/Types/Percentage/PercentageTests.cs @@ -37,9 +37,9 @@ public void Percentage_Constructor_RoundsFloatValues_WhenConstructedWithFloatInp { var testPercentage = 0.4f; - var percentage = Number.From(testPercentage); + var percentage = Percentage.From(testPercentage); - Assert.Equal(40, percentage.Value); + Assert.Equal(0.4, percentage.Value); } [Fact] @@ -49,7 +49,7 @@ void Test() { var pecentageValue = 0.45f; - var percentage = Number.From(pecentageValue); + var percentage = Percentage.From(pecentageValue); var percentageAsString = percentage.ToString(); From 0b319ef69277606fb22891350278d3ca507d2e6b Mon Sep 17 00:00:00 2001 From: Denis Vieira dos Anjos Date: Wed, 28 Jun 2023 10:37:35 -0300 Subject: [PATCH 3/5] Tests --- tests/Nox.Types.Tests/Types/Percentage/PercentageTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Nox.Types.Tests/Types/Percentage/PercentageTests.cs b/tests/Nox.Types.Tests/Types/Percentage/PercentageTests.cs index fae2e3e..fa2fe4a 100644 --- a/tests/Nox.Types.Tests/Types/Percentage/PercentageTests.cs +++ b/tests/Nox.Types.Tests/Types/Percentage/PercentageTests.cs @@ -39,7 +39,7 @@ public void Percentage_Constructor_RoundsFloatValues_WhenConstructedWithFloatInp var percentage = Percentage.From(testPercentage); - Assert.Equal(0.4, percentage.Value); + Assert.Equal(0.4f, percentage.Value); } [Fact] From a4a3d9500a2ede957d19294063f7086f99533343 Mon Sep 17 00:00:00 2001 From: Denis Vieira dos Anjos Date: Mon, 3 Jul 2023 23:42:40 -0300 Subject: [PATCH 4/5] ToString() culture independent --- src/Nox.Types/Types/Percentage/Percentage.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Nox.Types/Types/Percentage/Percentage.cs b/src/Nox.Types/Types/Percentage/Percentage.cs index c8d3e39..6b95a72 100644 --- a/src/Nox.Types/Types/Percentage/Percentage.cs +++ b/src/Nox.Types/Types/Percentage/Percentage.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; namespace Nox.Types; @@ -33,7 +34,13 @@ internal override ValidationResult Validate() } public override string ToString() - { - return $"{Value * 100}%"; - } + => $"{(Value * 100).ToString($"0.{new string('#', _percentageTypeOptions.Digits)}", CultureInfo.InvariantCulture)}%"; + + /// + /// Returns a string representation of the object using the specified . + /// + /// The format provider for the length value. + /// A string representation of the object with the value formatted using the specified . + public string ToString(IFormatProvider formatProvider) + => $"{Value.ToString(formatProvider)}%"; } From 3abd9198bd3a043cffb64a8eff667d78aeda63e8 Mon Sep 17 00:00:00 2001 From: Denis Vieira dos Anjos Date: Tue, 4 Jul 2023 01:17:20 -0300 Subject: [PATCH 5/5] Tests --- src/Nox.Types/Types/Percentage/Percentage.cs | 30 +++++++++-- .../Types/Percentage/PercentageTests.cs | 52 +++++++++++++++---- 2 files changed, 70 insertions(+), 12 deletions(-) diff --git a/src/Nox.Types/Types/Percentage/Percentage.cs b/src/Nox.Types/Types/Percentage/Percentage.cs index 6b95a72..d738993 100644 --- a/src/Nox.Types/Types/Percentage/Percentage.cs +++ b/src/Nox.Types/Types/Percentage/Percentage.cs @@ -8,7 +8,31 @@ namespace Nox.Types; /// public sealed class Percentage : ValueObject { - private readonly PercentageTypeOptions _percentageTypeOptions = new(); + private PercentageTypeOptions _percentageTypeOptions = new(); + + /// + /// Creates a new instance of class with the specified values. + /// + /// The value to create the with + /// + /// + public static Percentage From(float value, PercentageTypeOptions options) + { + var newObject = new Percentage + { + Value = value, + _percentageTypeOptions = options + }; + + var validationResult = newObject.Validate(); + + if (!validationResult.IsValid) + { + throw new TypeValidationException(validationResult.Errors); + } + + return newObject; + } /// /// Validates a object. @@ -18,12 +42,12 @@ internal override ValidationResult Validate() { var result = base.Validate(); - if (Value < _percentageTypeOptions.MinValue) + if (Value < _percentageTypeOptions.MinValue && !float.IsNaN(Value) && !float.IsInfinity(Value)) { result.Errors.Add(new ValidationFailure(nameof(Value), $"Could not create a Nox Percentage type as value {Value} is less than than the minimum specified value of {_percentageTypeOptions.MinValue}")); } - if (Value > _percentageTypeOptions.MaxValue) + if (Value > _percentageTypeOptions.MaxValue && !float.IsNaN(Value) && !float.IsInfinity(Value)) { result.Errors.Add(new ValidationFailure(nameof(Value), $"Could not create a Nox Percentage type a value {Value} is greater than than the maximum specified value of {_percentageTypeOptions.MaxValue}")); } diff --git a/tests/Nox.Types.Tests/Types/Percentage/PercentageTests.cs b/tests/Nox.Types.Tests/Types/Percentage/PercentageTests.cs index fa2fe4a..d9f7464 100644 --- a/tests/Nox.Types.Tests/Types/Percentage/PercentageTests.cs +++ b/tests/Nox.Types.Tests/Types/Percentage/PercentageTests.cs @@ -1,4 +1,7 @@ -namespace Nox.Types.Tests.Types; +using FluentAssertions; +using System.Globalization; + +namespace Nox.Types.Tests.Types; public class PercentageTests { @@ -9,7 +12,7 @@ public void Percentage_Constructor_ReturnsSameValue() var number = Percentage.From(testPercentage); - Assert.Equal(testPercentage, number.Value); + number.Value.Should().Be(testPercentage); } [Fact] @@ -17,9 +20,11 @@ public void Percentage_Constructor_ThrowsException_WhenValueExceedsMaxAllowed() { var testPercentage = 3.2f; - Assert.Throws(() => _ = - Percentage.From(testPercentage) - ); + var action = () => Percentage.From(testPercentage); + + action.Should().Throw() + .And.Errors.Should().BeEquivalentTo(new[] { new ValidationFailure("Value", "Could not create a Nox Percentage type a value 3.2 is greater than than the maximum specified value of 1") }); + } [Fact] @@ -27,9 +32,10 @@ public void Percentage_Constructor_ThrowsException_WhenValueIsLessThanMinAllowed { var testPercentage = -0.3f; - Assert.Throws(() => _ = - Percentage.From(testPercentage) - ); + var action = () => Percentage.From(testPercentage); + + action.Should().Throw() + .And.Errors.Should().BeEquivalentTo(new[] { new ValidationFailure("Value", "Could not create a Nox Percentage type as value -0.3 is less than than the minimum specified value of 0") }); } [Fact] @@ -39,7 +45,7 @@ public void Percentage_Constructor_RoundsFloatValues_WhenConstructedWithFloatInp var percentage = Percentage.From(testPercentage); - Assert.Equal(0.4f, percentage.Value); + percentage.Value.Should().Be(0.4f); } [Fact] @@ -58,4 +64,32 @@ void Test() TestUtility.RunInInvariantCulture(Test); } + + [Theory] + [InlineData("en-US")] + [InlineData("pt-PT")] + public void Percentage_ValueInFloat_ToString_IsCultureIndepdendent(string culture) + { + void Test() + { + var percentage = Percentage.From(0.25f); + percentage.ToString().Should().Be("25%"); + } + + TestUtility.RunInCulture(Test, culture); + } + + [Theory] + [InlineData("en-US", "0.43%")] + [InlineData("pt-PT", "0,43%")] + public void Percentage_ValueInFloat_ToString_IsCultureDependent(string culture, string expected) + { + void Test() + { + var percentage = Percentage.From(0.43f); + percentage.ToString(new CultureInfo(culture)).Should().Be(expected); + } + + TestUtility.RunInCulture(Test, culture); + } } \ No newline at end of file