Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 32 additions & 19 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,24 @@ on:

jobs:
build:
name: "Build"
name: Build
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- name: Cache
uses: actions/cache@v4
with:
uses: actions/cache@v5
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
restore-keys: |
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
restore-keys: |
${{ runner.os }}-nuget-

- name: Setup .NET
uses: actions/setup-dotnet@v4
uses: actions/setup-dotnet@v5
with:
global-json-file: global.json

- name: Restore dependencies
run: dotnet restore
Expand All @@ -32,24 +34,35 @@ jobs:
run: dotnet build --no-restore --configuration Release

- name: Test
run: dotnet test --no-build --no-restore --configuration Release -- --report-xunit-trx
run: |
dotnet test --no-build --no-restore --configuration Release \
--report-xunit-trx \
--coverage \
--coverage-output-format cobertura \
--coverage-output coverage.cobertura.xml \
--no-progress

- name: Publish Test Results
if: always()
run: |
dotnet tool install --global dotnet-reportgenerator-globaltool

reportgenerator \
-reports:"$(pwd)/**/coverage.cobertura.xml" \
-targetdir:"$(pwd)" \
-filefilters:"+*;-**/*.g.cs" \
-reporttypes:MarkdownSummaryGithub

cat SummaryGithub.md >> $GITHUB_STEP_SUMMARY

- name: Publish
if: github.event_name != 'pull_request'
run: dotnet publish src/WebApi/WebApi.csproj --no-build --no-restore --configuration Release --output ./publish

- name: Upload Artifact
uses: actions/upload-artifact@v4
if: github.event_name != 'pull_request'
uses: actions/upload-artifact@v6
with:
name: published-files
path: ./publish

