diff --git a/CommunityToolkit.Aspire.sln b/CommunityToolkit.Aspire.sln
index 670ea438..ac0f029b 100644
--- a/CommunityToolkit.Aspire.sln
+++ b/CommunityToolkit.Aspire.sln
@@ -325,6 +325,18 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "redis-ext", "redis-ext", "{
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Aspire.Hosting.Redis.Extensions.Tests", "tests\CommunityToolkit.Aspire.Hosting.Redis.Extensions.Tests\CommunityToolkit.Aspire.Hosting.Redis.Extensions.Tests.csproj", "{958A251E-9D2A-4D67-9D1C-47D6A9E36B3A}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Aspire.Hosting.LavinMQ", "src\CommunityToolkit.Aspire.Hosting.LavinMQ\CommunityToolkit.Aspire.Hosting.LavinMQ.csproj", "{F218A6C1-3D4F-4351-9580-C99DBC3774FF}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Aspire.Hosting.LavinMQ.Tests", "tests\CommunityToolkit.Aspire.Hosting.LavinMQ.Tests\CommunityToolkit.Aspire.Hosting.LavinMQ.Tests.csproj", "{E0B23172-E795-4AC5-B9E2-6922E4F167BB}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "lavinmq", "lavinmq", "{35E5A4C8-219B-44AC-AB93-B07A67BCC810}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost", "examples\lavinmq\CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost\CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost.csproj", "{7068DDA8-71B9-428E-BCC3-89658670B830}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit", "examples\lavinmq\CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit\CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit.csproj", "{8C463360-0156-461C-A065-CC30FE3B0595}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Aspire.Hosting.LavinMQ.ServiceDefaults", "examples\lavinmq\CommunityToolkit.Aspire.Hosting.LavinMQ.ServiceDefaults\CommunityToolkit.Aspire.Hosting.LavinMQ.ServiceDefaults.csproj", "{A5C87DA6-6793-4824-88E5-4F8310FE55B8}"
+EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "mailpit", "mailpit", "{734EF69D-EE4D-4E9B-96BD-A44E53C5D2EC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Aspire.Hosting.MailPit", "src\CommunityToolkit.Aspire.Hosting.MailPit\CommunityToolkit.Aspire.Hosting.MailPit.csproj", "{73E7BC19-A626-4C55-990B-A878BF84D4CA}"
@@ -867,6 +879,26 @@ Global
{958A251E-9D2A-4D67-9D1C-47D6A9E36B3A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{958A251E-9D2A-4D67-9D1C-47D6A9E36B3A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{958A251E-9D2A-4D67-9D1C-47D6A9E36B3A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F218A6C1-3D4F-4351-9580-C99DBC3774FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F218A6C1-3D4F-4351-9580-C99DBC3774FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F218A6C1-3D4F-4351-9580-C99DBC3774FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F218A6C1-3D4F-4351-9580-C99DBC3774FF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E0B23172-E795-4AC5-B9E2-6922E4F167BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E0B23172-E795-4AC5-B9E2-6922E4F167BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E0B23172-E795-4AC5-B9E2-6922E4F167BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E0B23172-E795-4AC5-B9E2-6922E4F167BB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7068DDA8-71B9-428E-BCC3-89658670B830}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7068DDA8-71B9-428E-BCC3-89658670B830}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7068DDA8-71B9-428E-BCC3-89658670B830}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7068DDA8-71B9-428E-BCC3-89658670B830}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8C463360-0156-461C-A065-CC30FE3B0595}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8C463360-0156-461C-A065-CC30FE3B0595}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8C463360-0156-461C-A065-CC30FE3B0595}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8C463360-0156-461C-A065-CC30FE3B0595}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A5C87DA6-6793-4824-88E5-4F8310FE55B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A5C87DA6-6793-4824-88E5-4F8310FE55B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A5C87DA6-6793-4824-88E5-4F8310FE55B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A5C87DA6-6793-4824-88E5-4F8310FE55B8}.Release|Any CPU.Build.0 = Release|Any CPU
{73E7BC19-A626-4C55-990B-A878BF84D4CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{73E7BC19-A626-4C55-990B-A878BF84D4CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{73E7BC19-A626-4C55-990B-A878BF84D4CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -1050,6 +1082,12 @@ Global
{CB491C52-A3D5-4F36-97C4-75C482F90A30} = {734EF69D-EE4D-4E9B-96BD-A44E53C5D1EC}
{734EF69D-EE4D-4E9B-96BD-A44E53C5D1EC} = {8519CC01-1370-47C8-AD94-B0F326B1563F}
{958A251E-9D2A-4D67-9D1C-47D6A9E36B3A} = {899F0713-7FC6-4750-BAFC-AC650B35B453}
+ {F218A6C1-3D4F-4351-9580-C99DBC3774FF} = {414151D4-7009-4E78-A5C6-D99EBD1E67D1}
+ {E0B23172-E795-4AC5-B9E2-6922E4F167BB} = {899F0713-7FC6-4750-BAFC-AC650B35B453}
+ {35E5A4C8-219B-44AC-AB93-B07A67BCC810} = {8519CC01-1370-47C8-AD94-B0F326B1563F}
+ {7068DDA8-71B9-428E-BCC3-89658670B830} = {35E5A4C8-219B-44AC-AB93-B07A67BCC810}
+ {8C463360-0156-461C-A065-CC30FE3B0595} = {35E5A4C8-219B-44AC-AB93-B07A67BCC810}
+ {A5C87DA6-6793-4824-88E5-4F8310FE55B8} = {35E5A4C8-219B-44AC-AB93-B07A67BCC810}
{73E7BC19-A626-4C55-990B-A878BF84D4CA} = {414151D4-7009-4E78-A5C6-D99EBD1E67D1}
{9C66ECFF-8077-4D5A-9CEB-6E9AB6CCBE94} = {899F0713-7FC6-4750-BAFC-AC650B35B453}
{734EF69D-EE4D-4E9B-96BD-A44E53C5D2EC} = {8519CC01-1370-47C8-AD94-B0F326B1563F}
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 0e3060ef..b2eaed91 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -17,6 +17,7 @@
+
diff --git a/README.md b/README.md
index e812d532..6292efb4 100644
--- a/README.md
+++ b/README.md
@@ -43,9 +43,9 @@ This repository contains the source code for the .NET Aspire Community Toolkit,
| - **Learn More**: [`Hosting.MongoDB.Extensions`][mongodb-ext-integration-docs]
- Stable 📦: [![CommunityToolkit.Aspire.MongoDB.Extensions][mongodb-ext-shields]][mongodb-ext-nuget]
- Preview 📦: [![CommunityToolkit.Aspire.Hosting.MongoDB.Extensions][mongodb-ext-shields-preview]][mongodb-ext-nuget-preview] | An integration that contains some additional extensions for hosting MongoDB container. |
| - **Learn More**: [`Hosting.PostgreSQL.Extensions`][postgres-ext-integration-docs]
- Stable 📦: [![CommunityToolkit.Aspire.PostgreSQL.Extensions][postgres-ext-shields]][postgres-ext-nuget]
- Preview 📦: [![CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions][postgres-ext-shields-preview]][postgres-ext-nuget-preview] | An integration that contains some additional extensions for hosting PostgreSQL container. |
| - **Learn More**: [`Hosting.Redis.Extensions`][redis-ext-integration-docs]
- Stable 📦: [![CommunityToolkit.Aspire.Redis.Extensions][redis-ext-shields]][redis-ext-nuget]
- Preview 📦: [![CommunityToolkit.Aspire.Hosting.Redis.Extensions][redis-ext-shields-preview]][redis-ext-nuget-preview] | An integration that contains some additional extensions for hosting Redis container. |
+| - **Learn More**: [`Hosting.LavinMQ`][lavinmq-integration-docs]
- Stable 📦: [![CommunityToolkit.Aspire.Hosting.LavinMQ][lavinmq-shields]][lavinmq-nuget]
- Preview 📦: [![CommunityToolkit.Aspire.Hosting.LavinMQ][lavinmq-shields-preview]][lavinmq-nuget-preview] | An Aspire hosting integration for [LavinMQ](https://www.lavinmq.com). |
| - **Learn More**: [`Hosting.MailPit`][mailpit-ext-integration-docs]
- Stable 📦: [![CommunityToolkit.Aspire.Hosting.MailPit][mailpit-ext-shields]][mailpit-ext-nuget]
- Preview 📦: [![CommunityToolkit.Aspire.Hosting.MailPit][mailpit-ext-shields-preview]][mailpit-ext-nuget-preview] | An Aspire integration leveraging the [MailPit](https://mailpit.axllent.org/) container. |
-
## 🙌 Getting Started
Each of the integrations in the toolkit is available as a NuGet package, and can be added to your .NET project. Refer to the table above for the available integrations and the documentation on how to use them.
@@ -224,8 +224,13 @@ This project is supported by the [.NET Foundation](https://dotnetfoundation.org)
[redis-ext-nuget]: https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.Redis.Extensions/
[redis-ext-shields-preview]: https://img.shields.io/nuget/vpre/CommunityToolkit.Aspire.Hosting.Redis.Extensions?label=nuget%20(preview)
[redis-ext-nuget-preview]: https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.Redis.Extensions/absoluteLatest
+[lavinmq-integration-docs]: https://learn.microsoft.com/dotnet/aspire/community-toolkit/hosting-lavinmq
+[lavinmq-shields]: https://img.shields.io/nuget/v/CommunityToolkit.Aspire.Hosting.LavinMQ
+[lavinmq-nuget]: https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.LavinMQ/
+[lavinmq-shields-preview]: https://img.shields.io/nuget/vpre/CommunityToolkit.Aspire.Hosting.LavinMQ?label=nuget%20(preview)
+[lavinmq-nuget-preview]: https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.LavinMQ/absoluteLatest
[mailpit-ext-integration-docs]: https://learn.microsoft.com/dotnet/aspire/community-toolkit/hosting-mailpit
[mailpit-ext-shields]: https://img.shields.io/nuget/v/CommunityToolkit.Aspire.Hosting.MailPit
[mailpit-ext-nuget]: https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.MailPit/
[mailpit-ext-shields-preview]: https://img.shields.io/nuget/vpre/CommunityToolkit.Aspire.Hosting.MailPit?label=nuget%20(preview)
-[mailpit-ext-nuget-preview]: https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.MailPit/absoluteLatest
\ No newline at end of file
+[mailpit-ext-nuget-preview]: https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.MailPit/absoluteLatest
diff --git a/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost/CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost.csproj b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost/CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost.csproj
new file mode 100644
index 00000000..650d15fb
--- /dev/null
+++ b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost/CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost.csproj
@@ -0,0 +1,23 @@
+
+
+
+
+
+ Exe
+ enable
+ enable
+ true
+ cb846a57-bdaf-4abd-a7fb-0299aa095f5e
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost/Program.cs b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost/Program.cs
new file mode 100644
index 00000000..6a614d1e
--- /dev/null
+++ b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost/Program.cs
@@ -0,0 +1,13 @@
+using Projects;
+
+var builder = DistributedApplication.CreateBuilder(args);
+
+var lavinmq = builder.AddLavinMQ("lavinmq")
+ .PublishAsConnectionString();
+
+builder.AddProject("masstransitExample")
+ .WithReference(lavinmq)
+ .WithHttpHealthCheck(path: "/health")
+ .WaitFor(lavinmq);
+
+builder.Build().Run();
diff --git a/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost/Properties/launchSettings.json b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost/Properties/launchSettings.json
new file mode 100644
index 00000000..ed873279
--- /dev/null
+++ b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost/Properties/launchSettings.json
@@ -0,0 +1,29 @@
+{
+ "$schema": "https://json.schemastore.org/launchsettings.json",
+ "profiles": {
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "applicationUrl": "https://localhost:17276;http://localhost:15246",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development",
+ "DOTNET_ENVIRONMENT": "Development",
+ "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21269",
+ "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22000"
+ }
+ },
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "applicationUrl": "http://localhost:15246",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development",
+ "DOTNET_ENVIRONMENT": "Development",
+ "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19099",
+ "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20012"
+ }
+ }
+ }
+}
diff --git a/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost/appsettings.json b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost/appsettings.json
new file mode 100644
index 00000000..31c092aa
--- /dev/null
+++ b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning",
+ "Aspire.Hosting.Dcp": "Warning"
+ }
+ }
+}
diff --git a/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit.csproj b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit.csproj
new file mode 100644
index 00000000..2fc7d9fd
--- /dev/null
+++ b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit.csproj
@@ -0,0 +1,18 @@
+
+
+
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit.http b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit.http
new file mode 100644
index 00000000..c6943b9e
--- /dev/null
+++ b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit.http
@@ -0,0 +1,13 @@
+@CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit_HostAddress = http://localhost:5004
+
+POST http://localhost:5004/send/Hello%20World
+Accept: application/json
+
+###
+GET http://localhost:5004/alive
+Accept: application/json
+
+###
+GET http://localhost:5004/received
+Accept: application/json
+
diff --git a/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit/HelloWorldConsumer.cs b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit/HelloWorldConsumer.cs
new file mode 100644
index 00000000..56ef18d3
--- /dev/null
+++ b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit/HelloWorldConsumer.cs
@@ -0,0 +1,16 @@
+using MassTransit;
+
+namespace CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit;
+
+public class HelloWorldConsumer(ILogger logger, MessageCounter messageCounter) : IConsumer
+{
+ public async Task Consume(ConsumeContext context)
+ {
+ logger.LogInformation("Received message: {Text}", context.Message.Text);
+ messageCounter.ReceivedMessages++;
+ await context.RespondAsync(new
+ {
+ Reply = "I've received your message: " + context.Message.Text
+ });
+ }
+}
\ No newline at end of file
diff --git a/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit/Message.cs b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit/Message.cs
new file mode 100644
index 00000000..d09087b1
--- /dev/null
+++ b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit/Message.cs
@@ -0,0 +1,6 @@
+namespace CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit;
+
+public record Message
+{
+ public required string Text { get; set; }
+}
\ No newline at end of file
diff --git a/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit/MessageCounter.cs b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit/MessageCounter.cs
new file mode 100644
index 00000000..7c6e2122
--- /dev/null
+++ b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit/MessageCounter.cs
@@ -0,0 +1,6 @@
+namespace CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit;
+
+public class MessageCounter
+{
+ public int ReceivedMessages { get; set; }
+}
\ No newline at end of file
diff --git a/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit/MessageReply.cs b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit/MessageReply.cs
new file mode 100644
index 00000000..5998854f
--- /dev/null
+++ b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit/MessageReply.cs
@@ -0,0 +1,6 @@
+namespace CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit;
+
+public record MessageReply
+{
+ public required string Reply { get; set; }
+}
\ No newline at end of file
diff --git a/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit/Program.cs b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit/Program.cs
new file mode 100644
index 00000000..07bb78c5
--- /dev/null
+++ b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit/Program.cs
@@ -0,0 +1,39 @@
+using CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit;
+using MassTransit;
+using Microsoft.AspNetCore.Mvc;
+
+var builder = WebApplication.CreateBuilder(args);
+
+builder.AddServiceDefaults();
+builder.Services
+ .AddSingleton(KebabCaseEndpointNameFormatter.Instance)
+ .AddSingleton();
+builder.Services.AddMassTransit(x =>
+{
+ x.UsingRabbitMq((context, cfg) =>
+ {
+ string connectionString = builder.Configuration.GetConnectionString("lavinmq")!;
+ cfg.ConfigureEndpoints(context, new KebabCaseEndpointNameFormatter("aspire", false));
+ cfg.Host(new Uri(connectionString), _ => {});
+ });
+ x.AddConsumers(typeof(HelloWorldConsumer).Assembly);
+});
+
+WebApplication app = builder.Build();
+
+app.MapPost("/send/{text}", async (string text,
+ [FromServices] IRequestClient requestClient,
+ [FromServices] ILogger logger) =>
+ {
+ logger.LogInformation("Send message: {Text}", text);
+ Response response = await requestClient.GetResponse(new { Text = text });
+ logger.LogInformation("Sent message: {Text}", text);
+ return response.Message.Reply;
+ })
+ .WithName("SendMessage");
+
+app.MapGet("/received", ([FromServices] MessageCounter messageCounter) => messageCounter)
+ .WithName("ReceivedMessages");
+
+app.MapDefaultEndpoints();
+app.Run();
diff --git a/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit/Properties/launchSettings.json b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit/Properties/launchSettings.json
new file mode 100644
index 00000000..54f0fa7a
--- /dev/null
+++ b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit/Properties/launchSettings.json
@@ -0,0 +1,23 @@
+{
+ "$schema": "https://json.schemastore.org/launchsettings.json",
+ "profiles": {
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": false,
+ "applicationUrl": "http://localhost:5004",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": false,
+ "applicationUrl": "https://localhost:7038;http://localhost:5004",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit/appsettings.json b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit/appsettings.json
new file mode 100644
index 00000000..4db6d5d4
--- /dev/null
+++ b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit/appsettings.json
@@ -0,0 +1,12 @@
+{
+ "ConnectionStrings": {
+ "lavinmq": "amqp://localhost:5672"
+ },
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.ServiceDefaults/CommunityToolkit.Aspire.Hosting.LavinMQ.ServiceDefaults.csproj b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.ServiceDefaults/CommunityToolkit.Aspire.Hosting.LavinMQ.ServiceDefaults.csproj
new file mode 100644
index 00000000..caa6344d
--- /dev/null
+++ b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.ServiceDefaults/CommunityToolkit.Aspire.Hosting.LavinMQ.ServiceDefaults.csproj
@@ -0,0 +1,21 @@
+
+
+
+ enable
+ enable
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.ServiceDefaults/Extensions.cs b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.ServiceDefaults/Extensions.cs
new file mode 100644
index 00000000..fe858bea
--- /dev/null
+++ b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.ServiceDefaults/Extensions.cs
@@ -0,0 +1,123 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Diagnostics.HealthChecks;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.ServiceDiscovery;
+using OpenTelemetry;
+using OpenTelemetry.Metrics;
+using OpenTelemetry.Trace;
+
+namespace Microsoft.Extensions.Hosting;
+
+// Adds common .NET Aspire services: service discovery, resilience, health checks, and OpenTelemetry.
+// This project should be referenced by each service project in your solution.
+// To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults
+public static class Extensions
+{
+ private const string MasstransitTag = "MassTransit";
+
+ public static IHostApplicationBuilder AddServiceDefaults(this IHostApplicationBuilder builder)
+ {
+ builder.ConfigureOpenTelemetry();
+
+ builder.AddDefaultHealthChecks();
+
+ builder.Services.AddServiceDiscovery();
+
+ builder.Services.ConfigureHttpClientDefaults(http =>
+ {
+ // Turn on resilience by default
+ http.AddStandardResilienceHandler();
+
+ // Turn on service discovery by default
+ http.AddServiceDiscovery();
+ });
+
+ // Uncomment the following to restrict the allowed schemes for service discovery.
+ // builder.Services.Configure(options =>
+ // {
+ // options.AllowedSchemes = ["https"];
+ // });
+
+ return builder;
+ }
+
+ public static IHostApplicationBuilder ConfigureOpenTelemetry(this IHostApplicationBuilder builder)
+ {
+ builder.Logging.AddOpenTelemetry(logging =>
+ {
+ logging.IncludeFormattedMessage = true;
+ logging.IncludeScopes = true;
+ });
+
+ builder.Services.AddOpenTelemetry()
+ .WithMetrics(metrics =>
+ {
+ metrics.AddAspNetCoreInstrumentation()
+ .AddHttpClientInstrumentation()
+ .AddRuntimeInstrumentation()
+ .AddMeter(MasstransitTag);
+ })
+ .WithTracing(tracing =>
+ {
+ tracing.AddAspNetCoreInstrumentation()
+ // Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package)
+ //.AddGrpcClientInstrumentation()
+ .AddHttpClientInstrumentation()
+ .AddSource(MasstransitTag);
+ });
+
+ builder.AddOpenTelemetryExporters();
+
+ return builder;
+ }
+
+ private static IHostApplicationBuilder AddOpenTelemetryExporters(this IHostApplicationBuilder builder)
+ {
+ var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]);
+
+ if (useOtlpExporter)
+ {
+ builder.Services.AddOpenTelemetry().UseOtlpExporter();
+ }
+
+ // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package)
+ //if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"]))
+ //{
+ // builder.Services.AddOpenTelemetry()
+ // .UseAzureMonitor();
+ //}
+
+ return builder;
+ }
+
+ public static IHostApplicationBuilder AddDefaultHealthChecks(this IHostApplicationBuilder builder)
+ {
+ builder.Services.AddHealthChecks()
+ // Add a default liveness check to ensure app is responsive
+ .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]);
+
+ return builder;
+ }
+
+ public static WebApplication MapDefaultEndpoints(this WebApplication app)
+ {
+ // Adding health checks endpoints to applications in non-development environments has security implications.
+ // See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments.
+ if (app.Environment.IsDevelopment())
+ {
+ // All health checks must pass for app to be considered ready to accept traffic after starting
+ app.MapHealthChecks("/health");
+
+ // Only health checks tagged with the "live" tag must pass for app to be considered alive
+ app.MapHealthChecks("/alive", new HealthCheckOptions
+ {
+ Predicate = r => r.Tags.Contains("live") ||
+ r.Tags.Contains("masstransit")
+ });
+ }
+
+ return app;
+ }
+}
diff --git a/src/CommunityToolkit.Aspire.Hosting.LavinMQ/CommunityToolkit.Aspire.Hosting.LavinMQ.csproj b/src/CommunityToolkit.Aspire.Hosting.LavinMQ/CommunityToolkit.Aspire.Hosting.LavinMQ.csproj
new file mode 100644
index 00000000..86ed193e
--- /dev/null
+++ b/src/CommunityToolkit.Aspire.Hosting.LavinMQ/CommunityToolkit.Aspire.Hosting.LavinMQ.csproj
@@ -0,0 +1,18 @@
+
+
+
+ hosting LavinMQ
+ A .NET Aspire hosting package for hosting LavinMQ.
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/CommunityToolkit.Aspire.Hosting.LavinMQ/LavinMQContainerImageSettings.cs b/src/CommunityToolkit.Aspire.Hosting.LavinMQ/LavinMQContainerImageSettings.cs
new file mode 100644
index 00000000..6ae606db
--- /dev/null
+++ b/src/CommunityToolkit.Aspire.Hosting.LavinMQ/LavinMQContainerImageSettings.cs
@@ -0,0 +1,13 @@
+namespace CommunityToolkit.Aspire.Hosting.LavinMQ;
+
+internal static class LavinMQContainerImageSettings
+{
+ /// docker.io
+ internal const string Registry = "docker.io";
+
+ /// cloudamqp/lavinmq
+ internal const string Image = "cloudamqp/lavinmq";
+
+ /// 2.1.0
+ internal const string Tag = "2.1.0";
+}
diff --git a/src/CommunityToolkit.Aspire.Hosting.LavinMQ/LavinMQContainerResource.cs b/src/CommunityToolkit.Aspire.Hosting.LavinMQ/LavinMQContainerResource.cs
new file mode 100644
index 00000000..dda14f63
--- /dev/null
+++ b/src/CommunityToolkit.Aspire.Hosting.LavinMQ/LavinMQContainerResource.cs
@@ -0,0 +1,32 @@
+namespace Aspire.Hosting.ApplicationModel;
+
+///
+/// Represents a container resource for LavinMQ with configurable authentication parameters
+/// and connection endpoint details, implementing the IResourceWithConnectionString interface.
+///
+/// The name of the LavinMQ resource instance.
+public class LavinMQContainerResource(string name)
+ : ContainerResource(name), IResourceWithConnectionString
+{
+ private const string DefaultUserName = "guest";
+ private const string DefaultPassword = "guest";
+ internal const string PrimaryEndpointName = "amqp";
+ internal const string ManagementEndpointName = "management";
+ internal const string PrimaryEndpointSchema = "amqp";
+ internal const string ManagementEndpointSchema = "http";
+ internal const int DefaultAmqpPort = 5672;
+ internal const int DefaultManagementPort = 15672;
+
+ private EndpointReference? _primaryEndpoint;
+ private EndpointReference PrimaryEndpoint => _primaryEndpoint ??= new EndpointReference(this, PrimaryEndpointName);
+
+ private static ReferenceExpression UserNameReference => ReferenceExpression.Create($"{DefaultUserName}");
+
+ private static ReferenceExpression PasswordReference => ReferenceExpression.Create($"{DefaultPassword}");
+
+ ///
+ /// ConnectionString for the LavinMQ server in the form of amqp://guest:guest@host:port/.
+ ///
+ public ReferenceExpression ConnectionStringExpression =>
+ ReferenceExpression.Create($"amqp://{UserNameReference}:{PasswordReference}@{PrimaryEndpoint.Property(EndpointProperty.Host)}:{PrimaryEndpoint.Property(EndpointProperty.Port)}/");
+}
\ No newline at end of file
diff --git a/src/CommunityToolkit.Aspire.Hosting.LavinMQ/LavinMQHostingExtension.cs b/src/CommunityToolkit.Aspire.Hosting.LavinMQ/LavinMQHostingExtension.cs
new file mode 100644
index 00000000..bc24b11c
--- /dev/null
+++ b/src/CommunityToolkit.Aspire.Hosting.LavinMQ/LavinMQHostingExtension.cs
@@ -0,0 +1,114 @@
+using Aspire.Hosting.ApplicationModel;
+using CommunityToolkit.Aspire.Hosting.LavinMQ;
+using Microsoft.Extensions.DependencyInjection;
+using RabbitMQ.Client;
+
+namespace Aspire.Hosting;
+
+///
+/// Provides extension methods for configuring and adding a LavinMQ container as a resource
+/// within a distributed application using Aspire.Hosting. This enables connection, health
+/// checks, and specific configurations for LavinMQ instances.
+///
+public static class LavinMQHostingExtension
+{
+ ///
+ /// Adds a LavinMQ container resource to the distributed application builder.
+ /// Configures the resource with specified parameters and sets up health checks for the resource.
+ ///
+ /// The distributed application builder to which the LavinMQ resource will be added.
+ /// The name of the LavinMQ resource.
+ /// The port number for the AMQP protocol. Default is 5672.
+ /// The port number for the management interface. Default is 15672.
+ /// A resource builder for the LavinMQ container resource.
+ /// Thrown when the resource addition fails or other errors occur during the process.
+ public static IResourceBuilder AddLavinMQ(this IDistributedApplicationBuilder builder,
+ [ResourceName] string name,
+ int amqpPort = LavinMQContainerResource.DefaultAmqpPort,
+ int managementPort = LavinMQContainerResource.DefaultManagementPort)
+ {
+ ArgumentNullException.ThrowIfNull(builder);
+ ArgumentNullException.ThrowIfNull(name);
+
+ LavinMQContainerResource instance = new LavinMQContainerResource(name);
+
+ string? connectionString = null;
+
+ builder.Eventing.Subscribe(instance, async (_, ct) =>
+ {
+ connectionString = await instance.ConnectionStringExpression.GetValueAsync(ct).ConfigureAwait(false);
+
+ if (connectionString is null)
+ {
+ throw new DistributedApplicationException($"ConnectionStringAvailableEvent was published for the '{instance.Name}' resource but the connection string was null.");
+ }
+ });
+
+ string healthCheckKey = $"{name}_check";
+ // cache the connection so it is reused on subsequent calls to the health check
+ IConnection? connection = null;
+ builder.Services.AddHealthChecks().AddRabbitMQ(async _ =>
+ {
+ // NOTE: Ensure that execution of this setup callback is deferred until after
+ // the container is built & started.
+ return connection ??= await CreateConnection(connectionString!).ConfigureAwait(false);
+
+ static Task CreateConnection(string connectionString)
+ {
+ ConnectionFactory factory = new ConnectionFactory
+ {
+ Uri = new(connectionString),
+ };
+ return factory.CreateConnectionAsync();
+ }
+ }, healthCheckKey);
+
+ return builder.AddResource(instance)
+ .WithImage(LavinMQContainerImageSettings.Image, LavinMQContainerImageSettings.Tag)
+ .WithImageRegistry(LavinMQContainerImageSettings.Registry)
+ .WithEndpoint(
+ port: amqpPort,
+ targetPort: LavinMQContainerResource.DefaultAmqpPort,
+ name: LavinMQContainerResource.PrimaryEndpointName,
+ scheme: LavinMQContainerResource.PrimaryEndpointSchema)
+ .WithEndpoint(
+ port: managementPort,
+ targetPort: LavinMQContainerResource.DefaultManagementPort,
+ name: LavinMQContainerResource.ManagementEndpointName,
+ scheme: LavinMQContainerResource.ManagementEndpointSchema)
+ .WithHealthCheck(healthCheckKey);
+ }
+
+ ///
+ /// Configures a data volume for the LavinMQ container resource by specifying its name and read-only status.
+ ///
+ /// The resource builder for the LavinMQ container resource.
+ /// The name of the data volume to be attached to the LavinMQ container resource.
+ /// Indicates whether the data volume should be mounted as read-only. Default is false.
+ /// The updated resource builder for the LavinMQ container resource.
+ public static IResourceBuilder WithDataVolume(this IResourceBuilder builder, string name,
+ bool isReadOnly = false)
+ {
+ ArgumentNullException.ThrowIfNull(builder);
+
+ return builder.WithVolume(name, "/var/lib/lavinmq", isReadOnly);
+ }
+
+ ///
+ /// Configures a bind mount for the LavinMQ container resource to allow data persistence.
+ /// The method mounts a specified source path on the host to the container's data directory.
+ ///
+ /// The resource builder for the LavinMQ container to which the data bind mount will be added.
+ /// The source path on the host machine to bind mount to the container's data directory.
+ /// Indicates if the bind mount should be configured as read-only. Default is false.
+ /// An updated resource builder for the LavinMQ container resource with the configured data bind mount.
+ /// Thrown when the builder or source parameters are null.
+ public static IResourceBuilder WithDataBindMount(this IResourceBuilder builder, string source,
+ bool isReadOnly = false)
+ {
+ ArgumentNullException.ThrowIfNull(builder);
+ ArgumentNullException.ThrowIfNull(source);
+
+ return builder.WithBindMount(source, "/var/lib/lavinmq", isReadOnly);
+ }
+}
\ No newline at end of file
diff --git a/src/CommunityToolkit.Aspire.Hosting.LavinMQ/PublicAPI.Shipped.txt b/src/CommunityToolkit.Aspire.Hosting.LavinMQ/PublicAPI.Shipped.txt
new file mode 100644
index 00000000..7dc5c581
--- /dev/null
+++ b/src/CommunityToolkit.Aspire.Hosting.LavinMQ/PublicAPI.Shipped.txt
@@ -0,0 +1 @@
+#nullable enable
diff --git a/src/CommunityToolkit.Aspire.Hosting.LavinMQ/PublicAPI.Unshipped.txt b/src/CommunityToolkit.Aspire.Hosting.LavinMQ/PublicAPI.Unshipped.txt
new file mode 100644
index 00000000..a3e9fd87
--- /dev/null
+++ b/src/CommunityToolkit.Aspire.Hosting.LavinMQ/PublicAPI.Unshipped.txt
@@ -0,0 +1,8 @@
+#nullable enable
+Aspire.Hosting.ApplicationModel.LavinMQContainerResource
+Aspire.Hosting.ApplicationModel.LavinMQContainerResource.ConnectionStringExpression.get -> Aspire.Hosting.ApplicationModel.ReferenceExpression!
+Aspire.Hosting.ApplicationModel.LavinMQContainerResource.LavinMQContainerResource(string! name) -> void
+Aspire.Hosting.LavinMQHostingExtension
+static Aspire.Hosting.LavinMQHostingExtension.AddLavinMQ(this Aspire.Hosting.IDistributedApplicationBuilder! builder, string! name, int amqpPort = 5672, int managementPort = 15672) -> Aspire.Hosting.ApplicationModel.IResourceBuilder!
+static Aspire.Hosting.LavinMQHostingExtension.WithDataBindMount(this Aspire.Hosting.ApplicationModel.IResourceBuilder! builder, string! source, bool isReadOnly = false) -> Aspire.Hosting.ApplicationModel.IResourceBuilder!
+static Aspire.Hosting.LavinMQHostingExtension.WithDataVolume(this Aspire.Hosting.ApplicationModel.IResourceBuilder! builder, string! name, bool isReadOnly = false) -> Aspire.Hosting.ApplicationModel.IResourceBuilder!
\ No newline at end of file
diff --git a/src/CommunityToolkit.Aspire.Hosting.LavinMQ/README.md b/src/CommunityToolkit.Aspire.Hosting.LavinMQ/README.md
new file mode 100644
index 00000000..b9a06864
--- /dev/null
+++ b/src/CommunityToolkit.Aspire.Hosting.LavinMQ/README.md
@@ -0,0 +1,23 @@
+# CommunityToolkit.Hosting.LavinMQ
+
+## Overview
+
+This .NET Aspire Integration can be used to include [LavinMQ](https://lavinmq.com/) in a container.
+
+## Usage
+
+### Example 1: LavinMQ with default ports
+
+```csharp
+var builder = DistributedApplication.CreateBuilder(args);
+
+var lavinmq = builder.AddLavinMQ("lavinmq");
+```
+
+### Example 2: LavinMQ with custom ports
+
+```csharp
+var builder = DistributedApplication.CreateBuilder(args);
+
+var lavinmq = builder.AddLavinMQ("lavinmq", amqpPort: 5672, managementPort: 15672);
+```
diff --git a/tests/CommunityToolkit.Aspire.Hosting.LavinMQ.Tests/AppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.LavinMQ.Tests/AppHostTests.cs
new file mode 100644
index 00000000..a1f17e05
--- /dev/null
+++ b/tests/CommunityToolkit.Aspire.Hosting.LavinMQ.Tests/AppHostTests.cs
@@ -0,0 +1,53 @@
+using Aspire.Components.Common.Tests;
+using CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit;
+using CommunityToolkit.Aspire.Testing;
+using System.Net.Http.Json;
+
+namespace CommunityToolkit.Aspire.Hosting.LavinMQ.Tests;
+
+[RequiresDocker]
+public class AppHostTests(AspireIntegrationTestFixture fixture) : IClassFixture>
+{
+ [Fact]
+ public async Task ResourceStartsAndRespondsOk()
+ {
+ const string resourceName = "masstransitExample";
+ await fixture.ResourceNotificationService.WaitForResourceHealthyAsync(resourceName).WaitAsync(TimeSpan.FromMinutes(2));
+ HttpClient httpClient = fixture.CreateHttpClient(resourceName);
+
+ HttpResponseMessage response = await httpClient.PostAsync("/send/Hello%20World", new StringContent(""));
+
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ }
+
+ [Fact]
+ public async Task ReplyShouldBeReceived()
+ {
+ const string resourceName = "masstransitExample";
+ await fixture.ResourceNotificationService.WaitForResourceHealthyAsync(resourceName).WaitAsync(TimeSpan.FromMinutes(2));
+ HttpClient httpClient = fixture.CreateHttpClient(resourceName);
+
+ HttpResponseMessage response = await httpClient.PostAsync("/send/Hello%20World", new StringContent(""));
+
+ string message = (await response.Content.ReadAsStringAsync());
+ Assert.Equal("I've received your message: Hello World", message);
+ }
+
+ [Fact]
+ public async Task WhenMessageIsSendItShouldBeReceivedByConsumer()
+ {
+ const string resourceName = "masstransitExample";
+ await fixture.ResourceNotificationService.WaitForResourceHealthyAsync(resourceName).WaitAsync(TimeSpan.FromMinutes(2));
+ HttpClient httpClient = fixture.CreateHttpClient(resourceName);
+
+ MessageCounter? oldMessageCounter = await httpClient.GetFromJsonAsync("/received");
+ Assert.NotNull(oldMessageCounter);
+
+ await httpClient.PostAsync("/send/Hello%20World", new StringContent(""));
+
+ MessageCounter? messageCounter = await httpClient.GetFromJsonAsync("/received");
+
+ Assert.NotNull(messageCounter);
+ Assert.True(messageCounter.ReceivedMessages > oldMessageCounter.ReceivedMessages);
+ }
+}
\ No newline at end of file
diff --git a/tests/CommunityToolkit.Aspire.Hosting.LavinMQ.Tests/CommunityToolkit.Aspire.Hosting.LavinMQ.Tests.csproj b/tests/CommunityToolkit.Aspire.Hosting.LavinMQ.Tests/CommunityToolkit.Aspire.Hosting.LavinMQ.Tests.csproj
new file mode 100644
index 00000000..c6bc4981
--- /dev/null
+++ b/tests/CommunityToolkit.Aspire.Hosting.LavinMQ.Tests/CommunityToolkit.Aspire.Hosting.LavinMQ.Tests.csproj
@@ -0,0 +1,15 @@
+
+
+
+ false
+ true
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/CommunityToolkit.Aspire.Hosting.LavinMQ.Tests/ContainerResourceCreationTests.cs b/tests/CommunityToolkit.Aspire.Hosting.LavinMQ.Tests/ContainerResourceCreationTests.cs
new file mode 100644
index 00000000..73aa4f34
--- /dev/null
+++ b/tests/CommunityToolkit.Aspire.Hosting.LavinMQ.Tests/ContainerResourceCreationTests.cs
@@ -0,0 +1,85 @@
+using Aspire.Hosting;
+
+namespace CommunityToolkit.Aspire.Hosting.LavinMQ.Tests;
+
+public class ContainerResourceCreationTests
+{
+ [Fact]
+ public void AddLavinMqApiBuilderBuilderShouldNotBeNull()
+ {
+ IDistributedApplicationBuilder builder = null!;
+ Assert.Throws(() => builder.AddLavinMQ("lavinmq"));
+ }
+
+ [Fact]
+ public void AddLavinMqApiBuilderNameShouldNotBeNullOrWhiteSpace()
+ {
+ IDistributedApplicationBuilder builder = DistributedApplication.CreateBuilder();
+ Assert.Throws(() => builder.AddLavinMQ(null!));
+ }
+
+ [Fact]
+ public void AddLavinMqApiBuilderContainerDetailsSetOnResource()
+ {
+ IDistributedApplicationBuilder builder = DistributedApplication.CreateBuilder();
+
+ builder.AddLavinMQ("lavinmq");
+
+ using DistributedApplication app = builder.Build();
+ DistributedApplicationModel appModel = app.Services.GetRequiredService();
+
+ LavinMQContainerResource? resource = appModel.Resources.OfType().SingleOrDefault();
+
+ Assert.NotNull(resource);
+ Assert.Equal("lavinmq", resource.Name);
+ ValidateLavinMqContainerImageAnnotations(resource);
+ ValidateEndpointAnnotations(resource, LavinMQContainerResource.DefaultAmqpPort, LavinMQContainerResource.DefaultManagementPort);
+ }
+
+ [Fact]
+ public void AddLavinMqApiBuilderContainerDetailsSetOnResourceCustomPorts()
+ {
+ const int amqpPort = 1111;
+ const int managementPort = 2222;
+
+ IDistributedApplicationBuilder builder = DistributedApplication.CreateBuilder();
+
+ builder.AddLavinMQ("lavinmq", amqpPort, managementPort);
+
+ using DistributedApplication app = builder.Build();
+ DistributedApplicationModel appModel = app.Services.GetRequiredService();
+
+ LavinMQContainerResource? resource = appModel.Resources.OfType().SingleOrDefault();
+
+ Assert.NotNull(resource);
+ Assert.Equal("lavinmq", resource.Name);
+ ValidateLavinMqContainerImageAnnotations(resource);
+ ValidateEndpointAnnotations(resource, amqpPort, managementPort);
+ }
+
+ private static void ValidateLavinMqContainerImageAnnotations(LavinMQContainerResource resource)
+ {
+ Assert.True(resource.TryGetLastAnnotation(out ContainerImageAnnotation? imageAnnotations));
+ Assert.Equal("2.1.0", imageAnnotations.Tag);
+ Assert.Equal("cloudamqp/lavinmq", imageAnnotations.Image);
+ Assert.Equal("docker.io", imageAnnotations.Registry);
+ }
+
+ private static void ValidateEndpointAnnotations(LavinMQContainerResource resource, int amqpPort, int managementPort)
+ {
+ resource.TryGetAnnotationsOfType(out IEnumerable? annotations);
+ Assert.NotNull(annotations);
+
+ List endpointAnnotations = annotations.ToList();
+
+ Assert.Equal(2, endpointAnnotations.Count);
+
+ Assert.Equal(LavinMQContainerResource.PrimaryEndpointSchema, endpointAnnotations[0].UriScheme);
+ Assert.Equal(LavinMQContainerResource.PrimaryEndpointName, endpointAnnotations[0].Name);
+ Assert.Equal(amqpPort, endpointAnnotations[0].Port);
+
+ Assert.Equal(LavinMQContainerResource.ManagementEndpointSchema, endpointAnnotations[1].UriScheme);
+ Assert.Equal(LavinMQContainerResource.ManagementEndpointName, endpointAnnotations[1].Name);
+ Assert.Equal(managementPort, endpointAnnotations[1].Port);
+ }
+}