Skip to content

Commit bb55c21

Browse files
authored
Merge pull request #12 from mehyaa/unit-tests
Add unit tests for Convey framework components
2 parents fa5de11 + 477ecef commit bb55c21

88 files changed

Lines changed: 4620 additions & 24 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/copilot-instructions.md

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,15 @@ Convey provides features for quickly setting up microservices including:
3030
#### 1. Restore Dependencies
3131
**ALWAYS run restore before building after a fresh clone or when dependencies change.**
3232
```bash
33-
dotnet restore Convey.sln
33+
dotnet restore Convey.slnx
3434
```
3535
- **Expected Time**: 10-15 seconds
3636
- **Expected Result**: All 35 projects restored successfully with no errors
3737
- **Note**: This is implicit in `dotnet build` unless you use `--no-restore`
3838

3939
#### 2. Build the Solution
4040
```bash
41-
dotnet build Convey.sln -c Release
41+
dotnet build Convey.slnx -c Release
4242
```
4343
- **Expected Time**: 12-20 seconds (clean build)
4444
- **Expected Result**: Build succeeded with 0 errors
@@ -47,30 +47,30 @@ dotnet build Convey.sln -c Release
4747

4848
#### 3. Clean Build Artifacts
4949
```bash
50-
dotnet clean Convey.sln -c Release
50+
dotnet clean Convey.slnx -c Release
5151
```
5252
- **Expected Time**: 2-3 seconds
5353
- **Use Case**: Run this before a clean build or when experiencing build issues
5454

5555
#### 4. Format Code
5656
**ALWAYS run format before committing code changes.**
5757
```bash
58-
dotnet format Convey.sln --no-restore
58+
dotnet format Convey.slnx --no-restore
5959
```
6060
- **Expected Time**: 5-10 seconds
6161
- **Expected Result**: May show whitespace formatting errors in existing code - this is NORMAL
6262
- **Important**: The repository has existing formatting issues. Focus only on formatting files you modify.
6363

6464
#### 5. Verify No Formatting Changes Needed
6565
```bash
66-
dotnet format Convey.sln --verify-no-changes --no-restore
66+
dotnet format Convey.slnx --verify-no-changes --no-restore
6767
```
6868
- **Use Case**: Check if code needs formatting without making changes
6969
- **Exit Code**: Returns non-zero if formatting is needed
7070

7171
#### 6. Run Tests
7272
```bash
73-
dotnet test Convey.sln --no-build --no-restore -c Release
73+
dotnet test Convey.slnx --no-build --no-restore -c Release
7474
```
7575
- **Expected Result**: No test projects exist - command completes immediately with success
7676
- **Note**: This repository does not have unit tests
@@ -81,30 +81,30 @@ dotnet test Convey.sln --no-build --no-restore -c Release
8181

8282
1. **Start Fresh** (if needed):
8383
```bash
84-
dotnet clean Convey.sln -c Release
84+
dotnet clean Convey.slnx -c Release
8585
```
8686

8787
2. **Restore Dependencies** (after fresh clone or dependency changes):
8888
```bash
89-
dotnet restore Convey.sln
89+
dotnet restore Convey.slnx
9090
```
9191

9292
3. **Build**:
9393
```bash
94-
dotnet build Convey.sln -c Release --no-restore
94+
dotnet build Convey.slnx -c Release --no-restore
9595
```
9696
- Expected time: 12-20 seconds
9797
- Generates NuGet packages in each project's bin/Release directory
9898

9999
4. **Format Your Changes**:
100100
```bash
101-
dotnet format Convey.sln --no-restore
101+
dotnet format Convey.slnx --no-restore
102102
```
103103
- Run on files you modified only
104104

105105
5. **Rebuild After Formatting**:
106106
```bash
107-
dotnet build Convey.sln -c Release --no-restore
107+
dotnet build Convey.slnx -c Release --no-restore
108108
```
109109

110110
## Project Structure
@@ -115,7 +115,7 @@ dotnet test Convey.sln --no-build --no-restore -c Release
115115
├── .github/workflows/main.yml # GitHub Actions CI/CD pipeline
116116
├── .gitignore # Standard .NET gitignore
117117
├── .travis.yml # Legacy Travis CI config (reference only)
118-
├── Convey.sln # Main solution file (746 lines, 35 projects)
118+
├── Convey.slnx # Main solution file (746 lines, 35 projects)
119119
├── Directory.Build.props # Shared MSBuild properties (target framework, versioning)
120120
├── LICENSE # MIT License
121121
├── README.md # Project documentation
@@ -170,7 +170,7 @@ Located in `samples/`:
170170