- name: Test Report
uses: test-summary/action@v2
if: success() || failure()
with:
paths: ./**/test-result.xml
output: test-summary.md
- name: Output job summary
if: success() || failure()
run: |
cat test-summary.md >> $GITHUB_STEP_SUMMARY
31 changes: 9 additions & 22 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,19 @@
<ItemGroup>
<PackageVersion Include="Ardalis.Specification" Version="9.3.1" />
<PackageVersion Include="Ardalis.Specification.EntityFrameworkCore" Version="9.3.1" />
<PackageVersion Include="Aspire.Hosting.Azure.ApplicationInsights" Version="13.0.2" />
<PackageVersion Include="Aspire.Hosting.Azure.AppService" Version="13.0.2-preview.1.25603.5" />
<PackageVersion Include="Aspire.Hosting.Azure.Sql" Version="13.0.2" />
<PackageVersion Include="Aspire.Hosting.Azure.ApplicationInsights" Version="13.1.0" />
<PackageVersion Include="Aspire.Hosting.Azure.AppService" Version="13.1.0-preview.1.25616.3" />
<PackageVersion Include="Aspire.Hosting.Azure.Sql" Version="13.1.0" />
<PackageVersion Include="AspNetCore.HealthChecks.SqlServer" Version="9.0.0" />
<PackageVersion Include="AspNetCore.HealthChecks.UI" Version="9.0.0" />
<PackageVersion Include="AspNetCore.HealthChecks.UI.Client" Version="9.0.0" />
<PackageVersion Include="AspNetCore.HealthChecks.UI.InMemory.Storage" Version="9.0.0" />
<PackageVersion Include="Aspire.Hosting.AppHost" Version="13.0.2" />
<PackageVersion Include="Aspire.Microsoft.EntityFrameworkCore.SqlServer" Version="13.0.2" />
<PackageVersion Include="Aspire.Hosting.AppHost" Version="13.1.0" />
<PackageVersion Include="Aspire.Microsoft.EntityFrameworkCore.SqlServer" Version="13.1.0" />
<PackageVersion Include="AwesomeAssertions" Version="9.3.0" />
<PackageVersion Include="Azure.Identity" Version="1.13.0" />
<PackageVersion Include="Bogus" Version="35.6.5" />
<PackageVersion Include="coverlet.collector" Version="6.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageVersion>
<PackageVersion Include="EntityFrameworkCore.Exceptions.SqlServer" Version="8.1.3" />
<PackageVersion Include="ErrorOr" Version="2.0.1" />
<PackageVersion Include="FluentValidation.DependencyInjectionExtensions" Version="12.1.1" />
<PackageVersion Include="JunitXml.TestLogger" Version="4.1.0" />
<!-- TODO: Remove when Aspire updates KubernetesClient dependency to fix https://github.com/advisories/GHSA-w7r3-mgwf-4mqq -->
<PackageVersion Include="KubernetesClient" Version="18.0.13" />
<PackageVersion Include="MediatR" Version="14.0.0" />
<PackageVersion Include="MediatR.Contracts" Version="2.0.1" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.1" />
Expand All @@ -37,27 +28,23 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageVersion>
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.1" />
<PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="10.0.1" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.1" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="10.0.1" />
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="10.1.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.1" />
<PackageVersion Include="Microsoft.Extensions.ServiceDiscovery" Version="10.1.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageVersion Include="Microsoft.Testing.Extensions.CodeCoverage" Version="18.1.0" />
<PackageVersion Include="NetArchTest.Rules" Version="1.3.2" />
<PackageVersion Include="NSubstitute" Version="5.3.0" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.14.0" />
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.14.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.14.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.14.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Runtime" Version="1.14.0" />
<PackageVersion Include="Polly" Version="8.6.5" />
<PackageVersion Include="Respawn" Version="7.0.0" />
<PackageVersion Include="Scalar.AspNetCore" Version="2.11.3" />
<PackageVersion Include="Scalar.AspNetCore" Version="2.11.10" />
<PackageVersion Include="Testcontainers" Version="4.9.0" />
<PackageVersion Include="Testcontainers.MsSql" Version="4.9.0" />
<PackageVersion Include="Vogen" Version="8.0.3" />
<PackageVersion Include="xunit.v3" Version="3.2.1" />
<PackageVersion Include="Vogen" Version="8.0.4" />
<PackageVersion Include="xunit.v3.mtp-v2" Version="3.2.1" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
8 changes: 4 additions & 4 deletions SSW.CleanArchitecture.slnx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Solution>
<Solution>
<Folder Name="/Solution Items/">
<File Path=".editorconfig" />
<File Path=".gitignore" />
Expand All @@ -16,10 +16,10 @@
<Folder Name="/tests/">
<Project Path="tests\Architecture.Tests\Architecture.Tests.csproj" />
<Project Path="tests\Domain.UnitTests\Domain.UnitTests.csproj" />
<Project Path="tests\WebApi.IntegrationTests\WebApi.IntegrationTests.csproj" Type="Classic C#" />
<Project Path="tests\WebApi.IntegrationTests\WebApi.IntegrationTests.csproj" />
</Folder>
<Folder Name="/tools/">
<Project Path="tools\AppHost\AppHost.csproj" Type="Classic C#" />
<Project Path="tools\MigrationService\MigrationService.csproj" Type="Classic C#" />
<Project Path="tools\AppHost\AppHost.csproj" />
<Project Path="tools\MigrationService\MigrationService.csproj" />
</Folder>
</Solution>
3 changes: 3 additions & 0 deletions global.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@
"version": "10.0.100",
"rollForward": "latestFeature",
"allowPrerelease": false
},
"test": {
"runner": "Microsoft.Testing.Platform"
}
}
3 changes: 0 additions & 3 deletions src/Application/Application.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@
<PackageReference Include="ErrorOr" />
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" />
<PackageReference Include="MediatR" />
<PackageReference Include="Microsoft.EntityFrameworkCore" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
</ItemGroup>

</Project>
2 changes: 1 addition & 1 deletion src/Infrastructure/DependencyInjection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public static void AddInfrastructure(this IHostApplicationBuilder builder)

var services = builder.Services;

services.AddScoped<IApplicationDbContext>(sp => sp.GetRequiredService<ApplicationDbContext>());
services.AddScoped<IApplicationDbContext, ApplicationDbContext>();

services.AddScoped<EntitySaveChangesInterceptor>();
services.AddScoped<DispatchDomainEventsInterceptor>();
Expand Down
5 changes: 1 addition & 4 deletions src/Infrastructure/Infrastructure.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>SSW.CleanArchitecture.Infrastructure</RootNamespace>
<AssemblyName>SSW.CleanArchitecture.Infrastructure</AssemblyName>
Expand All @@ -14,9 +14,6 @@

