diff --git a/.editorconfig b/.editorconfig index 563f844..45c9745 100644 --- a/.editorconfig +++ b/.editorconfig @@ -79,6 +79,10 @@ indent_style = space indent_size = 4 tab_width = 4 +[*.{xml,csproj}] +indent_size = 2 +tab_width = 2 + #### C# Coding Conventions #### [*.cs] diff --git a/test/Cnblogs.Architecture.IntegrationTestProject/Cnblogs.Architecture.IntegrationTestProject.csproj b/test/Cnblogs.Architecture.IntegrationTestProject/Cnblogs.Architecture.IntegrationTestProject.csproj index 1610ac2..9c5f369 100644 --- a/test/Cnblogs.Architecture.IntegrationTestProject/Cnblogs.Architecture.IntegrationTestProject.csproj +++ b/test/Cnblogs.Architecture.IntegrationTestProject/Cnblogs.Architecture.IntegrationTestProject.csproj @@ -1,13 +1,13 @@ - + - - - + + + - - - - - + + + + + diff --git a/test/Cnblogs.Architecture.IntegrationTestProject/Constants.cs b/test/Cnblogs.Architecture.IntegrationTestProject/Constants.cs new file mode 100644 index 0000000..96717c4 --- /dev/null +++ b/test/Cnblogs.Architecture.IntegrationTestProject/Constants.cs @@ -0,0 +1,7 @@ +namespace Cnblogs.Architecture.IntegrationTestProject; + +public static class Constants +{ + public const string AppName = "test-web"; + public const string IntegrationEventIdHeaderName = "X-IntegrationEvent-Id"; +} diff --git a/test/Cnblogs.Architecture.IntegrationTestProject/EventHandlers/TestIntegrationEventHandler.cs b/test/Cnblogs.Architecture.IntegrationTestProject/EventHandlers/TestIntegrationEventHandler.cs new file mode 100644 index 0000000..d363660 --- /dev/null +++ b/test/Cnblogs.Architecture.IntegrationTestProject/EventHandlers/TestIntegrationEventHandler.cs @@ -0,0 +1,32 @@ +using System.Diagnostics; +using Cnblogs.Architecture.Ddd.EventBus.Abstractions; +using Cnblogs.Architecture.TestIntegrationEvents; +using MediatR; + +namespace Cnblogs.Architecture.IntegrationTestProject.EventHandlers; + +public class TestIntegrationEventHandler : IIntegrationEventHandler +{ + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly ILogger _logger; + + public TestIntegrationEventHandler(IHttpContextAccessor httpContextAccessor, ILogger logger) + { + _httpContextAccessor = httpContextAccessor; + _logger = logger; + } + + public Task Handle(TestIntegrationEvent notification, CancellationToken cancellationToken) + { + var context = _httpContextAccessor.HttpContext; + context?.Response.OnStarting(() => + { + context.Response.Headers.Add(Constants.IntegrationEventIdHeaderName, notification.Id.ToString()); + return Task.CompletedTask; + }); + + _logger.LogInformation("Handled integration event {event}.", notification); + + return Task.CompletedTask; + } +} diff --git a/test/Cnblogs.Architecture.IntegrationTestProject/Program.cs b/test/Cnblogs.Architecture.IntegrationTestProject/Program.cs index bd06489..ba8f983 100644 --- a/test/Cnblogs.Architecture.IntegrationTestProject/Program.cs +++ b/test/Cnblogs.Architecture.IntegrationTestProject/Program.cs @@ -1,22 +1,25 @@ +using System.Reflection; using Cnblogs.Architecture.Ddd.Cqrs.AspNetCore; using Cnblogs.Architecture.Ddd.Cqrs.DependencyInjection.EventBus.Dapr; +using Cnblogs.Architecture.IntegrationTestProject; using Cnblogs.Architecture.IntegrationTestProject.Application.Commands; using Cnblogs.Architecture.IntegrationTestProject.Application.Queries; using Cnblogs.Architecture.IntegrationTestProject.Payloads; using Cnblogs.Architecture.TestIntegrationEvents; -const string appName = "test-web"; - var builder = WebApplication.CreateBuilder(args); -builder.Services.AddCqrs(typeof(Cnblogs.Architecture.IntegrationTestProject.Program).Assembly) +builder.Services.AddCqrs( + Assembly.GetExecutingAssembly(), + typeof(TestIntegrationEvent).Assembly) .AddDefaultDateTimeAndRandomProvider(); -builder.Services.AddDaprEventBus(appName); +builder.Services.AddDaprEventBus(Constants.AppName); builder.Services.AddControllers().AddCqrsModelBinderProvider(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddCnblogsApiVersioning(); builder.Services.AddSwaggerGen(); +builder.Services.AddHttpContextAccessor(); var app = builder.Build(); diff --git a/test/Cnblogs.Architecture.IntegrationTests/Cnblogs.Architecture.IntegrationTests.csproj b/test/Cnblogs.Architecture.IntegrationTests/Cnblogs.Architecture.IntegrationTests.csproj index 4f17551..df6a342 100644 --- a/test/Cnblogs.Architecture.IntegrationTests/Cnblogs.Architecture.IntegrationTests.csproj +++ b/test/Cnblogs.Architecture.IntegrationTests/Cnblogs.Architecture.IntegrationTests.csproj @@ -1,5 +1,4 @@ - - + diff --git a/test/Cnblogs.Architecture.IntegrationTests/DaprTests.cs b/test/Cnblogs.Architecture.IntegrationTests/DaprTests.cs index e0dced1..1d9be35 100644 --- a/test/Cnblogs.Architecture.IntegrationTests/DaprTests.cs +++ b/test/Cnblogs.Architecture.IntegrationTests/DaprTests.cs @@ -32,8 +32,9 @@ public async Task Dapr_SubscribeEndpoint_OkAsync() var response = await httpClient.GetAsync("/dapr/subscribe"); // Assert - response.StatusCode.Should().Be(HttpStatusCode.OK); + response.Should().BeSuccessful(); var responseText = await response.Content.ReadAsStringAsync(); + Debug.WriteLine(responseText); responseText.Should().Contain(nameof(TestIntegrationEvent)); } diff --git a/test/Cnblogs.Architecture.IntegrationTests/DddWebTestCollection.cs b/test/Cnblogs.Architecture.IntegrationTests/DddWebTestCollection.cs deleted file mode 100644 index 3ddac04..0000000 --- a/test/Cnblogs.Architecture.IntegrationTests/DddWebTestCollection.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Cnblogs.Architecture.IntegrationTests; - -[CollectionDefinition(Name)] -public class DddWebTestCollection : ICollectionFixture -{ - public const string Name = nameof(DddWebTestCollection); -} diff --git a/test/Cnblogs.Architecture.IntegrationTests/IntegrationEventHandlerTests.cs b/test/Cnblogs.Architecture.IntegrationTests/IntegrationEventHandlerTests.cs new file mode 100644 index 0000000..4087e33 --- /dev/null +++ b/test/Cnblogs.Architecture.IntegrationTests/IntegrationEventHandlerTests.cs @@ -0,0 +1,39 @@ +using System.Net.Http.Json; +using Cnblogs.Architecture.IntegrationTestProject; +using Cnblogs.Architecture.TestIntegrationEvents; +using FluentAssertions; +using Xunit.Abstractions; + +namespace Cnblogs.Architecture.IntegrationTests; + +[Collection(IntegrationTestCollection.Name)] +public class IntegrationEventHandlerTests +{ + private readonly IntegrationTestFactory _factory; + private readonly ITestOutputHelper _testOutputHelper; + + public IntegrationEventHandlerTests(IntegrationTestFactory factory, ITestOutputHelper testOutputHelper) + { + _factory = factory; + _testOutputHelper = testOutputHelper; + } + + [Fact] + public async Task IntegrationEventHandler_TestIntegrationEvent_SuccessAsync() + { + // Arrange + var client = _factory.CreateClient(); + var @event = new TestIntegrationEvent(Guid.NewGuid(), DateTimeOffset.Now, "Hello World!"); + + // Act + var subscriptions = await client.GetFromJsonAsync("/dapr/subscribe"); + var sub = subscriptions!.First(x => x.Route.Contains(nameof(TestIntegrationEvent))); + var response = await client.PostAsJsonAsync(sub.Route, @event); + _testOutputHelper.WriteLine("Subscription Route: " + sub.Route); + + // Assert + response.Should().BeSuccessful(); + response.Headers.Should().ContainKey(Constants.IntegrationEventIdHeaderName) + .WhoseValue.First().Should().Be(@event.Id.ToString()); + } +} \ No newline at end of file diff --git a/test/Cnblogs.Architecture.IntegrationTests/IntegrationTestCollection.cs b/test/Cnblogs.Architecture.IntegrationTests/IntegrationTestCollection.cs new file mode 100644 index 0000000..3ad7ace --- /dev/null +++ b/test/Cnblogs.Architecture.IntegrationTests/IntegrationTestCollection.cs @@ -0,0 +1,7 @@ +namespace Cnblogs.Architecture.IntegrationTests; + +[CollectionDefinition(Name)] +public class IntegrationTestCollection : ICollectionFixture +{ + public const string Name = nameof(IntegrationTestCollection); +} diff --git a/test/Cnblogs.Architecture.IntegrationTests/DddWebTestFactory.cs b/test/Cnblogs.Architecture.IntegrationTests/IntegrationTestFactory.cs similarity index 68% rename from test/Cnblogs.Architecture.IntegrationTests/DddWebTestFactory.cs rename to test/Cnblogs.Architecture.IntegrationTests/IntegrationTestFactory.cs index 784c68a..b638297 100644 --- a/test/Cnblogs.Architecture.IntegrationTests/DddWebTestFactory.cs +++ b/test/Cnblogs.Architecture.IntegrationTests/IntegrationTestFactory.cs @@ -3,6 +3,6 @@ namespace Cnblogs.Architecture.IntegrationTests; -public class DddWebTestFactory : WebApplicationFactory +public class IntegrationTestFactory : WebApplicationFactory { } diff --git a/test/Cnblogs.Architecture.IntegrationTests/IntegrationTestFramework.cs b/test/Cnblogs.Architecture.IntegrationTests/IntegrationTestFramework.cs new file mode 100644 index 0000000..d2b7b04 --- /dev/null +++ b/test/Cnblogs.Architecture.IntegrationTests/IntegrationTestFramework.cs @@ -0,0 +1,19 @@ +using System.Diagnostics; +using System.Text; +using Cnblogs.Architecture.IntegrationTests; +using Xunit.Abstractions; +using Xunit.Sdk; + +[assembly: TestFramework($"Cnblogs.Architecture.IntegrationTests.{nameof(IntegrationTestFramework)}", "Cnblogs.Architecture.IntegrationTests")] + +namespace Cnblogs.Architecture.IntegrationTests; + +public class IntegrationTestFramework : XunitTestFramework +{ + public IntegrationTestFramework(IMessageSink messageSink) + : base(messageSink) + { + Console.OutputEncoding = Encoding.UTF8; + Trace.Listeners.Add(new ConsoleTraceListener()); + } +} diff --git a/test/Cnblogs.Architecture.IntegrationTests/Subscription.cs b/test/Cnblogs.Architecture.IntegrationTests/Subscription.cs new file mode 100644 index 0000000..2fe8215 --- /dev/null +++ b/test/Cnblogs.Architecture.IntegrationTests/Subscription.cs @@ -0,0 +1,22 @@ +namespace Cnblogs.Architecture.IntegrationTests; + +/// +/// This class defines subscribe endpoint response for dapr +/// +internal class Subscription +{ + /// + /// Gets or sets the topic name. + /// + public string Topic { get; set; } = string.Empty; + + /// + /// Gets or sets the pubsub name + /// + public string PubsubName { get; set; } = string.Empty; + + /// + /// Gets or sets the route + /// + public string Route { get; set; } = string.Empty; +} diff --git a/test/Cnblogs.Architecture.TestIntegrationEvents/TestIntegrationEvent.cs b/test/Cnblogs.Architecture.TestIntegrationEvents/TestIntegrationEvent.cs index 14df8dd..6e76c83 100644 --- a/test/Cnblogs.Architecture.TestIntegrationEvents/TestIntegrationEvent.cs +++ b/test/Cnblogs.Architecture.TestIntegrationEvents/TestIntegrationEvent.cs @@ -2,4 +2,4 @@ namespace Cnblogs.Architecture.TestIntegrationEvents; -public record TestIntegrationEvent(Guid Id, DateTimeOffset CreatedTime) : IntegrationEvent(Id, CreatedTime); \ No newline at end of file +public record TestIntegrationEvent(Guid Id, DateTimeOffset CreatedTime, string Message) : IntegrationEvent(Id, CreatedTime); \ No newline at end of file