Skip to content
Merged
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
70 changes: 25 additions & 45 deletions Application.Tests.Unit/Articles/CreateArticleCommandHandlerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
using Application.Data;
using Application.Features.Articles.CreateArticle;
using Domain.Entities;
using MediatR;
using FluentValidation;
using MockQueryable.Moq;
using Slugify;
using FluentValidation.Results;

namespace Application.Tests.Unit.Articles;

Expand All @@ -13,7 +14,7 @@ public class CreateArticleCommandHandlerTests
private readonly Mock<IApplicationDbContext> _mockDbContext = new();
private readonly Mock<ISlugHelper> _mockSlugHelper = new();
private readonly Mock<ICurrentUserService> _mockCurrentUserService = new();
private readonly Mock<IPublisher> _mockPublisher = new();
private readonly Mock<IValidator<CreateArticleCommand>> _mockValidator = new();
private readonly CreateArticleCommandHandler _handler;

public CreateArticleCommandHandlerTests()
Expand All @@ -22,7 +23,7 @@ public CreateArticleCommandHandlerTests()
_mockDbContext.Object,
_mockSlugHelper.Object,
_mockCurrentUserService.Object,
_mockPublisher.Object
_mockValidator.Object
);
}

Expand All @@ -36,11 +37,13 @@ public async void Handle_Should_ReturnFailureResult_WhenGeneratedIdIsEmpty()

_mockDbContext.Setup(x => x.Articles).Returns(mockArticlesDbSet.Object);
_mockSlugHelper.Setup(x => x.GenerateSlug(" ")).Returns("");
_mockValidator.Setup(v => v.Validate(It.IsAny<CreateArticleCommand>()))
.Returns(new ValidationResult());

var command = new CreateArticleCommand(" ", "Lorem ipsum", "", []);

//Act
var result = await _handler.Handle(command, default);
var result = await _handler.HandleAsync(command, default);

//Assert
result.IsError.Should().BeTrue();
Expand All @@ -57,11 +60,13 @@ public async void Handle_Should_NotCallSaveChangesAsync_WhenGeneratedIdIsEmpty()

_mockDbContext.Setup(x => x.Articles).Returns(mockArticlesDbSet.Object);
_mockSlugHelper.Setup(x => x.GenerateSlug(" ")).Returns("");
_mockValidator.Setup(v => v.Validate(It.IsAny<CreateArticleCommand>()))
.Returns(new ValidationResult());

var command = new CreateArticleCommand(" ", "Lorem ipsum", "", []);

//Act
await _handler.Handle(command, default);
await _handler.HandleAsync(command, default);

//Assert
_mockDbContext.Verify(x => x.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Never);
Expand All @@ -77,11 +82,13 @@ public async void Handle_Should_ReturnFailureResult_WhenGeneratedIdIsNotUnique()

_mockDbContext.Setup(x => x.Articles).Returns(mockArticlesDbSet.Object);
_mockSlugHelper.Setup(x => x.GenerateSlug("something cool")).Returns("something-cool");
_mockValidator.Setup(v => v.Validate(It.IsAny<CreateArticleCommand>()))
.Returns(new ValidationResult());

var command = new CreateArticleCommand("something cool", "Lorem ipsum", "", []);

//Act
var result = await _handler.Handle(command, default);
var result = await _handler.HandleAsync(command, default);

//Assert
result.IsError.Should().BeTrue();
Expand All @@ -98,11 +105,13 @@ public async void Handle_Should_NotCallSaveChangesAsync_WhenGeneratedIdIsNotUniq

_mockDbContext.Setup(x => x.Articles).Returns(mockArticlesDbSet.Object);
_mockSlugHelper.Setup(x => x.GenerateSlug("something cool")).Returns("something-cool");
_mockValidator.Setup(v => v.Validate(It.IsAny<CreateArticleCommand>()))
.Returns(new ValidationResult());

