Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
be2f07c
added files for shopping cart service
kubapoke Apr 26, 2025
109be60
added necessary endpoint mocks for the shopping carts controller
kubapoke Apr 27, 2025
d138180
added mocks for the rest of necessary endpoints
kubapoke Apr 27, 2025
56e3a12
Merge branch 'develop' into feat/shopping-cart
kubapoke Apr 27, 2025
7ef6a91
Merge branch 'develop' into feat/shopping-cart
kubapoke May 2, 2025
cc84275
Merge branch 'develop' into feat/shopping-cart
kubapoke May 2, 2025
64e5670
added the base of ShoppingCartService.cs methods (headers still requi…
kubapoke May 2, 2025
e6619a4
added acquiring cart contents (from service) and adding ticket to car…
kubapoke May 3, 2025
b209d46
removed unnecessary using directive
kubapoke May 9, 2025
6c59aab
Merge branch 'develop' into feat/shopping-cart
kubapoke May 14, 2025
7f5f88a
added getting tickets from cart
kubapoke May 14, 2025
91fb526
removed NameOnTicket being necessary
kubapoke May 14, 2025
c3222df
implemented the basic version of getting tickets
kubapoke May 15, 2025
95698fe
reworked ticket buying logic to make the pipeline from frontend more …
kubapoke May 16, 2025
3edf2d6
relegated some of the ticket adding logic to the repository
kubapoke May 16, 2025
84dd97f
started updating some ticket retrieval logic
kubapoke May 16, 2025
9781f8d
Merge branch 'develop' into feat/shopping-cart
kubapoke May 20, 2025
ab228be
small aesthetic change in TicketRepository.cs
kubapoke May 20, 2025
0f0e843
updated cart retrieval logic to return more data
kubapoke May 20, 2025
5e3b95f
added functionality for removal of tickets from the shopping cart
kubapoke May 20, 2025
e877eae
implemented getting cart due amount and part of the checkout process
kubapoke May 21, 2025
68d9662
added payment endpoint
kubapoke May 21, 2025
05da8e5
changed due amount calculations
kubapoke May 21, 2025
2338c8e
implemented adding created tickets to database
kubapoke May 22, 2025
b0703de
tickets removed from cart after purchase
kubapoke May 22, 2025
fe4cf05
added methods for manipulating ticket type counters
kubapoke May 22, 2025
c1097b3
added reserved ticket counting
kubapoke May 22, 2025
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
25 changes: 25 additions & 0 deletions TickAPI/TickAPI.Tests/Common/Results/ResultTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,21 @@ public void Failure_ShouldReturnResultWithError()
Assert.Equal(statusCode, result.StatusCode);
}

[Fact]
public void PropagateError_WhenResultWithErrorPassed_ShouldReturnResultWithError()
{
const int statusCode = 500;
const string errorMsg = "error message";
var resultWithError = Result.Failure(statusCode, errorMsg);

var result = Result.PropagateError(resultWithError);

Assert.True(result.IsError);
Assert.False(result.IsSuccess);
Assert.Equal(errorMsg, result.ErrorMsg);
Assert.Equal(statusCode, result.StatusCode);
}

[Fact]
public void PropagateError_WhenGenericResultWithErrorPassed_ShouldReturnResultWithError()
{
Expand All @@ -55,6 +70,16 @@ public void PropagateError_WhenGenericResultWithErrorPassed_ShouldReturnResultWi
Assert.Equal(statusCode, result.StatusCode);
}

[Fact]
public void PropagateError_WhenResultWithSuccessPassed_ShouldThrowArgumentException()
{
var resultWithSuccess = Result.Success();

var act = () => Result.PropagateError(resultWithSuccess);

Assert.Throws<ArgumentException>(act);
}

