Skip to content

Commit e938fa4

Browse files
authored
Merge pull request #21 from kapozade/feature/#19
Feature/#19
2 parents 384cf0c + 6e461aa commit e938fa4

File tree

11 files changed

+270
-65
lines changed

11 files changed

+270
-65
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ Supreme.Api: Responsible for showing information to user and interpreting user's
2525
Supreme.Application: Defines the jobs that project is supposed to do and directs expressive domain objects to work.
2626
Supreme.Domain: Responsible for representing concepts of business, information about business situation and business rules.
2727
Supreme.Infrastructure: Provides generic technical capabilities that support higher layers.
28+
Supreme.Api.Tests: Provides you to add unit tests for API layer. It's up to you to enable integration tests at this layer.
29+
Supreme.Application.Tests: Provides you to add both unit and integration tests for Application Layer. For integration tests currently TestContainers are being used. Depending on your choices, RabbitMQ, MySQL, PostgreSQL containers will be registered.
30+
Supreme.Infrastructure.Tests: Provides you to add unit tests for Infrastructure Layer. It's up to you to enable integration tests at this layer.
2831
```
2932
<i>PS: When you name your project Supreme will be replaced with the name of project.</i>
3033

@@ -107,6 +110,7 @@ dotnet new supremeapi -n "MyService" -eop -eot -erl -db mysql
107110
* Serilog
108111
* StackExchange.Redis
109112
* Swashbuckle
113+
* Testcontainers
110114

111115
## Credits
112116

SupremeDotnetApiTemplate.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<PackageType>Template</PackageType>
5-
<PackageVersion>1.2.0</PackageVersion>
5+
<PackageVersion>1.3.0</PackageVersion>
66
<PackageId>Supreme.Dotnet.Api.Template</PackageId>
77
<Title>Supreme API Template</Title>
88
<Authors>Onur Kapçık</Authors>

templates/api/Directory.Build.targets

Lines changed: 43 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,65 +3,70 @@
33
<!-- Generated by dotnet-api-template. Check out https://github.com/kapozade/dotnet-api-template -->
44

55
<!-- Nuget -->
6-
<PackageReference Update="Ardalis.Specification" Version="6.1.0" />
7-
<PackageReference Update="Ardalis.Specification.EntityFrameworkCore" Version="6.1.0" />
8-
<PackageReference Update="EasyCaching.Bus.Redis" Version="1.9.0" />
9-
<PackageReference Update="EasyCaching.HybridCache" Version="1.9.0" />
10-
<PackageReference Update="EasyCaching.InMemory" Version="1.9.0" />
11-
<PackageReference Update="EasyCaching.Redis" Version="1.9.0" />
12-
<PackageReference Update="EasyCaching.Serialization.Json" Version="1.9.0" />
13-
<PackageReference Update="FluentValidation" Version="11.5.2" />
14-
<PackageReference Update="FluentValidation.DependencyInjectionExtensions" Version="11.5.2" />
15-
<PackageReference Update="MediatR" Version="12.0.1" />
16-
<PackageReference Update="Microsoft.EntityFrameworkCore.Design" Version="7.0.5" />
17-
<PackageReference Update="Microsoft.Extensions.Diagnostics.HealthChecks" Version="7.0.8" />
18-
<PackageReference Update="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="7.0.5"/>
19-
<PackageReference Update="Microsoft.CodeAnalysis.CSharp.CodeStyle" Version="4.5.0" />
6+
<PackageReference Update="Ardalis.Specification" Version="7.0.0" />
7+
<PackageReference Update="Ardalis.Specification.EntityFrameworkCore" Version="7.0.0" />
8+
<PackageReference Update="EasyCaching.Bus.Redis" Version="1.9.2" />
9+
<PackageReference Update="EasyCaching.HybridCache" Version="1.9.2" />
10+
<PackageReference Update="EasyCaching.InMemory" Version="1.9.2" />
11+
<PackageReference Update="EasyCaching.Redis" Version="1.9.2" />
12+
<PackageReference Update="EasyCaching.Serialization.Json" Version="1.9.2" />
13+
<PackageReference Update="FluentValidation" Version="11.9.0" />
14+
<PackageReference Update="FluentValidation.DependencyInjectionExtensions" Version="11.9.0" />
15+
<PackageReference Update="MediatR" Version="12.2.0" />
16+
<PackageReference Update="Microsoft.EntityFrameworkCore.Design" Version="7.0.14" />
17+
<PackageReference Update="Microsoft.Extensions.Diagnostics.HealthChecks" Version="7.0.14" />
18+
<PackageReference Update="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="7.0.14"/>
19+
<PackageReference Update="Microsoft.CodeAnalysis.CSharp.CodeStyle" Version="4.7.0" />
2020
<PackageReference Update="Swashbuckle.AspNetCore" Version="6.5.0" />
2121
<PackageReference Update="Scrutor" Version="4.2.2"/>
22-
<PackageReference Update="Serilog" Version="2.12.0" />
23-
<PackageReference Update="Serilog.Extensions.Hosting" Version="5.0.1" />
24-
<PackageReference Update="Serilog.Sinks.Console" Version="4.1.0" />
25-
<PackageReference Update="Serilog.Settings.Configuration" Version="3.4.0" />
22+
<PackageReference Update="Serilog" Version="3.1.1" />
23+
<PackageReference Update="Serilog.Extensions.Hosting" Version="7.0.0" />
24+
<PackageReference Update="Serilog.Sinks.Console" Version="5.0.1" />
25+
<PackageReference Update="Serilog.Settings.Configuration" Version="7.0.1" />
2626
<!-- Test -->
27-
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="17.5.0" />
28-
<PackageReference Update="Moq" Version="4.18.4" />
29-
<PackageReference Update="xunit" Version="2.4.2" />
30-
<PackageReference Update="xunit.runner.visualstudio" Version="2.4.5" />
27+
<PackageReference Update="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.14" />
28+
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="17.8.0" />
29+
<PackageReference Update="Moq" Version="4.20.70" />
30+
<PackageReference Update="Testcontainers" Version="3.6.0" />
31+
<PackageReference Update="xunit" Version="2.6.4" />
32+
<PackageReference Update="xunit.runner.visualstudio" Version="2.5.6" />
3133
</ItemGroup>
3234

3335
<ItemGroup Condition="'$(database)' == 'mysql'">
3436
<PackageReference Update="Pomelo.EntityFrameworkCore.MySql" Version="7.0.0" />
37+
<PackageReference Update="Testcontainers.MySql" Version="3.6.0" />
3538
</ItemGroup>
3639
<ItemGroup Condition="'$(database)' == 'postgres'">
37-
<PackageReference Update="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.4" />
40+
<PackageReference Update="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.11" />
41+
<PackageReference Update="Testcontainers.PostgreSql" Version="3.6.0" />
3842
</ItemGroup>
3943

4044
<ItemGroup Condition="$(enable-outbox-pattern)">
41-
<PackageReference Update="DotNetCore.CAP" Version="7.1.2" />
42-
<PackageReference Update="DotNetCore.CAP.RabbitMQ" Version="7.1.2" />
43-
<PackageReference Update="DotNetCore.CAP.Dashboard" Version="7.1.2" />
45+
<PackageReference Update="DotNetCore.CAP" Version="7.2.2" />
46+
<PackageReference Update="DotNetCore.CAP.RabbitMQ" Version="7.2.2" />
47+
<PackageReference Update="DotNetCore.CAP.Dashboard" Version="7.2.2" />
48+
<PackageReference Update="Testcontainers.RabbitMq" Version="3.6.0"/>
4449
</ItemGroup>
4550

4651
<ItemGroup Condition="'$(database)' == 'mysql' And $(enable-outbox-pattern)">
47-
<PackageReference Update="DotNetCore.CAP.MySql" Version="7.1.2" />
52+
<PackageReference Update="DotNetCore.CAP.MySql" Version="7.2.2" />
4853
</ItemGroup>
4954
<ItemGroup Condition="'$(database)' == 'postgres' And $(enable-outbox-pattern)">
50-
<PackageReference Update="DotNetCore.CAP.PostgreSql" Version="7.1.2" />
55+
<PackageReference Update="DotNetCore.CAP.PostgreSql" Version="7.2.2" />
5156
</ItemGroup>
5257

5358
<ItemGroup Condition="$(enable-open-telemetry)">
54-
<PackageReference Update="OpenTelemetry" Version="1.4.0" />
55-
<PackageReference Update="OpenTelemetry.Api" Version="1.4.0" />
56-
<PackageReference Update="OpenTelemetry.Api.ProviderBuilderExtensions" Version="1.4.0" />
57-
<PackageReference Update="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.4.0" />
58-
<PackageReference Update="OpenTelemetry.Exporter.Console" Version="1.4.0" />
59-
<PackageReference Update="OpenTelemetry.Extensions.Hosting" Version="1.4.0" />
60-
<PackageReference Update="OpenTelemetry.Instrumentation.AspNetCore" Version="1.0.0-rc9.14" />
61-
<PackageReference Update="OpenTelemetry.Instrumentation.Http" Version="1.0.0-rc9.14" />
62-
<PackageReference Update="OpenTelemetry.Instrumentation.SqlClient" Version="1.0.0-rc9.14" />
59+
<PackageReference Update="OpenTelemetry" Version="1.7.0" />
60+
<PackageReference Update="OpenTelemetry.Api" Version="1.7.0" />
61+
<PackageReference Update="OpenTelemetry.Api.ProviderBuilderExtensions" Version="1.7.0" />
62+
<PackageReference Update="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.7.0" />
63+
<PackageReference Update="OpenTelemetry.Exporter.Console" Version="1.7.0" />
64+
<PackageReference Update="OpenTelemetry.Extensions.Hosting" Version="1.7.0" />
65+
<PackageReference Update="OpenTelemetry.Instrumentation.AspNetCore" Version="1.7.0" />
66+
<PackageReference Update="OpenTelemetry.Instrumentation.Http" Version="1.7.0" />
67+
<PackageReference Update="OpenTelemetry.Instrumentation.SqlClient" Version="1.6.0-beta.3" />
6368
</ItemGroup>
6469
<ItemGroup Condition="$(enable-rate-limiting)">
65-
<PackageReference Update="StackExchange.Redis" Version="2.6.111" />
70+
<PackageReference Update="StackExchange.Redis" Version="2.7.10" />
6671
</ItemGroup>
6772
</Project>

templates/api/README.md

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,46 @@
22

33
## Requirements
44

5-
* <b>.NET7</b>
6-
* <b>Redis</b>
7-
* <b>MySQL or Postgres</b> (depending on your choice)
8-
* <b>Docker</b>
5+
* .NET7
6+
* Redis
7+
* MySQL or Postgres (depending on your choice)
8+
* Docker
99

1010
## How-to
1111

1212
If you decide to generate database by DDLs manually, you can skip Step 1 and Step 2.
1313

14-
<br/>
1514

16-
<b>Step 1: Building the project</b>
15+
### Step 1: Building the project
1716

1817
```bash
1918
dotnet build Supreme.sln -c Release
2019
```
21-
<br/>
2220

23-
<b>Step 2: Executing EFCore Migrations & Database Update</b>
21+
### Step 2: Run tests
22+
Tests contains both unit and integration. Integration tests are being handled by Testcontainers by default. Currently, below containers are registered as default.
2423

25-
Executing migrations and db update requires a working database. You can create a db instance by executing below command.
24+
* MySQL or Postgres (depending on your db choice)
25+
* RabbitMQ (depending on your outbox pattern choice)
26+
27+
To execute tests run below command.
28+
29+
```bash
30+
dotnet test
31+
```
32+
33+
### Step 3: Executing EFCore Migrations & Database Update
2634

27-
<br/>
35+
Executing migrations and db update requires a working database. You can create a db instance by executing below command.
2836

2937
```bash
3038
docker compose up -d database
3139
````
3240

