diff --git a/Streetcode/Streetcode.BLL/MediatR/Timeline/TimelineItem/GetById/GetTimelineItemByIdHandler.cs b/Streetcode/Streetcode.BLL/MediatR/Timeline/TimelineItem/GetById/GetTimelineItemByIdHandler.cs index 470b9e1..e30df2e 100644 --- a/Streetcode/Streetcode.BLL/MediatR/Timeline/TimelineItem/GetById/GetTimelineItemByIdHandler.cs +++ b/Streetcode/Streetcode.BLL/MediatR/Timeline/TimelineItem/GetById/GetTimelineItemByIdHandler.cs @@ -25,7 +25,7 @@ public async Task> Handle(GetTimelineItemByIdQuery reque { var timelineItem = await _repositoryWrapper.TimelineRepository .GetFirstOrDefaultAsync( - predicate: ti => true, + predicate: ti => ti.Id == request.Id, include: ti => ti .Include(til => til.HistoricalContextTimelines) .ThenInclude(x => x.HistoricalContext)!); diff --git a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Timeline/TimelineItem/GetAll/GetAllTimelineItemHandlerTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Timeline/TimelineItem/GetAll/GetAllTimelineItemHandlerTests.cs new file mode 100644 index 0000000..fc39c6e --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Timeline/TimelineItem/GetAll/GetAllTimelineItemHandlerTests.cs @@ -0,0 +1,214 @@ +// +// Copyright (c) PlaceholderCompany. All rights reserved. +// +namespace Streetcode.XUnitTest.BLL.MediatR.Timeline.TimelineItem.GetAll +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + using AutoMapper; + using Microsoft.EntityFrameworkCore.Query; + using Moq; + using Streetcode.BLL.DTO.Timeline; + using Streetcode.BLL.Interfaces.Logging; + using Streetcode.BLL.MediatR.Timeline.TimelineItem.GetAll; + using Streetcode.DAL.Entities.Timeline; + using Streetcode.DAL.Enums; + using Streetcode.DAL.Repositories.Interfaces.Base; + using Streetcode.DAL.Repositories.Interfaces.Timeline; + using Xunit; + + /// + /// Unit tests for GetAllTimelineItemsHandler. + /// + public class GetAllTimelineItemsHandlerTests + { + private readonly Mock repoWrapperMock; + private readonly Mock timelineRepoMock; + private readonly Mock mapperMock; + private readonly Mock loggerMock; + + private readonly GetAllTimelineItemsHandler handler; + + /// + /// Initializes a new instance of the class. + /// + public GetAllTimelineItemsHandlerTests() + { + this.repoWrapperMock = new Mock(); + this.timelineRepoMock = new Mock(); + this.mapperMock = new Mock(); + this.loggerMock = new Mock(); + + this.repoWrapperMock + .Setup(x => x.TimelineRepository) + .Returns(this.timelineRepoMock.Object); + + this.handler = new GetAllTimelineItemsHandler( + this.repoWrapperMock.Object, + this.mapperMock.Object, + this.loggerMock.Object); + } + + /// + /// Should return timeline item DTO collection when items exist. + /// + /// A task representing the asynchronous operation. + [Fact] + public async Task Handle_ShouldReturnTimelineItemDtos_WhenItemsExist() + { + var query = new GetAllTimelineItemsQuery(); + + var timelineItems = new List + { + new TimelineItem + { + Id = 1, + Title = "Test Title", + Description = "Description", + Date = new DateTime(2020, 01, 01), + DateViewPattern = DateViewPattern.Year, + HistoricalContextTimelines = new List + { + new HistoricalContextTimeline + { + HistoricalContext = new HistoricalContext + { + Id = 1, + Title = "Historical Context 1", + }, + }, + }, + }, + }; + + var expectedDtos = new List + { + new TimelineItemDTO + { + Id = 1, + Title = "Test Title", + Description = "Description", + Date = new DateTime(2020, 01, 01), + DateViewPattern = DateViewPattern.Year, + HistoricalContexts = new List + { + new HistoricalContextDTO + { + Id = 1, + Title = "Historical Context 1", + }, + }, + }, + }; + + this.timelineRepoMock + .Setup(r => r.GetAllAsync( + null, + It.IsAny, IIncludableQueryable>>())) + .ReturnsAsync(timelineItems); + + this.mapperMock + .Setup(m => m.Map>(It.IsAny>())) + .Returns(expectedDtos); + + var result = await this.handler.Handle(query, CancellationToken.None); + + Assert.True(result.IsSuccess); + Assert.NotNull(result.Value); + Assert.Single(result.Value); + + var item = result.Value.First(); + + Assert.Equal("Test Title", item.Title); + Assert.Equal(1, item.Id); + Assert.Equal("Description", item.Description); + + this.mapperMock.Verify( + m => m.Map>( + It.IsAny>()), + Times.Once); + + this.timelineRepoMock.Verify( + r => r.GetAllAsync( + null, + It.IsAny, + IIncludableQueryable>>()), + Times.Once); + } + + /// + /// Should throw exception when repository fails. + /// + /// A task representing the asynchronous operation. + [Fact] + public async Task Handle_ShouldPropagateException_WhenRepositoryThrows() + { + var query = new GetAllTimelineItemsQuery(); + + this.timelineRepoMock + .Setup(r => r.GetAllAsync( + null, + It.IsAny, + IIncludableQueryable>>())) + .ThrowsAsync(new Exception("Database failure")); + + var exception = await Assert.ThrowsAsync(() => + this.handler.Handle(query, CancellationToken.None)); + + Assert.Equal("Database failure", exception.Message); + + this.mapperMock.Verify( + m => m.Map>( + It.IsAny>()), + Times.Never); + + this.loggerMock.Verify( + l => l.LogError( + It.IsAny(), + It.IsAny()), + Times.Never); + } + + /// + /// Should return empty collection when no timeline items exist. + /// + /// A task representing the asynchronous operation. + [Fact] + public async Task Handle_ShouldReturnEmptyCollection_WhenNoItemsExist() + { + var query = new GetAllTimelineItemsQuery(); + + this.timelineRepoMock + .Setup(r => r.GetAllAsync( + null, + It.IsAny, + IIncludableQueryable>>())) + .ReturnsAsync(new List()); + + this.mapperMock + .Setup(m => m.Map>( + It.IsAny>())) + .Returns(new List()); + + var result = await this.handler.Handle(query, CancellationToken.None); + + Assert.True(result.IsSuccess); + Assert.NotNull(result.Value); + Assert.Empty(result.Value); + + this.mapperMock.Verify( + m => m.Map>( + It.IsAny>()), + Times.Once); + + this.timelineRepoMock.Verify( + r => r.GetAllAsync( + null, + It.IsAny, + IIncludableQueryable>>()), + Times.Once); + } + } +} diff --git a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Timeline/TimelineItem/GetById/GetTimelineItemByIdHandlerTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Timeline/TimelineItem/GetById/GetTimelineItemByIdHandlerTests.cs new file mode 100644 index 0000000..705cefe --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Timeline/TimelineItem/GetById/GetTimelineItemByIdHandlerTests.cs @@ -0,0 +1,229 @@ +// +// Copyright (c) PlaceholderCompany. All rights reserved. +// +namespace Streetcode.XUnitTest.BLL.MediatR.Timeline.TimelineItem.GetById +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Linq.Expressions; + using System.Threading.Tasks; + using AutoMapper; + using Microsoft.EntityFrameworkCore.Query; + using Moq; + using Streetcode.BLL.DTO.Timeline; + using Streetcode.BLL.Interfaces.Logging; + using Streetcode.BLL.MediatR.Timeline.TimelineItem.GetById; + using Streetcode.DAL.Entities.Timeline; + using Streetcode.DAL.Enums; + using Streetcode.DAL.Repositories.Interfaces.Base; + using Streetcode.DAL.Repositories.Interfaces.Timeline; + using Xunit; + + /// + /// Unit tests for GetTimelineItemByIdHandler. + /// + public class GetTimelineItemByIdHandlerTests + { + private readonly Mock repoWrapperMock; + private readonly Mock timelineRepoMock; + private readonly Mock mapperMock; + private readonly Mock loggerMock; + + private readonly GetTimelineItemByIdHandler handler; + + /// + /// Initializes a new instance of the class. + /// + public GetTimelineItemByIdHandlerTests() + { + this.repoWrapperMock = new Mock(); + this.timelineRepoMock = new Mock(); + this.mapperMock = new Mock(); + this.loggerMock = new Mock(); + + this.repoWrapperMock + .Setup(x => x.TimelineRepository) + .Returns(this.timelineRepoMock.Object); + + this.handler = new GetTimelineItemByIdHandler( + this.repoWrapperMock.Object, + this.mapperMock.Object, + this.loggerMock.Object); + } + + /// + /// Should return TimelineItemDTO when item exists. + /// + /// A task representing the asynchronous operation. + [Fact] + public async Task Handle_ShouldReturnTimelineItemDTO_WhenItemExists() + { + var query = new GetTimelineItemByIdQuery(1); + + var timelineItem = new TimelineItem + { + Id = 1, + Title = "Test Title", + Description = "Description", + Date = new DateTime(2020, 01, 01), + DateViewPattern = DateViewPattern.Year, + HistoricalContextTimelines = new List + { + new HistoricalContextTimeline + { + HistoricalContext = new HistoricalContext + { + Id = 1, + Title = "Historical Context 1", + }, + }, + }, + }; + + var expectedDto = new TimelineItemDTO + { + Id = 1, + Title = "Test Title", + Description = "Description", + Date = new DateTime(2020, 01, 01), + DateViewPattern = DateViewPattern.Year, + HistoricalContexts = new List + { + new HistoricalContextDTO + { + Id = 1, + Title = "Historical Context 1", + }, + }, + }; + + this.timelineRepoMock + .Setup(r => r.GetFirstOrDefaultAsync( + It.IsAny>>(), + It.IsAny, + IIncludableQueryable>>())) + .ReturnsAsync(timelineItem); + + this.mapperMock + .Setup(m => m.Map( + It.IsAny())) + .Returns(expectedDto); + + var result = await this.handler.Handle(query, CancellationToken.None); + + Assert.True(result.IsSuccess); + Assert.NotNull(result.Value); + + Assert.Equal(expectedDto.Id, result.Value.Id); + Assert.Equal(expectedDto.Title, result.Value.Title); + Assert.Equal(expectedDto.Description, result.Value.Description); + Assert.Equal(expectedDto.Date, result.Value.Date); + Assert.Equal(expectedDto.DateViewPattern, result.Value.DateViewPattern); + + Assert.Single(result.Value.HistoricalContexts); + + Assert.Equal( + expectedDto.HistoricalContexts.First().Title, + result.Value.HistoricalContexts.First().Title); + + this.mapperMock.Verify( + m => m.Map( + It.IsAny()), + Times.Once); + + this.timelineRepoMock.Verify( + r => r.GetFirstOrDefaultAsync( + It.IsAny>>(), + It.IsAny, + IIncludableQueryable>>()), + Times.Once); + } + + /// + /// Should return failure when item not found. + /// + /// A task representing the asynchronous operation. + [Fact] + public async Task Handle_ShouldReturnFail_WhenItemNotFound() + { + var query = new GetTimelineItemByIdQuery(1); + + var expectedErrorMessage = + $"Cannot find a timeline item with corresponding id: {query.Id}"; + + this.timelineRepoMock + .Setup(r => r.GetFirstOrDefaultAsync( + It.IsAny>>(), + It.IsAny, + IIncludableQueryable>>())) + .ReturnsAsync((TimelineItem?)null); + + var result = await this.handler.Handle(query, CancellationToken.None); + + Assert.True(result.IsFailed); + + Assert.Equal( + expectedErrorMessage, + result.Errors.First().Message); + + this.loggerMock.Verify( + l => l.LogError( + It.Is(q => q.Id == query.Id), + expectedErrorMessage), + Times.Once); + + this.mapperMock.Verify( + m => m.Map( + It.IsAny()), + Times.Never); + + this.timelineRepoMock.Verify( + r => r.GetFirstOrDefaultAsync( + It.IsAny>>(), + It.IsAny, + IIncludableQueryable>>()), + Times.Once); + } + + /// + /// Should throw exception when repository fails. + /// + /// A task representing the asynchronous operation. + [Fact] + public async Task Handle_ShouldPropagateException_WhenRepositoryThrows() + { + var query = new GetTimelineItemByIdQuery(1); + + this.timelineRepoMock + .Setup(r => r.GetFirstOrDefaultAsync( + It.IsAny>>(), + It.IsAny, + IIncludableQueryable>>())) + .ThrowsAsync(new Exception("Database failure")); + + var exception = await Assert.ThrowsAsync(() => + this.handler.Handle(query, CancellationToken.None)); + + Assert.Equal("Database failure", exception.Message); + + this.mapperMock.Verify( + m => m.Map( + It.IsAny()), + Times.Never); + + this.loggerMock.Verify( + l => l.LogError( + It.IsAny(), + It.IsAny()), + Times.Never); + + this.timelineRepoMock.Verify( + r => r.GetFirstOrDefaultAsync( + It.IsAny>>(), + It.IsAny, + IIncludableQueryable>>()), + Times.Once); + } + } +} diff --git a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Timeline/TimelineItem/GetByStreetcodeId/GetTimelineItemByStreetcodeIdHandlerTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Timeline/TimelineItem/GetByStreetcodeId/GetTimelineItemByStreetcodeIdHandlerTests.cs new file mode 100644 index 0000000..4c5317e --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Timeline/TimelineItem/GetByStreetcodeId/GetTimelineItemByStreetcodeIdHandlerTests.cs @@ -0,0 +1,232 @@ +// +// Copyright (c) PlaceholderCompany. All rights reserved. +// + +namespace Streetcode.XUnitTest.BLL.MediatR.Timeline.TimelineItem.GetByStreetcodeId +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Linq.Expressions; + using System.Threading.Tasks; + using AutoMapper; + using Microsoft.EntityFrameworkCore.Query; + using Moq; + using Streetcode.BLL.DTO.Timeline; + using Streetcode.BLL.Interfaces.Logging; + using Streetcode.BLL.MediatR.Timeline.TimelineItem.GetByStreetcodeId; + using Streetcode.DAL.Entities.Timeline; + using Streetcode.DAL.Enums; + using Streetcode.DAL.Repositories.Interfaces.Base; + using Streetcode.DAL.Repositories.Interfaces.Timeline; + using Xunit; + + /// + /// Unit tests for GetTimelineItemsByStreetcodeIdHandler. + /// + public class GetTimelineItemByStreetcodeIdHandlerTests + { + private readonly Mock repoWrapperMock; + private readonly Mock timelineRepoMock; + private readonly Mock mapperMock; + private readonly Mock loggerMock; + + private readonly GetTimelineItemsByStreetcodeIdHandler handler; + + /// + /// Initializes a new instance of the class. + /// + public GetTimelineItemByStreetcodeIdHandlerTests() + { + this.repoWrapperMock = new Mock(); + this.timelineRepoMock = new Mock(); + this.mapperMock = new Mock(); + this.loggerMock = new Mock(); + + this.repoWrapperMock + .Setup(x => x.TimelineRepository) + .Returns(this.timelineRepoMock.Object); + + this.handler = new GetTimelineItemsByStreetcodeIdHandler( + this.repoWrapperMock.Object, + this.mapperMock.Object, + this.loggerMock.Object); + } + + /// + /// Should return TimelineItemDTO when item exists. + /// + /// A task representing the asynchronous operation. + [Fact] + public async Task Handle_ShouldReturnTimelineItemDTO_WhenItemExists() + { + var query = new GetTimelineItemsByStreetcodeIdQuery(1); + + var timelineItems = new List + { + new TimelineItem + { + Id = 1, + Title = "Test Title", + Description = "Description", + Date = new DateTime(2020, 01, 01), + DateViewPattern = DateViewPattern.Year, + StreetcodeId = 1, + HistoricalContextTimelines = new List + { + new HistoricalContextTimeline + { + HistoricalContext = new HistoricalContext + { + Id = 1, + Title = "Historical Context 1", + }, + }, + }, + }, + }; + + var expectedDtos = new List + { + new TimelineItemDTO + { + Id = 1, + Title = "Test Title", + Description = "Description", + Date = new DateTime(2020, 01, 01), + DateViewPattern = DateViewPattern.Year, + HistoricalContexts = new List + { + new HistoricalContextDTO + { + Id = 1, + Title = "Historical Context 1", + }, + }, + }, + }; + + this.timelineRepoMock + .Setup(r => r.GetAllAsync( + It.IsAny>>(), + It.IsAny, + IIncludableQueryable>>())) + .ReturnsAsync(timelineItems); + + this.mapperMock + .Setup(m => m.Map>(It.IsAny>())) + .Returns(expectedDtos); + + var result = await this.handler.Handle(query, CancellationToken.None); + + Assert.True(result.IsSuccess); + Assert.NotNull(result.Value); + + var item = result.Value.First(); + + Assert.Equal(1, item.Id); + Assert.Equal(expectedDtos.First().Title, item.Title); + Assert.Equal(expectedDtos.First().Description, item.Description); + Assert.Equal(expectedDtos.First().Date, item.Date); + Assert.Equal(expectedDtos.First().DateViewPattern, item.DateViewPattern); + + Assert.Single(item.HistoricalContexts); + + Assert.Equal( + expectedDtos.First().HistoricalContexts.First().Title, + item.HistoricalContexts.First().Title); + + this.mapperMock.Verify( + m => m.Map>( + It.IsAny>()), + Times.Once); + + this.timelineRepoMock.Verify( + r => r.GetAllAsync( + It.IsAny>>(), + It.IsAny, + IIncludableQueryable>>()), + Times.Once); + } + + /// + /// Should throw exception when repository fails. + /// + /// A task representing the asynchronous operation. + [Fact] + public async Task Handle_ShouldPropagateException_WhenRepositoryThrows() + { + var query = new GetTimelineItemsByStreetcodeIdQuery(1); + + this.timelineRepoMock + .Setup(r => r.GetAllAsync( + It.IsAny>>(), + It.IsAny, + IIncludableQueryable>>())) + .ThrowsAsync(new Exception("Database failure")); + + var exception = await Assert.ThrowsAsync(() => + this.handler.Handle(query, CancellationToken.None)); + + Assert.Equal("Database failure", exception.Message); + + this.mapperMock.Verify( + m => m.Map>( + It.IsAny>()), + Times.Never); + + this.loggerMock.Verify( + l => l.LogError( + It.IsAny(), + It.IsAny()), + Times.Never); + + this.timelineRepoMock.Verify( + r => r.GetAllAsync( + It.IsAny>>(), + It.IsAny, + IIncludableQueryable>>()), + Times.Once); + } + + /// + /// Should return empty collection when no timeline items exist. + /// + /// A task representing the asynchronous operation. + [Fact] + public async Task Handle_ShouldReturnEmptyCollection_WhenNoItemsExist() + { + var query = new GetTimelineItemsByStreetcodeIdQuery(1); + + this.timelineRepoMock + .Setup(r => r.GetAllAsync( + It.IsAny>>(), + It.IsAny, + IIncludableQueryable>>())) + .ReturnsAsync(new List()); + + this.mapperMock + .Setup(m => m.Map>( + It.IsAny>())) + .Returns(new List()); + + var result = await this.handler.Handle(query, CancellationToken.None); + + Assert.True(result.IsSuccess); + Assert.NotNull(result.Value); + Assert.Empty(result.Value); + + this.mapperMock.Verify( + m => m.Map>( + It.IsAny>()), + Times.Once); + + this.timelineRepoMock.Verify( + r => r.GetAllAsync( + It.IsAny>>(), + It.IsAny, + IIncludableQueryable>>()), + Times.Once); + } + } +}