From d5ecb882dd8883faa8a31204086eb109161693bc Mon Sep 17 00:00:00 2001 From: Richard Pringle Date: Thu, 4 Apr 2024 11:52:26 +0800 Subject: [PATCH] #238 Resolve HybridMessageSerializer dependencies from IServiceProvider Signed-off-by: Richard Pringle --- docs/serialization.md | 35 +++--- .../Program.cs | 27 ++--- .../AvroMessageSerializer.cs | 4 +- .../HybridMessageSerializer.cs | 34 +++--- .../MessageBusBuilderExtensions.cs | 52 +++++++++ ...essageBus.Host.Serialization.Hybrid.csproj | 4 + src/SlimMessageBus.sln | 15 ++- .../Helpers/SampleMessages.cs | 5 + .../HybridMessageSerializerTests.cs | 110 ++++++++++++++++++ .../MessageBusBuilderExtensionsTests.cs | 92 +++++++++++++++ ...eBus.Host.Serialization.Hybrid.Test.csproj | 14 +++ .../Usings.cs | 1 + 12 files changed, 345 insertions(+), 48 deletions(-) create mode 100644 src/Tests/SlimMessageBus.Host.Serialization.Hybrid.Test/Helpers/SampleMessages.cs create mode 100644 src/Tests/SlimMessageBus.Host.Serialization.Hybrid.Test/HybridMessageSerializerTests.cs create mode 100644 src/Tests/SlimMessageBus.Host.Serialization.Hybrid.Test/MessageBusBuilderExtensionsTests.cs create mode 100644 src/Tests/SlimMessageBus.Host.Serialization.Hybrid.Test/SlimMessageBus.Host.Serialization.Hybrid.Test.csproj create mode 100644 src/Tests/SlimMessageBus.Host.Serialization.Hybrid.Test/Usings.cs diff --git a/docs/serialization.md b/docs/serialization.md index 961881a0..0a8b7c8a 100644 --- a/docs/serialization.md +++ b/docs/serialization.md @@ -175,21 +175,30 @@ The Hybrid plugin allows to have multiple serialization formats on one message b To use it install the nuget package `SlimMessageBus.Host.Serialization.Hybrid` and then configure the bus: ```cs -services.AddSlimMessageBus(mbb => -{ - // serializer 1 - var avroSerializer = new AvroMessageSerializer(); - // serializer 2 - var jsonSerializer = new JsonMessageSerializer(); + services + .AddSlimMessageBus(mbb => + { + mbb + // add required serializers to the DI container first + .AddAvroSerializer() + .AddGoogleProtobufSerializer() + .AddJsonSerializer() + + // Add hybrid serializer last so that it can update DI container and set itself as the default + .AddHybridSerializer( + o => { + + // Message1, Message2 => AvroSerializer + // Message3 => GoogleProtobufMessageSerializer + // all other messages by JsonMessageSerializer + + o.Add(typeof(Message1), typeof(Message2)); + o.Add(typeof(Message3)); + }) + ... + } - // Note: Certain messages will be serialized by the Avro serializer, other using the Json serializer - mbb.AddHybridSerializer(new Dictionary - { - [jsonSerializer] = new[] { typeof(SubtractCommand) }, // the first one will be the default serializer, no need to declare types here - [avroSerializer] = new[] { typeof(AddCommand), typeof(MultiplyRequest), typeof(MultiplyResponse) }, - }, defaultMessageSerializer: jsonSerializer); -}); ``` The routing to the proper serializer happens based on message type. When a type cannot be matched the default serializer will be used. diff --git a/src/Samples/Sample.Serialization.ConsoleApp/Program.cs b/src/Samples/Sample.Serialization.ConsoleApp/Program.cs index 15a7face..c775e5c0 100644 --- a/src/Samples/Sample.Serialization.ConsoleApp/Program.cs +++ b/src/Samples/Sample.Serialization.ConsoleApp/Program.cs @@ -11,7 +11,6 @@ using SlimMessageBus.Host; using SlimMessageBus.Host.Memory; using SlimMessageBus.Host.Redis; -using SlimMessageBus.Host.Serialization; using SlimMessageBus.Host.Serialization.Avro; using SlimMessageBus.Host.Serialization.Hybrid; using SlimMessageBus.Host.Serialization.Json; @@ -27,7 +26,7 @@ enum Provider /// /// This sample shows: -/// 1. How tu use the Avro serializer (for contract Avro IDL first apprach to generate C# code) +/// 1. How tu use the Avro serializer (for contract Avro IDL first approach to generate C# code) /// 2. How to combine two serializer approaches in one app (using the Hybrid serializer). /// class Program @@ -40,17 +39,11 @@ static async Task Main(string[] args) => await Host.CreateDefaultBuilder(args) services.AddHostedService(); - // alternatively a simpler approach, but using the slower ReflectionMessageCreationStategy and ReflectionSchemaLookupStrategy - var avroSerializer = new AvroMessageSerializer(); - - // Avro serialized using the AvroConvert library - no schema generation neeeded upfront. - var jsonSerializer = new JsonMessageSerializer(); - services .AddSlimMessageBus(mbb => { // Note: remember that Memory provider does not support req-resp yet. - var provider = Provider.Redis; + var provider = Provider.Memory; /* var sl = new DictionarySchemaLookupStrategy(); @@ -59,7 +52,7 @@ static async Task Main(string[] args) => await Host.CreateDefaultBuilder(args) sl.Add(typeof(MultiplyRequest), MultiplyRequest._SCHEMA); sl.Add(typeof(MultiplyResponse), MultiplyResponse._SCHEMA); - var mf = new DictionaryMessageCreationStategy(); + var mf = new DictionaryMessageCreationStrategy(); /// register all your types mf.Add(typeof(AddCommand), () => new AddCommand()); mf.Add(typeof(MultiplyRequest), () => new MultiplyRequest()); @@ -72,12 +65,14 @@ static async Task Main(string[] args) => await Host.CreateDefaultBuilder(args) mbb .AddServicesFromAssemblyContaining() // Note: Certain messages will be serialized by one Avro serializer, other using the Json serializer - .AddHybridSerializer(new Dictionary + .AddAvroSerializer() + .AddJsonSerializer() + // Include AddHybridSerializer after other serializers so that the DI container can be updated + .AddHybridSerializer(o => { - [jsonSerializer] = new[] { typeof(SubtractCommand) }, // the first one will be the default serializer, no need to declare types here - [avroSerializer] = new[] { typeof(AddCommand), typeof(MultiplyRequest), typeof(MultiplyResponse) }, - }, defaultMessageSerializer: jsonSerializer) - + //o.Add(typeof(SubtractCommand)); // can also be omitted as JsonMessageSerializer is the default + o.Add(typeof(AddCommand), typeof(MultiplyRequest), typeof(MultiplyResponse)); + }) .Produce(x => x.DefaultTopic("AddCommand")) .Consume(x => x.Topic("AddCommand").WithConsumer()) @@ -221,7 +216,7 @@ public class SubtractCommandConsumer : IConsumer { public async Task OnHandle(SubtractCommand message) { - Console.WriteLine("Consumer: Subracting {0} and {1} gives {2}", message.Left, message.Right, message.Left - message.Right); + Console.WriteLine("Consumer: Subtracting {0} and {1} gives {2}", message.Left, message.Right, message.Left - message.Right); await Task.Delay(50); // Simulate some work } } diff --git a/src/SlimMessageBus.Host.Serialization.Avro/AvroMessageSerializer.cs b/src/SlimMessageBus.Host.Serialization.Avro/AvroMessageSerializer.cs index 831796af..38a3a5a5 100644 --- a/src/SlimMessageBus.Host.Serialization.Avro/AvroMessageSerializer.cs +++ b/src/SlimMessageBus.Host.Serialization.Avro/AvroMessageSerializer.cs @@ -84,7 +84,7 @@ public object Deserialize(Type t, byte[] payload) var writerSchema = WriteSchemaLookup(t); AssertSchemaNotNull(t, writerSchema, true); - _logger.LogDebug("Type {0} writer schema: {1}, reader schema: {2}", t, writerSchema, readerSchema); + _logger.LogDebug("Type {MessageType} writer schema: {WriterSchema}, reader schema: {ReaderSchema}", t, writerSchema, readerSchema); var reader = new SpecificDefaultReader(writerSchema, readerSchema); reader.Read(message, dec); @@ -108,7 +108,7 @@ public byte[] Serialize(Type t, object message) var writerSchema = WriteSchemaLookup(t); AssertSchemaNotNull(t, writerSchema, true); - _logger.LogDebug("Type {0} writer schema: {1}", t, writerSchema); + _logger.LogDebug("Type {MessageType} writer schema: {WriterSchema}", t, writerSchema); var writer = new SpecificDefaultWriter(writerSchema); // Schema comes from pre-compiled, code-gen phase writer.Write(message, enc); diff --git a/src/SlimMessageBus.Host.Serialization.Hybrid/HybridMessageSerializer.cs b/src/SlimMessageBus.Host.Serialization.Hybrid/HybridMessageSerializer.cs index 5d2106f7..2645a345 100644 --- a/src/SlimMessageBus.Host.Serialization.Hybrid/HybridMessageSerializer.cs +++ b/src/SlimMessageBus.Host.Serialization.Hybrid/HybridMessageSerializer.cs @@ -8,10 +8,12 @@ public class HybridMessageSerializer : IMessageSerializer { private readonly ILogger _logger; - private readonly IList _serializers = new List(); - private readonly IDictionary _serializerByType = new Dictionary(); + private readonly Dictionary _serializerByType = []; + public IMessageSerializer DefaultSerializer { get; set; } + internal IReadOnlyDictionary SerializerByType => _serializerByType; + public HybridMessageSerializer(ILogger logger, IDictionary registration, IMessageSerializer defaultMessageSerializer = null) { _logger = logger; @@ -24,12 +26,14 @@ public HybridMessageSerializer(ILogger logger, IDiction public void Add(IMessageSerializer serializer, params Type[] supportedTypes) { - if (_serializers.Count == 0 && DefaultSerializer == null) - { - DefaultSerializer = serializer; - } +#if NETSTANDARD2_0 + if (serializer is null) throw new ArgumentNullException(nameof(serializer)); +#else + ArgumentNullException.ThrowIfNull(serializer); +#endif + + DefaultSerializer ??= serializer; - _serializers.Add(serializer); foreach (var type in supportedTypes) { _serializerByType.Add(type, serializer); @@ -38,19 +42,19 @@ public void Add(IMessageSerializer serializer, params Type[] supportedTypes) protected virtual IMessageSerializer MatchSerializer(Type t) { - if (_serializers.Count == 0) - { - throw new InvalidOperationException("No serializers registered."); - } - if (!_serializerByType.TryGetValue(t, out var serializer)) { - // use first as default - _logger.LogTrace("Serializer for type {0} not registered, will use default serializer", t); + _logger.LogTrace("Serializer for type {MessageType} not registered, will use default serializer", t); + + if (DefaultSerializer == null) + { + throw new InvalidOperationException("No serializers registered."); + } + serializer = DefaultSerializer; } - _logger.LogDebug("Serializer for type {0} will be {1}", t, serializer); + _logger.LogDebug("Serializer for type {MessageType} will be {Serializer}", t, serializer); return serializer; } diff --git a/src/SlimMessageBus.Host.Serialization.Hybrid/MessageBusBuilderExtensions.cs b/src/SlimMessageBus.Host.Serialization.Hybrid/MessageBusBuilderExtensions.cs index b2a82cb6..b9009ba9 100644 --- a/src/SlimMessageBus.Host.Serialization.Hybrid/MessageBusBuilderExtensions.cs +++ b/src/SlimMessageBus.Host.Serialization.Hybrid/MessageBusBuilderExtensions.cs @@ -24,4 +24,56 @@ public static MessageBusBuilder AddHybridSerializer(this MessageBusBuilder mbb, }); return mbb; } + + /// + /// Registers the with implementation as using serializers as registered in the . + /// + /// + /// Action to register serializers for dependency injection resolution. + /// The default serializer to be used when the message type cannot be matched + /// + public static MessageBusBuilder AddHybridSerializer(this MessageBusBuilder mbb, Action registration) + where TDefaultSerializer : class, IMessageSerializer + { + mbb.PostConfigurationActions.Add(services => + { + services.RemoveAll(typeof(IMessageSerializer)); + services.TryAddSingleton(svp => + { + var builder = new HybridSerializerOptionsBuilder(); + registration(builder); + + var registrations = builder.Registrations.ToDictionary(x => (IMessageSerializer)svp.GetRequiredService(x.Key), x => x.Value.ToArray()); + var defaultMessageSerializer = svp.GetRequiredService(); + return new HybridMessageSerializer(svp.GetRequiredService>(), registrations, defaultMessageSerializer); + }); + + services.TryAddSingleton(svp => svp.GetRequiredService()); + }); + return mbb; + } + + public sealed class HybridSerializerOptionsBuilder + { + internal Dictionary> Registrations { get; } = []; + + public HybridSerializerOptionsBuilder Add(params Type[] types) + where TMessageSerializer : IMessageSerializer + { + if (types.Length == 0) + { + return this; + } + + var key = typeof(TMessageSerializer); + if (!Registrations.TryGetValue(key, out var list)) + { + list = []; + Registrations.Add(key, list); + } + + list.AddRange(types); + return this; + } + } } diff --git a/src/SlimMessageBus.Host.Serialization.Hybrid/SlimMessageBus.Host.Serialization.Hybrid.csproj b/src/SlimMessageBus.Host.Serialization.Hybrid/SlimMessageBus.Host.Serialization.Hybrid.csproj index f6633595..eb09035e 100644 --- a/src/SlimMessageBus.Host.Serialization.Hybrid/SlimMessageBus.Host.Serialization.Hybrid.csproj +++ b/src/SlimMessageBus.Host.Serialization.Hybrid/SlimMessageBus.Host.Serialization.Hybrid.csproj @@ -16,4 +16,8 @@ + + + + diff --git a/src/SlimMessageBus.sln b/src/SlimMessageBus.sln index 885de7c5..ae9bdd87 100644 --- a/src/SlimMessageBus.sln +++ b/src/SlimMessageBus.sln @@ -230,9 +230,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SlimMessageBus.Host.RabbitM EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SlimMessageBus.Host.RabbitMQ.Test", "Tests\SlimMessageBus.Host.RabbitMQ.Test\SlimMessageBus.Host.RabbitMQ.Test.csproj", "{F5373E1D-A2B4-46CC-9B07-94F6655C8E29}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SlimMessageBus.Host.Sql", "SlimMessageBus.Host.Sql\SlimMessageBus.Host.Sql.csproj", "{5EED0E89-2475-40E0-81EF-0F05C9326612}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SlimMessageBus.Host.Sql", "SlimMessageBus.Host.Sql\SlimMessageBus.Host.Sql.csproj", "{5EED0E89-2475-40E0-81EF-0F05C9326612}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SlimMessageBus.Host.Sql.Common", "SlimMessageBus.Host.Sql.Common\SlimMessageBus.Host.Sql.Common.csproj", "{F19B7A21-7749-465A-8810-4C274A9E8956}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SlimMessageBus.Host.Sql.Common", "SlimMessageBus.Host.Sql.Common\SlimMessageBus.Host.Sql.Common.csproj", "{F19B7A21-7749-465A-8810-4C274A9E8956}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SlimMessageBus.Host.Serialization.Hybrid.Test", "Tests\SlimMessageBus.Host.Serialization.Hybrid.Test\SlimMessageBus.Host.Serialization.Hybrid.Test.csproj", "{DB624D5F-CB7C-4E16-B1E2-3B368FCB5A46}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -730,6 +732,14 @@ Global {F19B7A21-7749-465A-8810-4C274A9E8956}.Release|Any CPU.Build.0 = Release|Any CPU {F19B7A21-7749-465A-8810-4C274A9E8956}.Release|x86.ActiveCfg = Release|Any CPU {F19B7A21-7749-465A-8810-4C274A9E8956}.Release|x86.Build.0 = Release|Any CPU + {DB624D5F-CB7C-4E16-B1E2-3B368FCB5A46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DB624D5F-CB7C-4E16-B1E2-3B368FCB5A46}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DB624D5F-CB7C-4E16-B1E2-3B368FCB5A46}.Debug|x86.ActiveCfg = Debug|Any CPU + {DB624D5F-CB7C-4E16-B1E2-3B368FCB5A46}.Debug|x86.Build.0 = Debug|Any CPU + {DB624D5F-CB7C-4E16-B1E2-3B368FCB5A46}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DB624D5F-CB7C-4E16-B1E2-3B368FCB5A46}.Release|Any CPU.Build.0 = Release|Any CPU + {DB624D5F-CB7C-4E16-B1E2-3B368FCB5A46}.Release|x86.ActiveCfg = Release|Any CPU + {DB624D5F-CB7C-4E16-B1E2-3B368FCB5A46}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -803,6 +813,7 @@ Global {F5373E1D-A2B4-46CC-9B07-94F6655C8E29} = {9F005B5C-A856-4351-8C0C-47A8B785C637} {5EED0E89-2475-40E0-81EF-0F05C9326612} = {9291D340-B4FA-44A3-8060-C14743FB1712} {F19B7A21-7749-465A-8810-4C274A9E8956} = {9291D340-B4FA-44A3-8060-C14743FB1712} + {DB624D5F-CB7C-4E16-B1E2-3B368FCB5A46} = {9F005B5C-A856-4351-8C0C-47A8B785C637} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {435A0D65-610C-4B84-B1AA-2C7FBE72DB80} diff --git a/src/Tests/SlimMessageBus.Host.Serialization.Hybrid.Test/Helpers/SampleMessages.cs b/src/Tests/SlimMessageBus.Host.Serialization.Hybrid.Test/Helpers/SampleMessages.cs new file mode 100644 index 00000000..5ae040a1 --- /dev/null +++ b/src/Tests/SlimMessageBus.Host.Serialization.Hybrid.Test/Helpers/SampleMessages.cs @@ -0,0 +1,5 @@ +namespace SlimMessageBus.Host.Serialization.Hybrid.Test.Helpers; + +public record SampleOne; +public record SampleTwo; +public record SampleThree; \ No newline at end of file diff --git a/src/Tests/SlimMessageBus.Host.Serialization.Hybrid.Test/HybridMessageSerializerTests.cs b/src/Tests/SlimMessageBus.Host.Serialization.Hybrid.Test/HybridMessageSerializerTests.cs new file mode 100644 index 00000000..3b7a3b74 --- /dev/null +++ b/src/Tests/SlimMessageBus.Host.Serialization.Hybrid.Test/HybridMessageSerializerTests.cs @@ -0,0 +1,110 @@ +namespace SlimMessageBus.Host.Serialization.Hybrid.Test +{ + using FluentAssertions; + + using Microsoft.Extensions.Logging; + + using Moq; + + using SlimMessageBus.Host.Serialization.Hybrid.Test.Helpers; + + public class HybridMessageSerializerTests + { + [Fact] + public void When_ConstructorReceivesARepeatedDefinition_Then_ThrowException() + { + // arrange + var mockDefaultSerializer = new Mock(); + var mockSerializer1 = new Mock(); + var mockSerializer2 = new Mock(); + var mockLogger = new Mock>(); + + var serializers = new Dictionary + { + { mockSerializer1.Object, new[] { typeof(SampleOne), typeof(SampleTwo) } }, + { mockSerializer2.Object, new[] { typeof(SampleOne) } }, + }; + + // act + var act = () => new HybridMessageSerializer(mockLogger.Object, serializers, mockDefaultSerializer.Object); + + // assert + act.Should().Throw(); + } + + [Fact] + public void When_NoDefaultSerializerIsSupplied_Then_UseFirstSpecialist() + { + // arrange + var mockDefaultSerializer = new Mock(); + var mockSerializer1 = new Mock(); + var mockLogger = new Mock>(); + + var serializers = new Dictionary + { + { mockDefaultSerializer.Object, new[] { typeof(SampleOne) } }, + { mockSerializer1.Object, new[] { typeof(SampleTwo) } }, + }; + + // act + var target = new HybridMessageSerializer(mockLogger.Object, serializers, default); + var actual = target.DefaultSerializer; + + // assert + actual.Should().BeEquivalentTo(mockDefaultSerializer.Object); + } + + [Fact] + public void When_ASpecialisedMessageIsSerialized_Then_UseSpecializedSerializer() + { + // arrange + var mockDefaultSerializer = new Mock(); + + var mockSerializer1 = new Mock(); + mockSerializer1.Setup(x => x.Serialize(typeof(SampleOne), It.IsAny())).Verifiable(Times.Once()); + + var mockSerializer2 = new Mock(); + + var mockLogger = new Mock>(); + var serializers = new Dictionary + { + { mockSerializer1.Object, new[] { typeof(SampleOne) } }, + { mockSerializer2.Object, new[] { typeof(SampleTwo) } }, + }; + + // act + var target = new HybridMessageSerializer(mockLogger.Object, serializers, mockDefaultSerializer.Object); + var _ = target.Serialize(typeof(SampleOne), new SampleOne()); + + // assert + mockSerializer1.Verify(x => x.Serialize(typeof(SampleOne), It.IsAny())); + mockSerializer2.VerifyNoOtherCalls(); + mockDefaultSerializer.VerifyNoOtherCalls(); + } + + [Fact] + public void When_AGenericMessageIsSerialized_Then_UseDefaultSerializer() + { + // arrange + var mockDefaultSerializer = new Mock(); + mockDefaultSerializer.Setup(x => x.Serialize(typeof(SampleOne), It.IsAny())).Verifiable(Times.Once()); + + var mockSerializer1 = new Mock(); + + var mockLogger = new Mock>(); + var serializers = new Dictionary + { + { mockSerializer1.Object, new[] { typeof(SampleTwo) } } + }; + + // act + var target = new HybridMessageSerializer(mockLogger.Object, serializers, mockDefaultSerializer.Object); + var _ = target.Serialize(typeof(SampleOne), new SampleOne()); + + // assert + mockDefaultSerializer.Verify(x => x.Serialize(typeof(SampleOne), It.IsAny())); + mockSerializer1.VerifyNoOtherCalls(); + } + } +} + diff --git a/src/Tests/SlimMessageBus.Host.Serialization.Hybrid.Test/MessageBusBuilderExtensionsTests.cs b/src/Tests/SlimMessageBus.Host.Serialization.Hybrid.Test/MessageBusBuilderExtensionsTests.cs new file mode 100644 index 00000000..a6bfde2b --- /dev/null +++ b/src/Tests/SlimMessageBus.Host.Serialization.Hybrid.Test/MessageBusBuilderExtensionsTests.cs @@ -0,0 +1,92 @@ +namespace SlimMessageBus.Host.Serialization.Hybrid.Test; + +using FluentAssertions; + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Logging; + +using Moq; + +using SlimMessageBus.Host.Serialization.Hybrid; +using SlimMessageBus.Host.Serialization.Hybrid.Test.Helpers; + +public class MessageBusBuilderExtensionsTests +{ + private readonly IServiceCollection _services; + + public MessageBusBuilderExtensionsTests() + { + // arrange + var mockLogger = new Mock>(); + + _services = new ServiceCollection(); + _services.AddSingleton(mockLogger.Object); + _services.TryAddSingleton(svp => new SerializerOne()); + _services.TryAddSingleton(svp => svp.GetRequiredService()); + _services.TryAddSingleton(svp => new SerializerTwo()); + _services.TryAddSingleton(svp => svp.GetRequiredService()); + _services.TryAddSingleton(svp => new SerializerThree()); + _services.TryAddSingleton(svp => svp.GetRequiredService()); + + // act + _services.AddSlimMessageBus(cfg => + { + cfg.AddHybridSerializer(o => + { + o.Add(typeof(SampleTwo)); + o.Add(typeof(SampleThree)); + }); + }); + } + + [Fact] + public void When_IMessageSerializerRegistrationsAlreadyExist_Then_RemovePreviousRegistrations() + { + // assert + _services.Count(x => x.ServiceType == typeof(IMessageSerializer)).Should().Be(1); + } + + [Fact] + public void When_HybridMessageSerializerIsAdded_Then_RegisterAsIMessageSerializer() + { + // act + var serviceProvider = _services.BuildServiceProvider(); + var target = serviceProvider.GetServices().ToList(); + + // assert + target.Count.Should().Be(1); + target.Single().GetType().Should().Be(typeof(HybridMessageSerializer)); + } + + [Fact] + public void When_HybridMessageSerializerIsAdded_Then_SerializersAndTypesShouldConfigured() + { + // act + var serviceProvider = _services.BuildServiceProvider(); + var target = serviceProvider.GetService(); + + // assert + target.DefaultSerializer.GetType().Should().Be(typeof(SerializerOne)); + target.SerializerByType.Count.Should().Be(2); + target.SerializerByType.Should().ContainKey(typeof(SampleTwo)).WhoseValue.Should().BeOfType(); + target.SerializerByType.Should().ContainKey(typeof(SampleThree)).WhoseValue.Should().BeOfType(); + } + + public abstract class AbstractSerializer : IMessageSerializer + { + public object Deserialize(Type t, byte[] payload) + { + throw new NotImplementedException(); + } + + public byte[] Serialize(Type t, object message) + { + throw new NotImplementedException(); + } + } + + public class SerializerOne : AbstractSerializer { } + public class SerializerTwo : AbstractSerializer { } + public class SerializerThree : AbstractSerializer { } +} \ No newline at end of file diff --git a/src/Tests/SlimMessageBus.Host.Serialization.Hybrid.Test/SlimMessageBus.Host.Serialization.Hybrid.Test.csproj b/src/Tests/SlimMessageBus.Host.Serialization.Hybrid.Test/SlimMessageBus.Host.Serialization.Hybrid.Test.csproj new file mode 100644 index 00000000..5006b25a --- /dev/null +++ b/src/Tests/SlimMessageBus.Host.Serialization.Hybrid.Test/SlimMessageBus.Host.Serialization.Hybrid.Test.csproj @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/Tests/SlimMessageBus.Host.Serialization.Hybrid.Test/Usings.cs b/src/Tests/SlimMessageBus.Host.Serialization.Hybrid.Test/Usings.cs new file mode 100644 index 00000000..8c927eb7 --- /dev/null +++ b/src/Tests/SlimMessageBus.Host.Serialization.Hybrid.Test/Usings.cs @@ -0,0 +1 @@ +global using Xunit; \ No newline at end of file