From e60a78ac04e2889b56e47f1e73847d941bd4fdd7 Mon Sep 17 00:00:00 2001 From: Zhelyazko Zhelyazkov Date: Sat, 4 Jan 2025 00:30:41 +0200 Subject: [PATCH 1/8] Update GetByIdAsync to return null for missing products Modified the `GetByIdAsync` method in `ProductService.cs` to return `null` instead of a new instance of `GetProduct` when no product is found. This change improves the method's behavior by clearly indicating the absence of a product. --- BlazorShop.Application/Services/ProductService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BlazorShop.Application/Services/ProductService.cs b/BlazorShop.Application/Services/ProductService.cs index 6a0bed2..b84f526 100644 --- a/BlazorShop.Application/Services/ProductService.cs +++ b/BlazorShop.Application/Services/ProductService.cs @@ -31,7 +31,7 @@ public async Task> GetAllAsync() public async Task GetByIdAsync(Guid id) { var result = await _productRepository.GetByIdAsync(id); - return result != null ? _mapper.Map(result) : new GetProduct(); + return result != null ? _mapper.Map(result) : null; } public async Task AddAsync(CreateProduct product) From 3591affb3eddc37c788cde651950d7f20ace83c8 Mon Sep 17 00:00:00 2001 From: Zhelyazko Zhelyazkov Date: Sat, 4 Jan 2025 00:30:53 +0200 Subject: [PATCH 2/8] Refactor product retrieval test and add new test case Updated the test method for retrieving a product to reflect that it should return null when a product does not exist. Changed assertions accordingly. Added a new test method to verify successful product addition. --- .../Application/Services/ProductServiceTests.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/BlazorShop.Tests/Application/Services/ProductServiceTests.cs b/BlazorShop.Tests/Application/Services/ProductServiceTests.cs index 3f4d376..15c9171 100644 --- a/BlazorShop.Tests/Application/Services/ProductServiceTests.cs +++ b/BlazorShop.Tests/Application/Services/ProductServiceTests.cs @@ -105,24 +105,23 @@ public async Task GetByIdAsync_WhenProductExists_ShouldReturnMappedProduct() } [Fact] - public async Task GetByIdAsync_WhenProductDoesNotExist_ShouldReturnEmptyProduct() + public async Task GetByIdAsync_WhenProductDoesNotExist_ShouldReturnNull() { // Arrange var productId = Guid.NewGuid(); this._mockProductRepository.Setup(repo => repo.GetByIdAsync(productId)) - .ReturnsAsync((Product)null); + .ReturnsAsync((Product)null); // Act var result = await this._productService.GetByIdAsync(productId); // Assert - Assert.NotNull(result); - Assert.Equal(default, result.Id); - Assert.Null(result.Name); + Assert.Null(result); this._mockProductRepository.Verify(repo => repo.GetByIdAsync(productId), Times.Once); this._mockMapper.Verify(mapper => mapper.Map(It.IsAny()), Times.Never); } + [Fact] public async Task AddAsync_WhenProductIsAdded_ShouldReturnSuccessResponse() { From e75e78e50bc7bbcb39099eb0f09bdafdc0d36024 Mon Sep 17 00:00:00 2001 From: Zhelyazko Zhelyazkov Date: Sat, 4 Jan 2025 00:31:18 +0200 Subject: [PATCH 3/8] Add ServiceDefaults and enhance CORS configuration - Added project reference to `BlazorShop.ServiceDefaults`. - Included `Microsoft.Extensions.Hosting` for hosting features. - Integrated default services with `AddServiceDefaults()`. - Added `AddInfrastructure()` and `AddApplication()` methods. - Updated CORS policy to allow origins starting with `http://localhost` or `https://localhost`. --- .../BlazorShop.API/BlazorShop.API.csproj | 1 + BlazorShop.Presentation/BlazorShop.API/Program.cs | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/BlazorShop.Presentation/BlazorShop.API/BlazorShop.API.csproj b/BlazorShop.Presentation/BlazorShop.API/BlazorShop.API.csproj index af7e97e..379997a 100644 --- a/BlazorShop.Presentation/BlazorShop.API/BlazorShop.API.csproj +++ b/BlazorShop.Presentation/BlazorShop.API/BlazorShop.API.csproj @@ -17,6 +17,7 @@ + diff --git a/BlazorShop.Presentation/BlazorShop.API/Program.cs b/BlazorShop.Presentation/BlazorShop.API/Program.cs index 28ad6f5..ce6a227 100644 --- a/BlazorShop.Presentation/BlazorShop.API/Program.cs +++ b/BlazorShop.Presentation/BlazorShop.API/Program.cs @@ -7,6 +7,7 @@ namespace BlazorShop.API using BlazorShop.Infrastructure; using Microsoft.AspNetCore.Builder; + using Microsoft.Extensions.Hosting; using Serilog; @@ -30,6 +31,8 @@ public static void Main(string[] args) // Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi builder.Services.AddSwaggerGen(); + builder.AddServiceDefaults(); + builder.Services.AddInfrastructure(builder.Configuration); builder.Services.AddApplication(); builder.Services.AddCors( @@ -41,7 +44,10 @@ public static void Main(string[] args) opt.AllowAnyHeader() .AllowAnyMethod() .AllowCredentials() - .WithOrigins("https://localhost:7258"); + //.WithOrigins("https://localhost:7258"); + .SetIsOriginAllowed(origin => + origin.StartsWith("http://localhost") || + origin.StartsWith("https://localhost")); }); }); From 2c85c2002bc3aa6ff8345c7661b24c01fd5f53b8 Mon Sep 17 00:00:00 2001 From: Zhelyazko Zhelyazkov Date: Sat, 4 Jan 2025 00:31:38 +0200 Subject: [PATCH 4/8] Update project to .NET 9.0 with OpenTelemetry support - Updated `BlazorShop.ServiceDefaults.csproj` to target .NET 9.0 and added new package references for OpenTelemetry, service discovery, and resilience. - Expanded `Extensions.cs` with methods for configuring common services, including health checks, OpenTelemetry metrics and tracing, and HTTP client resilience. - Added health check endpoints with security considerations for non-development environments. --- .../BlazorShop.ServiceDefaults.csproj | 22 ++++ BlazorShop.ServiceDefaults/Extensions.cs | 119 ++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 BlazorShop.ServiceDefaults/BlazorShop.ServiceDefaults.csproj create mode 100644 BlazorShop.ServiceDefaults/Extensions.cs diff --git a/BlazorShop.ServiceDefaults/BlazorShop.ServiceDefaults.csproj b/BlazorShop.ServiceDefaults/BlazorShop.ServiceDefaults.csproj new file mode 100644 index 0000000..24b1b4f --- /dev/null +++ b/BlazorShop.ServiceDefaults/BlazorShop.ServiceDefaults.csproj @@ -0,0 +1,22 @@ + + + + net9.0 + enable + enable + true + + + + + + + + + + + + + + + diff --git a/BlazorShop.ServiceDefaults/Extensions.cs b/BlazorShop.ServiceDefaults/Extensions.cs new file mode 100644 index 0000000..13151bf --- /dev/null +++ b/BlazorShop.ServiceDefaults/Extensions.cs @@ -0,0 +1,119 @@ +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 +{ + public static TBuilder AddServiceDefaults(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + 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 TBuilder ConfigureOpenTelemetry(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + builder.Logging.AddOpenTelemetry(logging => + { + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; + }); + + builder.Services.AddOpenTelemetry() + .WithMetrics(metrics => + { + metrics.AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation(); + }) + .WithTracing(tracing => + { + tracing.AddSource(builder.Environment.ApplicationName) + .AddAspNetCoreInstrumentation() + // Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package) + //.AddGrpcClientInstrumentation() + .AddHttpClientInstrumentation(); + }); + + builder.AddOpenTelemetryExporters(); + + return builder; + } + + private static TBuilder AddOpenTelemetryExporters(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + 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 TBuilder AddDefaultHealthChecks(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + 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") + }); + } + + return app; + } +} From d7ffd2f6f528991cc60da7d36b27a4acc18377e3 Mon Sep 17 00:00:00 2001 From: Zhelyazko Zhelyazkov Date: Sat, 4 Jan 2025 00:31:57 +0200 Subject: [PATCH 5/8] Add testing framework for BlazorShop application Introduce `ApiTestBase` for API testing setup and create `ApiTests` for endpoint verification. Update project references in `BlazorShop.Tests.csproj` to include `Aspire.Hosting.Testing`. Add `WebTests` for testing web resource endpoints. --- BlazorShop.Tests/ApiTestBase.cs | 46 ++++++++++++++++++++++++ BlazorShop.Tests/ApiTests.cs | 45 +++++++++++++++++++++++ BlazorShop.Tests/BlazorShop.Tests.csproj | 2 ++ BlazorShop.Tests/WebTests.cs | 38 ++++++++++++++++++++ 4 files changed, 131 insertions(+) create mode 100644 BlazorShop.Tests/ApiTestBase.cs create mode 100644 BlazorShop.Tests/ApiTests.cs create mode 100644 BlazorShop.Tests/WebTests.cs diff --git a/BlazorShop.Tests/ApiTestBase.cs b/BlazorShop.Tests/ApiTestBase.cs new file mode 100644 index 0000000..e6171c3 --- /dev/null +++ b/BlazorShop.Tests/ApiTestBase.cs @@ -0,0 +1,46 @@ +namespace BlazorShop.Tests +{ + using System; + using System.Net.Http; + using System.Threading.Tasks; + + using Aspire.Hosting; + using Aspire.Hosting.ApplicationModel; + using Aspire.Hosting.Testing; + + using Microsoft.Extensions.DependencyInjection; + + using Xunit; + + public abstract class ApiTestBase : IAsyncLifetime + { + protected DistributedApplication App { get; private set; } + protected ResourceNotificationService ResourceNotificationService { get; private set; } + protected HttpClient HttpClient { get; private set; } + + public async Task InitializeAsync() + { + var appHost = await DistributedApplicationTestingBuilder.CreateAsync(); + + appHost.Services.ConfigureHttpClientDefaults(clientBuilder => + { + clientBuilder.AddStandardResilienceHandler(); + }); + + this.App = await appHost.BuildAsync(); + this.ResourceNotificationService = this.App.Services.GetRequiredService(); + await this.App.StartAsync(); + + this.HttpClient = this.App.CreateHttpClient("apiservice"); + await this.ResourceNotificationService.WaitForResourceAsync("apiservice", KnownResourceStates.Running).WaitAsync(TimeSpan.FromSeconds(30)); + } + + public async Task DisposeAsync() + { + if (this.App != null) + { + await this.App.DisposeAsync(); + } + } + } +} \ No newline at end of file diff --git a/BlazorShop.Tests/ApiTests.cs b/BlazorShop.Tests/ApiTests.cs new file mode 100644 index 0000000..511acc1 --- /dev/null +++ b/BlazorShop.Tests/ApiTests.cs @@ -0,0 +1,45 @@ +namespace BlazorShop.Tests +{ + using System.Net; + using System.Threading.Tasks; + + using Xunit; + + public class ApiTests : ApiTestBase + { + [Fact] + public async Task GetProductsEndpointReturnsOkStatusCode() + { + // Act + var response = await this.HttpClient.GetAsync("/api/product/all"); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + } + + [Fact] + public async Task GetAllProducts_ReturnsOkWithProducts() + { + // Act + var response = await this.HttpClient.GetAsync("/api/product/all"); + var content = await response.Content.ReadAsStringAsync(); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Contains("\"name\":", content); + } + + [Fact] + public async Task GetSingleProduct_ReturnsNotFound() + { + // Arrange + var productId = Guid.NewGuid(); + + // Act + var response = await this.HttpClient.GetAsync($"/api/product/single/{productId}"); + + // Assert + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + } +} \ No newline at end of file diff --git a/BlazorShop.Tests/BlazorShop.Tests.csproj b/BlazorShop.Tests/BlazorShop.Tests.csproj index aa6dce3..d43e625 100644 --- a/BlazorShop.Tests/BlazorShop.Tests.csproj +++ b/BlazorShop.Tests/BlazorShop.Tests.csproj @@ -14,6 +14,7 @@ + @@ -24,6 +25,7 @@ + diff --git a/BlazorShop.Tests/WebTests.cs b/BlazorShop.Tests/WebTests.cs new file mode 100644 index 0000000..9c0b52b --- /dev/null +++ b/BlazorShop.Tests/WebTests.cs @@ -0,0 +1,38 @@ +namespace BlazorShop.Tests +{ + using System.Net; + + using Aspire.Hosting.ApplicationModel; + using Aspire.Hosting.Testing; + + using Microsoft.Extensions.DependencyInjection; + + using Xunit; + + public class WebTests + { + [Fact] + public async Task GetWebResourceRootReturnsOkStatusCode() + { + // Arrange + var appHost = await DistributedApplicationTestingBuilder.CreateAsync(); + appHost.Services.ConfigureHttpClientDefaults(clientBuilder => + { + clientBuilder.AddStandardResilienceHandler(); + }); + // To output logs to the xUnit.net ITestOutputHelper, consider adding a package from https://www.nuget.org/packages?q=xunit+logging + + await using var app = await appHost.BuildAsync(); + var resourceNotificationService = app.Services.GetRequiredService(); + await app.StartAsync(); + + // Act + var httpClient = app.CreateHttpClient("webfrontend"); + await resourceNotificationService.WaitForResourceAsync("webfrontend", KnownResourceStates.Running).WaitAsync(TimeSpan.FromSeconds(30)); + var response = await httpClient.GetAsync("/"); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + } + } +} From bcc4d1a9ce914824f7c020ac381ffdfda38741fa Mon Sep 17 00:00:00 2001 From: Zhelyazko Zhelyazkov Date: Sat, 4 Jan 2025 00:32:53 +0200 Subject: [PATCH 6/8] Update project structure and configuration for Aspire SDK - Updated `BlazorShop.AppHost.csproj` to use `Aspire.AppHost.Sdk` version 9.0.0, with new properties and package references. - Modified `Program.cs` to implement a distributed application builder and configure project dependencies. - Added `launchSettings.json` with profiles for HTTPS and HTTP, including environment variables for development. - Introduced `appsettings.Development.json` for logging configuration. - Updated `appsettings.json` to include additional logging settings for `Aspire.Hosting.Dcp`. --- BlazorShop.AppHost/BlazorShop.AppHost.csproj | 23 +++++++++++++++ BlazorShop.AppHost/Program.cs | 10 +++++++ .../Properties/launchSettings.json | 29 +++++++++++++++++++ .../appsettings.Development.json | 8 +++++ BlazorShop.AppHost/appsettings.json | 9 ++++++ 5 files changed, 79 insertions(+) create mode 100644 BlazorShop.AppHost/BlazorShop.AppHost.csproj create mode 100644 BlazorShop.AppHost/Program.cs create mode 100644 BlazorShop.AppHost/Properties/launchSettings.json create mode 100644 BlazorShop.AppHost/appsettings.Development.json create mode 100644 BlazorShop.AppHost/appsettings.json diff --git a/BlazorShop.AppHost/BlazorShop.AppHost.csproj b/BlazorShop.AppHost/BlazorShop.AppHost.csproj new file mode 100644 index 0000000..09d074f --- /dev/null +++ b/BlazorShop.AppHost/BlazorShop.AppHost.csproj @@ -0,0 +1,23 @@ + + + + + + Exe + net9.0 + enable + enable + true + d3573504-3570-47c0-8129-3287c9a91907 + + + + + + + + + + + + diff --git a/BlazorShop.AppHost/Program.cs b/BlazorShop.AppHost/Program.cs new file mode 100644 index 0000000..0c45db9 --- /dev/null +++ b/BlazorShop.AppHost/Program.cs @@ -0,0 +1,10 @@ +var builder = DistributedApplication.CreateBuilder(args); + +var apiService = builder.AddProject("apiservice"); + +builder.AddProject("webfrontend") + .WithExternalHttpEndpoints() + .WithReference(apiService) + .WaitFor(apiService); + +builder.Build().Run(); diff --git a/BlazorShop.AppHost/Properties/launchSettings.json b/BlazorShop.AppHost/Properties/launchSettings.json new file mode 100644 index 0000000..f93ec4e --- /dev/null +++ b/BlazorShop.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:17025;http://localhost:15164", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21239", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22065" + } + }, + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:15164", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19204", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20124" + } + } + } +} diff --git a/BlazorShop.AppHost/appsettings.Development.json b/BlazorShop.AppHost/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/BlazorShop.AppHost/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/BlazorShop.AppHost/appsettings.json b/BlazorShop.AppHost/appsettings.json new file mode 100644 index 0000000..31c092a --- /dev/null +++ b/BlazorShop.AppHost/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Aspire.Hosting.Dcp": "Warning" + } + } +} From 9d3b50856f270d1459993d7065b32c5fb71db498 Mon Sep 17 00:00:00 2001 From: Zhelyazko Zhelyazkov Date: Sat, 4 Jan 2025 12:40:30 +0200 Subject: [PATCH 7/8] Add BlazorShop.AppHost and BlazorShop.ServiceDefaults Two new projects, "BlazorShop.AppHost" and "BlazorShop.ServiceDefaults", were added to the solution file `BlazorShop.sln`. The corresponding project files and unique identifiers were included for both projects. Additionally, the build configurations for these new projects were defined, allowing them to be built in both Debug and Release modes. --- BlazorShop.sln | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/BlazorShop.sln b/BlazorShop.sln index b269f71..a777091 100644 --- a/BlazorShop.sln +++ b/BlazorShop.sln @@ -19,6 +19,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorShop.Web.Shared", "Bl EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorShop.Tests", "BlazorShop.Tests\BlazorShop.Tests.csproj", "{FECE84AC-01C9-4F82-9EDA-DD56713E574A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorShop.AppHost", "BlazorShop.AppHost\BlazorShop.AppHost.csproj", "{3239FA28-A508-4EEC-9351-89E414E7B816}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorShop.ServiceDefaults", "BlazorShop.ServiceDefaults\BlazorShop.ServiceDefaults.csproj", "{CB802A92-ED5C-732A-C9BD-5887C0D03E13}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -53,6 +57,14 @@ Global {FECE84AC-01C9-4F82-9EDA-DD56713E574A}.Debug|Any CPU.Build.0 = Debug|Any CPU {FECE84AC-01C9-4F82-9EDA-DD56713E574A}.Release|Any CPU.ActiveCfg = Release|Any CPU {FECE84AC-01C9-4F82-9EDA-DD56713E574A}.Release|Any CPU.Build.0 = Release|Any CPU + {3239FA28-A508-4EEC-9351-89E414E7B816}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3239FA28-A508-4EEC-9351-89E414E7B816}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3239FA28-A508-4EEC-9351-89E414E7B816}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3239FA28-A508-4EEC-9351-89E414E7B816}.Release|Any CPU.Build.0 = Release|Any CPU + {CB802A92-ED5C-732A-C9BD-5887C0D03E13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CB802A92-ED5C-732A-C9BD-5887C0D03E13}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CB802A92-ED5C-732A-C9BD-5887C0D03E13}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CB802A92-ED5C-732A-C9BD-5887C0D03E13}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 5d530c675f1ce927dee3a60c134cd03adedd725a Mon Sep 17 00:00:00 2001 From: Zhelyazko Zhelyazkov Date: Sat, 4 Jan 2025 13:01:23 +0200 Subject: [PATCH 8/8] Remove API and web testing classes and methods This commit removes the `ApiTestBase.cs` and `ApiTests.cs` files, which included the base class for API testing and various unit tests for API endpoints. Additionally, the `WebTests.cs` file has been modified to eliminate the `WebTests` class and its associated test method, along with the setup for the distributed application and HTTP client creation. --- BlazorShop.Tests/ApiTestBase.cs | 46 --------------------------------- BlazorShop.Tests/ApiTests.cs | 45 -------------------------------- BlazorShop.Tests/WebTests.cs | 38 --------------------------- 3 files changed, 129 deletions(-) delete mode 100644 BlazorShop.Tests/ApiTestBase.cs delete mode 100644 BlazorShop.Tests/ApiTests.cs delete mode 100644 BlazorShop.Tests/WebTests.cs diff --git a/BlazorShop.Tests/ApiTestBase.cs b/BlazorShop.Tests/ApiTestBase.cs deleted file mode 100644 index e6171c3..0000000 --- a/BlazorShop.Tests/ApiTestBase.cs +++ /dev/null @@ -1,46 +0,0 @@ -namespace BlazorShop.Tests -{ - using System; - using System.Net.Http; - using System.Threading.Tasks; - - using Aspire.Hosting; - using Aspire.Hosting.ApplicationModel; - using Aspire.Hosting.Testing; - - using Microsoft.Extensions.DependencyInjection; - - using Xunit; - - public abstract class ApiTestBase : IAsyncLifetime - { - protected DistributedApplication App { get; private set; } - protected ResourceNotificationService ResourceNotificationService { get; private set; } - protected HttpClient HttpClient { get; private set; } - - public async Task InitializeAsync() - { - var appHost = await DistributedApplicationTestingBuilder.CreateAsync(); - - appHost.Services.ConfigureHttpClientDefaults(clientBuilder => - { - clientBuilder.AddStandardResilienceHandler(); - }); - - this.App = await appHost.BuildAsync(); - this.ResourceNotificationService = this.App.Services.GetRequiredService(); - await this.App.StartAsync(); - - this.HttpClient = this.App.CreateHttpClient("apiservice"); - await this.ResourceNotificationService.WaitForResourceAsync("apiservice", KnownResourceStates.Running).WaitAsync(TimeSpan.FromSeconds(30)); - } - - public async Task DisposeAsync() - { - if (this.App != null) - { - await this.App.DisposeAsync(); - } - } - } -} \ No newline at end of file diff --git a/BlazorShop.Tests/ApiTests.cs b/BlazorShop.Tests/ApiTests.cs deleted file mode 100644 index 511acc1..0000000 --- a/BlazorShop.Tests/ApiTests.cs +++ /dev/null @@ -1,45 +0,0 @@ -namespace BlazorShop.Tests -{ - using System.Net; - using System.Threading.Tasks; - - using Xunit; - - public class ApiTests : ApiTestBase - { - [Fact] - public async Task GetProductsEndpointReturnsOkStatusCode() - { - // Act - var response = await this.HttpClient.GetAsync("/api/product/all"); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - } - - [Fact] - public async Task GetAllProducts_ReturnsOkWithProducts() - { - // Act - var response = await this.HttpClient.GetAsync("/api/product/all"); - var content = await response.Content.ReadAsStringAsync(); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Contains("\"name\":", content); - } - - [Fact] - public async Task GetSingleProduct_ReturnsNotFound() - { - // Arrange - var productId = Guid.NewGuid(); - - // Act - var response = await this.HttpClient.GetAsync($"/api/product/single/{productId}"); - - // Assert - Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); - } - } -} \ No newline at end of file diff --git a/BlazorShop.Tests/WebTests.cs b/BlazorShop.Tests/WebTests.cs deleted file mode 100644 index 9c0b52b..0000000 --- a/BlazorShop.Tests/WebTests.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace BlazorShop.Tests -{ - using System.Net; - - using Aspire.Hosting.ApplicationModel; - using Aspire.Hosting.Testing; - - using Microsoft.Extensions.DependencyInjection; - - using Xunit; - - public class WebTests - { - [Fact] - public async Task GetWebResourceRootReturnsOkStatusCode() - { - // Arrange - var appHost = await DistributedApplicationTestingBuilder.CreateAsync(); - appHost.Services.ConfigureHttpClientDefaults(clientBuilder => - { - clientBuilder.AddStandardResilienceHandler(); - }); - // To output logs to the xUnit.net ITestOutputHelper, consider adding a package from https://www.nuget.org/packages?q=xunit+logging - - await using var app = await appHost.BuildAsync(); - var resourceNotificationService = app.Services.GetRequiredService(); - await app.StartAsync(); - - // Act - var httpClient = app.CreateHttpClient("webfrontend"); - await resourceNotificationService.WaitForResourceAsync("webfrontend", KnownResourceStates.Running).WaitAsync(TimeSpan.FromSeconds(30)); - var response = await httpClient.GetAsync("/"); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - } - } -}