Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions src/ByteBard.AsyncAPI.Readers/ParseNodes/ValueNode.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace ByteBard.AsyncAPI.Readers.ParseNodes
{
using System;
using System.Globalization;
using System.Text.Json.Nodes;
using ByteBard.AsyncAPI.Exceptions;
using ByteBard.AsyncAPI.Models;
Expand Down Expand Up @@ -47,7 +48,7 @@ public override string GetScalarValueOrDefault(string defaultValue = null)

public override int GetIntegerValue()
{
if (int.TryParse(this.GetScalarValue(), out int value))
if (int.TryParse(this.GetScalarValue(), NumberStyles.Integer, this.Context.Settings.CultureInfo, out int value))
{
return value;
}
Expand All @@ -57,7 +58,7 @@ public override int GetIntegerValue()

public override int? GetIntegerValueOrDefault(int? defaultValue = null)
{
if (int.TryParse(this.GetScalarValue(), out int value))
if (int.TryParse(this.GetScalarValue(), NumberStyles.Integer, this.Context.Settings.CultureInfo, out int value))
{
return value;
}
Expand All @@ -67,7 +68,7 @@ public override int GetIntegerValue()

public override long GetLongValue()
{
if (long.TryParse(this.GetScalarValue(), out long value))
if (long.TryParse(this.GetScalarValue(), NumberStyles.Integer, this.Context.Settings.CultureInfo, out long value))
{
return value;
}
Expand All @@ -77,7 +78,7 @@ public override long GetLongValue()

public override long? GetLongValueOrDefault(long? defaultValue = null)
{
if (long.TryParse(this.GetScalarValue(), out long value))
if (long.TryParse(this.GetScalarValue(), NumberStyles.Integer, this.Context.Settings.CultureInfo, out long value))
{
return value;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ public class AsyncApiAvroSchemaDeserializer
{
{ "type", (a, n) => { } },
{ "logicalType", (a, n) => { } },
{ "precision", (a, n) => a.Precision = int.Parse(n.GetScalarValue()) },
{ "scale", (a, n) => a.Scale = int.Parse(n.GetScalarValue()) },
{ "precision", (a, n) => a.Precision = n.GetIntegerValue() },
{ "scale", (a, n) => a.Scale = n.GetIntegerValue() },
};

private static readonly FixedFieldMap<AvroUUID> UUIDFixedFields = new()
Expand Down
92 changes: 92 additions & 0 deletions test/ByteBard.AsyncAPI.Tests/IntegerParsing_Should.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
namespace ByteBard.AsyncAPI.Tests
{
using System.Globalization;
using FluentAssertions;
using ByteBard.AsyncAPI.Bindings;
using ByteBard.AsyncAPI.Bindings.IBMMQ;
using ByteBard.AsyncAPI.Bindings.ROS2;
using ByteBard.AsyncAPI.Models;
using ByteBard.AsyncAPI.Readers;
using NUnit.Framework;

[NonParallelizable]
internal class IntegerParsing_Should : TestBase
{
private CultureInfo originalCulture;

[SetUp]
public void SetCulture()
{
this.originalCulture = CultureInfo.CurrentCulture;
}

[TearDown]
public void RestoreCulture()
{
CultureInfo.CurrentCulture = this.originalCulture;
}

// These cultures use a negative sign other than a plain '-' (e.g. U+061C U+002D for ar-SA).
[TestCase("ar-SA")]
[TestCase("he-IL")]
[TestCase("fa-IR")]
public void NegativeIntegers_AreParsed_RegardlessOfCurrentCulture(string culture)
{
CultureInfo.CurrentCulture = new CultureInfo(culture);
var yaml =
"""
action: send
channel:
$ref: '#/channels/camera'
bindings:
ros2:
qosPolicies:
lifespan: -1
deadline: -1
leaseDuration: -1
""";

var settings = new AsyncApiReaderSettings { Bindings = BindingsCollection.ROS2 };
var operation = new AsyncApiStringReader(settings).ReadFragment<AsyncApiOperation>(yaml, AsyncApiVersion.AsyncApi3_0, out var diagnostics);

diagnostics.Errors.Should().BeEmpty();
var qos = ((ROS2OperationBinding)operation.Bindings["ros2"]).QosPolicies;
qos.Lifespan.Should().Be(-1);
qos.Deadline.Should().Be(-1);
qos.LeaseDuration.Should().Be(-1);
}

[TestCase("ar-SA")]
[TestCase("he-IL")]
public void NegativeLongs_AreParsed_RegardlessOfCurrentCulture(string culture)
{
CultureInfo.CurrentCulture = new CultureInfo(culture);
var json = """{ "bindings": { "ibmmq": { "expiry": -5 } } }""";

var settings = new AsyncApiReaderSettings { Bindings = BindingsCollection.IBMMQ };
var message = new AsyncApiStringReader(settings).ReadFragment<AsyncApiMessage>(json, AsyncApiVersion.AsyncApi3_0, out var diagnostics);

diagnostics.Errors.Should().BeEmpty();
((IBMMQMessageBinding)message.Bindings["ibmmq"]).Expiry.Should().Be(-5);
}

[TestCase("precision")]
[TestCase("scale")]
public void AvroDecimal_WithNonIntegerSize_ReportsErrorInsteadOfThrowing(string field)
{
var input = $$"""
{
"type": "bytes",
"logicalType": "decimal",
"{{field}}": "abc"
}
""";

AsyncApiDiagnostic diagnostics = null;
var act = () => new AsyncApiStringReader().ReadFragment<AsyncApiAvroSchema>(input, AsyncApiVersion.AsyncApi2_0, out diagnostics);

act.Should().NotThrow();
diagnostics.Errors.Should().ContainSingle().Which.Message.Should().Contain("could not parse to integer");
}
}
}
Loading