Skip to content

Commit

Permalink
test: ✅ some dependency test fixes (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
mehdihadeli authored Sep 8, 2024
1 parent 8373656 commit 848e805
Show file tree
Hide file tree
Showing 19 changed files with 200 additions and 151 deletions.
2 changes: 2 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,8 @@ resharper_web_config_module_not_resolved_highlighting = warning
resharper_web_config_type_not_resolved_highlighting = warning
resharper_web_config_wrong_module_highlighting = warning

# https://www.jetbrains.com/help/rider/ClassNeverInstantiated.Global.html
resharper_class_never_instantiated_global_highlighting = none

##################################################################################
## https://github.com/DotNetAnalyzers/StyleCopAnalyzers/tree/master/documentation
Expand Down
160 changes: 84 additions & 76 deletions .github/release-drafter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,46 +15,49 @@ template: |
$CHANGES
**Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION
categories:
- title: 🚀 Features
labels:
- feature
- title: ♻️ Enhancement
labels:
- enhancement
- refactor
- title: 🐛 Bug Fixes
labels:
- fix
- bug
- title: 👷 CI
labels:
- ci
- title: ⚠️ Breaking Changes
labels:
- breaking-changes
- title: ⛔️ Deprecated
labels:
- deprecated
- title: 🗑 Removed
labels:
- removed
- title: 🔐 Security
labels:
- security
- title: 📄 Documentation
labels:
- docs
- documentation
- title: 🧩 Dependency Updates
labels:
- deps
- dependencies
- title: 🧰 Maintenance
label: 'chore'
- title: 🧺 Miscellaneous #Everything except ABAP
label: misc
- title: 🚩 Other changes
## putting no labels pr to `Other Changes` category with no label - https://github.com/release-drafter/release-drafter/issues/139#issuecomment-480473934
- title: 🚀 Features
labels:
- feature
- title: ♻️ Enhancement
labels:
- enhancement
- refactor
- title: 🐛 Bug Fixes
labels:
- fix
- bug
- title: 👷 CI
labels:
- ci
- title: ⚠️ Breaking Changes
labels:
- breaking-changes
- title: ⛔️ Deprecated
labels:
- deprecated
- title: 🗑 Removed
labels:
- removed
- title: 🔐 Security
labels:
- security
- title: 🧪 Test
labels:
- test
- title: 📄 Documentation
labels:
- docs
- documentation
- title: 🧩 Dependency Updates
labels:
- deps
- dependencies
- title: 🧰 Maintenance
label: 'chore'
- title: 🧺 Miscellaneous #Everything except ABAP
label: misc
- title: 🚩 Other changes
## putting no labels pr to `Other Changes` category with no label - https://github.com/release-drafter/release-drafter/issues/139#issuecomment-480473934


# https://www.trywilco.com/post/wilco-ci-cd-github-heroku
Expand All @@ -64,52 +67,57 @@ categories:
# Using regex for defining rules - https://regexr.com/
# https://stackoverflow.com/questions/58899999/regexp-to-match-conventional-commit-syntax
autolabeler:
- label: 'chore'
branch:
- '(chore)(\([a-z ]+\))?\/.'
title:
- '^(chore)(\([a-z ]+\))?: .'
- label: 'bug'
branch:
- '(fix)(\([a-z ]+\))?\/.'
title:
- '^(fix)(\([a-z ]+\))?: .'
- label: 'feature'
branch:
- '(feat)(\([a-z ]+\))?\/.'
title:
- '^(feat)(\([a-z ]+\))?: .'
- label: 'ci/cd'
branch:
- '(ci)(\([a-z ]+\))?\/.'
title:
- '^(ci)(\([a-z ]+\))?: .'
- label: 'minor'
branch:
- '(feat)(\([a-z ]+\))?\/.'
title:
- '^(feat)(\([a-z ]+\))?: .'
- label: 'patch'
branch:
- '(fix)(\([a-z ]+\))?\/.'
- '(ci)(\([a-z ]+\))?\/.'
title:
- '^(fix)(\([a-z ]+\))?: .'
- '^(ci)(\([a-z ]+\))?: .'
- label: 'chore'
branch:
- '(chore)(\([a-z ]+\))?\/.'
title:
- '^(chore)(\([a-z ]+\))?: .'
- label: 'bug'
branch:
- '(fix)(\([a-z ]+\))?\/.'
title:
- '^(fix)(\([a-z ]+\))?: .'
- label: 'test'
branch:
- '(test)(\([a-z ]+\))?\/.'
title:
- '^(test)(\([a-z ]+\))?: .'
- label: 'feature'
branch:
- '(feat)(\([a-z ]+\))?\/.'
title:
- '^(feat)(\([a-z ]+\))?: .'
- label: 'ci/cd'
branch:
- '(ci)(\([a-z ]+\))?\/.'
title:
- '^(ci)(\([a-z ]+\))?: .'
- label: 'minor'
branch:
- '(feat)(\([a-z ]+\))?\/.'
title:
- '^(feat)(\([a-z ]+\))?: .'
- label: 'patch'
branch:
- '(fix)(\([a-z ]+\))?\/.'
- '(ci)(\([a-z ]+\))?\/.'
title:
- '^(fix)(\([a-z ]+\))?: .'
- '^(ci)(\([a-z ]+\))?: .'

change-template: '- $TITLE @$AUTHOR (#$NUMBER)'
change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks.
version-resolver:
major:
labels:
- breaking-changes
- breaking-changes
minor:
labels:
- minor
- minor
patch:
labels:
- patch
- patch
default: patch

exclude-labels:
- skip-changelog
- skip-changelog
13 changes: 5 additions & 8 deletions src/App/Vertical.Slice.Template.Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Shared.Core.Extensions.ServiceCollectionsExtensions;
using Shared.Logging;
using Shared.Swagger;
using Shared.Web.Extensions;
using Shared.Web.Minimal.Extensions;
using Vertical.Slice.Template;
using Vertical.Slice.Template.Shared;
Expand All @@ -28,7 +29,7 @@
{
var isDevMode =
context.HostingEnvironment.IsDevelopment()
|| context.HostingEnvironment.IsEnvironment("test")
|| context.HostingEnvironment.IsTest()
|| context.HostingEnvironment.IsStaging();

// Handling Captive Dependency Problem
Expand All @@ -49,13 +50,9 @@

var app = builder.Build();

if (app.Environment.IsDevelopment() && app.Environment.IsEnvironment("test"))
if (app.Environment.IsDependencyTest())
{
app.Services.ValidateDependencies(
builder.Services,
typeof(CatalogsMetadata).Assembly,
Assembly.GetExecutingAssembly()
);
return;
}

// https://learn.microsoft.com/en-us/aspnet/core/fundamentals/error-handling
Expand All @@ -66,7 +63,7 @@
app.UseExceptionHandler(options: new ExceptionHandlerOptions { AllowStatusCode404Response = true });

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment() || app.Environment.IsEnvironment("test"))
if (app.Environment.IsDevelopment() || app.Environment.IsTest())
{
// https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/handle-errrors
app.UseDeveloperExceptionPage();
Expand Down
5 changes: 2 additions & 3 deletions src/App/Vertical.Slice.Template.Api/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,12 @@
},
"PostgresOptions": {
"ConnectionString": "Server=localhost;Port=5432;Database=catalogs;User Id=postgres;Password=postgres;Include Error Detail=true",
"UseInMemory": true
"UseInMemory": false
},
"SieveOptions": {
"DefaultPageSize": 10,
"MaxPageSize": 10
},

"CacheOptions": {
"ExpirationTimeInMinute": 5
},
Expand All @@ -32,7 +31,7 @@
]
},
"CatalogsApiClientOptions": {
"BaseAddress": ""
"BaseAddress": "http://localhost:4000"
},
"RikAndMortyApiClientOptions": {
"BaseAddress": "https://rickandmortyapi.com",
Expand Down
3 changes: 1 addition & 2 deletions src/App/Vertical.Slice.Template.Api/appsettings.test.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
{
"CatalogsApiClientOptions": {
"CatalogBaseApiAddress": "",
"BaseAddress":"http://localhost:4000"
},
"UsersHttpClientOptions":{
"BaseAddress":"https://dummyjson.com",
"UsersEndpoint": "users"
},
"CacheOptions": {
"ExpirationTimeInMinute": 5
"ExpirationTimeInMinute": 10
},
"PostgresOptions": {
"UseInMemory": false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ namespace Vertical.Slice.Template.Shared.Clients.Catalogs;

public class CatalogsClient(ICatalogsApiClient catalogsApiClient) : ICatalogsClient
{
public const string ClientName = "CatalogsClient";

public async Task<Guid> CreateProductAsync(
CreateProductClientDto createProductClientDto,
CancellationToken cancellationToken
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
using Catalogs.ApiClient;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Options;
using Shared.Core.Extensions;
using Shared.Core.Extensions.ServiceCollectionsExtensions;
using Shared.Resiliency.Extensions;
using Shared.Resiliency.Options;
using Vertical.Slice.Template.Shared.Clients.Catalogs;
using Vertical.Slice.Template.Shared.Clients.Users;

Expand All @@ -26,19 +30,20 @@ private static void AddUsersHttpClient(this WebApplicationBuilder builder)

private static void AddCatalogsApiClient(this WebApplicationBuilder builder)
{
builder.Services.AddTransient<ICatalogsClient, CatalogsClient>();

// https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests#named-clients
// Add custom `http client` for using in CatalogsApiClient
builder.Services.AddCustomHttpClient<CatalogsApiClientOptions>(CatalogsClient.ClientName);
builder.Services.AddValidatedOptions<CatalogsApiClientOptions>();
builder.Services.AddHttpClient<ICatalogsApiClient, CatalogsApiClient>(
(client, sp) =>
{
var catalogApiOptions = sp.GetRequiredService<IOptions<CatalogsApiClientOptions>>();
var policyOptions = sp.GetRequiredService<IOptions<PolicyOptions>>();
catalogApiOptions.Value.NotBeNull();

var baseAddress = catalogApiOptions.Value.BaseAddress;
client.BaseAddress = new Uri(baseAddress);
return new CatalogsApiClient(client);
}
);

builder.Services.AddTransient<ICatalogsApiClient, CatalogsApiClient>(sp =>
{
var factory = sp.GetRequiredService<IHttpClientFactory>();

// https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests#createclient
var httpClient = factory.CreateClient(CatalogsClient.ClientName);
return new CatalogsApiClient(httpClient);
});
builder.Services.AddTransient<ICatalogsClient, CatalogsClient>();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Reflection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;

namespace Shared.Core.Extensions.ServiceCollectionsExtensions;

Expand Down Expand Up @@ -272,30 +273,45 @@ public static void ValidateDependencies(
params Assembly[] assembliesToScan
)
{
var scanAssemblies = assembliesToScan.Any() ? assembliesToScan : new[] { Assembly.GetExecutingAssembly(), };
var exceptions = new List<string>();
var scanAssemblies = assembliesToScan.Length != 0 ? assembliesToScan : [Assembly.GetExecutingAssembly(),];

// for resolving scoped based dependencies without errors
using var scope = rootServiceProvider.CreateScope();
var sp = scope.ServiceProvider;

foreach (var serviceDescriptor in services)
{
// Skip services that are not typically resolved directly or are special cases
if (
serviceDescriptor.ServiceType == typeof(IHostedService)
|| serviceDescriptor.ServiceType == typeof(IApplicationLifetime)
)
{
continue;
}

try
{
var serviceType = serviceDescriptor.ServiceType;
if (scanAssemblies.Contains(serviceType.Assembly))
sp.GetRequiredService(serviceType);
{
// Attempt to resolve the service
var service = sp.GetService(serviceType);

// Assert: Check that the service was resolved if it's not meant to be optional
if (
serviceDescriptor.ImplementationInstance == null
&& serviceDescriptor.ImplementationFactory == null
)
{
service.NotBeNull();
}
}
}
catch (Exception e)
catch (Exception ex)
{
exceptions.Add($"Unable to resolve '{serviceDescriptor.ServiceType.FullName}', detail: {e.Message}");
throw new($"Failed to resolve service {serviceDescriptor.ServiceType.FullName}: {ex.Message}", ex);
}
}

if (exceptions.Any())
{
throw new Exception(string.Join("\n", exceptions));
}
}
}
Loading

0 comments on commit 848e805

Please sign in to comment.