Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Retrieve event details by eventId #6

Merged
merged 1 commit into from
Jun 22, 2024
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
using AutoMapper;
using BotickrAPI.Application.Features.Events.Queries.GetEventDetailsById;
using BotickrAPI.Application.UnitTests.MapperProfiles;
using BotickrAPI.Domain.Entities;
using BotickrAPI.Domain.Enums;
using BotickrAPI.Domain.Exceptions;
using BotickrAPI.Domain.Repositories;
using FluentAssertions;
using Moq;
using Xunit;
using static Microsoft.EntityFrameworkCore.DbLoggerCategory.Database;

namespace BotickrAPI.Application.UnitTests.Features.Event.Queries.GetEventDetailsById;

public class GetEventDetailsByIdQueryHandlerTests : IClassFixture<MappingTestFixture>
{
private readonly Mock<IEventRepository> _eventRepositoryMock;

private readonly Mock<ITicketRepository> _ticketRepositoryMock;

private readonly IMapper _mapper;

private GetEventDetailsByIdQuery _query;

private readonly GetEventDetailsByIdQueryHandler _handler;

private readonly EventEntity _event;

private readonly List<TicketEntity> _tickets;
public GetEventDetailsByIdQueryHandlerTests(MappingTestFixture fixture)
{
_mapper = fixture.Mapper;
_eventRepositoryMock = new Mock<IEventRepository>();
_ticketRepositoryMock = new Mock<ITicketRepository>();
_handler = new(_mapper, _eventRepositoryMock.Object, _ticketRepositoryMock.Object);

_event = new()
{
Description = "description",
Duration = TimeSpan.FromSeconds(10),
Id = 1,
LocationId = 1,
Name = "name",
ImagePath = "test",
Status = EventStatus.Approved.ToString(),
OrganizerId = "testid",
EventType = "TestType"
};

_tickets = new List<TicketEntity>()
{
new()
{

BookingDetails = new List<BookingDetailEntity>()
{
new()
{
Id = 1,
Quantity = 1,
TicketId = 1,
BookingId = 1
},
new()
{
Id = 2,
Quantity = 9,
TicketId = 1,
BookingId = 2
}
},
Id =1,
EventId = 1,
Price = 50,
Quantity = 110,
TicketType = "NORMAL"
}
};
}


[Fact]
public async Task Handle_ShouldReturnMappedModel_WhenQueryProvided()
{
//Arrange
_eventRepositoryMock.Setup(x => x.GetByIdAsync(It.IsAny<int>(), default)).ReturnsAsync(_event);
_ticketRepositoryMock.Setup(x => x.GetTicketsByEventIdAsync(It.IsAny<int>(), default)).ReturnsAsync(_tickets);
_query = new()
{
Id = 1
};

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

//Assert
result.Should().NotBeNull();
result.Tickets.First().AvailableQuantity.Should().Be(100);
result.Tickets.First().TotalQuantity.Should().Be(110);
result.Tickets.First().IsSoldOut.Should().Be(false);
result.ImagePath.Should().Be(_event.ImagePath);
result.EventType.Should().Be(_event.EventType);
result.Description.Should().Be(_event.Description);
result.Duration.Should().Be(_event.Duration);
result.Id.Should().Be(_event.Id);
result.StartTime.Should().Be(_event.StartTime);
result.LocationId.Should().Be(_event.LocationId);
}

[Fact]
public async Task Handle_ShouldThrowException_WhenTicketDoesntExist()
{
//Arrange
_eventRepositoryMock.Setup(x => x.GetByIdAsync(It.IsAny<int>(), default)).ReturnsAsync(_event);
_ticketRepositoryMock.Setup(x => x.GetTicketsByEventIdAsync(It.IsAny<int>(), default)).ReturnsAsync(new List<TicketEntity>());
_query = new()
{
Id = 1
};

//Act & assert
await Assert.ThrowsAsync<NotFoundException>(() => _handler.Handle(_query, CancellationToken.None));
}

[Fact]
public async Task Handle_ShouldThrowException_WhenEventtDoesntExist()
{
//Arrange
_eventRepositoryMock
.Setup(x => x.GetByIdAsync(It.IsAny<int>(), default))
.ReturnsAsync(() => (EventEntity)null);
_ticketRepositoryMock.Setup(x => x.GetTicketsByEventIdAsync(It.IsAny<int>(), default)).ReturnsAsync(_tickets);
_query = new()
{
Id = 1
};

//Act & assert
await Assert.ThrowsAsync<NotFoundException>(() => _handler.Handle(_query, CancellationToken.None));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
using AutoMapper;
using BotickrAPI.Application.Dtos.Tickets;
using BotickrAPI.Domain.Entities;
using FluentAssertions;
using System.Collections.Generic;
using Xunit;

namespace BotickrAPI.Application.UnitTests.MapperProfiles.Tickets
{
public class TicketProfileTests : IClassFixture<MappingTestFixture>
{
private readonly IMapper _mapper;

public TicketProfileTests(MappingTestFixture fixture)
{
_mapper = fixture.Mapper;
}

[Fact]
public void Should_Map_NewTicketDto_To_TicketEntity()
{
// Arrange
var newTicketDto = new NewTicketDto
{
Price = 100.00,
Quantity = 50,
TicketType = "VIP"
};

// Act
var ticketEntity = _mapper.Map<TicketEntity>(newTicketDto);

// Assert
ticketEntity.Price.Should().Be(newTicketDto.Price);
ticketEntity.Quantity.Should().Be(newTicketDto.Quantity);
ticketEntity.TicketType.Should().Be(newTicketDto.TicketType);
ticketEntity.Event.Should().BeNull();
ticketEntity.StatusId.Should().Be(1);
ticketEntity.Created.Should().Be(default);
ticketEntity.BookingDetails.Should().BeNull();
ticketEntity.Modified.Should().Be(null);
ticketEntity.ModifiedBy.Should().BeNull();
ticketEntity.Inactivated.Should().BeNull();
ticketEntity.InactivatedBy.Should().BeNull();
ticketEntity.EventId.Should().Be(0);
ticketEntity.Id.Should().Be(default);
ticketEntity.CreatedBy.Should().Be(string.Empty);
}

[Fact]
public void Should_Map_TicketEntity_To_TicketDto()
{
// Arrange
var bookingDetails = new List<BookingDetailEntity>
{
new BookingDetailEntity { Quantity = 20 },
new BookingDetailEntity { Quantity = 10 }
};

var ticketEntity = new TicketEntity
{
Price = 150.00,
Quantity = 50,
TicketType = "Standard",
BookingDetails = bookingDetails
};

// Act
var ticketDto = _mapper.Map<TicketDto>(ticketEntity);

// Assert
ticketDto.Price.Should().Be(ticketEntity.Price);
ticketDto.TicketType.Should().Be(ticketEntity.TicketType);
ticketDto.TotalQuantity.Should().Be(ticketEntity.Quantity);
ticketDto.AvailableQuantity.Should().Be(ticketEntity.Quantity - 30);
ticketDto.IsSoldOut.Should().BeFalse();
}

[Fact]
public void Should_Map_TicketEntity_To_TicketDto_When_SoldOut()
{
// Arrange
var bookingDetails = new List<BookingDetailEntity>
{
new BookingDetailEntity { Quantity = 25 },
new BookingDetailEntity { Quantity = 25 }
};

var ticketEntity = new TicketEntity
{
Price = 200.00,
Quantity = 50,
TicketType = "Standard",
BookingDetails = bookingDetails
};

// Act
var ticketDto = _mapper.Map<TicketDto>(ticketEntity);

// Assert
ticketDto.Price.Should().Be(ticketEntity.Price);
ticketDto.TicketType.Should().Be(ticketEntity.TicketType);
ticketDto.TotalQuantity.Should().Be(ticketEntity.Quantity);
ticketDto.AvailableQuantity.Should().Be(0);
ticketDto.IsSoldOut.Should().BeTrue();
}

[Fact]
public void Should_Map_TicketEntity_To_TicketDto_When_BookingDetails_Empty()
{
var ticketEntity = new TicketEntity
{
Price = 100,
Quantity = 10,
TicketType = "VIP",
BookingDetails = new List<BookingDetailEntity>()
};

var ticketDto = _mapper.Map<TicketDto>(ticketEntity);

ticketDto.Price.Should().Be(ticketEntity.Price);
ticketDto.TotalQuantity.Should().Be(ticketEntity.Quantity);
ticketDto.TicketType.Should().Be(ticketEntity.TicketType);
ticketDto.AvailableQuantity.Should().Be(ticketEntity.Quantity);
ticketDto.IsSoldOut.Should().BeFalse();
}
}
}
29 changes: 29 additions & 0 deletions BotickrAPI.Application/Dtos/Events/DetailEventInfoDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using BotickrAPI.Application.Dtos.Tickets;
using BotickrAPI.Application.Helpers;
using System.Text.Json.Serialization;

namespace BotickrAPI.Application.Dtos.Events;

public class DetailEventInfoDto
{
public int Id { get; set; }

public string Name { get; set; } = string.Empty;

public string EventType { get; set; } = string.Empty;

public string ImagePath { get; set; } = string.Empty;

public string Description { get; set; } = string.Empty;

public DateTime StartTime { get; set; }

[JsonConverter(typeof(TimeSpanConverter))]
public TimeSpan Duration { get; set; }

public string Status { get; set; } = string.Empty;

public int LocationId { get; set; }

public IEnumerable<TicketDto> Tickets { get; set; } = [];
}
2 changes: 2 additions & 0 deletions BotickrAPI.Application/Dtos/Events/EventDto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ namespace BotickrAPI.Application.Dtos.Events;

public class EventDto
{
public int Id { get; set; }

public string Name { get; set; } = string.Empty;

public string EventType { get; set; } = string.Empty;
Expand Down
14 changes: 14 additions & 0 deletions BotickrAPI.Application/Dtos/Tickets/TicketDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace BotickrAPI.Application.Dtos.Tickets;

public class TicketDto
{
public double Price { get; set; }

public int TotalQuantity { get; set; }

public int AvailableQuantity { get; set; }

public string TicketType { get; set; } = string.Empty;

public bool IsSoldOut { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using BotickrAPI.Application.Dtos.Events;
using MediatR;

namespace BotickrAPI.Application.Features.Events.Queries.GetEventDetailsById;

public class GetEventDetailsByIdQuery : IRequest<DetailEventInfoDto>
{
public int Id { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using AutoMapper;
using BotickrAPI.Application.Dtos.Events;
using BotickrAPI.Application.Dtos.Tickets;
using BotickrAPI.Domain.Exceptions;
using BotickrAPI.Domain.Repositories;
using MediatR;

namespace BotickrAPI.Application.Features.Events.Queries.GetEventDetailsById
{
public class GetEventDetailsByIdQueryHandler(IMapper _mapper, IEventRepository _eventRepository, ITicketRepository _ticketRepository) : IRequestHandler<GetEventDetailsByIdQuery, DetailEventInfoDto>
{
private readonly IEventRepository _eventRepository = _eventRepository;

private readonly IMapper _mapper = _mapper;

private readonly ITicketRepository _ticketRepository = _ticketRepository;


public async Task<DetailEventInfoDto> Handle(GetEventDetailsByIdQuery request, CancellationToken cancellationToken)
{

var events = await _eventRepository.GetByIdAsync(request.Id, cancellationToken);
if (events == null)
{
throw new NotFoundException();
}
var eventsDto = _mapper.Map<DetailEventInfoDto>(events);

var tickets = await _ticketRepository.GetTicketsByEventIdAsync(request.Id, cancellationToken);
if (!tickets.Any())
{
throw new NotFoundException();
}
var ticketDto = _mapper.Map<IEnumerable<TicketDto>>(tickets);

eventsDto.Tickets = ticketDto;

return eventsDto;
}
}
}
14 changes: 14 additions & 0 deletions BotickrAPI.Application/MapperProfiles/Events/EventProfile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,19 @@ public EventProfile()

CreateMap<EventEntity, EventDto>()
.ForMember(dest => dest.Artists, method => method.MapFrom(src => src.EventArtists.Select(p => p.Artist)));

CreateMap<EventEntity, DetailEventInfoDto>()
.ForMember(dest => dest.EventType, method => method.MapFrom(src => src.EventType))
.ForMember(dest => dest.Duration, method => method.MapFrom(src => src.Duration))
.ForMember(dest => dest.Description, method => method.MapFrom(src => src.Description))
.ForMember(dest => dest.Status, method => method.MapFrom(src => src.Status))
.ForMember(dest => dest.StartTime, method => method.MapFrom(src => src.StartTime))
.ForMember(dest => dest.LocationId, method => method.MapFrom(src => src.LocationId))
.ForMember(dest => dest.Id, method => method.MapFrom(src => src.Id))
.ForMember(dest => dest.Name, method => method.MapFrom(src => src.Name))
.ForMember(dest => dest.ImagePath, method => method.MapFrom(src => src.ImagePath))
.ForMember(dest => dest.Tickets, method => method.Ignore());


}
}
Loading
Loading