171171
### Build Configuration in CI
172172
```bash
173-
dotnet build Convey.sln \
173+
dotnet build Convey.slnx \
174174
--configuration Release \
175175
--verbosity minimal \
176176
--property:Version="$VERSION" \
@@ -268,18 +268,18 @@ services.AddConvey()
268268

269269
```bash
270270
# Fresh start
271-
dotnet clean Convey.sln -c Release
272-
dotnet restore Convey.sln
273-
dotnet build Convey.sln -c Release --no-restore
271+
dotnet clean Convey.slnx -c Release
272+
dotnet restore Convey.slnx
273+
dotnet build Convey.slnx -c Release --no-restore
274274

275275
# Format code (files you changed)
276-
dotnet format Convey.sln --no-restore
276+
dotnet format Convey.slnx --no-restore
277277

278278
# Check formatting
279-
dotnet format Convey.sln --verify-no-changes --no-restore
279+
dotnet format Convey.slnx --verify-no-changes --no-restore
280280

281281
# Rebuild
282-
dotnet build Convey.sln -c Release --no-restore
282+
dotnet build Convey.slnx -c Release --no-restore
283283
```
284284

285285
**Trust these instructions** - they are validated against the actual repository. Only search for additional information if you encounter unexpected behavior or errors not documented here.

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ jobs:
3131
packages: write
3232

3333
env:
34-
SOLUTION: "Convey.sln"
34+
SOLUTION: "Convey.slnx"
3535
BUILD_CONFIG: "Release"
3636
PACKAGE_SOURCE: "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json"
3737

Convey.slnx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,4 +138,38 @@
138138
<Folder Name="/src/Convey.WebApi.Swagger/src/">
139139
<Project Path="src/Convey.WebApi.Swagger/src/Convey.WebApi.Swagger/Convey.WebApi.Swagger.csproj" />
140140
</Folder>
141+
<Folder Name="/test/">
142+
<Project Path="test/Convey.Auth.Distributed.Tests/Convey.Auth.Distributed.Tests.csproj" />
143+
<Project Path="test/Convey.Auth.Tests/Convey.Auth.Tests.csproj" />
144+
<Project Path="test/Convey.CQRS.Commands.Tests/Convey.CQRS.Commands.Tests.csproj" />
145+
<Project Path="test/Convey.CQRS.Events.Tests/Convey.CQRS.Events.Tests.csproj" />
146+
<Project Path="test/Convey.CQRS.Queries.Tests/Convey.CQRS.Queries.Tests.csproj" />
147+
<Project Path="test/Convey.Discovery.Consul.Tests/Convey.Discovery.Consul.Tests.csproj" />
148+
<Project Path="test/Convey.Docs.Swagger.Tests/Convey.Docs.Swagger.Tests.csproj" />
149+
<Project Path="test/Convey.HTTP.RestEase.Tests/Convey.HTTP.RestEase.Tests.csproj" />
150+
<Project Path="test/Convey.HTTP.Tests/Convey.HTTP.Tests.csproj" />
151+
<Project Path="test/Convey.LoadBalancing.Fabio.Tests/Convey.LoadBalancing.Fabio.Tests.csproj" />
152+
<Project Path="test/Convey.Logging.CQRS.Tests/Convey.Logging.CQRS.Tests.csproj" />
153+
<Project Path="test/Convey.Logging.Tests/Convey.Logging.Tests.csproj" />
154+
<Project Path="test/Convey.MessageBrokers.CQRS.Tests/Convey.MessageBrokers.CQRS.Tests.csproj" />
155+
<Project Path="test/Convey.MessageBrokers.Outbox.EntityFramework.Tests/Convey.MessageBrokers.Outbox.EntityFramework.Tests.csproj" />
156+
<Project Path="test/Convey.MessageBrokers.Outbox.Mongo.Tests/Convey.MessageBrokers.Outbox.Mongo.Tests.csproj" />
157+
<Project Path="test/Convey.MessageBrokers.Outbox.Tests/Convey.MessageBrokers.Outbox.Tests.csproj" />
158+
<Project Path="test/Convey.MessageBrokers.RabbitMQ.Tests/Convey.MessageBrokers.RabbitMQ.Tests.csproj" />
159+
<Project Path="test/Convey.MessageBrokers.Tests/Convey.MessageBrokers.Tests.csproj" />
160+
<Project Path="test/Convey.Metrics.AppMetrics.Tests/Convey.Metrics.AppMetrics.Tests.csproj" />
161+
<Project Path="test/Convey.Metrics.Prometheus.Tests/Convey.Metrics.Prometheus.Tests.csproj" />
162+
<Project Path="test/Convey.Persistence.MongoDB.Tests/Convey.Persistence.MongoDB.Tests.csproj" />
163+
<Project Path="test/Convey.Persistence.OpenStack.OCS.Tests/Convey.Persistence.OpenStack.OCS.Tests.csproj" />
164+
<Project Path="test/Convey.Persistence.Redis.Tests/Convey.Persistence.Redis.Tests.csproj" />
165+
<Project Path="test/Convey.Secrets.Vault.Tests/Convey.Secrets.Vault.Tests.csproj" />
166+
<Project Path="test/Convey.Security.Tests/Convey.Security.Tests.csproj" />
167+
<Project Path="test/Convey.Tests/Convey.Tests.csproj" />
168+
<Project Path="test/Convey.Tracing.Jaeger.RabbitMQ.Tests/Convey.Tracing.Jaeger.RabbitMQ.Tests.csproj" />
169+
<Project Path="test/Convey.Tracing.Jaeger.Tests/Convey.Tracing.Jaeger.Tests.csproj" />
170+
<Project Path="test/Convey.WebApi.CQRS.Tests/Convey.WebApi.CQRS.Tests.csproj" />
171+
<Project Path="test/Convey.WebApi.Security.Tests/Convey.WebApi.Security.Tests.csproj" />
172+
<Project Path="test/Convey.WebApi.Swagger.Tests/Convey.WebApi.Swagger.Tests.csproj" />
173+
<Project Path="test/Convey.WebApi.Tests/Convey.WebApi.Tests.csproj" />
174+
</Folder>
141175
</Solution>

src/Directory.Build.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,6 @@
4848

4949
<ItemGroup>
5050
<InternalsVisibleTo Include="$(MSBuildProjectName).Tests" />
51+
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
5152
</ItemGroup>
5253
</Project>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<Project Sdk="Microsoft.NET.Sdk" />
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using System.Linq;
2+
using Microsoft.Extensions.DependencyInjection;
3+
using Shouldly;
4+
using Xunit;
5+
6+
namespace Convey.Auth.Distributed.Tests
7+
{
8+
public class ExtensionsTests
9+
{
10+
[Fact]
11+
public void AddDistributedAccessTokenValidator_Should_Register_Service()
12+
{
13+
// Arrange
14+
var services = new ServiceCollection();
15+
var builder = ConveyBuilder.Create(services);
16+
17+
// Act
18+
builder.AddDistributedAccessTokenValidator();
19+
20+
// Assert
21+
services.ShouldContain(s => s.ServiceType == typeof(IAccessTokenService));
22+
}
23+
24+
[Fact]
25+
public void AddDistributedAccessTokenValidator_Should_Register_As_Singleton()
26+
{
27+
// Arrange
28+
var services = new ServiceCollection();
29+
var builder = ConveyBuilder.Create(services);
30+
31+
// Act
32+
builder.AddDistributedAccessTokenValidator();
33+
34+
// Assert
35+
var descriptor = services.First(s => s.ServiceType == typeof(IAccessTokenService));
36+
descriptor.Lifetime.ShouldBe(ServiceLifetime.Singleton);
37+
}
38+
39+
[Fact]
40+
public void AddDistributedAccessTokenValidator_Should_Replace_Existing_Service()
41+
{
42+
// Arrange
43+
var services = new ServiceCollection();
44+
services.AddSingleton<IAccessTokenService, DummyAccessTokenService>();
45+
var builder = ConveyBuilder.Create(services);
46+
47+
// Act
48+
builder.AddDistributedAccessTokenValidator();
49+
50+
// Assert
51+
var descriptors = services.Where(s => s.ServiceType == typeof(IAccessTokenService)).ToList();
52+
descriptors.Count.ShouldBeGreaterThanOrEqualTo(1);
53+
}
54+
55+
// Helper class
56+
private class DummyAccessTokenService : IAccessTokenService
57+
{
58+
public System.Threading.Tasks.Task<bool> IsTokenActiveAsync(string token) => System.Threading.Tasks.Task.FromResult(true);
59+
public System.Threading.Tasks.Task<bool> IsTokenActiveAsync() => System.Threading.Tasks.Task.FromResult(true);
60+
public System.Threading.Tasks.Task DeactivateTokenAsync(string token) => System.Threading.Tasks.Task.CompletedTask;
61+
public System.Threading.Tasks.Task DeactivateTokenAsync() => System.Threading.Tasks.Task.CompletedTask;
62+
}
63+
}
64+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
using System.Collections.Generic;
2+
using System.Threading.Tasks;
3+
using Microsoft.AspNetCore.Http;
4+
using Moq;
5+
using Shouldly;
6+
using Xunit;
7+
8+
namespace Convey.Auth.Tests;
9+
10+
public class AccessTokenValidatorMiddlewareTests
11+
{
12+
private readonly Mock<IAccessTokenService> _accessTokenServiceMock;
13+
private readonly JwtOptions _jwtOptions;
14+
private readonly AccessTokenValidatorMiddleware _middleware;
15+
16+
public AccessTokenValidatorMiddlewareTests()
17+
{
18+
_accessTokenServiceMock = new Mock<IAccessTokenService>();
19+
_jwtOptions = new JwtOptions();
20+
_middleware = new AccessTokenValidatorMiddleware(_accessTokenServiceMock.Object, _jwtOptions);
21+
}
22+
23+
[Fact]
24+
public async Task InvokeAsync_Should_Call_Next_When_Endpoint_Is_Allowed()
25+
{
26+
// Arrange
27+
_jwtOptions.AllowAnonymousEndpoints = new List<string> { "/allowed" };
28+
// Re-instantiate because options are passed in constructor
29+
var middleware = new AccessTokenValidatorMiddleware(_accessTokenServiceMock.Object, _jwtOptions);
30+
31+
var context = new DefaultHttpContext();
32+
context.Request.Path = "/allowed";
33+
var nextCalled = false;
34+
RequestDelegate next = (ctx) => { nextCalled = true; return Task.CompletedTask; };
35+
36+
// Act
37+
await middleware.InvokeAsync(context, next);
38+
39+
// Assert
40+
nextCalled.ShouldBeTrue();
41+
context.Response.StatusCode.ShouldBe(200); // Default
42+
_accessTokenServiceMock.Verify(x => x.IsTokenActiveAsync(), Times.Never);
43+
}
44+
45+
[Fact]
46+
public async Task InvokeAsync_Should_Call_Next_When_Token_Is_Active()
47+
{
48+
// Arrange
49+
_accessTokenServiceMock.Setup(x => x.IsTokenActiveAsync()).ReturnsAsync(true);
50+
var context = new DefaultHttpContext();
51+
context.Request.Path = "/protected";
52+
var nextCalled = false;
53+
RequestDelegate next = (ctx) => { nextCalled = true; return Task.CompletedTask; };
54+
55+
// Act
56+
await _middleware.InvokeAsync(context, next);
57+
58+
// Assert
59+
nextCalled.ShouldBeTrue();
60+
context.Response.StatusCode.ShouldBe(200);
61+
}
62+
63+
[Fact]
64+
public async Task InvokeAsync_Should_Return_Unauthorized_When_Token_Is_Not_Active()
65+
{
66+
// Arrange
67+
_accessTokenServiceMock.Setup(x => x.IsTokenActiveAsync()).ReturnsAsync(false);
68+
var context = new DefaultHttpContext();
69+
context.Request.Path = "/protected";
70+
var nextCalled = false;
71+
RequestDelegate next = (ctx) => { nextCalled = true; return Task.CompletedTask; };
72+
73+
// Act
74+
await _middleware.InvokeAsync(context, next);
75+
76+
// Assert
77+
nextCalled.ShouldBeFalse();
78+
context.Response.StatusCode.ShouldBe(401);
79+
}
80+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using Shouldly;
2+
using Xunit;
3+
4+
namespace Convey.Auth.Tests;
5+
6+
public class AuthAttributeTests
7+
{
8+
[Fact]
9+
public void Constructor_Should_Set_AuthenticationSchemes()
10+
{
11+
// Arrange & Act
12+
var attribute = new AuthAttribute("TestScheme");
13+
14+
// Assert
15+
attribute.AuthenticationSchemes.ShouldBe("TestScheme");
16+
}
17+
18+
[Fact]
19+
public void Constructor_Should_Set_Policy_When_Provided()
20+
{
21+
// Arrange & Act
22+
var attribute = new AuthAttribute("TestScheme", "TestPolicy");
23+
24+
// Assert
25+
attribute.Policy.ShouldBe("TestPolicy");
26+
attribute.AuthenticationSchemes.ShouldBe("TestScheme");
27+
}
28+
29+
[Fact]
30+
public void Constructor_Should_Set_Empty_Policy_When_Not_Provided()
31+
{
32+
// Arrange & Act
33+
var attribute = new AuthAttribute("TestScheme");
34+
35+
// Assert
36+
attribute.Policy.ShouldBe("");
37+
}
38+
39+
[Fact]
40+
public void Should_Be_AuthorizeAttribute()
41+
{
42+
// Arrange
43+
var attribute = new AuthAttribute("TestScheme");
44+
45+
// Act & Assert
46+
attribute.ShouldBeAssignableTo<Microsoft.AspNetCore.Authorization.AuthorizeAttribute>();
47+
}
48+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<Project Sdk="Microsoft.NET.Sdk" />

0 commit comments

Comments
 (0)