<PackageReference Include="Aspire.Microsoft.EntityFrameworkCore.SqlServer"/>
<PackageReference Include="EntityFrameworkCore.Exceptions.SqlServer" />
<!-- TODO: Remove when Aspire updates KubernetesClient dependency to fix https://github.com/advisories/GHSA-w7r3-mgwf-4mqq -->
<PackageReference Include="KubernetesClient" />

<PackageReference Include="Microsoft.Extensions.Http.Resilience"/>
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery"/>
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" />
Expand Down
10 changes: 3 additions & 7 deletions src/Infrastructure/ServiceDefaults/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,14 @@
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using OpenTelemetry;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;

// TODO: Investigate and fix IDE0055 warnings on Unix (runners)
#pragma warning disable IDE0055
namespace SSW.CleanArchitecture.Infrastructure.ServiceDefaults;

// ReSharper disable once CheckNamespace
#pragma warning disable IDE0130
namespace Microsoft.Extensions.Hosting;
#pragma warning restore IDE0130
// 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
Expand Down Expand Up @@ -120,4 +116,4 @@ public static WebApplication MapDefaultEndpoints(this WebApplication app)
return app;
}
}
#pragma warning restore IDE0055

21 changes: 6 additions & 15 deletions src/WebApi/HealthChecks/WebApplicationHealthCheckExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,28 +33,19 @@ public static void AddHealthChecks(this IServiceCollection services, IConfigurat
// Check 1: Check the SQL Server Connectivity (no EF, no DBContext, hardly anything to go wrong)
.AddSqlServer(
name: "SQL Server",
connectionString: config["ConnectionStrings:DefaultConnection"]!,
connectionString: config.GetConnectionString("CleanArchitecture")!,
healthQuery: $"-- SqlServerHealthCheck{Environment.NewLine}SELECT 123;",
failureStatus: HealthStatus.Unhealthy,
tags: ["db", "sql", "sqlserver"])

// Check 2: Check the Entity Framework DbContext (requires the DbContext Options, DI, Interceptors, Configurations, etc. to all be correct), and
// then run a sample query to test important data
// Note: Add TagWith("HealthCheck") to show up in SQL Profiling tools (usually as the opening comment) so that you know that the constant DB Queries are
// for the health check of the current application and not something strange/unidentified.
// then run a query to test pending migrations
.AddEntityFrameworkDbContextCheck<ApplicationDbContext>(
name: "Entity Framework DbContext",
tags: ["db", "dbContext", "sql"],
testQuery: async (ctx, ct) =>
{
// TODO: Replace the custom test query below with something appropriate for your project that is always expected to be valid
_ = await ctx
.Heroes
// allows you to understand why you might see constant db queries in sql profile
.TagWith("HealthCheck")
.FirstOrDefaultAsync(ct);

return new DbHealthCheckResult("Database Context is healthy");
});
testQuery: async (ctx, ct) => (await ctx.Database.GetPendingMigrationsAsync(ct)).Any()
? new DbHealthCheckResult("There are pending database migrations. Please apply them first.", exception: null)
: new DbHealthCheckResult("All migrations are applied")
);
}
}
2 changes: 1 addition & 1 deletion src/WebApi/Program.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using SSW.CleanArchitecture.Application;
using SSW.CleanArchitecture.Infrastructure;
using SSW.CleanArchitecture.Infrastructure.ServiceDefaults;
using SSW.CleanArchitecture.WebApi;
using SSW.CleanArchitecture.WebApi.Endpoints;
using SSW.CleanArchitecture.WebApi.Extensions;
Expand Down Expand Up @@ -31,7 +32,6 @@
app.MapCustomScalarApiReference();
app.UseHealthChecks();
app.UseHttpsRedirection();
app.UseStaticFiles();

app.MapHeroEndpoints();
app.MapTeamEndpoints();
Expand Down
1 change: 0 additions & 1 deletion src/WebApi/WebApi.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