[Fact]
public void PropagateError_WhenGenericResultWithSuccessPassed_ShouldThrowArgumentException()
{
Expand Down
2 changes: 1 addition & 1 deletion TickAPI/TickAPI.Tests/Events/Services/EventServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,7 @@ public async Task GetEventDetailsAsync_WhenSuccessful_ShouldReturnEventDetails()
.ReturnsAsync(Result<Event>.Success(@event));

ticketServiceMock
.Setup(m => m.GetNumberOfAvailableTicketsByType(It.IsAny<TicketType>()))
.Setup(m => m.GetNumberOfAvailableTicketsByTypeAsync(It.IsAny<TicketType>()))
.Returns((TicketType input) =>
Result<uint>.Success((uint)(input.Price / 10))
);
Expand Down
84 changes: 62 additions & 22 deletions TickAPI/TickAPI.Tests/Tickets/Services/TicketServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,60 +10,68 @@
using TickAPI.Customers.Models;
using TickAPI.Events.Models;
using TickAPI.Organizers.Models;
using TickAPI.ShoppingCarts.Abstractions;
using TickAPI.Tickets.Abstractions;
using TickAPI.Tickets.DTOs.Response;
using TickAPI.Tickets.Models;
using TickAPI.Tickets.Services;
using TickAPI.TicketTypes.Abstractions;
using TickAPI.TicketTypes.Models;

namespace TickAPI.Tests.Tickets.Services;

public class TicketServiceTests
{
[Fact]
public void GetNumberOfAvailableTicketsByType_AmountsAreCorrect_ShouldReturnCorrectNumberOfTickets()
public async Task GetNumberOfAvailableTicketsByType_AmountsAreCorrect_ShouldReturnCorrectNumberOfTickets()
{
// Arrange
var type = new TicketType { MaxCount = 30 };
var ticketList = new List<Ticket>(new Ticket[10]);

var ticketRepositoryMock = new Mock<ITicketRepository>();
var ticketTypeRepositoryMock = new Mock<ITicketTypeRepository>();
var shoppingCartRepositoryMock = new Mock<IShoppingCartRepository>();
var paginationServiceMock = new Mock<IPaginationService>();
var qrServiceMock = new Mock<IQRCodeService>();

ticketRepositoryMock
.Setup(m => m.GetAllTicketsByTicketType(type))
.Returns(ticketList.AsQueryable());

var sut = new TicketService(ticketRepositoryMock.Object, paginationServiceMock.Object, qrServiceMock.Object);
var sut = new TicketService(ticketRepositoryMock.Object, ticketTypeRepositoryMock.Object,
shoppingCartRepositoryMock.Object, paginationServiceMock.Object, qrServiceMock.Object);

// Act
var result = sut.GetNumberOfAvailableTicketsByType(type);
var result = await sut.GetNumberOfAvailableTicketsByTypeAsync(type);

// Assert
Assert.True(result.IsSuccess);
Assert.Equal(20u, result.Value);
}

[Fact]
public void GetNumberOfAvailableTicketsByType_WhenMoreTicketExistThanMaxCount_ShouldReturnError()
public async Task GetNumberOfAvailableTicketsByType_WhenMoreTicketExistThanMaxCount_ShouldReturnError()
{
// Arrange
var type = new TicketType { MaxCount = 30 };
var ticketList = new List<Ticket>(new Ticket[50]);

var ticketRepositoryMock = new Mock<ITicketRepository>();
var ticketTypeRepositoryMock = new Mock<ITicketTypeRepository>();
var shoppingCartRepositoryMock = new Mock<IShoppingCartRepository>();
var paginationServiceMock = new Mock<IPaginationService>();
var qrServiceMock = new Mock<IQRCodeService>();

ticketRepositoryMock
.Setup(m => m.GetAllTicketsByTicketType(type))
.Returns(ticketList.AsQueryable());

var sut = new TicketService(ticketRepositoryMock.Object, paginationServiceMock.Object, qrServiceMock.Object);
var sut = new TicketService(ticketRepositoryMock.Object, ticketTypeRepositoryMock.Object,
shoppingCartRepositoryMock.Object, paginationServiceMock.Object, qrServiceMock.Object);

// Act
var result = sut.GetNumberOfAvailableTicketsByType(type);
var result = await sut.GetNumberOfAvailableTicketsByTypeAsync(type);

// Assert
Assert.True(result.IsError);
Expand Down Expand Up @@ -111,6 +119,9 @@ public async Task GetTicketsForResellAsync_WhenDataIsValid_ShouldReturnSuccess()
ticketRepositoryMock.Setup(repo => repo.GetTicketsByEventId(eventId))
.Returns(allTickets);

var ticketTypeRepositoryMock = new Mock<ITicketTypeRepository>();
var shoppingCartRepositoryMock = new Mock<IShoppingCartRepository>();

var paginatedTickets = new PaginatedData<Ticket>(
new List<Ticket> { ticket1, ticket2 },
page,
Expand Down Expand Up @@ -156,7 +167,8 @@ public async Task GetTicketsForResellAsync_WhenDataIsValid_ShouldReturnSuccess()

var qrServiceMock = new Mock<IQRCodeService>();

var sut = new TicketService(ticketRepositoryMock.Object, paginationServiceMock.Object, qrServiceMock.Object);
var sut = new TicketService(ticketRepositoryMock.Object, ticketTypeRepositoryMock.Object,
shoppingCartRepositoryMock.Object, paginationServiceMock.Object, qrServiceMock.Object);

// Act
var result = await sut.GetTicketsForResellAsync(eventId, page, pageSize);
Expand Down Expand Up @@ -206,6 +218,9 @@ public async Task GetTicketsForResellAsync_WhenNoTicketsForResell_ShouldReturnEm
ticketRepositoryMock.Setup(repo => repo.GetTicketsByEventId(eventId))
.Returns(tickets);

var ticketTypeRepositoryMock = new Mock<ITicketTypeRepository>();
var shoppingCartRepositoryMock = new Mock<IShoppingCartRepository>();

var paginatedData = new PaginatedData<Ticket>(
new List<Ticket>(),
page,
Expand Down Expand Up @@ -234,7 +249,8 @@ public async Task GetTicketsForResellAsync_WhenNoTicketsForResell_ShouldReturnEm
.Returns(mappedData);
var qrServiceMock = new Mock<IQRCodeService>();

var sut = new TicketService(ticketRepositoryMock.Object, paginationServiceMock.Object, qrServiceMock.Object);
var sut = new TicketService(ticketRepositoryMock.Object, ticketTypeRepositoryMock.Object,
shoppingCartRepositoryMock.Object, paginationServiceMock.Object, qrServiceMock.Object);

// Act
var result = await sut.GetTicketsForResellAsync(eventId, page, pageSize);
Expand Down Expand Up @@ -272,14 +288,18 @@ public async Task GetTicketsForResellAsync_WhenPaginationFails_ShouldPropagateEr
var ticketRepositoryMock = new Mock<ITicketRepository>();
ticketRepositoryMock.Setup(repo => repo.GetTicketsByEventId(eventId))
.Returns(tickets);

var ticketTypeRepositoryMock = new Mock<ITicketTypeRepository>();
var shoppingCartRepositoryMock = new Mock<IShoppingCartRepository>();

var paginationServiceMock = new Mock<IPaginationService>();
paginationServiceMock.Setup(p => p.PaginateAsync(It.IsAny<IQueryable<Ticket>>(), pageSize, page))
.ReturnsAsync(Result<PaginatedData<Ticket>>.Failure(statusCode, errorMsg));

var qrServiceMock = new Mock<IQRCodeService>();

var sut = new TicketService(ticketRepositoryMock.Object, paginationServiceMock.Object, qrServiceMock.Object);
var sut = new TicketService(ticketRepositoryMock.Object, ticketTypeRepositoryMock.Object,
shoppingCartRepositoryMock.Object, paginationServiceMock.Object, qrServiceMock.Object);

// Act
var result = await sut.GetTicketsForResellAsync(eventId, page, pageSize);
Expand All @@ -304,6 +324,9 @@ public async Task GetTicketsForResellAsync_WhenNoTicketsForEvent_ShouldReturnEmp
ticketRepositoryMock.Setup(repo => repo.GetTicketsByEventId(eventId))
.Returns(tickets);

var ticketTypeRepositoryMock = new Mock<ITicketTypeRepository>();
var shoppingCartRepositoryMock = new Mock<IShoppingCartRepository>();

var paginatedData = new PaginatedData<Ticket>(
new List<Ticket>(),
page,
Expand Down Expand Up @@ -332,7 +355,8 @@ public async Task GetTicketsForResellAsync_WhenNoTicketsForEvent_ShouldReturnEmp
.Returns(mappedData);
var qrServiceMock = new Mock<IQRCodeService>();

var sut = new TicketService(ticketRepositoryMock.Object, paginationServiceMock.Object, qrServiceMock.Object);
var sut = new TicketService(ticketRepositoryMock.Object, ticketTypeRepositoryMock.Object,
shoppingCartRepositoryMock.Object, paginationServiceMock.Object, qrServiceMock.Object);

// Act
var result = await sut.GetTicketsForResellAsync(eventId, page, pageSize);
Expand Down Expand Up @@ -380,9 +404,11 @@ public async Task GetTicketDetailsAsync_WhenTicketExistsForTheUser_ShouldReturnT
}
},
};
string email = "123@123.com";
string scanurl = "http://localhost";
Mock<ITicketRepository> ticketRepositoryMock = new Mock<ITicketRepository>();
const string email = "123@123.com";
const string scanurl = "http://localhost";
var ticketRepositoryMock = new Mock<ITicketRepository>();
var ticketTypeRepositoryMock = new Mock<ITicketTypeRepository>();
var shoppingCartRepositoryMock = new Mock<IShoppingCartRepository>();

var paginationServiceMock = new Mock<IPaginationService>();

Expand All @@ -392,7 +418,8 @@ public async Task GetTicketDetailsAsync_WhenTicketExistsForTheUser_ShouldReturnT
var qrServiceMock = new Mock<IQRCodeService>();
qrServiceMock.Setup(m => m.GenerateQrCode(scanurl)).Returns([]);

var sut = new TicketService(ticketRepositoryMock.Object, paginationServiceMock.Object, qrServiceMock.Object);
var sut = new TicketService(ticketRepositoryMock.Object, ticketTypeRepositoryMock.Object,
shoppingCartRepositoryMock.Object, paginationServiceMock.Object, qrServiceMock.Object);

// Act

Expand Down Expand Up @@ -426,18 +453,21 @@ public async Task GetTicketDetailsAsync_WhenTicketDoesNotExistForTheUser_ShouldR

// Arrange

Guid ticketId = Guid.NewGuid();
string email = "123@123.com";
string scanUrl = "http://localhost";
var ticketId = Guid.NewGuid();
const string email = "123@123.com";
const string scanUrl = "http://localhost";

Mock<ITicketRepository> ticketRepositoryMock = new Mock<ITicketRepository>();
var ticketRepositoryMock = new Mock<ITicketRepository>();
ticketRepositoryMock.Setup(m => m.GetTicketWithDetailsByIdAndEmailAsync(ticketId, email)).
ReturnsAsync(Result<Ticket>.Failure(StatusCodes.Status404NotFound, "Ticket with this id doesn't exist " +
"for this user"));
var ticketTypeRepositoryMock = new Mock<ITicketTypeRepository>();
var shoppingCartRepositoryMock = new Mock<IShoppingCartRepository>();
var paginationServiceMock = new Mock<IPaginationService>();
var qrServiceMock = new Mock<IQRCodeService>();

var sut = new TicketService(ticketRepositoryMock.Object, paginationServiceMock.Object, qrServiceMock.Object);
var sut = new TicketService(ticketRepositoryMock.Object, ticketTypeRepositoryMock.Object,
shoppingCartRepositoryMock.Object, paginationServiceMock.Object, qrServiceMock.Object);

// Act

Expand Down Expand Up @@ -513,6 +543,8 @@ public async Task GetTicketsForCustomerAsync_WithValidInput_ReturnsSuccessResult

var ticketRepositoryMock = new Mock<ITicketRepository>();
ticketRepositoryMock.Setup(r => r.GetTicketsByCustomerEmail(email)).Returns(tickets.AsQueryable());
var ticketTypeRepositoryMock = new Mock<ITicketTypeRepository>();
var shoppingCartRepositoryMock = new Mock<IShoppingCartRepository>();

var paginationServiceMock = new Mock<IPaginationService>();
paginationServiceMock.Setup(p => p.PaginateAsync(tickets.AsQueryable(), pageSize, page))
Expand All @@ -523,7 +555,8 @@ public async Task GetTicketsForCustomerAsync_WithValidInput_ReturnsSuccessResult

var qrServiceMock = new Mock<IQRCodeService>();

var sut = new TicketService(ticketRepositoryMock.Object, paginationServiceMock.Object, qrServiceMock.Object);
var sut = new TicketService(ticketRepositoryMock.Object, ticketTypeRepositoryMock.Object,
shoppingCartRepositoryMock.Object, paginationServiceMock.Object, qrServiceMock.Object);

// Act
var result = await sut.GetTicketsForCustomerAsync(email, page, pageSize);
Expand Down Expand Up @@ -555,6 +588,9 @@ public async Task GetTicketsForCustomerAsync_WhenUserHasNoTickets_ReturnsEmptyPa

var ticketRepositoryMock = new Mock<ITicketRepository>();
ticketRepositoryMock.Setup(r => r.GetTicketsByCustomerEmail(email)).Returns(emptyTickets.AsQueryable());

var ticketTypeRepositoryMock = new Mock<ITicketTypeRepository>();
var shoppingCartRepositoryMock = new Mock<IShoppingCartRepository>();

var paginationServiceMock = new Mock<IPaginationService>();
paginationServiceMock.Setup(p => p.PaginateAsync(emptyTickets.AsQueryable(), pageSize, page)).ReturnsAsync(paginatedResult);
Expand All @@ -563,7 +599,8 @@ public async Task GetTicketsForCustomerAsync_WhenUserHasNoTickets_ReturnsEmptyPa

var qrServiceMock = new Mock<IQRCodeService>();

var sut = new TicketService(ticketRepositoryMock.Object, paginationServiceMock.Object, qrServiceMock.Object);
var sut = new TicketService(ticketRepositoryMock.Object, ticketTypeRepositoryMock.Object,
shoppingCartRepositoryMock.Object, paginationServiceMock.Object, qrServiceMock.Object);

// Act
var result = await sut.GetTicketsForCustomerAsync(email, page, pageSize);
Expand All @@ -580,9 +617,12 @@ public async Task ScanTicket_WhenScanningSuccesful_ShouldReturnSuccess()
var guid = Guid.NewGuid();
var ticketRepositoryMock = new Mock<ITicketRepository>();
ticketRepositoryMock.Setup(m => m.MarkTicketAsUsed(guid)).ReturnsAsync(Result.Success());
var ticketTypeRepositoryMock = new Mock<ITicketTypeRepository>();
var shoppingCartRepositoryMock = new Mock<IShoppingCartRepository>();
var paginationServiceMock = new Mock<IPaginationService>();
var qrServiceMock = new Mock<IQRCodeService>();
var sut = new TicketService(ticketRepositoryMock.Object, paginationServiceMock.Object, qrServiceMock.Object);
var sut = new TicketService(ticketRepositoryMock.Object, ticketTypeRepositoryMock.Object,
shoppingCartRepositoryMock.Object, paginationServiceMock.Object, qrServiceMock.Object);

// Act
var res = await sut.ScanTicket(guid);
Expand Down
10 changes: 10 additions & 0 deletions TickAPI/TickAPI/Common/Results/Result.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ public static Result Failure(int statusCode, string errorMsg)
return new Result(false, statusCode, errorMsg);
}

public static Result PropagateError(Result other)
{
if (other.IsSuccess)
{
throw new ArgumentException("Trying to propagate error from successful value");
}

return Failure(other.StatusCode, other.ErrorMsg);
}

public static Result PropagateError<TE>(Result<TE> other)
{
if (other.IsSuccess)
Expand Down
22 changes: 18 additions & 4 deletions TickAPI/TickAPI/Events/Services/EventService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,24 @@ public async Task<Result<GetEventDetailsResponseDto>> GetEventDetailsAsync(Guid
? ev.Categories.Select((c) => new GetEventResponseCategoryDto(c.Name)).ToList()
: new List<GetEventResponseCategoryDto>();

var ticketTypes = ev.TicketTypes.Count > 0
? ev.TicketTypes.Select((t) => new GetEventDetailsResponseTicketTypeDto(t.Id, t.Description, t.Price,
t.Currency, t.AvailableFrom, _ticketService.GetNumberOfAvailableTicketsByType(t).Value)).ToList()
: new List<GetEventDetailsResponseTicketTypeDto>();
var ticketTypes = new List<GetEventDetailsResponseTicketTypeDto>();

if (ev.TicketTypes.Count > 0)
{
foreach (var t in ev.TicketTypes)
{
var availableCount = await _ticketService.GetNumberOfAvailableTicketsByTypeAsync(t);

ticketTypes.Add(new GetEventDetailsResponseTicketTypeDto(
t.Id,
t.Description,
t.Price,
t.Currency,
t.AvailableFrom,
availableCount.Value
));
}
}

var address = new GetEventResponseAddressDto(ev.Address.Country, ev.Address.City, ev.Address.PostalCode,
ev.Address.Street, ev.Address.HouseNumber, ev.Address.FlatNumber);
Expand Down
Loading
Loading