Skip to content

Commit

Permalink
fix: ensure number and decimal does best effort parsing (#147)
Browse files Browse the repository at this point in the history
  • Loading branch information
SondreJDigdir authored Dec 18, 2024
1 parent c6ad6fe commit 7a35d4d
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 3 deletions.
1 change: 1 addition & 0 deletions Dan.Common.UnitTest/Dan.Common.UnitTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="7.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="MSTest.TestAdapter" Version="3.5.1" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using Dan.Common.Extensions;
using FluentAssertions;

namespace Dan.Common.UnitTest.Extensions;

[TestClass]
public class EvidenceHarvesterRequestExtensionTests
{
[DataTestMethod]
[DataRow(1, true, 1)]
[DataRow("1", true, 1)]
[DataRow(null, false, 0)]
[DataRow(3000000000, false, 0)]
[DataRow(-3000000000, false, 0)]
public void TryGetParameter_Numbers(object value, bool expectedBool, int expectedInt)
{
// Arrange
const string parameterName = "NumberParam";
var request = new EvidenceHarvesterRequest
{
Parameters =
[
new EvidenceParameter()
{
EvidenceParamName = parameterName,
Value = value
}
]
};

// Act
var actualBool = request.TryGetParameter(parameterName, out int actualInt);

// Assert
actualBool.Should().Be(expectedBool);
actualInt.Should().Be(expectedInt);
}

[DataTestMethod]
[DataRow(1, true, 1)]
[DataRow(1.2, true, 1.2)]
[DataRow("1", true, 1)]
[DataRow("1.2", true, 1.2)]
[DataRow(null, false, 0)]
[DataRow(3000000000d, true, 3000000000)]
[DataRow(-3000000000d, true, -3000000000)]
public void TryGetParameter_Decimal(object value, bool expectedBool, double expectedTemp)
{
// Arrange
// decimal is not const so need to cast
var expectedDecimal = (decimal)expectedTemp;
const string parameterName = "NumberParam";
var request = new EvidenceHarvesterRequest
{
Parameters =
[
new EvidenceParameter()
{
EvidenceParamName = parameterName,
Value = value
}
]
};

// Act
var actualBool = request.TryGetParameter(parameterName, out decimal actualDecimal);

// Assert
actualBool.Should().Be(expectedBool);
actualDecimal.Should().Be(expectedDecimal);
}
}
64 changes: 61 additions & 3 deletions Dan.Common/Extensions/EvidenceHarvesterRequestExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,40 @@ public static bool TryGetParameter(this EvidenceHarvesterRequest ehr, string par
/// <returns>True if the parameter was supplied and was valid; otherwise false</returns>
public static bool TryGetParameter(this EvidenceHarvesterRequest ehr, string paramName, out int value)
{
// Can't find parameter? Return false
if (!ehr.TryGetParameter(paramName, out EvidenceParameter? parameter))
{
value = default;
return false;
}

return int.TryParse((string?)parameter?.Value, out value);
switch (parameter?.Value)
{
// Null? That's not a number, at least give a proper zero
case null:
value = default;
return false;
// If int, just return
case int intValue:
value = intValue;
return true;
// If long, return if not overflown above max int value
case long and (> int.MaxValue or < int.MinValue):
value = default;
return false;
case long longValue:
value = Convert.ToInt32(longValue);
return true;
// If string, parse
case string stringValue:
return int.TryParse(stringValue, out value);
// Otherwise return false, can't think of any other realistic value type, I dont want to do floats for
// int values due to rounding, I'd rather just throw an invalid value because don't give us a decimal
// number if we request a whole one
default:
value = default;
return false;
}
}

/// <summary>
Expand All @@ -56,8 +83,39 @@ public static bool TryGetParameter(this EvidenceHarvesterRequest ehr, string par
value = default;
return false;
}

return decimal.TryParse((string?)parameter?.Value, out value);

switch (parameter?.Value)
{
// Null? That's not a number, at least give a proper zero
case null:
value = default;
return false;
// If decimal, just return
case decimal decimalValue:
value = decimalValue;
return true;
// In case of other number values, convert
case int intValue:
value = Convert.ToDecimal(intValue);
return true;
case long longValue:
value = Convert.ToDecimal(longValue);
return true;
case float floatValue:
value = Convert.ToDecimal(floatValue);
return true;
case double doubleValue:
value = Convert.ToDecimal(doubleValue);
return true;
// If string, try to parse
case string stringValue:
return decimal.TryParse(stringValue, out value);
// Otherwise return false, can't think of any other realistic value type that could be interpreted
// as a decimal type
default:
value = default;
return false;
}
}

/// <summary>
Expand Down
1 change: 1 addition & 0 deletions Dan.Core.UnitTest/Dan.Core.UnitTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="Moq" Version="4.20.70" />
Expand Down

0 comments on commit 7a35d4d

Please sign in to comment.