Add unit tests for Router configuration and transport layers
- Implemented tests for RouterConfig, RoutingOptions, StaticInstanceConfig, and RouterConfigOptions to ensure default values are set correctly. - Added tests for RouterConfigProvider to validate configurations and ensure defaults are returned when no file is specified. - Created tests for ConfigValidationResult to check success and error scenarios. - Developed tests for ServiceCollectionExtensions to verify service registration for RouterConfig. - Introduced UdpTransportTests to validate serialization, connection, request-response, and error handling in UDP transport. - Added scripts for signing authority gaps and hashing DevPortal SDK snippets.
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
namespace Examples.Integration.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Integration tests for the Billing microservice endpoints.
|
||||
/// </summary>
|
||||
public sealed class BillingEndpointTests : IClassFixture<GatewayFixture>
|
||||
{
|
||||
private readonly GatewayFixture _fixture;
|
||||
|
||||
public BillingEndpointTests(GatewayFixture fixture)
|
||||
{
|
||||
_fixture = fixture;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateInvoice_WithValidRequest_ReturnsCreatedInvoice()
|
||||
{
|
||||
// Arrange
|
||||
var request = new
|
||||
{
|
||||
customerId = "CUST-001",
|
||||
amount = 99.99m,
|
||||
description = "Test invoice"
|
||||
};
|
||||
|
||||
// Act
|
||||
var response = await _fixture.GatewayClient.PostAsJsonAsync("/invoices", request);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
content.Should().Contain("invoiceId");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetInvoice_WithValidId_ReturnsInvoice()
|
||||
{
|
||||
// Arrange
|
||||
var invoiceId = "INV-12345";
|
||||
|
||||
// Act
|
||||
var response = await _fixture.GatewayClient.GetAsync($"/invoices/{invoiceId}");
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
content.Should().Contain(invoiceId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UploadAttachment_WithStreamingData_ReturnsSuccess()
|
||||
{
|
||||
// Arrange
|
||||
var invoiceId = "INV-12345";
|
||||
var attachmentData = Encoding.UTF8.GetBytes("This is test attachment content");
|
||||
using var content = new ByteArrayContent(attachmentData);
|
||||
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
|
||||
|
||||
// Act
|
||||
var response = await _fixture.GatewayClient.PostAsync(
|
||||
$"/invoices/{invoiceId}/attachments",
|
||||
content);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
responseContent.Should().Contain("attachmentId");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
||||
<PackageReference Include="xunit" Version="2.9.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.1">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Examples.Gateway\Examples.Gateway.csproj" />
|
||||
<ProjectReference Include="..\..\src\Examples.Billing.Microservice\Examples.Billing.Microservice.csproj" />
|
||||
<ProjectReference Include="..\..\src\Examples.Inventory.Microservice\Examples.Inventory.Microservice.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,114 @@
|
||||
using Examples.Billing.Microservice.Endpoints;
|
||||
using Examples.Inventory.Microservice.Endpoints;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using StellaOps.Microservice;
|
||||
using StellaOps.Router.Common.Enums;
|
||||
using StellaOps.Router.Transport.InMemory;
|
||||
using Xunit;
|
||||
|
||||
namespace Examples.Integration.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Test fixture that sets up the gateway and microservices for integration testing.
|
||||
/// Uses in-memory transport for fast, isolated tests.
|
||||
/// </summary>
|
||||
public sealed class GatewayFixture : IAsyncLifetime
|
||||
{
|
||||
private WebApplicationFactory<Examples.Gateway.Program>? _gatewayFactory;
|
||||
private IHost? _billingHost;
|
||||
private IHost? _inventoryHost;
|
||||
|
||||
public HttpClient GatewayClient { get; private set; } = null!;
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
// Start the gateway
|
||||
_gatewayFactory = new WebApplicationFactory<Examples.Gateway.Program>()
|
||||
.WithWebHostBuilder(builder =>
|
||||
{
|
||||
builder.UseEnvironment("Testing");
|
||||
builder.ConfigureServices(services =>
|
||||
{
|
||||
services.AddInMemoryTransport();
|
||||
});
|
||||
});
|
||||
|
||||
GatewayClient = _gatewayFactory.CreateClient();
|
||||
|
||||
// Start billing microservice
|
||||
var billingBuilder = Host.CreateApplicationBuilder();
|
||||
billingBuilder.Services.AddStellaMicroservice(options =>
|
||||
{
|
||||
options.ServiceName = "billing";
|
||||
options.Version = "1.0.0";
|
||||
options.Region = "test";
|
||||
options.InstanceId = "billing-test";
|
||||
options.Routers =
|
||||
[
|
||||
new RouterEndpointConfig
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = 5100,
|
||||
TransportType = TransportType.InMemory
|
||||
}
|
||||
];
|
||||
});
|
||||
billingBuilder.Services.AddScoped<CreateInvoiceEndpoint>();
|
||||
billingBuilder.Services.AddScoped<GetInvoiceEndpoint>();
|
||||
billingBuilder.Services.AddScoped<UploadAttachmentEndpoint>();
|
||||
billingBuilder.Services.AddInMemoryTransport();
|
||||
|
||||
_billingHost = billingBuilder.Build();
|
||||
await _billingHost.StartAsync();
|
||||
|
||||
// Start inventory microservice
|
||||
var inventoryBuilder = Host.CreateApplicationBuilder();
|
||||
inventoryBuilder.Services.AddStellaMicroservice(options =>
|
||||
{
|
||||
options.ServiceName = "inventory";
|
||||
options.Version = "1.0.0";
|
||||
options.Region = "test";
|
||||
options.InstanceId = "inventory-test";
|
||||
options.Routers =
|
||||
[
|
||||
new RouterEndpointConfig
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = 5100,
|
||||
TransportType = TransportType.InMemory
|
||||
}
|
||||
];
|
||||
});
|
||||
inventoryBuilder.Services.AddScoped<ListItemsEndpoint>();
|
||||
inventoryBuilder.Services.AddScoped<GetItemEndpoint>();
|
||||
inventoryBuilder.Services.AddInMemoryTransport();
|
||||
|
||||
_inventoryHost = inventoryBuilder.Build();
|
||||
await _inventoryHost.StartAsync();
|
||||
|
||||
// Allow services to register
|
||||
await Task.Delay(100);
|
||||
}
|
||||
|
||||
public async Task DisposeAsync()
|
||||
{
|
||||
GatewayClient.Dispose();
|
||||
|
||||
if (_billingHost is not null)
|
||||
{
|
||||
await _billingHost.StopAsync();
|
||||
_billingHost.Dispose();
|
||||
}
|
||||
|
||||
if (_inventoryHost is not null)
|
||||
{
|
||||
await _inventoryHost.StopAsync();
|
||||
_inventoryHost.Dispose();
|
||||
}
|
||||
|
||||
_gatewayFactory?.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
namespace Examples.Integration.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Integration tests for the Inventory microservice endpoints.
|
||||
/// </summary>
|
||||
public sealed class InventoryEndpointTests : IClassFixture<GatewayFixture>
|
||||
{
|
||||
private readonly GatewayFixture _fixture;
|
||||
|
||||
public InventoryEndpointTests(GatewayFixture fixture)
|
||||
{
|
||||
_fixture = fixture;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ListItems_WithoutFilters_ReturnsAllItems()
|
||||
{
|
||||
// Act
|
||||
var response = await _fixture.GatewayClient.GetAsync("/items");
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
content.Should().Contain("items");
|
||||
content.Should().Contain("totalCount");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ListItems_WithCategoryFilter_ReturnsFilteredItems()
|
||||
{
|
||||
// Act
|
||||
var response = await _fixture.GatewayClient.GetAsync("/items?category=widgets");
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
content.Should().Contain("widgets");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ListItems_WithPagination_ReturnsPaginatedResponse()
|
||||
{
|
||||
// Act
|
||||
var response = await _fixture.GatewayClient.GetAsync("/items?page=1&pageSize=10");
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
content.Should().Contain("\"page\":1");
|
||||
content.Should().Contain("\"pageSize\":10");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetItem_WithValidSku_ReturnsItem()
|
||||
{
|
||||
// Arrange
|
||||
var sku = "SKU-001";
|
||||
|
||||
// Act
|
||||
var response = await _fixture.GatewayClient.GetAsync($"/items/{sku}");
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
content.Should().Contain(sku);
|
||||
content.Should().Contain("name");
|
||||
content.Should().Contain("quantityOnHand");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
using System.Net;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
namespace Examples.Integration.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests that verify multiple microservices can register and receive
|
||||
/// correctly routed requests through the gateway.
|
||||
/// </summary>
|
||||
public sealed class MultiServiceRoutingTests : IClassFixture<GatewayFixture>
|
||||
{
|
||||
private readonly GatewayFixture _fixture;
|
||||
|
||||
public MultiServiceRoutingTests(GatewayFixture fixture)
|
||||
{
|
||||
_fixture = fixture;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Gateway_RoutesBillingRequests_ToBillingService()
|
||||
{
|
||||
// Act
|
||||
var response = await _fixture.GatewayClient.GetAsync("/invoices/INV-001");
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
content.Should().Contain("INV-001");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Gateway_RoutesInventoryRequests_ToInventoryService()
|
||||
{
|
||||
// Act
|
||||
var response = await _fixture.GatewayClient.GetAsync("/items/SKU-001");
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
content.Should().Contain("SKU-001");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Gateway_HandlesSequentialRequestsToDifferentServices()
|
||||
{
|
||||
// Act - Send requests to both services
|
||||
var billingResponse = await _fixture.GatewayClient.GetAsync("/invoices/INV-001");
|
||||
var inventoryResponse = await _fixture.GatewayClient.GetAsync("/items/SKU-001");
|
||||
|
||||
// Assert - Both should succeed
|
||||
billingResponse.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
inventoryResponse.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Gateway_HandlesConcurrentRequestsToDifferentServices()
|
||||
{
|
||||
// Act - Send requests to both services concurrently
|
||||
var billingTask = _fixture.GatewayClient.GetAsync("/invoices/INV-001");
|
||||
var inventoryTask = _fixture.GatewayClient.GetAsync("/items/SKU-001");
|
||||
|
||||
await Task.WhenAll(billingTask, inventoryTask);
|
||||
|
||||
// Assert - Both should succeed
|
||||
billingTask.Result.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
inventoryTask.Result.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Gateway_ReturnsNotFound_ForUnknownRoute()
|
||||
{
|
||||
// Act
|
||||
var response = await _fixture.GatewayClient.GetAsync("/unknown/route");
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user