var command = new CreateArticleCommand("something cool", "Lorem ipsum", "", []);

//Act
var result = await _handler.Handle(command, default);
var result = await _handler.HandleAsync(command, default);

//Assert
_mockDbContext.Verify(x => x.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Never);
Expand All @@ -126,10 +135,12 @@ public async void Handle_Should_CallSaveChangesAsync_WhenGeneratedIdIsUnique()
_mockDbContext.Setup(x => x.Categories).Returns(mockCategoriesDbSet.Object);
_mockDbContext.Setup(x => x.Revisions).Returns(mockRevisionsDbSet.Object);
_mockSlugHelper.Setup(x => x.GenerateSlug("something extra cool")).Returns("something-extra-cool");
_mockValidator.Setup(v => v.Validate(It.IsAny<CreateArticleCommand>()))
.Returns(new ValidationResult());
var command = new CreateArticleCommand("something extra cool", "Lorem ipsum", "", []);

//Act
await _handler.Handle(command, default);
await _handler.HandleAsync(command, default);

//Assert
_mockDbContext.Verify(x => x.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Once);
Expand All @@ -153,11 +164,13 @@ public async void Handle_Should_ReturnSuccessResultWithArticleId_WhenGeneratedId
_mockDbContext.Setup(x => x.Categories).Returns(mockCategoriesDbSet.Object);
_mockDbContext.Setup(x => x.Revisions).Returns(mockRevisionsDbSet.Object);
_mockSlugHelper.Setup(x => x.GenerateSlug("something extra cool")).Returns("something-extra-cool");
_mockValidator.Setup(v => v.Validate(It.IsAny<CreateArticleCommand>()))
.Returns(new ValidationResult());

var command = new CreateArticleCommand("something extra cool", "Lorem ipsum", "", []);

//Act
var result = await _handler.Handle(command, default);
var result = await _handler.HandleAsync(command, default);