33-
<i>Note: Before running below commands, be aware that those commands require `dotnet-ef` that should have already been installed globally. <br/>
41+
Note: Before running below commands, be aware that those commands require `dotnet-ef` that should have already been installed globally.
3442

3543
To install globally run this command => ```dotnet tool install --global dotnet-ef``` </i>
3644

37-
<br/>
3845

3946
Change directory to Supreme.Infrastructure project folder. You should wait a few seconds until the database is up & running before running commands below. Having up & running database, you can run below migration generation and db update statements on your CLI.
4047

@@ -47,23 +54,17 @@ dotnet ef migrations add InitialCreate -s "../Supreme.Api/" -o "Db/Migrations/"
4754
dotnet ef database update -s "../Supreme.Api"
4855
```
4956

50-
<br/>
51-
<b>Step 3: Building docker image</b>
52-
53-
<br/>
54-
<b>Go back to the solution folder before running below command.</b>
57+
### Step 4: Building docker image
5558

56-
<br/>
59+
Go back to the solution folder before running below command.
5760

5861
```bash
5962
docker build \
6063
--rm=false --file "./container/Dockerfile.app" -t app/supreme .
6164
```
6265

63-
<br/>
64-
<b>Step 4: Run dependencies and application</b>
66+
### Step 5: Run dependencies and application
6567

66-
<br/>
6768
Run below statements one by one.
6869
Before running the app, make sure that all dependencies are up & running.
6970

templates/api/src/Supreme.Api/Program.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ void App()
6767

6868
List<Assembly> GetProjectAssemblies()
6969
{
70-
var startupAssembly = typeof(ReadinessController).Assembly;
70+
var startupAssembly = typeof(Program).Assembly;
7171
var list = new List<Assembly> { startupAssembly };
7272
list.AddRange(startupAssembly.GetReferencedAssemblies()
7373
.Where(x => !string.IsNullOrWhiteSpace(x.Name)
@@ -77,4 +77,6 @@ List<Assembly> GetProjectAssemblies()
7777
return list;
7878
}
7979

80-
App();
80+
App();
81+
82+
public partial class Program { }

templates/api/src/Supreme.Infrastructure/CompositionRoot.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ private static void AddExternalLibraryDependencies(this IServiceCollection servi
9090
})
9191
);
9292

93-
serviceCollection.Configure<AspNetCoreInstrumentationOptions>(opt =>
93+
serviceCollection.Configure<AspNetCoreTraceInstrumentationOptions>(opt =>
9494
{
9595
opt.RecordException = true;
9696
});

templates/api/test/Supreme.Api.Tests/Supreme.Api.Tests.csproj

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,25 @@
88
</PropertyGroup>
99

1010
<ItemGroup>
11+
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
1112
<PackageReference Include="Microsoft.NET.Test.Sdk" />
1213
<PackageReference Include="Moq" />
1314
<PackageReference Include="xunit" />
1415
<PackageReference Include="xunit.runner.visualstudio" />
16+
<PackageReference Include="Testcontainers" />
1517
</ItemGroup>
16-
18+
19+
<ItemGroup Condition="'$(database)' == 'postgres'">
20+
<PackageReference Include="Testcontainers.PostgreSql" />
21+
</ItemGroup>
22+
<ItemGroup Condition="'$(database)' == 'mysql'">
23+
<PackageReference Include="Testcontainers.MySql" />
24+
</ItemGroup>
25+
<ItemGroup Condition="$(enable-outbox-pattern)">
26+
<PackageReference Include="Testcontainers.RabbitMq"/>
27+
</ItemGroup>
28+
1729
<ItemGroup>
1830
<ProjectReference Include="..\..\src\Supreme.Api\Supreme.Api.csproj" />
1931
</ItemGroup>
20-
2132
</Project>
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
using DotNet.Testcontainers.Builders;
2+
using Microsoft.AspNetCore.Hosting;
3+
using Microsoft.AspNetCore.Mvc.Testing;
4+
using Microsoft.AspNetCore.TestHost;
5+
using Microsoft.EntityFrameworkCore;
6+
using Microsoft.Extensions.DependencyInjection;
7+
#if (database == "mysql")
8+
using Testcontainers.MySql;
9+
#endif
10+
#if (database == "postgres")
11+
using Testcontainers.PostgreSql;
12+
#endif
13+
#if (enable-outbox-pattern)
14+
using Testcontainers.RabbitMq;
15+
#endif
16+
using Supreme.Infrastructure.Db;
17+
using Xunit;
18+
19+
namespace Supreme.Api.Tests;
20+
21+
public sealed class WebAppFactory : WebApplicationFactory<Program>, IAsyncLifetime
22+
{
23+
#if (database == "mysql")
24+
private readonly MySqlContainer _dbContainer = new MySqlBuilder()
25+
.WithImage("mysql:8.0")
26+
.WithCleanUp(true)
27+
.WithDatabase("Belindax")
28+
.WithUsername("sqlsa")
29+
.WithPassword("SuperPass1")
30+
.WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(3306))
31+
.Build();
32+
#endif
33+
#if (database == "postgres")
34+
private readonly PostgreSqlContainer _dbContainer = new PostgreSqlBuilder()
35+
.WithImage("postgres:latest")
36+
.WithCleanUp(true)
37+
.WithDatabase("Supreme")
38+
.WithUsername("sqlsa")
39+
.WithPassword("SuperPass1")
40+
.WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(5432))
41+
.Build();
42+
#endif
43+
#if (enable-outbox-pattern)
44+
private readonly RabbitMqContainer _rabbitMqContainer = new RabbitMqBuilder()
45+
.WithImage("heidiks/rabbitmq-delayed-message-exchange")
46+
.WithUsername("guest")
47+
.WithPassword("guest")
48+
.WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(5672))
49+
.WithCleanUp(true)
50+
.Build();
51+
#endif
52+
53+
protected override void ConfigureWebHost(IWebHostBuilder builder)
54+
{
55+
builder.ConfigureTestServices(services =>
56+
{
57+
var dbContextDescriptor =
58+
services.SingleOrDefault(x => x.ServiceType == typeof(DbContextOptions<SupremeContext>));
59+
60+
if (dbContextDescriptor is not null)
61+
services.Remove(dbContextDescriptor);
62+
63+
#if (database == "mysql")
64+
services.AddDbContextPool<BelindaxContext>(opt =>
65+
{
66+
var connectionString = $"{_dbContainer.GetConnectionString()};IgnoreCommandTransaction=true";
67+
opt.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString));
68+
}
69+
);
70+
#endif
71+
#if (database == "postgres")
72+
services.AddDbContextPool<SupremeContext>(opt =>
73+
opt.UseNpgsql(_dbContainer.GetConnectionString()));
74+
#endif
75+
var serviceProvider = services.BuildServiceProvider();
76+
using var scope = serviceProvider.CreateScope();
77+
var dbContext = scope.ServiceProvider.GetRequiredService<SupremeContext>();
78+
dbContext.Database.EnsureCreated();
79+
dbContext.Database.Migrate();
80+
});
81+
}
82+
83+
public async Task InitializeAsync()
84+
{
85+
await _dbContainer.StartAsync();
86+
#if (enable-outbox-pattern)
87+
await _rabbitMqContainer.StopAsync();
88+
#endif
89+
}
90+
91+
public new async Task DisposeAsync()
92+
{
93+
await _dbContainer.StopAsync();
94+
#if (enable-outbox-pattern)
95+
await _rabbitMqContainer.StopAsync();
96+
#endif
97+
}
98+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using MediatR;
2+
using Microsoft.Extensions.DependencyInjection;
3+
using Supreme.Api.Tests;
4+
using Supreme.Infrastructure.Db;
5+
using Xunit;
6+
7+
namespace Supreme.Application.Tests;
8+
9+
public abstract class BaseTestFixture : IClassFixture<WebAppFactory>
10+
{
11+
protected readonly IMediator _mediator;
12+
protected readonly SupremeContext _dbContext;
13+
14+
protected BaseTestFixture(WebAppFactory webAppFactory)
15+
{
16+
var scope = webAppFactory.Services.CreateScope();
17+
_mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
18+
_dbContext = scope.ServiceProvider.GetRequiredService<SupremeContext>();
19+
}
20+
}

0 commit comments

Comments
 (0)