diff --git a/Modules/StocksModule.cs b/Modules/StocksModule.cs index e7908ae..6ae209c 100644 --- a/Modules/StocksModule.cs +++ b/Modules/StocksModule.cs @@ -116,7 +116,7 @@ public async Task StockSellAsync(string target, string amountStr = "all") decimal? sharesToSell = null; if (amountStr.ToLower() != "all") { - if (!decimal.TryParse(amountStr, out decimal parsed) || parsed <= 0) + if (!StockInputParser.TryParsePositiveAmount(amountStr, out decimal parsed)) { await ReplyAsync("Invalid amount. Use a positive number or `all`."); return; @@ -168,7 +168,7 @@ public async Task StockTransferAsync(string target, IUser targetUser, string amo decimal? sharesToTransfer = null; if (amountStr.ToLower() != "all") { - if (!decimal.TryParse(amountStr, out decimal parsed) || parsed <= 0) + if (!StockInputParser.TryParsePositiveAmount(amountStr, out decimal parsed)) { await ReplyAsync("Invalid amount. Use a positive number or `all`."); return; diff --git a/Morpheus.Tests/StockInputParserTests.cs b/Morpheus.Tests/StockInputParserTests.cs new file mode 100644 index 0000000..1ca2743 --- /dev/null +++ b/Morpheus.Tests/StockInputParserTests.cs @@ -0,0 +1,39 @@ +using System.Globalization; +using Morpheus.Utilities; + +namespace Morpheus.Tests; + +public class StockInputParserTests +{ + [Fact] + public void TryParsePositiveAmount_UsesInvariantDecimalSeparator() + { + CultureInfo originalCulture = CultureInfo.CurrentCulture; + try + { + CultureInfo culture = (CultureInfo)CultureInfo.InvariantCulture.Clone(); + culture.NumberFormat.NumberDecimalSeparator = ","; + culture.NumberFormat.NumberGroupSeparator = "."; + CultureInfo.CurrentCulture = culture; + + bool parsed = StockInputParser.TryParsePositiveAmount("12.50", out decimal amount); + + Assert.True(parsed); + Assert.Equal(12.50m, amount); + } + finally + { + CultureInfo.CurrentCulture = originalCulture; + } + } + + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData("0")] + [InlineData("-1")] + public void TryParsePositiveAmount_RejectsMissingAndNonPositiveValues(string? input) + { + Assert.False(StockInputParser.TryParsePositiveAmount(input, out _)); + } +} diff --git a/Utilities/StockInputParser.cs b/Utilities/StockInputParser.cs new file mode 100644 index 0000000..87e2779 --- /dev/null +++ b/Utilities/StockInputParser.cs @@ -0,0 +1,11 @@ +using System.Globalization; + +namespace Morpheus.Utilities; + +internal static class StockInputParser +{ + public static bool TryParsePositiveAmount(string? input, out decimal amount) + { + return decimal.TryParse(input, NumberStyles.Number, CultureInfo.InvariantCulture, out amount) && amount > 0; + } +}