//Assert
_mockDbContext.Verify(x => x.Articles.AddAsync(
Expand Down Expand Up @@ -194,12 +207,14 @@ public async void Handle_Should_CreateRevisionWithContent_WhenGeneratedIdIsUniqu
_mockDbContext.Setup(x => x.Revisions).Returns(mockRevisionsDbSet.Object);
_mockSlugHelper.Setup(x => x.GenerateSlug("something extra cool")).Returns("something-extra-cool");
_mockCurrentUserService.Setup(x => x.UserId).Returns("test-user-id");
_mockValidator.Setup(v => v.Validate(It.IsAny<CreateArticleCommand>()))
.Returns(new ValidationResult());
var expectedCategoryIds = new List<string> {"category1", "category2"};
var command = new CreateArticleCommand("something extra cool", "Lorem ipsum", "",
["category1", "category2", "category3"]);

//Act
var result = await _handler.Handle(command, default);
var result = await _handler.HandleAsync(command, default);

//Assert
_mockDbContext.Verify(x => x.Revisions.AddAsync(
Expand All @@ -215,39 +230,4 @@ public async void Handle_Should_CreateRevisionWithContent_WhenGeneratedIdIsUniqu
Times.Once
);
}

[Fact]
public async void Handle_Should_PublishArticleCreatedEvent_WhenGeneratedIdIsUnique()
{
//Arrange
var mockArticlesDbSet = new List<Article> {new() {Id = "something-cool", Title = "something-cool"}}
.AsQueryable()
.BuildMockDbSet();
var mockCategoriesDbSet = new List<Category>()
.AsQueryable()
.BuildMockDbSet();
var mockRevisionsDbSet = new List<Revision>()
.AsQueryable()
.BuildMockDbSet();

_mockDbContext.Setup(x => x.Articles).Returns(mockArticlesDbSet.Object);
_mockDbContext.Setup(x => x.Categories).Returns(mockCategoriesDbSet.Object);
_mockDbContext.Setup(x => x.Revisions).Returns(mockRevisionsDbSet.Object);
_mockSlugHelper.Setup(x => x.GenerateSlug("something extra cool")).Returns("something-extra-cool");
_mockCurrentUserService.Setup(x => x.UserId).Returns("test-user-id");
var command = new CreateArticleCommand("something extra cool", "Lorem ipsum", "", []);
var expectedCategoryIds = new List<string> {"category1", "category2"};

//Act
var result = await _handler.Handle(command, default);

//Assert
_mockPublisher.Verify(
x => x.Publish(
It.Is<ArticleCreatedEvent>(e => e.Id == result.Value.Id),
It.IsAny<CancellationToken>()
),
Times.Once
);
}
}
1 change: 0 additions & 1 deletion Application/Application.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
<PackageReference Include="ErrorOr" Version="1.9.0"/>
<PackageReference Include="FluentValidation" Version="11.9.0"/>
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.9.0"/>
<PackageReference Include="MediatR" Version="12.2.0"/>
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="8.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
16 changes: 0 additions & 16 deletions Application/Common/Behaviours/QueryCachingBehavior.cs

This file was deleted.

52 changes: 0 additions & 52 deletions Application/Common/Behaviours/ValidationBehavior.cs

This file was deleted.

6 changes: 3 additions & 3 deletions Application/Common/Messaging/ICachedQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ namespace Application.Common.Messaging;
public interface ICachedQuery<TResponse> : IQuery<TResponse>, ICachedQuery;

public interface ICachedQuery
{
{
string Key { get; }

TimeSpan? Expiration { get; }

bool IgnoreCaching { get; }
}
3 changes: 3 additions & 0 deletions Application/Common/Messaging/ICommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace Application.Common.Messaging;

public interface ICommand<TResult>;
8 changes: 8 additions & 0 deletions Application/Common/Messaging/ICommandHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Application.Common.Messaging;

using ErrorOr;

public interface ICommandHandler<in TCommand, TResult> where TCommand : ICommand<TResult>
{
Task<ErrorOr<TResult>> HandleAsync(TCommand command, CancellationToken cancellationToken = default);
}
5 changes: 1 addition & 4 deletions Application/Common/Messaging/IQuery.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
using ErrorOr;
using MediatR;

namespace Application.Common.Messaging;

public interface IQuery<TResponse> : IRequest<ErrorOr<TResponse>>;
public interface IQuery<TResult>;
8 changes: 8 additions & 0 deletions Application/Common/Messaging/IQueryHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Application.Common.Messaging;

using ErrorOr;

public interface IQueryHandler<in TQuery, TResult> where TQuery : IQuery<TResult>
{
Task<ErrorOr<TResult>> HandleAsync(TQuery query, CancellationToken cancellationToken = default);
}
24 changes: 24 additions & 0 deletions Application/Common/Utils/CachingHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Application.Common.Caching;
using Application.Common.Messaging;

namespace Application.Common.Utils;

public static class CachingHelper
{
public static async Task<TResponse> GetOrCacheAsync<TRequest, TResponse>(
ICacheService cacheService,
TRequest request,
Func<Task<TResponse>> dataRetriever,
CancellationToken token)
where TRequest : ICachedQuery
{
if (request.IgnoreCaching)
return await dataRetriever();

return await cacheService.GetOrCreateAsync(
request.Key,
_ => dataRetriever(),
request.Expiration,
token);
}
}
16 changes: 16 additions & 0 deletions Application/Common/Utils/ValidatorHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using ErrorOr;
using FluentValidation;

namespace Application.Common.Utils;

public static class ValidatorHelper
{
public static ErrorOr<Success> Validate<TRequest>(IValidator<TRequest> validator, TRequest request)
{
var validationResult = validator.Validate(request);

return validationResult.IsValid
? Result.Success
: validationResult.Errors.ConvertAll(e => Error.Validation(e.PropertyName, e.ErrorMessage));
}
}
Loading
Loading