Skip to content

Commit

Permalink
Refactor: Messagebus (#57)
Browse files Browse the repository at this point in the history
  • Loading branch information
oveldman authored Oct 11, 2024
1 parent f514122 commit 189df6b
Show file tree
Hide file tree
Showing 16 changed files with 288 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
<ItemGroup>
<PackageReference Include="coverlet.collector" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="NSubstitute" />
<PackageReference Include="Shouldly" />
<PackageReference Include="Testcontainers.RabbitMq" />
<PackageReference Include="xunit" />
Expand Down
30 changes: 30 additions & 0 deletions src/Clients.Admin.Bff.IntegrationTests/Base/AdminBffFactory.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
using MadWorldNL.MantaRayPlan.Api;
using MassTransit;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using NSubstitute;
using Testcontainers.RabbitMq;

namespace MadWorldNL.MantaRayPlan.Base;

public class AdminBffFactory : WebApplicationFactory<Program>, IAsyncLifetime
{
public MessageBusService.MessageBusServiceClient MessageBusServiceClient = Substitute.For<MessageBusService.MessageBusServiceClient>();

private const string BusUser = "development";
private const string BusPassword = "Password1234";

Expand All @@ -23,6 +30,26 @@ public async Task InitializeAsync()

await _rabbitMqContainer.StartAsync();
}

public IServiceScope GetServiceProvider()
{
return Services.GetService<IServiceScopeFactory>()!.CreateScope();
}

public async Task<HubConnection> CreateSignalRClientAsync<TReceiveType>(string hubName, string methodeName, Action<TReceiveType> handler)
{
var connection = new HubConnectionBuilder()
.WithUrl(
$"{Server.BaseAddress}{hubName}",
o => o.HttpMessageHandlerFactory = _ => Server.CreateHandler())
.Build();

connection.On(methodeName, handler);

await connection.StartAsync();

return connection;
}

protected override IHost CreateHost(IHostBuilder builder)
{
Expand All @@ -41,6 +68,9 @@ protected override IHost CreateHost(IHostBuilder builder)

builder.ConfigureServices(services =>
{
services.RemoveAll<MessageBusService.MessageBusServiceClient>();
services.AddScoped<MessageBusService.MessageBusServiceClient>(_ => MessageBusServiceClient);
// For more info about testing message bus:
// https://masstransit.io/documentation/concepts/testing
services.AddMassTransitTestHarness();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System.Net.Http.Json;
using Google.Protobuf.WellKnownTypes;
using MadWorldNL.MantaRayPlan.Api;
using MadWorldNL.MantaRayPlan.Base;
using MadWorldNL.MantaRayPlan.MessageBuses;
using NSubstitute;
using Shouldly;

namespace MadWorldNL.MantaRayPlan.Endpoints;

[Collection(TestDefinitions.Default)]
public class MessageBusEndpointsTests(AdminBffFactory factory)
{
[Fact]
public async Task GetStatus_WhenSendWithoutParameters_ShouldReturnCurrentCounter()
{
// Arrange
factory.MessageBusServiceClient.GetStatus(new Empty()).Returns(new GetMessageBusStatusReply()
{
Counter = 1,
Message = ""
});

var client = factory.CreateClient();

// Act
var response = await client.GetFromJsonAsync<GetStatusResponse>("/MessageBus/Status");

// Assert
response.ShouldNotBeNull();
response.message.ShouldBe(string.Empty);
response.counter.ShouldBe(1);
}

[Fact]
public async Task PostStatus_WhenSendWithoutParameters_ShouldIncreaseCounter()
{
// Arrange
factory.MessageBusServiceClient.PostStatus(new Empty()).Returns(new PostMessageBusStatusReply()
{
Message = ""
});

var client = factory.CreateClient();

// Act
var httpResponse = await client.PostAsJsonAsync<string>("/MessageBus/Status", "RandomData");

// Assert
httpResponse.ShouldNotBeNull();
httpResponse.EnsureSuccessStatusCode();

var response = await httpResponse.Content.ReadFromJsonAsync<PostStatusResponse>();
response.ShouldNotBeNull();
response.message.ShouldBe(string.Empty);
}
}
39 changes: 39 additions & 0 deletions src/Clients.Admin.Bff.IntegrationTests/Hubs/EventsHubTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using MadWorldNL.MantaRayPlan.Base;
using MadWorldNL.MantaRayPlan.Events;
using MassTransit.Internals;
using MassTransit.Testing;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;

namespace MadWorldNL.MantaRayPlan.Hubs;

[Collection(TestDefinitions.Default)]
public class EventsHubTests(AdminBffFactory factory)
{
[Fact]
public async Task NewEvent_WhenGivenNewEvent_ShouldReceiveEvent()
{
// Arrange
var serviceProvider = factory.GetServiceProvider().ServiceProvider;
var harness = serviceProvider.GetRequiredService<ITestHarness>();

var source = new TaskCompletionSource<MessageBusStatusEvent>();
await factory.CreateSignalRClientAsync<MessageBusStatusEvent>("Events",
"NewEvent", @event =>
{
source.TrySetResult(@event);
});

// Act
await harness.Bus.Publish(new MessageBusStatusEvent()
{
Count = 10
});

var statusEvent = await source.Task.OrTimeout(TimeSpan.FromSeconds(3));

// Assert
statusEvent.ShouldNotBeNull();
statusEvent.Count.ShouldBe(10);
}
}
20 changes: 2 additions & 18 deletions src/Clients.Viewer.Bff/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,24 +62,8 @@

app.MapHealthChecks("/healthz");

var summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

app.MapGet("/weatherforecast", () =>
{
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
(
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
Random.Shared.Next(-20, 55),
summaries[Random.Shared.Next(summaries.Length)]
))
.ToArray();
return forecast;
})
.WithName("GetWeatherForecast")
app.MapGet("/TODO", () => "NotImplemented")
.WithName("TODO")
.WithOpenApi()
.RequireRateLimiting(RateLimiterConfig.DefaultName);

Expand Down
6 changes: 0 additions & 6 deletions src/Clients.Viewer.Bff/WeatherForecast.cs

This file was deleted.

1 change: 1 addition & 0 deletions src/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<PackageVersion Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageVersion Include="Microsoft.Web.LibraryManager.Build" Version="2.1.175" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.8" />
<PackageVersion Include="NSubstitute" Version="5.1.0" />
<PackageVersion Include="OpenTelemetry" Version="1.9.0" />
<PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.9.0" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
using Grpc.Net.Client;
using MadWorldNL.MantaRayPlan.Extensions;
using MadWorldNL.MantaRayPlan.MessageBuses;
using MassTransit;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Testcontainers.PostgreSql;
using Testcontainers.RabbitMq;
Expand All @@ -11,12 +14,16 @@ namespace MadWorldNL.MantaRayPlan.Base;

public class GrpcFactory : WebApplicationFactory<Program>, IAsyncLifetime
{
public GrpcChannel Channel => _grpcChannel ??= CreateGrpcChannel();

private const string DbName = "MantaRayPlan";
private const string DbUser = "postgres";
private const string DbPassword = "Password1234!";

private const string BusUser = "development";
private const string BusPassword = "Password1234";

private GrpcChannel? _grpcChannel;

private PostgreSqlContainer? _postgreSqlContainer;
private RabbitMqContainer? _rabbitMqContainer;
Expand All @@ -40,6 +47,19 @@ public async Task InitializeAsync()
await _rabbitMqContainer.StartAsync();
}

private GrpcChannel CreateGrpcChannel()
{
return GrpcChannel.ForAddress(Server.BaseAddress, new GrpcChannelOptions()
{
HttpHandler = Server.CreateHandler()
});
}

public IServiceScope GetServiceProvider()
{
return Services.GetService<IServiceScopeFactory>()!.CreateScope();
}

protected override IHost CreateHost(IHostBuilder builder)
{
var newSettings = new Dictionary<string, string>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
using System.Net;
using MadWorldNL.MantaRayPlan.Base;
using Microsoft.AspNetCore.Mvc.Testing;
using Shouldly;

namespace MadWorldNL.MantaRayPlan.Endpoints;

[Collection(TestDefinitions.Default)]
public class HealthCheckTests(GrpcFactory factory) : IAsyncLifetime
public class HealthCheckTests(GrpcFactory factory)
{
[Fact]
public async Task Healthz_GivenEmptyRequest_ShouldBeHealthy()
Expand All @@ -20,8 +19,4 @@ public async Task Healthz_GivenEmptyRequest_ShouldBeHealthy()
// Assert
response.StatusCode.ShouldBe(HttpStatusCode.OK);
}

public Task InitializeAsync() => Task.CompletedTask;

public Task DisposeAsync() => factory.DisposeAsync();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using Google.Protobuf.WellKnownTypes;
using MadWorldNL.MantaRayPlan.Api;
using MadWorldNL.MantaRayPlan.Base;
using MadWorldNL.MantaRayPlan.Events;
using MassTransit.Testing;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;

namespace MadWorldNL.MantaRayPlan.Services;

[Collection(TestDefinitions.Default)]
public class EventServiceProxyTests(GrpcFactory factory)
{
[Fact]
public async Task Subscribe_WhenGivenNewEvent_ShouldReceiveEvent()
{
// Arrange
var eventClient = new EventService.EventServiceClient(factory.Channel);
var serviceProvider = factory.GetServiceProvider().ServiceProvider;
var harness = serviceProvider.GetRequiredService<ITestHarness>();

// Act
var responses = eventClient.Subscribe(new Empty());
await harness.Bus.Publish(new MessageBusStatusEvent
{
Count = 10
});

// Assert
await responses.ResponseStream.MoveNext(CancellationToken.None);
var @event = responses.ResponseStream.Current;
@event.Type.ShouldBe(nameof(MessageBusStatusEvent));
@event.Json.ShouldBe("{\"Count\":10}");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using Google.Protobuf.WellKnownTypes;
using MadWorldNL.MantaRayPlan.Api;
using MadWorldNL.MantaRayPlan.Base;
using MadWorldNL.MantaRayPlan.MessageBuses;
using MassTransit.Testing;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;

namespace MadWorldNL.MantaRayPlan.Services;

[Collection(TestDefinitions.Default)]
public class MessageBusServiceProxyTests(GrpcFactory factory)
{
[Fact]
public async Task GetStatus_WhenGivenEmptyRequest_ShouldReturnValidCounter()
{
// Arrange
const int counter = 10;

var messageBusClient = new MessageBusService.MessageBusServiceClient(factory.Channel);
var serviceProvider = factory.GetServiceProvider().ServiceProvider;

var dbContext = serviceProvider.GetRequiredService<MantaRayPlanDbContext>();
var newStatus = new MessageBusStatus()
{
Id = 1,
Count = counter
};
dbContext.MessageBusStatus.Add(newStatus);
await dbContext.SaveChangesAsync();

// Act
var status = await messageBusClient.GetStatusAsync(new Empty());

// Assert
status.Counter.ShouldBe(counter);
status.Message.ShouldBe(string.Empty);
}

[Fact]
public async Task PostStatus_WhenGivenEmptyRequest_ShouldIncreaseCounterValue()
{
// Arrange
var messageBusClient = new MessageBusService.MessageBusServiceClient(factory.Channel);

var serviceProvider = factory.GetServiceProvider().ServiceProvider;

// Act
var response = await messageBusClient.PostStatusAsync(new Empty());

// Assert
response.Message.ShouldBe("");

var harness = serviceProvider.GetRequiredService<ITestHarness>();
await harness.Sent.Any<MessageBusStatusCommand>();
}
}
Loading

0 comments on commit 189df6b

Please sign in to comment.