Skip to content

Commit 32905f4

Browse files
authored
Added internal verify-dependencies command (#20)
Simulates a start of a console application to verify that it has all services registered that could be requested during runtime. Useful as a check in CI workflows.
1 parent 46a407f commit 32905f4

15 files changed

Lines changed: 235 additions & 121 deletions

.github/workflows/cd-production.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ on:
77
env:
88
ARTIFACTS_FEED_URL: https://api.nuget.org/v3/index.json
99
BUILD_CONFIGURATION: "Release"
10-
DOTNET_VERSION: "6.x"
10+
DOTNET_VERSION: "8.x"
1111

1212
jobs:
1313
build-pack-push:

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: CI
22

3-
on:
3+
on:
44
push:
55
branches: [ main ]
66
pull_request:
@@ -9,7 +9,7 @@ on:
99

1010
env:
1111
BUILD_CONFIGURATION: "Release"
12-
DOTNET_VERSION: "6.x"
12+
DOTNET_VERSION: "8.x"
1313

1414
jobs:
1515
build:

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Added a built-in `verify-dependencies` command that validates the application's Dependency Injection setup by ensuring all required services are fully and correctly registered in the composition root.
13+
14+
### Changed
15+
16+
- Updated NLog.Extensions.Logging to v5.3.15
17+
1018
## [3.0.4] - 2024-11-28
1119

1220
### Fixed

Neolution.DotNet.Console.SampleAsync/Neolution.DotNet.Console.SampleAsync.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<PackageReference Include="CommandLineParser" Version="2.9.1" />
1212
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.0" />
1313
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
14-
<PackageReference Include="Neolution.CodeAnalysis" Version="3.1.2">
14+
<PackageReference Include="Neolution.CodeAnalysis" Version="3.2.1">
1515
<PrivateAssets>all</PrivateAssets>
1616
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1717
</PackageReference>
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
{
22
"profiles": {
3-
"Neolution.DotNet.Console.SampleAsync": {
3+
"Sample Console App": {
44
"commandName": "Project",
55
"environmentVariables": {
66
"DOTNET_ENVIRONMENT": "Development"
77
}
8+
},
9+
"Sample Console App: verify-dependencies": {
10+
"commandName": "Project",
11+
"commandLineArgs": "verify-dependencies",
12+
"environmentVariables": {
13+
"DOTNET_ENVIRONMENT": "Development"
14+
}
815
}
916
}
1017
}

Neolution.DotNet.Console.UnitTests/DotNetConsoleBuilderTests.cs

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
using System;
44
using System.Collections.Generic;
55
using System.Reflection;
6+
using Microsoft.Extensions.DependencyInjection;
67
using Neolution.DotNet.Console.UnitTests.Fakes;
8+
using Neolution.DotNet.Console.UnitTests.Stubs;
79
using Shouldly;
810
using Xunit;
911

@@ -28,15 +30,15 @@ public void GivenMistypedVerb_WhenDefaultVerbIsDefined_ThenShouldThrowOnBuilding
2830
}
2931

3032
/// <summary>
31-
/// Given a valid argument, when default verb is defined, then should not throw on building.
33+
/// Given a valid argument, when default verb is defined, then should not throw on creating.
3234
/// </summary>
3335
/// <param name="args">The arguments.</param>
3436
[Theory]
3537
[InlineData("")]
3638
[InlineData("echo")]
3739
[InlineData("--silent")]
3840
[InlineData("-s")]
39-
public void GivenValidArgument_WhenDefaultVerbIsDefined_ThenShouldNotThrowOnBuilding(string args)
41+
public void GivenValidArgument_WhenDefaultVerbIsDefined_ThenShouldNotThrowOnCreating(string args)
4042
{
4143
// Arrange
4244
var servicesAssembly = Assembly.GetAssembly(typeof(EchoCommand))!;
@@ -48,7 +50,7 @@ public void GivenValidArgument_WhenDefaultVerbIsDefined_ThenShouldNotThrowOnBuil
4850
}
4951

5052
/// <summary>
51-
/// Given a reserved argument name, when default verb is defined, then should not throw on building.
53+
/// Given a reserved argument name, when default verb is defined, then should not throw on creating.
5254
/// </summary>
5355
/// <param name="args">The arguments.</param>
5456
[Theory]
@@ -57,7 +59,7 @@ public void GivenValidArgument_WhenDefaultVerbIsDefined_ThenShouldNotThrowOnBuil
5759
[InlineData("--help")]
5860
[InlineData("--version")]
5961
[InlineData("help echo")]
60-
public void GivenReservedArgumentName_WhenDefaultVerbIsDefined_ThenShouldNotThrowOnBuilding(string args)
62+
public void GivenReservedArgumentName_WhenDefaultVerbIsDefined_ThenShouldNotThrowOnCreating(string args)
6163
{
6264
// Arrange
6365
var servicesAssembly = Assembly.GetAssembly(typeof(EchoCommand))!;
@@ -69,15 +71,15 @@ public void GivenReservedArgumentName_WhenDefaultVerbIsDefined_ThenShouldNotThro
6971
}
7072

7173
/// <summary>
72-
/// Given invalid arguments, when no default verb is defined, then should not throw on console building.
74+
/// Given invalid arguments, when no default verb is defined, then should not throw on console creating.
7375
/// </summary>
7476
/// <param name="args">The arguments.</param>
7577
[Theory]
7678
[InlineData("")]
7779
[InlineData("verb-that-does-not-exist")]
7880
[InlineData("--option-that-does-not-exist")]
7981
[InlineData("-o")]
80-
public void GivenInvalidArguments_WhenNoDefaultVerbIsDefined_ThenShouldNotThrowOnBuilding(string args)
82+
public void GivenInvalidArguments_WhenNoDefaultVerbIsDefined_ThenShouldNotThrowOnCreating(string args)
8183
{
8284
// Arrange
8385
var servicesAssembly = Assembly.GetAssembly(typeof(EchoCommand))!;
@@ -88,5 +90,23 @@ public void GivenInvalidArguments_WhenNoDefaultVerbIsDefined_ThenShouldNotThrowO
8890
// Assert
8991
Should.NotThrow(() => DotNetConsole.CreateBuilderWithReference(servicesAssembly, verbTypes, args.Split(" ")));
9092
}
93+
94+
/// <summary>
95+
/// Given the verify-dependencies builder, when registration is missing, then should throw on console building.
96+
/// </summary>
97+
[Fact]
98+
public void GivenVerifyDependenciesCommand_WhenRegistrationIsMissing_ThenShouldThrow()
99+
{
100+
// Arrange
101+
var builder = DotNetConsole.CreateBuilderWithReference(Assembly.GetAssembly(typeof(DefaultCommand))!, new[] { "verify-dependencies" });
102+
103+
// Intentionally only registering the transient service and not the scoped and singleton services.
104+
builder.Services.AddTransient<ITransientServiceStub, TransientServiceStub>();
105+
106+
// Act
107+
108+
// Assert
109+
Should.Throw(() => builder.Build(), typeof(AggregateException));
110+
}
91111
}
92112
}

