From 5419a1ece4b3ea16163a8a869d461b33c4ec8908 Mon Sep 17 00:00:00 2001 From: Byron Mayne Date: Sun, 10 Mar 2024 14:14:38 -0400 Subject: [PATCH 1/8] Converted the project to .netstandard This allows it to be used by any project type rather then just .netframework --- AsyncAPI.sln | 3 ++- Common.Build.props | 13 ++++++++++++ .../BindingsCollection.cs | 11 ++++++++-- .../LEGO.AsyncAPI.Bindings.csproj | 21 ++++++------------- .../LEGO.AsyncAPI.Readers.csproj | 17 +++++---------- src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj | 19 ++++++----------- 6 files changed, 41 insertions(+), 43 deletions(-) create mode 100644 Common.Build.props diff --git a/AsyncAPI.sln b/AsyncAPI.sln index f972fa3d..db79f153 100644 --- a/AsyncAPI.sln +++ b/AsyncAPI.sln @@ -12,9 +12,10 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{DE167614-5BCB-4046-BD4C-ABB70E9F3462}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig + Common.Build.props = Common.Build.props EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LEGO.AsyncAPI.Bindings", "src\LEGO.AsyncAPI.Bindings\LEGO.AsyncAPI.Bindings.csproj", "{33CA31F4-ECFE-4227-BFE9-F49783DD29A0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LEGO.AsyncAPI.Bindings", "src\LEGO.AsyncAPI.Bindings\LEGO.AsyncAPI.Bindings.csproj", "{33CA31F4-ECFE-4227-BFE9-F49783DD29A0}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/Common.Build.props b/Common.Build.props new file mode 100644 index 00000000..f4f9797f --- /dev/null +++ b/Common.Build.props @@ -0,0 +1,13 @@ + + + + 10 + netstandard2.0 + disable + The LEGO Group + https://github.com/LEGO/AsyncAPI.NET + README.md + https://github.com/LEGO/AsyncAPI.NET + asyncapi .net openapi documentation + + \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs b/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs index e52392b6..ccfad8a4 100644 --- a/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs +++ b/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs @@ -19,8 +19,15 @@ public static TCollection Add( IEnumerable source) where TCollection : ICollection { - ArgumentNullException.ThrowIfNull(destination); - ArgumentNullException.ThrowIfNull(source); + if (destination == null) + { + throw new ArgumentNullException(nameof(destination)); + } + + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } if (destination is List list) { diff --git a/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj b/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj index e05aacab..751ea5c6 100644 --- a/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj +++ b/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj @@ -1,20 +1,11 @@ - - + + - net6.0 - disable - The LEGO Group - https://github.com/LEGO/AsyncAPI.NET - README.md - AsyncAPI.NET Bindings - asyncapi .net openapi documentation - AsyncAPI.NET.Bindings - LEGO.AsyncAPI.Bindings - LEGO.AsyncAPI.Bindings - https://github.com/LEGO/AsyncAPI.NET + AsyncAPI.NET Bindings + AsyncAPI.NET.Bindings + LEGO.AsyncAPI.Bindings + LEGO.AsyncAPI.Bindings - - diff --git a/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj b/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj index 6fd9d2e7..b0164afd 100644 --- a/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj +++ b/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj @@ -1,18 +1,11 @@  - + - net6.0 - disable disable - The LEGO Group - https://github.com/LEGO/AsyncAPI.NET - README.md - AsyncAPI.NET Readers for JSON and YAML documents - asyncapi .net openapi documentation - AsyncAPI.NET.Readers - LEGO.AsyncAPI.Readers - LEGO.AsyncAPI.Readers - https://github.com/LEGO/AsyncAPI.NET + AsyncAPI.NET Readers for JSON and YAML documents + AsyncAPI.NET.Readers + LEGO.AsyncAPI.Readers + LEGO.AsyncAPI.Readers diff --git a/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj b/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj index 8d4b9501..c2dd1598 100644 --- a/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj +++ b/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj @@ -1,19 +1,11 @@  - + - net6.0 - disable - The LEGO Group - https://github.com/LEGO/AsyncAPI.NET - README.md - AsyncAPI.NET models - asyncapi .net openapi documentation - AsyncAPI.NET - LEGO.AsyncAPI - LEGO.AsyncAPI - https://github.com/LEGO/AsyncAPI.NET + AsyncAPI.NET models + AsyncAPI.NET + LEGO.AsyncAPI + LEGO.AsyncAPI - @@ -27,6 +19,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + <_Parameter1>$(MSBuildProjectName).Tests From ca7b075865f428eb15dc1a63f2711470aaad5561 Mon Sep 17 00:00:00 2001 From: Byron Mayne Date: Sun, 10 Mar 2024 15:13:49 -0400 Subject: [PATCH 2/8] Fixed unit tests being broken for serialization Added new unit test to validate the special characters string extensions, which was failing tests --- .../SpecialCharacterStringExtensions.cs | 36 +- .../AsyncApiDocumentV2Tests.cs | 407 +++++++++--------- .../LEGO.AsyncAPI.Tests.csproj | 98 ++--- .../SpecialCharacterStringExtensionsTests.cs | 73 ++++ 4 files changed, 359 insertions(+), 255 deletions(-) create mode 100644 test/LEGO.AsyncAPI.Tests/Writers/SpecialCharacterStringExtensionsTests.cs diff --git a/src/LEGO.AsyncAPI/Writers/SpecialCharacterStringExtensions.cs b/src/LEGO.AsyncAPI/Writers/SpecialCharacterStringExtensions.cs index 1a091066..60f4fd8e 100644 --- a/src/LEGO.AsyncAPI/Writers/SpecialCharacterStringExtensions.cs +++ b/src/LEGO.AsyncAPI/Writers/SpecialCharacterStringExtensions.cs @@ -100,10 +100,15 @@ public static class SpecialCharacterStringExtensions /// Escapes all special characters and put the string in quotes if necessary to /// get a YAML-compatible string. /// - internal static string GetYamlCompatibleString(this string input) + internal static string GetYamlCompatibleString(this string? input) { + if (input == null) + { + return "null"; + } + // If string is an empty string, wrap it in quote to ensure it is not recognized as null. - if (input == "") + if (input.Length == 0) { return "''"; } @@ -163,7 +168,7 @@ internal static string GetYamlCompatibleString(this string input) input = input.Replace("\x1e", "\\x1e"); input = input.Replace("\x1f", "\\x1f"); - return $"\"{input}\""; + return $"'{input}'"; } // If string @@ -183,11 +188,26 @@ internal static string GetYamlCompatibleString(this string input) return $"'{input}'"; } - // If string can be mistaken as a number, a boolean, or a timestamp, - // wrap it in quote to indicate that this is indeed a string, not a number, a boolean, or a timestamp - if (decimal.TryParse(input, NumberStyles.Float, CultureInfo.InvariantCulture, out var _) || - bool.TryParse(input, out var _) || - DateTime.TryParse(input, out var _)) + // Handle lexemes that can be intperated as as string + // https://yaml.org/spec/1.2-old/spec.html#id2761292 + switch (input.ToLower()) + { + // Example 2.20. Floating Point + case "-.inf": + case ".inf": + case ".nan": + // Example 2.21. Miscellaneous + case "null": + + // Booleans + case "true": + case "false": + return $"'{input}'"; + } + + // Handle numbers + char first = input[0]; + if (char.IsDigit(first) || first == '-' || first == '+') { return $"'{input}'"; } diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs index a598ac18..d78ea65e 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs @@ -22,8 +22,19 @@ public class ExtensionClass public string Key { get; set; } public long OtherKey { get; set; } } + public class AsyncApiDocumentV2Tests { + [Test] + public void AsyncApiDocumentYaml_DefalutVersionNumber_IsWrappedInSingleQuotes() + { + string actual = new AsyncApiDocumentBuilder() + .Build() + .SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + + Assert.AreEqual("asyncapi: '2.6.0'\ninfo: { }", actual); + } + [Test] public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() { @@ -32,7 +43,7 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() @"asyncapi: '2.6.0' info: title: Streetlights Kafka API - version: 1.0.0 + version: '1.0.0' description: The Smartylighting Streetlights API allows you to remotely manage the city lights. license: name: Apache 2.0 @@ -200,25 +211,25 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() maximum: 100 minimum: 0"; - var asyncApiDocument = new AsyncApiDocumentBuilder() - .WithInfo(new AsyncApiInfo - { - Title = "Streetlights Kafka API", - Version = "1.0.0", - Description = "The Smartylighting Streetlights API allows you to remotely manage the city lights.", - License = new AsyncApiLicense + var asyncApiDocument = new AsyncApiDocumentBuilder() + .WithInfo(new AsyncApiInfo { - Name = "Apache 2.0", - Url = new Uri("https://www.apache.org/licenses/LICENSE-2.0"), - }, - }) - .WithServer("scram-connections", new AsyncApiServer - { - Url = "test.mykafkacluster.org:18092", - Protocol = "kafka-secure", - Description = "Test broker secured with scramSha256", - Security = new List + Title = "Streetlights Kafka API", + Version = "1.0.0", + Description = "The Smartylighting Streetlights API allows you to remotely manage the city lights.", + License = new AsyncApiLicense + { + Name = "Apache 2.0", + Url = new Uri("https://www.apache.org/licenses/LICENSE-2.0"), + }, + }) + .WithServer("scram-connections", new AsyncApiServer { + Url = "test.mykafkacluster.org:18092", + Protocol = "kafka-secure", + Description = "Test broker secured with scramSha256", + Security = new List + { new AsyncApiSecurityRequirement { { @@ -232,9 +243,9 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }, new List() }, }, - }, - Tags = new List - { + }, + Tags = new List + { new AsyncApiTag { Name = "env:test-scram", @@ -250,15 +261,15 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Name = "visibility:private", Description = "This resource is private and only available to certain users", }, - }, - }) - .WithServer("mtls-connections", new AsyncApiServer - { - Url = "test.mykafkacluster.org:28092", - Protocol = "kafka-secure", - Description = "Test broker secured with X509", - Security = new List + }, + }) + .WithServer("mtls-connections", new AsyncApiServer { + Url = "test.mykafkacluster.org:28092", + Protocol = "kafka-secure", + Description = "Test broker secured with X509", + Security = new List + { new AsyncApiSecurityRequirement { { @@ -272,9 +283,9 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }, new List() }, }, - }, - Tags = new List - { + }, + Tags = new List + { new AsyncApiTag { Name = "env:test-mtls", @@ -290,16 +301,16 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Name = "visibility:private", Description = "This resource is private and only available to certain users", }, - }, - }) - .WithDefaultContentType() - .WithChannel( - "smartylighting.streetlights.1.0.event.{streetlightId}.lighting.measured", - new AsyncApiChannel() - { - Description = "The topic on which measured values may be produced and consumed.", - Parameters = new Dictionary + }, + }) + .WithDefaultContentType() + .WithChannel( + "smartylighting.streetlights.1.0.event.{streetlightId}.lighting.measured", + new AsyncApiChannel() { + Description = "The topic on which measured values may be produced and consumed.", + Parameters = new Dictionary + { { "streetlightId", new AsyncApiParameter() { @@ -310,13 +321,13 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }, } }, - }, - Publish = new AsyncApiOperation() - { - Summary = "Inform about environmental lighting conditions of a particular streetlight.", - OperationId = "receiveLightMeasurement", - Traits = new List + }, + Publish = new AsyncApiOperation() { + Summary = "Inform about environmental lighting conditions of a particular streetlight.", + OperationId = "receiveLightMeasurement", + Traits = new List + { new AsyncApiOperationTrait() { Reference = new AsyncApiReference() @@ -325,9 +336,9 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Type = ReferenceType.OperationTrait, }, }, - }, - Message = new List - { + }, + Message = new List + { new AsyncApiMessage() { Reference = new AsyncApiReference() @@ -336,15 +347,15 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Type = ReferenceType.Message, }, }, + }, }, - }, - }) - .WithChannel( - "smartylighting.streetlights.1.0.action.{streetlightId}.turn.on", - new AsyncApiChannel() - { - Parameters = new Dictionary + }) + .WithChannel( + "smartylighting.streetlights.1.0.action.{streetlightId}.turn.on", + new AsyncApiChannel() { + Parameters = new Dictionary + { { "streetlightId", new AsyncApiParameter() { @@ -355,12 +366,12 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }, } }, - }, - Subscribe = new AsyncApiOperation() - { - OperationId = "turnOn", - Traits = new List + }, + Subscribe = new AsyncApiOperation() { + OperationId = "turnOn", + Traits = new List + { new AsyncApiOperationTrait() { Reference = new AsyncApiReference() @@ -369,9 +380,9 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Type = ReferenceType.OperationTrait, }, }, - }, - Message = new List - { + }, + Message = new List + { new AsyncApiMessage() { Reference = new AsyncApiReference() @@ -380,15 +391,15 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Type = ReferenceType.Message, }, }, + }, }, - }, - }) - .WithChannel( - "smartylighting.streetlights.1.0.action.{streetlightId}.turn.off", - new AsyncApiChannel() - { - Parameters = new Dictionary + }) + .WithChannel( + "smartylighting.streetlights.1.0.action.{streetlightId}.turn.off", + new AsyncApiChannel() { + Parameters = new Dictionary + { { "streetlightId", new AsyncApiParameter() { @@ -399,12 +410,12 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }, } }, - }, - Subscribe = new AsyncApiOperation() - { - OperationId = "turnOff", - Traits = new List + }, + Subscribe = new AsyncApiOperation() { + OperationId = "turnOff", + Traits = new List + { new AsyncApiOperationTrait() { Reference = new AsyncApiReference() @@ -413,9 +424,9 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Type = ReferenceType.OperationTrait, }, }, - }, - Message = new List - { + }, + Message = new List + { new AsyncApiMessage() { Reference = new AsyncApiReference() @@ -424,15 +435,15 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Type = ReferenceType.Message, }, }, + }, }, - }, - }) - .WithChannel( - "smartylighting.streetlights.1.0.action.{streetlightId}.dim", - new AsyncApiChannel() - { - Parameters = new Dictionary + }) + .WithChannel( + "smartylighting.streetlights.1.0.action.{streetlightId}.dim", + new AsyncApiChannel() { + Parameters = new Dictionary + { { "streetlightId", new AsyncApiParameter() { @@ -443,12 +454,12 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }, } }, - }, - Subscribe = new AsyncApiOperation() - { - OperationId = "dimLight", - Traits = new List + }, + Subscribe = new AsyncApiOperation() { + OperationId = "dimLight", + Traits = new List + { new AsyncApiOperationTrait() { Reference = new AsyncApiReference() @@ -457,9 +468,9 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Type = ReferenceType.OperationTrait, }, }, - }, - Message = new List - { + }, + Message = new List + { new AsyncApiMessage() { Reference = new AsyncApiReference() @@ -468,17 +479,17 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Type = ReferenceType.Message, }, }, + }, }, - }, - }) - .WithComponent("lightMeasured", new AsyncApiMessage() - { - Name = "lightMeasured", - Title = "Light measured", - Summary = "Inform about environmental lighting conditions of a particular streetlight.", - ContentType = "application/json", - Traits = new List() + }) + .WithComponent("lightMeasured", new AsyncApiMessage() { + Name = "lightMeasured", + Title = "Light measured", + Summary = "Inform about environmental lighting conditions of a particular streetlight.", + ContentType = "application/json", + Traits = new List() + { new AsyncApiMessageTrait() { Reference = new AsyncApiReference() @@ -487,23 +498,23 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Id = "commonHeaders", }, }, - }, - Payload = new AsyncApiSchema() - { - Reference = new AsyncApiReference() + }, + Payload = new AsyncApiSchema() { - Type = ReferenceType.Schema, - Id = "lightMeasuredPayload", + Reference = new AsyncApiReference() + { + Type = ReferenceType.Schema, + Id = "lightMeasuredPayload", + }, }, - }, - }) - .WithComponent("turnOnOff", new AsyncApiMessage() - { - Name = "turnOnOff", - Title = "Turn on/off", - Summary = "Command a particular streetlight to turn the lights on or off.", - Traits = new List() + }) + .WithComponent("turnOnOff", new AsyncApiMessage() { + Name = "turnOnOff", + Title = "Turn on/off", + Summary = "Command a particular streetlight to turn the lights on or off.", + Traits = new List() + { new AsyncApiMessageTrait() { Reference = new AsyncApiReference() @@ -512,23 +523,23 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Id = "commonHeaders", }, }, - }, - Payload = new AsyncApiSchema() - { - Reference = new AsyncApiReference() + }, + Payload = new AsyncApiSchema() { - Type = ReferenceType.Schema, - Id = "turnOnOffPayload", + Reference = new AsyncApiReference() + { + Type = ReferenceType.Schema, + Id = "turnOnOffPayload", + }, }, - }, - }) - .WithComponent("dimLight", new AsyncApiMessage() - { - Name = "dimLight", - Title = "Dim light", - Summary = "Command a particular streetlight to dim the lights.", - Traits = new List() + }) + .WithComponent("dimLight", new AsyncApiMessage() { + Name = "dimLight", + Title = "Dim light", + Summary = "Command a particular streetlight to dim the lights.", + Traits = new List() + { new AsyncApiMessageTrait() { Reference = new AsyncApiReference() @@ -537,21 +548,21 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Id = "commonHeaders", }, }, - }, - Payload = new AsyncApiSchema() - { - Reference = new AsyncApiReference() + }, + Payload = new AsyncApiSchema() { - Type = ReferenceType.Schema, - Id = "dimLightPayload", + Reference = new AsyncApiReference() + { + Type = ReferenceType.Schema, + Id = "dimLightPayload", + }, }, - }, - }) - .WithComponent("lightMeasuredPayload", new AsyncApiSchema() - { - Type = SchemaType.Object, - Properties = new Dictionary() + }) + .WithComponent("lightMeasuredPayload", new AsyncApiSchema() { + Type = SchemaType.Object, + Properties = new Dictionary() + { { "lumens", new AsyncApiSchema() { @@ -570,13 +581,13 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }, } }, - }, - }) - .WithComponent("turnOnOffPayload", new AsyncApiSchema() - { - Type = SchemaType.Object, - Properties = new Dictionary() + }, + }) + .WithComponent("turnOnOffPayload", new AsyncApiSchema() { + Type = SchemaType.Object, + Properties = new Dictionary() + { { "command", new AsyncApiSchema() { @@ -599,13 +610,13 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }, } }, - }, - }) - .WithComponent("dimLightPayload", new AsyncApiSchema() - { - Type = SchemaType.Object, - Properties = new Dictionary() + }, + }) + .WithComponent("dimLightPayload", new AsyncApiSchema() { + Type = SchemaType.Object, + Properties = new Dictionary() + { { "percentage", new AsyncApiSchema() { @@ -625,40 +636,40 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }, } }, - }, - }) - .WithComponent("sentAt", new AsyncApiSchema() - { - Type = SchemaType.String, - Format = "date-time", - Description = "Date and time when the message was sent.", - - }) - .WithComponent("saslScram", new AsyncApiSecurityScheme - { - Type = SecuritySchemeType.ScramSha256, - Description = "Provide your username and password for SASL/SCRAM authentication", - }) - .WithComponent("certs", new AsyncApiSecurityScheme - { - Type = SecuritySchemeType.X509, - Description = "Download the certificate files from service provider", - }) - .WithComponent("streetlightId", new AsyncApiParameter() - { - Description = "The ID of the streetlight.", - Schema = new AsyncApiSchema() + }, + }) + .WithComponent("sentAt", new AsyncApiSchema() { Type = SchemaType.String, - }, - }) - .WithComponent("commonHeaders", new AsyncApiMessageTrait() - { - Headers = new AsyncApiSchema() + Format = "date-time", + Description = "Date and time when the message was sent.", + + }) + .WithComponent("saslScram", new AsyncApiSecurityScheme { - Type = SchemaType.Object, - Properties = new Dictionary() + Type = SecuritySchemeType.ScramSha256, + Description = "Provide your username and password for SASL/SCRAM authentication", + }) + .WithComponent("certs", new AsyncApiSecurityScheme + { + Type = SecuritySchemeType.X509, + Description = "Download the certificate files from service provider", + }) + .WithComponent("streetlightId", new AsyncApiParameter() + { + Description = "The ID of the streetlight.", + Schema = new AsyncApiSchema() { + Type = SchemaType.String, + }, + }) + .WithComponent("commonHeaders", new AsyncApiMessageTrait() + { + Headers = new AsyncApiSchema() + { + Type = SchemaType.Object, + Properties = new Dictionary() + { { "my-app-header", new AsyncApiSchema() { @@ -667,13 +678,13 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Maximum = 100, } }, + }, }, - }, - }) - .WithComponent("kafka", new AsyncApiOperationTrait() - { - Bindings = new AsyncApiBindings() + }) + .WithComponent("kafka", new AsyncApiOperationTrait() { + Bindings = new AsyncApiBindings() + { { "kafka", new KafkaOperationBinding() { @@ -687,9 +698,9 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }, } }, - }, - }) - .Build(); + }, + }) + .Build(); // Act var actual = asyncApiDocument.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); @@ -868,7 +879,7 @@ public void SerializeV2_WithFullSpec_Serializes() string authorizationUrl = "https://example.com/authorization"; string requirementString = "requirementItem"; - + var document = new AsyncApiDocument() { Id = documentId, @@ -1171,7 +1182,7 @@ public void Serialize_WithBindingReferences_SerializesDeserializes() Id = "bindings", }, }, - } + } } }, ServerBindings = new Dictionary>() @@ -1193,7 +1204,7 @@ public void Serialize_WithBindingReferences_SerializesDeserializes() { new PulsarChannelBinding() { - Namespace = "users", + Namespace = "users", Persistence = AsyncAPI.Models.Bindings.Pulsar.Persistence.Persistent, } } @@ -1216,7 +1227,7 @@ public void Serialize_WithBindingReferences_SerializesDeserializes() var reader = new AsyncApiStringReader(settings); var deserialized = reader.Read(actual, out var diagnostic); } - + [Test] public void Serializev2_WithBindings_Serializes() { diff --git a/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj b/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj index 1be4a264..f10a92bc 100644 --- a/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj +++ b/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj @@ -1,55 +1,55 @@  - - net6.0 - disable - enable + + net6.0 + disable + enable + false + $(NoWarn);SA1600 + - false - + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + diff --git a/test/LEGO.AsyncAPI.Tests/Writers/SpecialCharacterStringExtensionsTests.cs b/test/LEGO.AsyncAPI.Tests/Writers/SpecialCharacterStringExtensionsTests.cs new file mode 100644 index 00000000..a71b678e --- /dev/null +++ b/test/LEGO.AsyncAPI.Tests/Writers/SpecialCharacterStringExtensionsTests.cs @@ -0,0 +1,73 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests.Writers +{ + using LEGO.AsyncAPI.Writers; + using NUnit.Framework; + using System; + + internal class SpecialCharacterStringExtensionsTests + { + [Test] + public void GetYamlCompatibleString_NullValue_ReturnsNull() + => this.Compose(null, "null"); + + [Test] + public void GetYamlCompatibleString_EmptyValue_ReturnsNull() + => this.Compose(string.Empty, "''"); + + [Test] + public void GetYamlCompatibleString_NullWordString_ReturnsWrappedValue() + => this.Compose("null", "'null'"); + + [Test] + public void GetYamlCompatibleString_TildaWordString_ReturnsWrappedValue() + => this.Compose("~", "'~'"); + + [Test] + [TestCase("\0", "\\0")] + [TestCase("\x01", "\\x01")] + [TestCase("\x02", "\\x02")] + [TestCase("\x03", "\\x03")] + [TestCase("\x04", "\\x04")] + [TestCase("\x05", "\\x05")] + [TestCase("\x06", "\\x06")] + [TestCase("\a", "\\a")] + [TestCase("\b", "\\b")] + [TestCase("\t", "\\t")] + [TestCase("\n", "\\n")] + [TestCase("\v", "\\v")] + [TestCase("\f", "\\f")] + [TestCase("\r", "\\r")] + [TestCase("\x0e", "\\x0e")] + [TestCase("\x0f", "\\x0f")] + [TestCase("\x10", "\\x10")] + [TestCase("\x11", "\\x11")] + [TestCase("\x12", "\\x12")] + [TestCase("\x13", "\\x13")] + [TestCase("\x14", "\\x14")] + [TestCase("\x15", "\\x15")] + [TestCase("\x16", "\\x16")] + [TestCase("\x17", "\\x17")] + [TestCase("\x18", "\\x18")] + [TestCase("\x19", "\\x19")] + [TestCase("\x1a", "\\x1a")] + [TestCase("\x1b", "\\x1b")] + [TestCase("\x1c", "\\x1c")] + [TestCase("\x1d", "\\x1d")] + [TestCase("\x1e", "\\x1e")] + [TestCase("\x1f", "\\x1f")] + public void GetYamlCompatibleString_ControlCharacters_AreEscaped(string input, string expected) + => this.Compose($"value {input}", $"'value {expected}'"); + + private void Compose( + string? input, + string expected) + { + string actual = SpecialCharacterStringExtensions.GetYamlCompatibleString(input); + Console.WriteLine($"Expected: <{expected}>"); + Console.WriteLine($"Actual: <{actual}>"); + Assert.AreEqual(expected, actual); + } + } +} From 010e2dc0b0d70a485d8f28ff6bfd9780b24f02e1 Mon Sep 17 00:00:00 2001 From: Byron Mayne Date: Sun, 10 Mar 2024 15:32:17 -0400 Subject: [PATCH 3/8] Fixed all the unit tests and added more tests --- .../SpecialCharacterStringExtensions.cs | 6 ++-- .../AsyncApiDocumentV2Tests.cs | 12 +++---- .../Models/AsyncApiSchema_Should.cs | 4 +-- .../SpecialCharacterStringExtensionsTests.cs | 36 +++++++++++++++++++ 4 files changed, 48 insertions(+), 10 deletions(-) diff --git a/src/LEGO.AsyncAPI/Writers/SpecialCharacterStringExtensions.cs b/src/LEGO.AsyncAPI/Writers/SpecialCharacterStringExtensions.cs index 60f4fd8e..bb515ae3 100644 --- a/src/LEGO.AsyncAPI/Writers/SpecialCharacterStringExtensions.cs +++ b/src/LEGO.AsyncAPI/Writers/SpecialCharacterStringExtensions.cs @@ -5,9 +5,12 @@ namespace LEGO.AsyncAPI.Writers using System; using System.Globalization; using System.Linq; + using System.Text.RegularExpressions; public static class SpecialCharacterStringExtensions { + private static readonly Regex numberRegex = new Regex("^[+-]?[0-9]*\\.?[0-9]*$", RegexOptions.Compiled); + // Plain style strings cannot start with indicators. // http://www.yaml.org/spec/1.2/spec.html#indicator// private static readonly char[] yamlIndicators = @@ -206,8 +209,7 @@ internal static string GetYamlCompatibleString(this string? input) } // Handle numbers - char first = input[0]; - if (char.IsDigit(first) || first == '-' || first == '+') + if (numberRegex.IsMatch(input)) { return $"'{input}'"; } diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs index d78ea65e..fb2ab526 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs @@ -26,13 +26,13 @@ public class ExtensionClass public class AsyncApiDocumentV2Tests { [Test] - public void AsyncApiDocumentYaml_DefalutVersionNumber_IsWrappedInSingleQuotes() + public void AsyncApiDocumentYaml_DefalutVersionNumber_IsNotWrappedInSingleQuotes() { string actual = new AsyncApiDocumentBuilder() .Build() .SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - Assert.AreEqual("asyncapi: '2.6.0'\ninfo: { }", actual); + Assert.AreEqual("asyncapi: 2.6.0\ninfo: { }", actual); } [Test] @@ -40,10 +40,10 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() { // Arrange var expected = -@"asyncapi: '2.6.0' +@"asyncapi: 2.6.0 info: title: Streetlights Kafka API - version: '1.0.0' + version: 1.0.0 description: The Smartylighting Streetlights API allows you to remotely manage the city lights. license: name: Apache 2.0 @@ -715,7 +715,7 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() public void SerializeV2_WithFullSpec_Serializes() { var expected = - @"asyncapi: '2.6.0' + @"asyncapi: 2.6.0 info: title: apiTitle version: apiVersion @@ -1231,7 +1231,7 @@ public void Serialize_WithBindingReferences_SerializesDeserializes() [Test] public void Serializev2_WithBindings_Serializes() { - var expected = @"asyncapi: '2.6.0' + var expected = @"asyncapi: 2.6.0 info: description: test description servers: diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs index 686ed9b6..cce55a71 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs @@ -284,7 +284,7 @@ public class AsyncApiSchema_Should }; private string NoInlinedReferences => - @"asyncapi: '2.6.0' + @"asyncapi: 2.6.0 info: title: Streetlights Kafka API version: 1.0.0 @@ -320,7 +320,7 @@ public class AsyncApiSchema_Should description: test"; private string InlinedReferences => - @"asyncapi: '2.6.0' + @"asyncapi: 2.6.0 info: title: Streetlights Kafka API version: 1.0.0 diff --git a/test/LEGO.AsyncAPI.Tests/Writers/SpecialCharacterStringExtensionsTests.cs b/test/LEGO.AsyncAPI.Tests/Writers/SpecialCharacterStringExtensionsTests.cs index a71b678e..f58e1b99 100644 --- a/test/LEGO.AsyncAPI.Tests/Writers/SpecialCharacterStringExtensionsTests.cs +++ b/test/LEGO.AsyncAPI.Tests/Writers/SpecialCharacterStringExtensionsTests.cs @@ -24,6 +24,42 @@ public void GetYamlCompatibleString_NullWordString_ReturnsWrappedValue() public void GetYamlCompatibleString_TildaWordString_ReturnsWrappedValue() => this.Compose("~", "'~'"); + [Test] + public void GetYamlCompatibleString_IntegerWithTwoPeriods_RendersPlainStyle() + => this.Compose("1.2.3", "1.2.3"); + + [Test] + public void GetYamlCompatibleString_Float_WrappedWithQuotes() + => this.Compose("1.2", "'1.2'"); + + [Test] + public void GetYamlCompatibleString_PositiveFloat_WrappedWithQuotes() + => this.Compose("+1.2", "'+1.2'"); + + [Test] + public void GetYamlCompatibleString_NegativeFloat_WrappedWithQuotes() + => this.Compose("-1.2", "'-1.2'"); + + [Test] + public void GetYamlCompatibleString_PositiveInfinityFloat_WrappedWithQuotes() + => this.Compose(".inf", "'.inf'"); + + [Test] + public void GetYamlCompatibleString_NegativeInfinityFloat_WrappedWithQuotes() + => this.Compose("-.inf", "'-.inf'"); + + [Test] + public void GetYamlCompatibleString_NanFloat_WrappedWithQuotes() + => this.Compose(".nan", "'.nan'"); + + [Test] + public void GetYamlCompatibleString_TrueString_WrappedWithQuotes() + => this.Compose("true", "'true'"); + + [Test] + public void GetYamlCompatibleString_FalseString_WrappedWithQuotes() + => this.Compose("false", "'flase'"); + [Test] [TestCase("\0", "\\0")] [TestCase("\x01", "\\x01")] From 95fc07047e0101f9651324c6b09e47b9078ac41b Mon Sep 17 00:00:00 2001 From: Byron Mayne Date: Tue, 12 Mar 2024 09:00:01 -0400 Subject: [PATCH 4/8] Updated the unit test to match existing behaviour --- .../SpecialCharacterStringExtensions.cs | 7 +++++- .../SpecialCharacterStringExtensionsTests.cs | 22 ++++++++++++++----- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/LEGO.AsyncAPI/Writers/SpecialCharacterStringExtensions.cs b/src/LEGO.AsyncAPI/Writers/SpecialCharacterStringExtensions.cs index bb515ae3..300b12e0 100644 --- a/src/LEGO.AsyncAPI/Writers/SpecialCharacterStringExtensions.cs +++ b/src/LEGO.AsyncAPI/Writers/SpecialCharacterStringExtensions.cs @@ -9,7 +9,7 @@ namespace LEGO.AsyncAPI.Writers public static class SpecialCharacterStringExtensions { - private static readonly Regex numberRegex = new Regex("^[+-]?[0-9]*\\.?[0-9]*$", RegexOptions.Compiled); + private static readonly Regex numberRegex = new Regex("^[+-]?[0-9]*\\.?[0-9]*$", RegexOptions.Compiled, TimeSpan.FromSeconds(1)); // Plain style strings cannot start with indicators. // http://www.yaml.org/spec/1.2/spec.html#indicator// @@ -191,6 +191,11 @@ internal static string GetYamlCompatibleString(this string? input) return $"'{input}'"; } + if (DateTime.TryParse(input, out var _)) + { + return $"'{input}'"; + } + // Handle lexemes that can be intperated as as string // https://yaml.org/spec/1.2-old/spec.html#id2761292 switch (input.ToLower()) diff --git a/test/LEGO.AsyncAPI.Tests/Writers/SpecialCharacterStringExtensionsTests.cs b/test/LEGO.AsyncAPI.Tests/Writers/SpecialCharacterStringExtensionsTests.cs index f58e1b99..4c5a775c 100644 --- a/test/LEGO.AsyncAPI.Tests/Writers/SpecialCharacterStringExtensionsTests.cs +++ b/test/LEGO.AsyncAPI.Tests/Writers/SpecialCharacterStringExtensionsTests.cs @@ -24,10 +24,10 @@ public void GetYamlCompatibleString_NullWordString_ReturnsWrappedValue() public void GetYamlCompatibleString_TildaWordString_ReturnsWrappedValue() => this.Compose("~", "'~'"); - [Test] - public void GetYamlCompatibleString_IntegerWithTwoPeriods_RendersPlainStyle() - => this.Compose("1.2.3", "1.2.3"); - + // [Test] + // public void GetYamlCompatibleString_IntegerWithTwoPeriods_RendersPlainStyle() + // => this.Compose("1.2.3", "1.2.3"); + // TODO: This actually wraps in quotes which is wrong but this is already existing behaviour [Test] public void GetYamlCompatibleString_Float_WrappedWithQuotes() => this.Compose("1.2", "'1.2'"); @@ -58,7 +58,19 @@ public void GetYamlCompatibleString_TrueString_WrappedWithQuotes() [Test] public void GetYamlCompatibleString_FalseString_WrappedWithQuotes() - => this.Compose("false", "'flase'"); + => this.Compose("false", "'false'"); + + [Test] + public void GetYamlCompatibleString_DateTimeSlashString_WrappedWithQuotes() + => this.Compose("12/31/2022 23:59:59", "'12/31/2022 23:59:59'"); + + [Test] + public void GetYamlCompatibleString_DateTimeDashString_WrappedWithQuotes() + => this.Compose("2022-12-31 23:59:59", "'2022-12-31 23:59:59'"); + + [Test] + public void GetYamlCompatibleString_DateTimeISOString_WrappedWithQuotes() + => this.Compose("2022-12-31T23:59:59Z", "'2022-12-31T23:59:59Z'"); [Test] [TestCase("\0", "\\0")] From 99f7058b03c3c921da2760367aaee6450fc71575 Mon Sep 17 00:00:00 2001 From: Byron Mayne Date: Tue, 12 Mar 2024 22:45:38 -0400 Subject: [PATCH 5/8] Removed the changes for Yaml --- .../SpecialCharacterStringExtensions.cs | 43 ++----- .../SpecialCharacterStringExtensionsTests.cs | 121 ------------------ 2 files changed, 8 insertions(+), 156 deletions(-) delete mode 100644 test/LEGO.AsyncAPI.Tests/Writers/SpecialCharacterStringExtensionsTests.cs diff --git a/src/LEGO.AsyncAPI/Writers/SpecialCharacterStringExtensions.cs b/src/LEGO.AsyncAPI/Writers/SpecialCharacterStringExtensions.cs index 300b12e0..1a091066 100644 --- a/src/LEGO.AsyncAPI/Writers/SpecialCharacterStringExtensions.cs +++ b/src/LEGO.AsyncAPI/Writers/SpecialCharacterStringExtensions.cs @@ -5,12 +5,9 @@ namespace LEGO.AsyncAPI.Writers using System; using System.Globalization; using System.Linq; - using System.Text.RegularExpressions; public static class SpecialCharacterStringExtensions { - private static readonly Regex numberRegex = new Regex("^[+-]?[0-9]*\\.?[0-9]*$", RegexOptions.Compiled, TimeSpan.FromSeconds(1)); - // Plain style strings cannot start with indicators. // http://www.yaml.org/spec/1.2/spec.html#indicator// private static readonly char[] yamlIndicators = @@ -103,15 +100,10 @@ public static class SpecialCharacterStringExtensions /// Escapes all special characters and put the string in quotes if necessary to /// get a YAML-compatible string. /// - internal static string GetYamlCompatibleString(this string? input) + internal static string GetYamlCompatibleString(this string input) { - if (input == null) - { - return "null"; - } - // If string is an empty string, wrap it in quote to ensure it is not recognized as null. - if (input.Length == 0) + if (input == "") { return "''"; } @@ -171,7 +163,7 @@ internal static string GetYamlCompatibleString(this string? input) input = input.Replace("\x1e", "\\x1e"); input = input.Replace("\x1f", "\\x1f"); - return $"'{input}'"; + return $"\"{input}\""; } // If string @@ -191,30 +183,11 @@ internal static string GetYamlCompatibleString(this string? input) return $"'{input}'"; } - if (DateTime.TryParse(input, out var _)) - { - return $"'{input}'"; - } - - // Handle lexemes that can be intperated as as string - // https://yaml.org/spec/1.2-old/spec.html#id2761292 - switch (input.ToLower()) - { - // Example 2.20. Floating Point - case "-.inf": - case ".inf": - case ".nan": - // Example 2.21. Miscellaneous - case "null": - - // Booleans - case "true": - case "false": - return $"'{input}'"; - } - - // Handle numbers - if (numberRegex.IsMatch(input)) + // If string can be mistaken as a number, a boolean, or a timestamp, + // wrap it in quote to indicate that this is indeed a string, not a number, a boolean, or a timestamp + if (decimal.TryParse(input, NumberStyles.Float, CultureInfo.InvariantCulture, out var _) || + bool.TryParse(input, out var _) || + DateTime.TryParse(input, out var _)) { return $"'{input}'"; } diff --git a/test/LEGO.AsyncAPI.Tests/Writers/SpecialCharacterStringExtensionsTests.cs b/test/LEGO.AsyncAPI.Tests/Writers/SpecialCharacterStringExtensionsTests.cs deleted file mode 100644 index 4c5a775c..00000000 --- a/test/LEGO.AsyncAPI.Tests/Writers/SpecialCharacterStringExtensionsTests.cs +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Tests.Writers -{ - using LEGO.AsyncAPI.Writers; - using NUnit.Framework; - using System; - - internal class SpecialCharacterStringExtensionsTests - { - [Test] - public void GetYamlCompatibleString_NullValue_ReturnsNull() - => this.Compose(null, "null"); - - [Test] - public void GetYamlCompatibleString_EmptyValue_ReturnsNull() - => this.Compose(string.Empty, "''"); - - [Test] - public void GetYamlCompatibleString_NullWordString_ReturnsWrappedValue() - => this.Compose("null", "'null'"); - - [Test] - public void GetYamlCompatibleString_TildaWordString_ReturnsWrappedValue() - => this.Compose("~", "'~'"); - - // [Test] - // public void GetYamlCompatibleString_IntegerWithTwoPeriods_RendersPlainStyle() - // => this.Compose("1.2.3", "1.2.3"); - // TODO: This actually wraps in quotes which is wrong but this is already existing behaviour - [Test] - public void GetYamlCompatibleString_Float_WrappedWithQuotes() - => this.Compose("1.2", "'1.2'"); - - [Test] - public void GetYamlCompatibleString_PositiveFloat_WrappedWithQuotes() - => this.Compose("+1.2", "'+1.2'"); - - [Test] - public void GetYamlCompatibleString_NegativeFloat_WrappedWithQuotes() - => this.Compose("-1.2", "'-1.2'"); - - [Test] - public void GetYamlCompatibleString_PositiveInfinityFloat_WrappedWithQuotes() - => this.Compose(".inf", "'.inf'"); - - [Test] - public void GetYamlCompatibleString_NegativeInfinityFloat_WrappedWithQuotes() - => this.Compose("-.inf", "'-.inf'"); - - [Test] - public void GetYamlCompatibleString_NanFloat_WrappedWithQuotes() - => this.Compose(".nan", "'.nan'"); - - [Test] - public void GetYamlCompatibleString_TrueString_WrappedWithQuotes() - => this.Compose("true", "'true'"); - - [Test] - public void GetYamlCompatibleString_FalseString_WrappedWithQuotes() - => this.Compose("false", "'false'"); - - [Test] - public void GetYamlCompatibleString_DateTimeSlashString_WrappedWithQuotes() - => this.Compose("12/31/2022 23:59:59", "'12/31/2022 23:59:59'"); - - [Test] - public void GetYamlCompatibleString_DateTimeDashString_WrappedWithQuotes() - => this.Compose("2022-12-31 23:59:59", "'2022-12-31 23:59:59'"); - - [Test] - public void GetYamlCompatibleString_DateTimeISOString_WrappedWithQuotes() - => this.Compose("2022-12-31T23:59:59Z", "'2022-12-31T23:59:59Z'"); - - [Test] - [TestCase("\0", "\\0")] - [TestCase("\x01", "\\x01")] - [TestCase("\x02", "\\x02")] - [TestCase("\x03", "\\x03")] - [TestCase("\x04", "\\x04")] - [TestCase("\x05", "\\x05")] - [TestCase("\x06", "\\x06")] - [TestCase("\a", "\\a")] - [TestCase("\b", "\\b")] - [TestCase("\t", "\\t")] - [TestCase("\n", "\\n")] - [TestCase("\v", "\\v")] - [TestCase("\f", "\\f")] - [TestCase("\r", "\\r")] - [TestCase("\x0e", "\\x0e")] - [TestCase("\x0f", "\\x0f")] - [TestCase("\x10", "\\x10")] - [TestCase("\x11", "\\x11")] - [TestCase("\x12", "\\x12")] - [TestCase("\x13", "\\x13")] - [TestCase("\x14", "\\x14")] - [TestCase("\x15", "\\x15")] - [TestCase("\x16", "\\x16")] - [TestCase("\x17", "\\x17")] - [TestCase("\x18", "\\x18")] - [TestCase("\x19", "\\x19")] - [TestCase("\x1a", "\\x1a")] - [TestCase("\x1b", "\\x1b")] - [TestCase("\x1c", "\\x1c")] - [TestCase("\x1d", "\\x1d")] - [TestCase("\x1e", "\\x1e")] - [TestCase("\x1f", "\\x1f")] - public void GetYamlCompatibleString_ControlCharacters_AreEscaped(string input, string expected) - => this.Compose($"value {input}", $"'value {expected}'"); - - private void Compose( - string? input, - string expected) - { - string actual = SpecialCharacterStringExtensions.GetYamlCompatibleString(input); - Console.WriteLine($"Expected: <{expected}>"); - Console.WriteLine($"Actual: <{actual}>"); - Assert.AreEqual(expected, actual); - } - } -} From d4fbd1672280d49c40a1b7b97ece1d6cf93c46a0 Mon Sep 17 00:00:00 2001 From: Byron Mayne Date: Tue, 12 Mar 2024 22:47:06 -0400 Subject: [PATCH 6/8] Removed the AsyncApiSchema_ShouldChanges --- test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs index cce55a71..e7d7725e 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs @@ -263,7 +263,7 @@ public class AsyncApiSchema_Should { ["property6"] = new AsyncApiSchema { - Type = SchemaType.Boolean , + Type = SchemaType.Boolean, }, }, }, @@ -284,7 +284,7 @@ public class AsyncApiSchema_Should }; private string NoInlinedReferences => - @"asyncapi: 2.6.0 + @"asyncapi: '2.6.0' info: title: Streetlights Kafka API version: 1.0.0 @@ -320,7 +320,7 @@ public class AsyncApiSchema_Should description: test"; private string InlinedReferences => - @"asyncapi: 2.6.0 + @"asyncapi: '2.6.0' info: title: Streetlights Kafka API version: 1.0.0 From 00bc4a920c4628436a06ab5e4eaeb302b72d6b9f Mon Sep 17 00:00:00 2001 From: Byron Mayne Date: Tue, 12 Mar 2024 22:48:58 -0400 Subject: [PATCH 7/8] Added the missing space --- test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs index e7d7725e..686ed9b6 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs @@ -263,7 +263,7 @@ public class AsyncApiSchema_Should { ["property6"] = new AsyncApiSchema { - Type = SchemaType.Boolean, + Type = SchemaType.Boolean , }, }, }, From ba2d8a949fad1769f0fba83971214aecb35cd6f5 Mon Sep 17 00:00:00 2001 From: Byron Mayne Date: Wed, 13 Mar 2024 20:56:09 -0400 Subject: [PATCH 8/8] Reverted the test changes --- .../AsyncApiDocumentV2Tests.cs | 411 +++++++++--------- 1 file changed, 200 insertions(+), 211 deletions(-) diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs index fb2ab526..a598ac18 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs @@ -22,25 +22,14 @@ public class ExtensionClass public string Key { get; set; } public long OtherKey { get; set; } } - public class AsyncApiDocumentV2Tests { - [Test] - public void AsyncApiDocumentYaml_DefalutVersionNumber_IsNotWrappedInSingleQuotes() - { - string actual = new AsyncApiDocumentBuilder() - .Build() - .SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - - Assert.AreEqual("asyncapi: 2.6.0\ninfo: { }", actual); - } - [Test] public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() { // Arrange var expected = -@"asyncapi: 2.6.0 +@"asyncapi: '2.6.0' info: title: Streetlights Kafka API version: 1.0.0 @@ -211,25 +200,25 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() maximum: 100 minimum: 0"; - var asyncApiDocument = new AsyncApiDocumentBuilder() - .WithInfo(new AsyncApiInfo + var asyncApiDocument = new AsyncApiDocumentBuilder() + .WithInfo(new AsyncApiInfo + { + Title = "Streetlights Kafka API", + Version = "1.0.0", + Description = "The Smartylighting Streetlights API allows you to remotely manage the city lights.", + License = new AsyncApiLicense { - Title = "Streetlights Kafka API", - Version = "1.0.0", - Description = "The Smartylighting Streetlights API allows you to remotely manage the city lights.", - License = new AsyncApiLicense - { - Name = "Apache 2.0", - Url = new Uri("https://www.apache.org/licenses/LICENSE-2.0"), - }, - }) - .WithServer("scram-connections", new AsyncApiServer + Name = "Apache 2.0", + Url = new Uri("https://www.apache.org/licenses/LICENSE-2.0"), + }, + }) + .WithServer("scram-connections", new AsyncApiServer + { + Url = "test.mykafkacluster.org:18092", + Protocol = "kafka-secure", + Description = "Test broker secured with scramSha256", + Security = new List { - Url = "test.mykafkacluster.org:18092", - Protocol = "kafka-secure", - Description = "Test broker secured with scramSha256", - Security = new List - { new AsyncApiSecurityRequirement { { @@ -243,9 +232,9 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }, new List() }, }, - }, - Tags = new List - { + }, + Tags = new List + { new AsyncApiTag { Name = "env:test-scram", @@ -261,15 +250,15 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Name = "visibility:private", Description = "This resource is private and only available to certain users", }, - }, - }) - .WithServer("mtls-connections", new AsyncApiServer + }, + }) + .WithServer("mtls-connections", new AsyncApiServer + { + Url = "test.mykafkacluster.org:28092", + Protocol = "kafka-secure", + Description = "Test broker secured with X509", + Security = new List { - Url = "test.mykafkacluster.org:28092", - Protocol = "kafka-secure", - Description = "Test broker secured with X509", - Security = new List - { new AsyncApiSecurityRequirement { { @@ -283,9 +272,9 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }, new List() }, }, - }, - Tags = new List - { + }, + Tags = new List + { new AsyncApiTag { Name = "env:test-mtls", @@ -301,16 +290,16 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Name = "visibility:private", Description = "This resource is private and only available to certain users", }, - }, - }) - .WithDefaultContentType() - .WithChannel( - "smartylighting.streetlights.1.0.event.{streetlightId}.lighting.measured", - new AsyncApiChannel() + }, + }) + .WithDefaultContentType() + .WithChannel( + "smartylighting.streetlights.1.0.event.{streetlightId}.lighting.measured", + new AsyncApiChannel() + { + Description = "The topic on which measured values may be produced and consumed.", + Parameters = new Dictionary { - Description = "The topic on which measured values may be produced and consumed.", - Parameters = new Dictionary - { { "streetlightId", new AsyncApiParameter() { @@ -321,13 +310,13 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }, } }, - }, - Publish = new AsyncApiOperation() + }, + Publish = new AsyncApiOperation() + { + Summary = "Inform about environmental lighting conditions of a particular streetlight.", + OperationId = "receiveLightMeasurement", + Traits = new List { - Summary = "Inform about environmental lighting conditions of a particular streetlight.", - OperationId = "receiveLightMeasurement", - Traits = new List - { new AsyncApiOperationTrait() { Reference = new AsyncApiReference() @@ -336,9 +325,9 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Type = ReferenceType.OperationTrait, }, }, - }, - Message = new List - { + }, + Message = new List + { new AsyncApiMessage() { Reference = new AsyncApiReference() @@ -347,15 +336,15 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Type = ReferenceType.Message, }, }, - }, }, - }) - .WithChannel( - "smartylighting.streetlights.1.0.action.{streetlightId}.turn.on", - new AsyncApiChannel() + }, + }) + .WithChannel( + "smartylighting.streetlights.1.0.action.{streetlightId}.turn.on", + new AsyncApiChannel() + { + Parameters = new Dictionary { - Parameters = new Dictionary - { { "streetlightId", new AsyncApiParameter() { @@ -366,12 +355,12 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }, } }, - }, - Subscribe = new AsyncApiOperation() + }, + Subscribe = new AsyncApiOperation() + { + OperationId = "turnOn", + Traits = new List { - OperationId = "turnOn", - Traits = new List - { new AsyncApiOperationTrait() { Reference = new AsyncApiReference() @@ -380,9 +369,9 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Type = ReferenceType.OperationTrait, }, }, - }, - Message = new List - { + }, + Message = new List + { new AsyncApiMessage() { Reference = new AsyncApiReference() @@ -391,15 +380,15 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Type = ReferenceType.Message, }, }, - }, }, - }) - .WithChannel( - "smartylighting.streetlights.1.0.action.{streetlightId}.turn.off", - new AsyncApiChannel() + }, + }) + .WithChannel( + "smartylighting.streetlights.1.0.action.{streetlightId}.turn.off", + new AsyncApiChannel() + { + Parameters = new Dictionary { - Parameters = new Dictionary - { { "streetlightId", new AsyncApiParameter() { @@ -410,12 +399,12 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }, } }, - }, - Subscribe = new AsyncApiOperation() + }, + Subscribe = new AsyncApiOperation() + { + OperationId = "turnOff", + Traits = new List { - OperationId = "turnOff", - Traits = new List - { new AsyncApiOperationTrait() { Reference = new AsyncApiReference() @@ -424,9 +413,9 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Type = ReferenceType.OperationTrait, }, }, - }, - Message = new List - { + }, + Message = new List + { new AsyncApiMessage() { Reference = new AsyncApiReference() @@ -435,15 +424,15 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Type = ReferenceType.Message, }, }, - }, }, - }) - .WithChannel( - "smartylighting.streetlights.1.0.action.{streetlightId}.dim", - new AsyncApiChannel() + }, + }) + .WithChannel( + "smartylighting.streetlights.1.0.action.{streetlightId}.dim", + new AsyncApiChannel() + { + Parameters = new Dictionary { - Parameters = new Dictionary - { { "streetlightId", new AsyncApiParameter() { @@ -454,12 +443,12 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }, } }, - }, - Subscribe = new AsyncApiOperation() + }, + Subscribe = new AsyncApiOperation() + { + OperationId = "dimLight", + Traits = new List { - OperationId = "dimLight", - Traits = new List - { new AsyncApiOperationTrait() { Reference = new AsyncApiReference() @@ -468,9 +457,9 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Type = ReferenceType.OperationTrait, }, }, - }, - Message = new List - { + }, + Message = new List + { new AsyncApiMessage() { Reference = new AsyncApiReference() @@ -479,17 +468,17 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Type = ReferenceType.Message, }, }, - }, }, - }) - .WithComponent("lightMeasured", new AsyncApiMessage() + }, + }) + .WithComponent("lightMeasured", new AsyncApiMessage() + { + Name = "lightMeasured", + Title = "Light measured", + Summary = "Inform about environmental lighting conditions of a particular streetlight.", + ContentType = "application/json", + Traits = new List() { - Name = "lightMeasured", - Title = "Light measured", - Summary = "Inform about environmental lighting conditions of a particular streetlight.", - ContentType = "application/json", - Traits = new List() - { new AsyncApiMessageTrait() { Reference = new AsyncApiReference() @@ -498,23 +487,23 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Id = "commonHeaders", }, }, - }, - Payload = new AsyncApiSchema() + }, + Payload = new AsyncApiSchema() + { + Reference = new AsyncApiReference() { - Reference = new AsyncApiReference() - { - Type = ReferenceType.Schema, - Id = "lightMeasuredPayload", - }, + Type = ReferenceType.Schema, + Id = "lightMeasuredPayload", }, - }) - .WithComponent("turnOnOff", new AsyncApiMessage() + }, + }) + .WithComponent("turnOnOff", new AsyncApiMessage() + { + Name = "turnOnOff", + Title = "Turn on/off", + Summary = "Command a particular streetlight to turn the lights on or off.", + Traits = new List() { - Name = "turnOnOff", - Title = "Turn on/off", - Summary = "Command a particular streetlight to turn the lights on or off.", - Traits = new List() - { new AsyncApiMessageTrait() { Reference = new AsyncApiReference() @@ -523,23 +512,23 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Id = "commonHeaders", }, }, - }, - Payload = new AsyncApiSchema() + }, + Payload = new AsyncApiSchema() + { + Reference = new AsyncApiReference() { - Reference = new AsyncApiReference() - { - Type = ReferenceType.Schema, - Id = "turnOnOffPayload", - }, + Type = ReferenceType.Schema, + Id = "turnOnOffPayload", }, - }) - .WithComponent("dimLight", new AsyncApiMessage() + }, + }) + .WithComponent("dimLight", new AsyncApiMessage() + { + Name = "dimLight", + Title = "Dim light", + Summary = "Command a particular streetlight to dim the lights.", + Traits = new List() { - Name = "dimLight", - Title = "Dim light", - Summary = "Command a particular streetlight to dim the lights.", - Traits = new List() - { new AsyncApiMessageTrait() { Reference = new AsyncApiReference() @@ -548,21 +537,21 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Id = "commonHeaders", }, }, - }, - Payload = new AsyncApiSchema() + }, + Payload = new AsyncApiSchema() + { + Reference = new AsyncApiReference() { - Reference = new AsyncApiReference() - { - Type = ReferenceType.Schema, - Id = "dimLightPayload", - }, + Type = ReferenceType.Schema, + Id = "dimLightPayload", }, - }) - .WithComponent("lightMeasuredPayload", new AsyncApiSchema() + }, + }) + .WithComponent("lightMeasuredPayload", new AsyncApiSchema() + { + Type = SchemaType.Object, + Properties = new Dictionary() { - Type = SchemaType.Object, - Properties = new Dictionary() - { { "lumens", new AsyncApiSchema() { @@ -581,13 +570,13 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }, } }, - }, - }) - .WithComponent("turnOnOffPayload", new AsyncApiSchema() + }, + }) + .WithComponent("turnOnOffPayload", new AsyncApiSchema() + { + Type = SchemaType.Object, + Properties = new Dictionary() { - Type = SchemaType.Object, - Properties = new Dictionary() - { { "command", new AsyncApiSchema() { @@ -610,13 +599,13 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }, } }, - }, - }) - .WithComponent("dimLightPayload", new AsyncApiSchema() + }, + }) + .WithComponent("dimLightPayload", new AsyncApiSchema() + { + Type = SchemaType.Object, + Properties = new Dictionary() { - Type = SchemaType.Object, - Properties = new Dictionary() - { { "percentage", new AsyncApiSchema() { @@ -636,40 +625,40 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }, } }, - }, - }) - .WithComponent("sentAt", new AsyncApiSchema() - { - Type = SchemaType.String, - Format = "date-time", - Description = "Date and time when the message was sent.", + }, + }) + .WithComponent("sentAt", new AsyncApiSchema() + { + Type = SchemaType.String, + Format = "date-time", + Description = "Date and time when the message was sent.", - }) - .WithComponent("saslScram", new AsyncApiSecurityScheme - { - Type = SecuritySchemeType.ScramSha256, - Description = "Provide your username and password for SASL/SCRAM authentication", - }) - .WithComponent("certs", new AsyncApiSecurityScheme - { - Type = SecuritySchemeType.X509, - Description = "Download the certificate files from service provider", - }) - .WithComponent("streetlightId", new AsyncApiParameter() + }) + .WithComponent("saslScram", new AsyncApiSecurityScheme + { + Type = SecuritySchemeType.ScramSha256, + Description = "Provide your username and password for SASL/SCRAM authentication", + }) + .WithComponent("certs", new AsyncApiSecurityScheme + { + Type = SecuritySchemeType.X509, + Description = "Download the certificate files from service provider", + }) + .WithComponent("streetlightId", new AsyncApiParameter() + { + Description = "The ID of the streetlight.", + Schema = new AsyncApiSchema() { - Description = "The ID of the streetlight.", - Schema = new AsyncApiSchema() - { - Type = SchemaType.String, - }, - }) - .WithComponent("commonHeaders", new AsyncApiMessageTrait() + Type = SchemaType.String, + }, + }) + .WithComponent("commonHeaders", new AsyncApiMessageTrait() + { + Headers = new AsyncApiSchema() { - Headers = new AsyncApiSchema() + Type = SchemaType.Object, + Properties = new Dictionary() { - Type = SchemaType.Object, - Properties = new Dictionary() - { { "my-app-header", new AsyncApiSchema() { @@ -678,13 +667,13 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Maximum = 100, } }, - }, }, - }) - .WithComponent("kafka", new AsyncApiOperationTrait() + }, + }) + .WithComponent("kafka", new AsyncApiOperationTrait() + { + Bindings = new AsyncApiBindings() { - Bindings = new AsyncApiBindings() - { { "kafka", new KafkaOperationBinding() { @@ -698,9 +687,9 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }, } }, - }, - }) - .Build(); + }, + }) + .Build(); // Act var actual = asyncApiDocument.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); @@ -715,7 +704,7 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() public void SerializeV2_WithFullSpec_Serializes() { var expected = - @"asyncapi: 2.6.0 + @"asyncapi: '2.6.0' info: title: apiTitle version: apiVersion @@ -879,7 +868,7 @@ public void SerializeV2_WithFullSpec_Serializes() string authorizationUrl = "https://example.com/authorization"; string requirementString = "requirementItem"; - + var document = new AsyncApiDocument() { Id = documentId, @@ -1182,7 +1171,7 @@ public void Serialize_WithBindingReferences_SerializesDeserializes() Id = "bindings", }, }, - } + } } }, ServerBindings = new Dictionary>() @@ -1204,7 +1193,7 @@ public void Serialize_WithBindingReferences_SerializesDeserializes() { new PulsarChannelBinding() { - Namespace = "users", + Namespace = "users", Persistence = AsyncAPI.Models.Bindings.Pulsar.Persistence.Persistent, } } @@ -1227,11 +1216,11 @@ public void Serialize_WithBindingReferences_SerializesDeserializes() var reader = new AsyncApiStringReader(settings); var deserialized = reader.Read(actual, out var diagnostic); } - + [Test] public void Serializev2_WithBindings_Serializes() { - var expected = @"asyncapi: 2.6.0 + var expected = @"asyncapi: '2.6.0' info: description: test description servers: