280 lines
12 KiB
C#
280 lines
12 KiB
C#
using Guestbooky.API.Controllers;
|
|
using Guestbooky.API.DTOs.Messages;
|
|
using Guestbooky.Application.UseCases.CountGuestbookMessages;
|
|
using Guestbooky.Application.UseCases.DeleteGuestbookMessage;
|
|
using Guestbooky.Application.UseCases.InsertGuestMessage;
|
|
using Guestbooky.Application.UseCases.ListGuestbookMessages;
|
|
using MediatR;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.Extensions.Logging;
|
|
using Moq;
|
|
|
|
namespace Guestbooky.UnitTests.API.Controllers;
|
|
|
|
public class MessageControllerTests
|
|
{
|
|
private readonly Mock<IMediator> _mediatorMock;
|
|
private readonly Mock<ILogger<MessageController>> _loggerMock;
|
|
|
|
public MessageControllerTests()
|
|
{
|
|
_mediatorMock = new Mock<IMediator>();
|
|
_loggerMock = new Mock<ILogger<MessageController>>();
|
|
}
|
|
|
|
#region Insert()
|
|
[Fact]
|
|
public async Task Insert_ReturnsCreated_WhenValidMessageSent()
|
|
{
|
|
// Arrange
|
|
var controller = new MessageController(_mediatorMock.Object, _loggerMock.Object);
|
|
|
|
var requestDto = new InsertMessageRequestDto() { Author = "tester", Message = "message", CaptchaResponse = "validCaptcha" };
|
|
|
|
_mediatorMock.Setup(m => m.Send(It.IsAny<InsertGuestbookMessageCommand>(), default))
|
|
.ReturnsAsync(new InsertGuestbookMessageResult(true, string.Empty));
|
|
|
|
// Act
|
|
var result = await controller.Insert(requestDto, default);
|
|
|
|
// Assert
|
|
Assert.IsType<CreatedResult>(result);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Insert_ReturnsForbidden_WhenInvalidInputProvided()
|
|
{
|
|
// Arrange
|
|
var controller = new MessageController(_mediatorMock.Object, _loggerMock.Object);
|
|
|
|
var requestDto = new InsertMessageRequestDto() { Author = "tester", Message = "message", CaptchaResponse = "invalidCaptcha" };
|
|
|
|
_mediatorMock.Setup(m => m.Send(It.IsAny<InsertGuestbookMessageCommand>(), default))
|
|
.ReturnsAsync(new InsertGuestbookMessageResult(false, "An invalid captcha has been provided"));
|
|
|
|
// Act
|
|
var result = await controller.Insert(requestDto, default);
|
|
|
|
// Assert
|
|
Assert.NotNull(result);
|
|
var objectResult = Assert.IsType<ObjectResult>(result);
|
|
Assert.Equal(403, objectResult.StatusCode);
|
|
var problemDetails = Assert.IsType<ProblemDetails>(objectResult.Value);
|
|
Assert.Equal("An invalid captcha has been provided", problemDetails.Detail);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Insert_ReturnsProblemDetails_WhenExceptionIsThrown()
|
|
{
|
|
// Arrange
|
|
var controller = new MessageController(_mediatorMock.Object, _loggerMock.Object);
|
|
|
|
var requestDto = new InsertMessageRequestDto() { Author = "tester", Message = "message", CaptchaResponse = "invalidCaptcha" };
|
|
|
|
_mediatorMock.Setup(m => m.Send(It.IsAny<InsertGuestbookMessageCommand>(), default))
|
|
.ThrowsAsync(new Exception());
|
|
|
|
// Act
|
|
var result = await controller.Insert(requestDto, default);
|
|
|
|
// Assert
|
|
Assert.NotNull(result);
|
|
var objectResult = Assert.IsType<ObjectResult>(result);
|
|
Assert.Equal(500, objectResult.StatusCode);
|
|
var problemDetails = Assert.IsType<ProblemDetails>(objectResult.Value);
|
|
Assert.StartsWith("An error occurred on the server:", problemDetails.Detail);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Get
|
|
[Fact]
|
|
public async Task Get_ReturnsMessages_WhenRangeValueSent()
|
|
{
|
|
// Arrange
|
|
var httpContextMock = new Mock<HttpContext>();
|
|
var httpResponseMock = new Mock<HttpResponse>();
|
|
var headers = new HeaderDictionary();
|
|
|
|
httpResponseMock.SetupProperty(r => r.StatusCode);
|
|
httpResponseMock.SetupGet(r => r.Headers).Returns(headers);
|
|
httpContextMock.SetupGet(h => h.Response).Returns(httpResponseMock.Object);
|
|
|
|
var controller = new MessageController(_mediatorMock.Object, _loggerMock.Object)
|
|
{
|
|
ControllerContext = new ControllerContext()
|
|
{
|
|
HttpContext = httpContextMock.Object
|
|
}
|
|
};
|
|
|
|
var requestedRange = new System.Net.Http.Headers.RangeItemHeaderValue(1, 3);
|
|
List<GuestbookMessageQueryResult> availableMessages = [
|
|
new GuestbookMessageQueryResult("test-id1", "test-author", "test-message1", DateTimeOffset.UnixEpoch.AddDays(1)),
|
|
new GuestbookMessageQueryResult("test-id2", "test-author", "test-message2", DateTimeOffset.UnixEpoch.AddDays(2)),
|
|
new GuestbookMessageQueryResult("test-id3", "test-author", "test-message3", DateTimeOffset.UnixEpoch.AddDays(3)),
|
|
new GuestbookMessageQueryResult("test-id4", "test-author", "test-message4", DateTimeOffset.UnixEpoch.AddDays(4)),
|
|
new GuestbookMessageQueryResult("test-id5", "test-author", "test-message5", DateTimeOffset.UnixEpoch.AddDays(5)),
|
|
];
|
|
var intendedMessages = availableMessages.Skip((int)requestedRange.From! - 1).Take((int)requestedRange.From! - 1 + (int)requestedRange.To!);
|
|
|
|
var requestDto = new GetMessagesRequestDto() { Range = new System.Net.Http.Headers.RangeHeaderValue() { Unit = "messages" } };
|
|
requestDto.Range.Ranges.Add(requestedRange);
|
|
|
|
_mediatorMock.Setup(m => m.Send(It.IsAny<ListGuestbookMessagesQuery>(), default))
|
|
.ReturnsAsync(new ListGuestbookMessagesQueryResult(intendedMessages));
|
|
_mediatorMock.Setup(m => m.Send(It.IsAny<CountGuestbookMessagesQuery>(), default))
|
|
.ReturnsAsync(new CountGuestbookMessagesQueryResult(availableMessages.Count));
|
|
|
|
// Act
|
|
var result = await controller.Get(requestDto, default);
|
|
|
|
// Assert
|
|
Assert.NotNull(result);
|
|
var objectResult = Assert.IsType<ObjectResult>(result);
|
|
Assert.IsAssignableFrom<IEnumerable<GetMessagesResponseDto>>(objectResult.Value);
|
|
|
|
httpResponseMock.VerifySet(r => r.StatusCode = StatusCodes.Status206PartialContent);
|
|
Assert.Equal("messages", headers["Accept-Ranges"]);
|
|
Assert.Contains("messages", headers["Content-Range"].ToString());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Get_ReturnsMessages_WithNoRange()
|
|
{
|
|
// Arrange
|
|
var httpContextMock = new Mock<HttpContext>();
|
|
var httpResponseMock = new Mock<HttpResponse>();
|
|
var headers = new HeaderDictionary();
|
|
|
|
httpResponseMock.SetupProperty(r => r.StatusCode);
|
|
httpResponseMock.SetupGet(r => r.Headers).Returns(headers);
|
|
httpContextMock.SetupGet(h => h.Response).Returns(httpResponseMock.Object);
|
|
|
|
var controller = new MessageController(_mediatorMock.Object, _loggerMock.Object)
|
|
{
|
|
ControllerContext = new ControllerContext()
|
|
{
|
|
HttpContext = httpContextMock.Object
|
|
}
|
|
};
|
|
|
|
List<GuestbookMessageQueryResult> availableMessages = [
|
|
new GuestbookMessageQueryResult("test-id1", "test-author", "test-message1", DateTimeOffset.UnixEpoch.AddDays(1)),
|
|
new GuestbookMessageQueryResult("test-id2", "test-author", "test-message2", DateTimeOffset.UnixEpoch.AddDays(2)),
|
|
new GuestbookMessageQueryResult("test-id3", "test-author", "test-message3", DateTimeOffset.UnixEpoch.AddDays(3)),
|
|
new GuestbookMessageQueryResult("test-id4", "test-author", "test-message4", DateTimeOffset.UnixEpoch.AddDays(4)),
|
|
new GuestbookMessageQueryResult("test-id5", "test-author", "test-message5", DateTimeOffset.UnixEpoch.AddDays(5)),
|
|
];
|
|
var intendedMessages = availableMessages.Skip(0).Take(50);
|
|
|
|
var requestDto = new GetMessagesRequestDto() { Range = null! };
|
|
|
|
_mediatorMock.Setup(m => m.Send(It.IsAny<ListGuestbookMessagesQuery>(), default))
|
|
.ReturnsAsync(new ListGuestbookMessagesQueryResult(intendedMessages));
|
|
_mediatorMock.Setup(m => m.Send(It.IsAny<CountGuestbookMessagesQuery>(), default))
|
|
.ReturnsAsync(new CountGuestbookMessagesQueryResult(availableMessages.Count));
|
|
|
|
// Act
|
|
var result = await controller.Get(requestDto, default);
|
|
|
|
// Assert
|
|
Assert.NotNull(result);
|
|
var objectResult = Assert.IsType<ObjectResult>(result);
|
|
Assert.IsAssignableFrom<IEnumerable<GetMessagesResponseDto>>(objectResult.Value);
|
|
|
|
httpResponseMock.VerifySet(r => r.StatusCode = StatusCodes.Status206PartialContent);
|
|
Assert.Equal("messages", headers["Accept-Ranges"]);
|
|
Assert.Contains("messages", headers["Content-Range"].ToString());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Get_ReturnsProblemDetails_WhenExceptionIsThrown()
|
|
{
|
|
// Arrange
|
|
var controller = new MessageController(_mediatorMock.Object, _loggerMock.Object);
|
|
|
|
var requestDto = new GetMessagesRequestDto() { Range = null! };
|
|
|
|
_mediatorMock.Setup(m => m.Send(It.IsAny<ListGuestbookMessagesQuery>(), default))
|
|
.ThrowsAsync(new Exception());
|
|
|
|
// Act
|
|
var result = await controller.Get(requestDto, default);
|
|
|
|
// Assert
|
|
Assert.NotNull(result);
|
|
var objectResult = Assert.IsType<ObjectResult>(result);
|
|
Assert.Equal(500, objectResult.StatusCode);
|
|
var problemDetails = Assert.IsType<ProblemDetails>(objectResult.Value);
|
|
Assert.StartsWith("An error occurred on the server:", problemDetails.Detail);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Delete
|
|
[Fact]
|
|
public async Task Delete_ReturnsOk_WithValidMessage()
|
|
{
|
|
// Arrange
|
|
var controller = new MessageController(_mediatorMock.Object, _loggerMock.Object);
|
|
|
|
var requestDto = new DeleteMessageRequestDto() { Id = "test_id" };
|
|
|
|
_mediatorMock.Setup(m => m.Send(It.IsAny<DeleteGuestbookMessageCommand>(), default))
|
|
.ReturnsAsync(new DeleteGuestbookMessageResult(true));
|
|
|
|
// Act
|
|
var result = await controller.Delete(requestDto, default);
|
|
|
|
// Assert
|
|
Assert.IsType<OkResult>(result);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Delete_ReturnsForbidden_WhenInvalidInputProvided()
|
|
{
|
|
// Arrange
|
|
var controller = new MessageController(_mediatorMock.Object, _loggerMock.Object);
|
|
|
|
var requestDto = new DeleteMessageRequestDto() { Id = "invalid_test_id" };
|
|
|
|
_mediatorMock.Setup(m => m.Send(It.IsAny<DeleteGuestbookMessageCommand>(), default))
|
|
.ReturnsAsync(new DeleteGuestbookMessageResult(false));
|
|
|
|
// Act
|
|
var result = await controller.Delete(requestDto, default);
|
|
|
|
// Assert
|
|
Assert.NotNull(result);
|
|
var objectResult = Assert.IsType<ObjectResult>(result);
|
|
Assert.Equal(500, objectResult.StatusCode);
|
|
var problemDetails = Assert.IsType<ProblemDetails>(objectResult.Value);
|
|
Assert.StartsWith("Could not delete the guestbook entry", problemDetails.Detail);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Delete_ReturnsProblemDetails_WhenExceptionIsThrown()
|
|
{
|
|
// Arrange
|
|
var controller = new MessageController(_mediatorMock.Object, _loggerMock.Object);
|
|
|
|
var requestDto = new DeleteMessageRequestDto() { Id = "invalid_test_id" };
|
|
|
|
_mediatorMock.Setup(m => m.Send(It.IsAny<DeleteGuestbookMessageCommand>(), default))
|
|
.ThrowsAsync(new Exception());
|
|
|
|
// Act
|
|
var result = await controller.Delete(requestDto, default);
|
|
|
|
// Assert
|
|
Assert.NotNull(result);
|
|
var objectResult = Assert.IsType<ObjectResult>(result);
|
|
Assert.Equal(500, objectResult.StatusCode);
|
|
var problemDetails = Assert.IsType<ProblemDetails>(objectResult.Value);
|
|
Assert.StartsWith("An error occurred on the server:", problemDetails.Detail);
|
|
}
|
|
#endregion
|
|
|
|
}
|