Neolution.DotNet.Console.UnitTests/DotNetConsoleGrammarTests.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Threading.Tasks;
55
using Microsoft.Extensions.DependencyInjection;
66
using Microsoft.Extensions.DependencyInjection.Extensions;
7+
using Neolution.DotNet.Console.Abstractions;
78
using Neolution.DotNet.Console.UnitTests.Fakes;
89
using Neolution.DotNet.Console.UnitTests.Spies;
910
using Shouldly;
@@ -123,7 +124,7 @@ public async Task GivenBuiltConsoleApp_WhenCallingVerbWithScalarOption_ThenShoul
123124
/// <param name="args">The arguments.</param>
124125
/// <param name="tracker">The logger.</param>
125126
/// <returns>A built console app ready to run.</returns>
126-
private static DotNetConsole CreateConsoleAppWithLogger(string args, IUnitTestLogger tracker)
127+
private static IDotNetConsole CreateConsoleAppWithLogger(string args, IUnitTestLogger tracker)
127128
{
128129
var builder = DotNetConsole.CreateBuilderWithReference(Assembly.GetAssembly(typeof(DefaultCommand))!, args.Split(" "));
129130

Neolution.DotNet.Console.UnitTests/DotNetConsoleRunTests.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ public void GivenServicesWithVariousServiceLifetimes_WhenRunningConsoleApp_ThenS
5454
[InlineData("--version")]
5555
[InlineData("help echo")]
5656
[InlineData("echo --help")]
57+
[InlineData("verify-dependencies")]
5758
public void GivenValidArguments_WhenNoDefaultVerbIsDefined_ThenShouldNotThrow([NotNull] string args)
5859
{
5960
if (args == null)
@@ -67,6 +68,10 @@ public void GivenValidArguments_WhenNoDefaultVerbIsDefined_ThenShouldNotThrow([N
6768
var logger = new UnitTestLogger();
6869
builder.Services.AddSingleton(typeof(IUnitTestLogger), logger);
6970

71+
builder.Services.AddTransient<ITransientServiceStub, TransientServiceStub>();
72+
builder.Services.AddScoped<IScopedServiceStub, ScopedServiceStub>();
73+
builder.Services.AddSingleton<ISingletonServiceStub, SingletonServiceStub>();
74+
7075
var console = builder.Build();
7176

7277
// Act

Neolution.DotNet.Console.UnitTests/Neolution.DotNet.Console.UnitTests.csproj

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,23 @@
1313
</PropertyGroup>
1414

1515
<ItemGroup>
16-
<PackageReference Include="coverlet.msbuild" Version="6.0.2">
16+
<PackageReference Include="coverlet.msbuild" Version="6.0.3">
1717
<PrivateAssets>all</PrivateAssets>
1818
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1919
</PackageReference>
2020
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
21-
<PackageReference Include="Neolution.CodeAnalysis.TestsRuleset" Version="3.1.2">
21+
<PackageReference Include="Neolution.CodeAnalysis.TestsRuleset" Version="3.2.1">
2222
<PrivateAssets>all</PrivateAssets>
2323
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2424
</PackageReference>
25-
<PackageReference Include="Objectivity.AutoFixture.XUnit2.AutoNSubstitute" Version="3.6.1" />
25+
<PackageReference Include="Objectivity.AutoFixture.XUnit2.AutoNSubstitute" Version="3.6.2" />
2626
<PackageReference Include="Shouldly" Version="4.2.1" />
27-
<PackageReference Include="xunit" Version="2.9.2" />
28-
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
27+
<PackageReference Include="xunit" Version="2.9.3" />
28+
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.1">
2929
<PrivateAssets>all</PrivateAssets>
3030
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
3131
</PackageReference>
32-
<PackageReference Include="coverlet.collector" Version="6.0.2">
32+
<PackageReference Include="coverlet.collector" Version="6.0.3">
3333
<PrivateAssets>all</PrivateAssets>
3434
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
3535
</PackageReference>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
namespace Neolution.DotNet.Console.Abstractions
2+
{
3+
using System;
4+
using System.Threading.Tasks;
5+
6+
/// <summary>
7+
/// The console application.
8+
/// </summary>
9+
public interface IDotNetConsole
10+
{
11+
/// <summary>
12+
/// Gets the services.
13+
/// </summary>
14+
IServiceProvider Services { get; }
15+
16+
/// <summary>
17+
/// Runs the application.
18+
/// </summary>
19+
/// <returns>The <see cref="Task"/>.</returns>
20+
Task RunAsync();
21+
}
22+
}

0 commit comments

Comments
 (0)