<ItemGroup>
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" />
<PackageReference Include="AspNetCore.HealthChecks.UI" />
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" />
<PackageReference Include="AspNetCore.HealthChecks.UI.InMemory.Storage" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" />
Expand Down
3 changes: 0 additions & 3 deletions src/WebApi/appsettings.Development.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,5 @@
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"ConnectionStrings": {
"DefaultConnection": "Server=localhost,1500;Initial Catalog=CleanArchitecture;Persist Security Info=False;User ID=sa;Password=yourStrong(!)Password;MultipleActiveResultSets=True;TrustServerCertificate=True;Connection Timeout=30;"
}
}
5 changes: 3 additions & 2 deletions tests/Architecture.Tests/Architecture.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>SSW.CleanArchitecture.Architecture.UnitTests</RootNamespace>
<AssemblyName>SSW.CleanArchitecture.Architecture.UnitTests</AssemblyName>
Expand All @@ -9,7 +9,8 @@
<PackageReference Include="AwesomeAssertions"/>
<PackageReference Include="NetArchTest.Rules" />
<PackageReference Include="Microsoft.NET.Test.Sdk"/>
<PackageReference Include="xunit.v3"/>
<PackageReference Include="Microsoft.Testing.Extensions.CodeCoverage" />
<PackageReference Include="xunit.v3.mtp-v2"/>
<PackageReference Include="xunit.runner.visualstudio">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
Expand Down
8 changes: 3 additions & 5 deletions tests/Domain.UnitTests/Domain.UnitTests.csproj
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>SSW.CleanArchitecture.Domain.UnitTests</RootNamespace>
<AssemblyName>SSW.CleanArchitecture.Domain.UnitTests</AssemblyName>
<IsPackable>false</IsPackable>
<TestingPlatformDotnetTestSupport>true</TestingPlatformDotnetTestSupport>
<UseMicrosoftTestingPlatformRunner>true</UseMicrosoftTestingPlatformRunner>
<OutputType>Exe</OutputType>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AwesomeAssertions"/>
<PackageReference Include="Microsoft.NET.Test.Sdk"/>
<PackageReference Include="xunit.v3"/>
<PackageReference Include="Microsoft.Testing.Extensions.CodeCoverage" />
<PackageReference Include="xunit.v3.mtp-v2"/>
<PackageReference Include="xunit.runner.visualstudio">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
Expand Down
11 changes: 5 additions & 6 deletions tests/WebApi.IntegrationTests/WebApi.IntegrationTests.csproj
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">

<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>SSW.CleanArchitecture.WebApi.IntegrationTests</RootNamespace>
<AssemblyName>SSW.CleanArchitecture.WebApi.IntegrationTests</AssemblyName>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AwesomeAssertions"/>
<PackageReference Include="Bogus" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
<PackageReference Include="NSubstitute" />
<PackageReference Include="Respawn" />
<PackageReference Include="Testcontainers" />
<PackageReference Include="Testcontainers.MsSql" />
<PackageReference Include="Polly" />
<PackageReference Include="Microsoft.NET.Test.Sdk"/>
<PackageReference Include="xunit.v3"/>
<PackageReference Include="Microsoft.Testing.Extensions.CodeCoverage" />
<PackageReference Include="xunit.v3.mtp-v2"/>
<PackageReference Include="xunit.runner.visualstudio">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
Expand Down
2 changes: 1 addition & 1 deletion tools/AppHost/AppHost.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Aspire.AppHost.Sdk/13.0.2">
<Project Sdk="Aspire.AppHost.Sdk/13.1.0">

<PropertyGroup>
<OutputType>Exe</OutputType>
Expand Down
1 change: 0 additions & 1 deletion tools/MigrationService/MigrationService.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
<ItemGroup>
<PackageReference Include="Aspire.Microsoft.EntityFrameworkCore.SqlServer" />
<PackageReference Include="Bogus" />
<PackageReference Include="Microsoft.Extensions.Hosting" />
</ItemGroup>

<ItemGroup>
Expand Down
1 change: 1 addition & 0 deletions tools/MigrationService/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using SSW.CleanArchitecture.Application.Common.Interfaces;
using SSW.CleanArchitecture.Infrastructure.Persistence;
using SSW.CleanArchitecture.Infrastructure.Persistence.Interceptors;
using SSW.CleanArchitecture.Infrastructure.ServiceDefaults;

var builder = Host.CreateApplicationBuilder(args);

Expand Down