From 89e8ffcabac29f8a191d87b0bb5ad4dad0ead59d Mon Sep 17 00:00:00 2001 From: kTrzcinskii Date: Thu, 3 Apr 2025 17:17:45 +0200 Subject: [PATCH 01/14] Add option to map data in `PaginatedData` --- .../Common/Pagination/Abstractions/IPaginationService.cs | 1 + .../TickAPI/Common/Pagination/Services/PaginationService.cs | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/TickAPI/TickAPI/Common/Pagination/Abstractions/IPaginationService.cs b/TickAPI/TickAPI/Common/Pagination/Abstractions/IPaginationService.cs index 4aec7fc..260effb 100644 --- a/TickAPI/TickAPI/Common/Pagination/Abstractions/IPaginationService.cs +++ b/TickAPI/TickAPI/Common/Pagination/Abstractions/IPaginationService.cs @@ -7,4 +7,5 @@ public interface IPaginationService { public Result GetPaginationDetails(ICollection collection, int pageSize); public Result> Paginate(ICollection collection, int pageSize, int page); + public PaginatedData MapData(PaginatedData source, Func mapFunction); } \ No newline at end of file diff --git a/TickAPI/TickAPI/Common/Pagination/Services/PaginationService.cs b/TickAPI/TickAPI/Common/Pagination/Services/PaginationService.cs index c7670ac..88e978b 100644 --- a/TickAPI/TickAPI/Common/Pagination/Services/PaginationService.cs +++ b/TickAPI/TickAPI/Common/Pagination/Services/PaginationService.cs @@ -55,4 +55,10 @@ public Result> Paginate(ICollection collection, int pageS return Result>.Success(paginatedData); } + + public PaginatedData MapData(PaginatedData source, Func mapFunction) + { + var newData = source.Data.Select(mapFunction).ToList(); + return new PaginatedData(newData, source.PageNumber, source.PageSize, source.HasNextPage, source.HasPreviousPage, source.PaginationDetails); + } } \ No newline at end of file From cfc08f74e14aa80a45d7b32086b785cff59a15b7 Mon Sep 17 00:00:00 2001 From: kTrzcinskii Date: Thu, 3 Apr 2025 17:32:06 +0200 Subject: [PATCH 02/14] Add test for `PaginationService.MapData` --- .../Services/PaginationServiceTests.cs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/TickAPI/TickAPI.Tests/Common/Pagination/Services/PaginationServiceTests.cs b/TickAPI/TickAPI.Tests/Common/Pagination/Services/PaginationServiceTests.cs index e234b47..906f28c 100644 --- a/TickAPI/TickAPI.Tests/Common/Pagination/Services/PaginationServiceTests.cs +++ b/TickAPI/TickAPI.Tests/Common/Pagination/Services/PaginationServiceTests.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Http; +using TickAPI.Common.Pagination.Responses; using TickAPI.Common.Pagination.Services; namespace TickAPI.Tests.Common.Pagination.Services; @@ -10,8 +11,10 @@ public class PaginationServiceTests [Fact] public void Paginate_WhenPageSizeNegative_ShouldReturnFailure() { + // Act var result = _paginationService.Paginate(new List(), -5, 0); + // Assert Assert.True(result.IsError); Assert.Equal(StatusCodes.Status400BadRequest, result.StatusCode); Assert.Equal("'pageSize' param must be > 0, got: -5", result.ErrorMsg); @@ -20,8 +23,10 @@ public void Paginate_WhenPageSizeNegative_ShouldReturnFailure() [Fact] public void Paginate_WhenPageSizeZero_ShouldReturnFailure() { + // Act var result = _paginationService.Paginate(new List(), 0, 0); + // Assert Assert.True(result.IsError); Assert.Equal(StatusCodes.Status400BadRequest, result.StatusCode); Assert.Equal("'pageSize' param must be > 0, got: 0", result.ErrorMsg); @@ -30,8 +35,10 @@ public void Paginate_WhenPageSizeZero_ShouldReturnFailure() [Fact] public void Paginate_WhenPageNegative_ShouldReturnFailure() { + // Act var result = _paginationService.Paginate(new List(), 1, -12); + // Assert Assert.True(result.IsError); Assert.Equal(StatusCodes.Status400BadRequest, result.StatusCode); Assert.Equal("'page' param must be >= 0, got: -12", result.ErrorMsg); @@ -40,12 +47,15 @@ public void Paginate_WhenPageNegative_ShouldReturnFailure() [Fact] public void Paginate_WhenCollectionLengthSmallerThanPageSize_ShouldReturnAllElements() { + // Arrange var data = new List { 1, 2, 3, 4, 5 }; int pageSize = data.Count + 1; const int pageNumber = 0; + // Act var result = _paginationService.Paginate(data, pageSize, pageNumber); + // Assert Assert.True(result.IsSuccess); Assert.Equal(data, result.Value?.Data); Assert.Equal(pageNumber, result.Value?.PageNumber); @@ -59,12 +69,15 @@ public void Paginate_WhenCollectionLengthSmallerThanPageSize_ShouldReturnAllElem [Fact] public void Paginate_WhenCollectionLengthBiggerThanPageSize_ShouldReturnPartOfCollection() { + // Arrange var data = new List { 1, 2, 3, 4, 5 }; const int pageSize = 2; const int pageNumber = 0; + // Act var result = _paginationService.Paginate(data, pageSize, pageNumber); + // Assert Assert.True(result.IsSuccess); Assert.Equal(new List {1, 2}, result.Value?.Data); Assert.Equal(pageNumber, result.Value?.PageNumber); @@ -78,12 +91,15 @@ public void Paginate_WhenCollectionLengthBiggerThanPageSize_ShouldReturnPartOfCo [Fact] public void Paginate_WhenTakingElementsFromTheMiddle_ShouldReturnPaginatedDataWithBothBooleansTrue() { + // Arrange var data = new List { 1, 2, 3, 4, 5 }; const int pageSize = 2; const int pageNumber = 1; + // Act var result = _paginationService.Paginate(data, pageSize, pageNumber); + // Assert Assert.True(result.IsSuccess); Assert.Equal(new List {3, 4}, result.Value?.Data); Assert.Equal(pageNumber, result.Value?.PageNumber); @@ -97,12 +113,15 @@ public void Paginate_WhenTakingElementsFromTheMiddle_ShouldReturnPaginatedDataWi [Fact] public void Paginate_WhenExceededMaxPageNumber_ShouldReturnFailure() { + // Arrange var data = new List { 1, 2, 3, 4, 5 }; const int pageSize = 2; const int pageNumber = 3; + // Act var result = _paginationService.Paginate(data, pageSize, pageNumber); + // Assert Assert.True(result.IsError); Assert.Equal(StatusCodes.Status400BadRequest, result.StatusCode); Assert.Equal("'page' param must be <= 2, got: 3", result.ErrorMsg); @@ -111,12 +130,15 @@ public void Paginate_WhenExceededMaxPageNumber_ShouldReturnFailure() [Fact] public void Paginate_WhenOnLastPage_ShouldReturnHasNextPageSetToFalse() { + // Arrange var data = new List { 1, 2, 3, 4, 5 }; const int pageSize = 2; const int pageNumber = 2; + // Act var result = _paginationService.Paginate(data, pageSize, pageNumber); + // Assert Assert.True(result.IsSuccess); Assert.Equal(new List() { 5 }, result.Value?.Data); Assert.Equal(pageNumber, result.Value?.PageNumber); @@ -130,12 +152,15 @@ public void Paginate_WhenOnLastPage_ShouldReturnHasNextPageSetToFalse() [Fact] public void Paginate_WhenCollectionEmptyAndFirstPageIsRequested_ShouldReturnSuccess() { + // Arrange var data = new List(); const int pageSize = 2; const int pageNumber = 0; + // Act var result = _paginationService.Paginate(data, pageSize, pageNumber); + // Assert Assert.True(result.IsSuccess); Assert.Equal(new List(), result.Value?.Data); Assert.Equal(pageNumber, result.Value?.PageNumber); @@ -145,4 +170,25 @@ public void Paginate_WhenCollectionEmptyAndFirstPageIsRequested_ShouldReturnSucc Assert.Equal(data.Count, result.Value?.PaginationDetails.AllElementsCount); Assert.Equal(0, result.Value?.PaginationDetails.MaxPageNumber); } + + [Fact] + public void MapData_ShouldApplyLambdaToEachObject() + { + // Arrange + var data = new List() {1,2,3,4,5}; + var paginatedData = new PaginatedData(data, 0, 5, true, false, new PaginationDetails(1, 10)); + Func lambda = i => i * 2; + var expectedData = new List() { 2, 4, 6, 8, 10 }; + + // Act + var result = _paginationService.MapData(paginatedData, lambda); + + // Assert + Assert.Equal(expectedData, result.Data); + Assert.Equal(paginatedData.PageNumber, result.PageNumber); + Assert.Equal(paginatedData.PageSize, result.PageSize); + Assert.Equal(paginatedData.HasPreviousPage, result.HasPreviousPage); + Assert.Equal(paginatedData.HasNextPage, result.HasNextPage); + Assert.Equal(paginatedData.PaginationDetails, result.PaginationDetails); + } } \ No newline at end of file From a4c7df0518a150a0938f4bfb901efdc2f2a90f4b Mon Sep 17 00:00:00 2001 From: kTrzcinskii Date: Thu, 3 Apr 2025 17:32:48 +0200 Subject: [PATCH 03/14] Create DTOs for getting events as organizer --- .../DTOs/Response/GetEventResponseAddressDto.cs | 10 ++++++++++ .../DTOs/Response/GetEventResponseCategoryDto.cs | 5 +++++ .../Events/DTOs/Response/GetEventResponseDto.cs | 14 ++++++++++++++ 3 files changed, 29 insertions(+) create mode 100644 TickAPI/TickAPI/Events/DTOs/Response/GetEventResponseAddressDto.cs create mode 100644 TickAPI/TickAPI/Events/DTOs/Response/GetEventResponseCategoryDto.cs create mode 100644 TickAPI/TickAPI/Events/DTOs/Response/GetEventResponseDto.cs diff --git a/TickAPI/TickAPI/Events/DTOs/Response/GetEventResponseAddressDto.cs b/TickAPI/TickAPI/Events/DTOs/Response/GetEventResponseAddressDto.cs new file mode 100644 index 0000000..0f6f14f --- /dev/null +++ b/TickAPI/TickAPI/Events/DTOs/Response/GetEventResponseAddressDto.cs @@ -0,0 +1,10 @@ +namespace TickAPI.Events.DTOs.Response; + +public record GetEventResponseAddressDto( + string Country, + string City, + string PostalCode, + string? Stree, + uint? HouseNumber, + uint? FlatNumber +); diff --git a/TickAPI/TickAPI/Events/DTOs/Response/GetEventResponseCategoryDto.cs b/TickAPI/TickAPI/Events/DTOs/Response/GetEventResponseCategoryDto.cs new file mode 100644 index 0000000..4ba5d7a --- /dev/null +++ b/TickAPI/TickAPI/Events/DTOs/Response/GetEventResponseCategoryDto.cs @@ -0,0 +1,5 @@ +namespace TickAPI.Events.DTOs.Response; + +public record GetEventResponseCategoryDto( + string Name +); diff --git a/TickAPI/TickAPI/Events/DTOs/Response/GetEventResponseDto.cs b/TickAPI/TickAPI/Events/DTOs/Response/GetEventResponseDto.cs new file mode 100644 index 0000000..cafb0cc --- /dev/null +++ b/TickAPI/TickAPI/Events/DTOs/Response/GetEventResponseDto.cs @@ -0,0 +1,14 @@ +using TickAPI.Events.Models; + +namespace TickAPI.Events.DTOs.Response; + +public record GetEventResponseDto( + string Name, + string Description, + DateTime StartDate, + DateTime EndDate, + uint? Minimumage, + List Categories, + EventStatus Status, + GetEventResponseAddressDto Addres +); From f0117ac9ddc5536a41301eeb676b3b57ff9aed7d Mon Sep 17 00:00:00 2001 From: kTrzcinskii Date: Thu, 3 Apr 2025 17:34:10 +0200 Subject: [PATCH 04/14] Add function for getting organizer events --- .../Events/Services/EventServiceTests.cs | 21 ++++++++------- .../Events/Abstractions/IEventService.cs | 5 +++- .../TickAPI/Events/Services/EventService.cs | 27 +++++++++++++++++-- 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/TickAPI/TickAPI.Tests/Events/Services/EventServiceTests.cs b/TickAPI/TickAPI.Tests/Events/Services/EventServiceTests.cs index 939104b..5205ff3 100644 --- a/TickAPI/TickAPI.Tests/Events/Services/EventServiceTests.cs +++ b/TickAPI/TickAPI.Tests/Events/Services/EventServiceTests.cs @@ -1,18 +1,15 @@ -using System.Runtime.CompilerServices; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Http; using TickAPI.Events.Abstractions; -using TickAPI.Events.DTOs.Request; using Moq; using TickAPI.Addresses.Abstractions; using TickAPI.Addresses.DTOs.Request; using TickAPI.Addresses.Models; +using TickAPI.Common.Pagination.Abstractions; using TickAPI.Events.Models; using TickAPI.Organizers.Abstractions; using TickAPI.Organizers.Models; using TickAPI.Common.Results.Generic; using TickAPI.Common.Time.Abstractions; -using TickAPI.Events.DTOs.Response; using TickAPI.Events.Services; namespace TickAPI.Tests.Events.Services; @@ -59,7 +56,9 @@ public async Task CreateNewEventAsync_WhenEventDataIsValid_ShouldReturnNewEvent( var dateTimeServiceMock = new Mock(); dateTimeServiceMock.Setup(m => m.GetCurrentDateTime()).Returns(new DateTime(2003, 7, 11)); - var sut = new EventService(eventRepositoryMock.Object, organizerServiceMock.Object, addressServiceMock.Object, dateTimeServiceMock.Object); + var paginationServiceMock = new Mock(); + + var sut = new EventService(eventRepositoryMock.Object, organizerServiceMock.Object, addressServiceMock.Object, dateTimeServiceMock.Object, paginationServiceMock.Object); // act var result = await sut.CreateNewEventAsync(name, description, startDate, endDate, minimumAge, createAddress, eventStatus, organizerEmail); @@ -102,7 +101,9 @@ public async Task CreateNewEventAsync_WhenEndDateIsBeforeStartDate_ShouldReturnB var dateTimeServiceMock = new Mock(); - var sut = new EventService(eventRepositoryMock.Object, organizerServiceMock.Object, addressServiceMock.Object, dateTimeServiceMock.Object); + var paginationServiceMock = new Mock(); + + var sut = new EventService(eventRepositoryMock.Object, organizerServiceMock.Object, addressServiceMock.Object, dateTimeServiceMock.Object, paginationServiceMock.Object); // act var res = await sut.CreateNewEventAsync(name, description, startDate, endDate, minimumAge, createAddress, eventStatus, organizerEmail); @@ -139,7 +140,9 @@ public async Task CreateNewEventAsync_WhenStartDateIsBeforeNow_ShouldReturnBadRe var dateTimeServiceMock = new Mock(); dateTimeServiceMock.Setup(m => m.GetCurrentDateTime()).Returns(new DateTime(2025, 5, 11)); - var sut = new EventService(eventRepositoryMock.Object, organizerServiceMock.Object, addressServiceMock.Object, dateTimeServiceMock.Object); + var paginationServiceMock = new Mock(); + + var sut = new EventService(eventRepositoryMock.Object, organizerServiceMock.Object, addressServiceMock.Object, dateTimeServiceMock.Object, paginationServiceMock.Object); // act var res = await sut.CreateNewEventAsync(name, description, startDate, endDate, minimumAge, createAddress, eventStatus, organizerEmail); @@ -149,6 +152,4 @@ public async Task CreateNewEventAsync_WhenStartDateIsBeforeNow_ShouldReturnBadRe Assert.Equal(StatusCodes.Status400BadRequest, res.StatusCode); Assert.Equal("Start date is in the past", res.ErrorMsg); } - - } \ No newline at end of file diff --git a/TickAPI/TickAPI/Events/Abstractions/IEventService.cs b/TickAPI/TickAPI/Events/Abstractions/IEventService.cs index 1a2e531..8aee8db 100644 --- a/TickAPI/TickAPI/Events/Abstractions/IEventService.cs +++ b/TickAPI/TickAPI/Events/Abstractions/IEventService.cs @@ -1,7 +1,9 @@ using TickAPI.Addresses.DTOs.Request; +using TickAPI.Common.Pagination.Responses; using TickAPI.Events.Models; using TickAPI.Common.Results.Generic; -using TickAPI.Events.DTOs.Request; +using TickAPI.Events.DTOs.Response; +using TickAPI.Organizers.Models; namespace TickAPI.Events.Abstractions; @@ -9,4 +11,5 @@ public interface IEventService { public Task> CreateNewEventAsync(string name, string description, DateTime startDate, DateTime endDate, uint? minimumAge, CreateAddressDto createAddress, EventStatus eventStatus, string organizerEmail); + public Result> GetOrganizerEvents(Organizer organizer, int page, int pageSize); } \ No newline at end of file diff --git a/TickAPI/TickAPI/Events/Services/EventService.cs b/TickAPI/TickAPI/Events/Services/EventService.cs index 7f38590..30dafd2 100644 --- a/TickAPI/TickAPI/Events/Services/EventService.cs +++ b/TickAPI/TickAPI/Events/Services/EventService.cs @@ -1,11 +1,14 @@ using TickAPI.Addresses.Abstractions; using TickAPI.Addresses.DTOs.Request; +using TickAPI.Common.Pagination.Abstractions; +using TickAPI.Common.Pagination.Responses; using TickAPI.Common.Time.Abstractions; using TickAPI.Events.Abstractions; using TickAPI.Events.Models; using TickAPI.Common.Results.Generic; +using TickAPI.Events.DTOs.Response; using TickAPI.Organizers.Abstractions; -using TickAPI.Events.DTOs.Request; +using TickAPI.Organizers.Models; namespace TickAPI.Events.Services; @@ -15,13 +18,15 @@ public class EventService : IEventService private readonly IEventRepository _eventRepository; private readonly IAddressService _addressService; private readonly IDateTimeService _dateTimeService; + private readonly IPaginationService _paginationService; - public EventService(IEventRepository eventRepository, IOrganizerService organizerService, IAddressService addressService, IDateTimeService dateTimeService) + public EventService(IEventRepository eventRepository, IOrganizerService organizerService, IAddressService addressService, IDateTimeService dateTimeService, IPaginationService paginationService) { _eventRepository = eventRepository; _organizerService = organizerService; _addressService = addressService; _dateTimeService = dateTimeService; + _paginationService = paginationService; } public async Task> CreateNewEventAsync(string name, string description, DateTime startDate, DateTime endDate, uint? minimumAge, CreateAddressDto createAddress, EventStatus eventStatus, string organizerEmail) @@ -54,4 +59,22 @@ public async Task> CreateNewEventAsync(string name, string descri await _eventRepository.AddNewEventAsync(@event); return Result.Success(@event); } + + public Result> GetOrganizerEvents(Organizer organizer, int page, int pageSize) + { + var paginatedEventsResult = _paginationService.Paginate(organizer.Events, pageSize, page); + if (paginatedEventsResult.IsError) + { + return Result>.PropagateError(paginatedEventsResult); + } + + var paginatedData = _paginationService.MapData(paginatedEventsResult.Value!, ev => + { + var categories = ev.Categories.Select((c) => new GetEventResponseCategoryDto(c.CategoryName)).ToList(); + var address = new GetEventResponseAddressDto(ev.Address.Country, ev.Address.City, ev.Address.PostalCode, ev.Address.Street, ev.Address.HouseNumber, ev.Address.FlatNumber); + return new GetEventResponseDto(ev.Name, ev.Description, ev.StartDate, ev.EndDate, ev.MinimumAge, categories, ev.EventStatus, address); + }); + + return Result>.Success(paginatedData); + } } \ No newline at end of file From ff401f19bcaf3032032566414e72ff94af854098 Mon Sep 17 00:00:00 2001 From: kTrzcinskii Date: Thu, 3 Apr 2025 17:34:35 +0200 Subject: [PATCH 05/14] Setup endpoint for getting events as organizer --- .../Controllers/EventControllerTests.cs | 9 ++++-- .../Events/Controllers/EventController.cs | 29 +++++++++++++++++-- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/TickAPI/TickAPI.Tests/Events/Controllers/EventControllerTests.cs b/TickAPI/TickAPI.Tests/Events/Controllers/EventControllerTests.cs index 3853bb2..17c9e7d 100644 --- a/TickAPI/TickAPI.Tests/Events/Controllers/EventControllerTests.cs +++ b/TickAPI/TickAPI.Tests/Events/Controllers/EventControllerTests.cs @@ -10,6 +10,7 @@ using TickAPI.Events.Abstractions; using TickAPI.Common.Results.Generic; using TickAPI.Events.DTOs.Response; +using TickAPI.Organizers.Abstractions; namespace TickAPI.Tests.Events.Controllers; @@ -49,8 +50,10 @@ public async Task CreateEvent_WhenDataIsValid_ShouldReturnSuccess() var claimsServiceMock = new Mock(); claimsServiceMock.Setup(m => m.GetEmailFromClaims(controllerContext.HttpContext.User.Claims)).Returns(Result.Success(email)); + + var organizerServiceMock = new Mock(); - var sut = new EventController(eventServiceMock.Object, claimsServiceMock.Object); + var sut = new EventController(eventServiceMock.Object, claimsServiceMock.Object, organizerServiceMock.Object); sut.ControllerContext = controllerContext; @@ -81,7 +84,9 @@ public async Task CreateEvent_WhenMissingEmailClaims_ShouldReturnBadRequest() var claimsServiceMock = new Mock(); claimsServiceMock.Setup(m => m.GetEmailFromClaims(It.IsAny>())).Returns(Result.Failure(StatusCodes.Status400BadRequest, "missing email claim")); - var sut = new EventController(eventServiceMock.Object, claimsServiceMock.Object); + var organizerServiceMock = new Mock(); + + var sut = new EventController(eventServiceMock.Object, claimsServiceMock.Object, organizerServiceMock.Object); sut.ControllerContext = new ControllerContext { diff --git a/TickAPI/TickAPI/Events/Controllers/EventController.cs b/TickAPI/TickAPI/Events/Controllers/EventController.cs index f75f70d..52355f2 100644 --- a/TickAPI/TickAPI/Events/Controllers/EventController.cs +++ b/TickAPI/TickAPI/Events/Controllers/EventController.cs @@ -6,6 +6,7 @@ using TickAPI.Common.Claims.Abstractions; using TickAPI.Common.Pagination.Responses; using TickAPI.Events.Abstractions; +using TickAPI.Organizers.Abstractions; namespace TickAPI.Events.Controllers; @@ -17,11 +18,13 @@ public class EventController : ControllerBase { private readonly IEventService _eventService; private readonly IClaimsService _claimsService; + private readonly IOrganizerService _organizerService; - public EventController(IEventService eventService, IClaimsService claimsService) + public EventController(IEventService eventService, IClaimsService claimsService, IOrganizerService organizerService) { _eventService = eventService; _claimsService = claimsService; + _organizerService = organizerService; } [AuthorizeWithPolicy(AuthPolicies.VerifiedOrganizerPolicy)] @@ -45,8 +48,28 @@ public async Task> CreateEvent([FromBody] C [AuthorizeWithPolicy(AuthPolicies.VerifiedOrganizerPolicy)] [HttpGet("get-organizer-events")] - public async Task>> GetOrganizerEvents() + public async Task>> GetOrganizerEvents([FromQuery] int pageSize, [FromQuery] int page) { - throw new NotImplementedException(); + var emailResult = _claimsService.GetEmailFromClaims(User.Claims); + if (emailResult.IsError) + { + return StatusCode(emailResult.StatusCode, emailResult.ErrorMsg); + } + var email = emailResult.Value!; + + var organizerResult = await _organizerService.GetOrganizerByEmailAsync(email); + if (organizerResult.IsError) + { + return StatusCode(organizerResult.StatusCode, organizerResult.ErrorMsg); + } + var organizer = organizerResult.Value!; + + var paginatedDataResult = _eventService.GetOrganizerEvents(organizer, page, pageSize); + if (paginatedDataResult.IsError) + { + return StatusCode(paginatedDataResult.StatusCode, paginatedDataResult.ErrorMsg); + } + + return Ok(paginatedDataResult.Value!); } } \ No newline at end of file From feb09d8a3adee7cf9c0e2551e7f963d073d9c84e Mon Sep 17 00:00:00 2001 From: kTrzcinskii Date: Thu, 3 Apr 2025 17:41:04 +0200 Subject: [PATCH 06/14] Add endpoint for getting organizer events pagination details --- .../Events/Abstractions/IEventService.cs | 1 + .../Events/Controllers/EventController.cs | 27 +++++++++++++++++++ .../TickAPI/Events/Services/EventService.cs | 5 ++++ 3 files changed, 33 insertions(+) diff --git a/TickAPI/TickAPI/Events/Abstractions/IEventService.cs b/TickAPI/TickAPI/Events/Abstractions/IEventService.cs index 8aee8db..a7876d8 100644 --- a/TickAPI/TickAPI/Events/Abstractions/IEventService.cs +++ b/TickAPI/TickAPI/Events/Abstractions/IEventService.cs @@ -12,4 +12,5 @@ public interface IEventService public Task> CreateNewEventAsync(string name, string description, DateTime startDate, DateTime endDate, uint? minimumAge, CreateAddressDto createAddress, EventStatus eventStatus, string organizerEmail); public Result> GetOrganizerEvents(Organizer organizer, int page, int pageSize); + public Result GetOrganizerEventsPaginationDetails(Organizer organizer, int pageSize); } \ No newline at end of file diff --git a/TickAPI/TickAPI/Events/Controllers/EventController.cs b/TickAPI/TickAPI/Events/Controllers/EventController.cs index 52355f2..1b2cf50 100644 --- a/TickAPI/TickAPI/Events/Controllers/EventController.cs +++ b/TickAPI/TickAPI/Events/Controllers/EventController.cs @@ -72,4 +72,31 @@ public async Task>> GetOrganizer return Ok(paginatedDataResult.Value!); } + + [AuthorizeWithPolicy(AuthPolicies.VerifiedOrganizerPolicy)] + [HttpGet("get-organizer-events-pagination-details")] + public async Task> GetOrganizerEventsPaginationDetails([FromQuery] int pageSize) + { + var emailResult = _claimsService.GetEmailFromClaims(User.Claims); + if (emailResult.IsError) + { + return StatusCode(emailResult.StatusCode, emailResult.ErrorMsg); + } + var email = emailResult.Value!; + + var organizerResult = await _organizerService.GetOrganizerByEmailAsync(email); + if (organizerResult.IsError) + { + return StatusCode(organizerResult.StatusCode, organizerResult.ErrorMsg); + } + var organizer = organizerResult.Value!; + + var paginationDetailsResult = _eventService.GetOrganizerEventsPaginationDetails(organizer, pageSize); + if (paginationDetailsResult.IsError) + { + return StatusCode(paginationDetailsResult.StatusCode, paginationDetailsResult.ErrorMsg); + } + + return Ok(paginationDetailsResult.Value!); + } } \ No newline at end of file diff --git a/TickAPI/TickAPI/Events/Services/EventService.cs b/TickAPI/TickAPI/Events/Services/EventService.cs index 30dafd2..498ac09 100644 --- a/TickAPI/TickAPI/Events/Services/EventService.cs +++ b/TickAPI/TickAPI/Events/Services/EventService.cs @@ -77,4 +77,9 @@ public Result> GetOrganizerEvents(Organizer o return Result>.Success(paginatedData); } + + public Result GetOrganizerEventsPaginationDetails(Organizer organizer, int pageSize) + { + return _paginationService.GetPaginationDetails(organizer.Events!, pageSize); + } } \ No newline at end of file From 4cf852c9a5957ca0af406f49b7bfd416d181cca3 Mon Sep 17 00:00:00 2001 From: kTrzcinskii Date: Thu, 3 Apr 2025 20:39:01 +0200 Subject: [PATCH 07/14] Add tests for `GetOrganizerEvents` --- .../Events/Services/EventServiceTests.cs | 173 ++++++++++++++++-- 1 file changed, 161 insertions(+), 12 deletions(-) diff --git a/TickAPI/TickAPI.Tests/Events/Services/EventServiceTests.cs b/TickAPI/TickAPI.Tests/Events/Services/EventServiceTests.cs index 5205ff3..e46c6ed 100644 --- a/TickAPI/TickAPI.Tests/Events/Services/EventServiceTests.cs +++ b/TickAPI/TickAPI.Tests/Events/Services/EventServiceTests.cs @@ -4,12 +4,15 @@ using TickAPI.Addresses.Abstractions; using TickAPI.Addresses.DTOs.Request; using TickAPI.Addresses.Models; +using TickAPI.Categories.Models; using TickAPI.Common.Pagination.Abstractions; +using TickAPI.Common.Pagination.Responses; using TickAPI.Events.Models; using TickAPI.Organizers.Abstractions; using TickAPI.Organizers.Models; using TickAPI.Common.Results.Generic; using TickAPI.Common.Time.Abstractions; +using TickAPI.Events.DTOs.Response; using TickAPI.Events.Services; namespace TickAPI.Tests.Events.Services; @@ -20,7 +23,7 @@ public class EventServiceTests public async Task CreateNewEventAsync_WhenEventDataIsValid_ShouldReturnNewEvent() { - // arrange + // Arrange string name = "Concert"; string description = "Description of a concert"; DateTime startDate = new DateTime(2025, 5, 1); @@ -59,13 +62,11 @@ public async Task CreateNewEventAsync_WhenEventDataIsValid_ShouldReturnNewEvent( var paginationServiceMock = new Mock(); var sut = new EventService(eventRepositoryMock.Object, organizerServiceMock.Object, addressServiceMock.Object, dateTimeServiceMock.Object, paginationServiceMock.Object); - // act + // Act var result = await sut.CreateNewEventAsync(name, description, startDate, endDate, minimumAge, createAddress, eventStatus, organizerEmail); - // assert - - + // Assert Assert.True(result.IsSuccess); Assert.Equal(new DateTime(2025, 5, 1), result.Value!.StartDate); Assert.Equal(new DateTime(2025, 6, 1), result.Value!.EndDate); @@ -79,7 +80,7 @@ public async Task CreateNewEventAsync_WhenEventDataIsValid_ShouldReturnNewEvent( [Fact] public async Task CreateNewEventAsync_WhenEndDateIsBeforeStartDate_ShouldReturnBadRequest() { - // arrange + // Arrange string name = "Concert"; string description = "Description of a concert"; DateTime startDate = new DateTime(2025, 8, 1); @@ -104,11 +105,11 @@ public async Task CreateNewEventAsync_WhenEndDateIsBeforeStartDate_ShouldReturnB var paginationServiceMock = new Mock(); var sut = new EventService(eventRepositoryMock.Object, organizerServiceMock.Object, addressServiceMock.Object, dateTimeServiceMock.Object, paginationServiceMock.Object); - // act + // Act var res = await sut.CreateNewEventAsync(name, description, startDate, endDate, minimumAge, createAddress, eventStatus, organizerEmail); - // assert + // Assert Assert.False(res.IsSuccess); Assert.Equal(StatusCodes.Status400BadRequest, res.StatusCode); Assert.Equal("End date should be after start date", res.ErrorMsg); @@ -117,7 +118,7 @@ public async Task CreateNewEventAsync_WhenEndDateIsBeforeStartDate_ShouldReturnB [Fact] public async Task CreateNewEventAsync_WhenStartDateIsBeforeNow_ShouldReturnBadRequest() { - // arrange + // Arrange string name = "Concert"; string description = "Description of a concert"; DateTime startDate = new DateTime(2025, 5, 1); @@ -125,7 +126,6 @@ public async Task CreateNewEventAsync_WhenStartDateIsBeforeNow_ShouldReturnBadRe uint? minimumAge = 18; string organizerEmail = "123@mail.com"; EventStatus eventStatus = EventStatus.TicketsAvailable; - Guid id = Guid.NewGuid(); CreateAddressDto createAddress = new CreateAddressDto("United States", "New York", "Main st", 20, null, "00-000"); var eventRepositoryMock = new Mock(); @@ -143,13 +143,162 @@ public async Task CreateNewEventAsync_WhenStartDateIsBeforeNow_ShouldReturnBadRe var paginationServiceMock = new Mock(); var sut = new EventService(eventRepositoryMock.Object, organizerServiceMock.Object, addressServiceMock.Object, dateTimeServiceMock.Object, paginationServiceMock.Object); - // act + // Act var res = await sut.CreateNewEventAsync(name, description, startDate, endDate, minimumAge, createAddress, eventStatus, organizerEmail); - // assert + // Assert Assert.False(res.IsSuccess); Assert.Equal(StatusCodes.Status400BadRequest, res.StatusCode); Assert.Equal("Start date is in the past", res.ErrorMsg); } + + [Fact] + public void GetOrganizerEvents_WhenPaginationSucceeds_ShouldReturnPaginatedEvents() + { + // Arrange + var organizer = new Organizer + { + Email = "organizer@example.com", + IsVerified = true, + Events = new List + { + CreateSampleEvent("Event 1"), + CreateSampleEvent("Event 2"), + CreateSampleEvent("Event 3") + } + }; + int page = 0; + int pageSize = 2; + + var eventRepositoryMock = new Mock(); + var organizerServiceMock = new Mock(); + var addressServiceMock = new Mock(); + var dateTimeServiceMock = new Mock(); + var paginationServiceMock = new Mock(); + + var paginatedEvents = new PaginatedData( + organizer.Events.Take(pageSize).ToList(), + page, + pageSize, + true, + false, + new PaginationDetails(1, 3) + ); + + paginationServiceMock + .Setup(p => p.Paginate(organizer.Events, pageSize, page)) + .Returns(Result>.Success(paginatedEvents)); + + paginationServiceMock + .Setup(p => p.MapData(paginatedEvents, It.IsAny>())) + .Returns(new PaginatedData( + new List + { + CreateSampleEventResponseDto("Event 1"), + CreateSampleEventResponseDto("Event 2") + }, + page, + pageSize, + true, + false, + new PaginationDetails(1, 3) + )); + + var sut = new EventService(eventRepositoryMock.Object, organizerServiceMock.Object, addressServiceMock.Object, + dateTimeServiceMock.Object, paginationServiceMock.Object); + + // Act + var result = sut.GetOrganizerEvents(organizer, page, pageSize); + + // Assert + Assert.True(result.IsSuccess); + Assert.Equal(2, result.Value!.Data.Count); + Assert.Equal("Event 1", result.Value!.Data[0].Name); + Assert.Equal("Event 2", result.Value!.Data[1].Name); + Assert.Equal(0, result.Value!.PageNumber); + Assert.Equal(2, result.Value!.PageSize); + Assert.True(result.Value!.HasNextPage); + Assert.False(result.Value!.HasPreviousPage); + Assert.Equal(1, result.Value!.PaginationDetails.MaxPageNumber); + Assert.Equal(3, result.Value!.PaginationDetails.AllElementsCount); + } + + [Fact] + public void GetOrganizerEvents_WhenPaginationFails_ShouldPropagateError() + { + // Arrange + var organizer = new Organizer + { + Email = "organizer@example.com", + IsVerified = true, + Events = new List + { + CreateSampleEvent("Event 1"), + CreateSampleEvent("Event 2") + } + }; + int page = 2; // Invalid page + int pageSize = 2; + + var eventRepositoryMock = new Mock(); + var organizerServiceMock = new Mock(); + var addressServiceMock = new Mock(); + var dateTimeServiceMock = new Mock(); + var paginationServiceMock = new Mock(); + + paginationServiceMock + .Setup(p => p.Paginate(organizer.Events, pageSize, page)) + .Returns(Result>.Failure(StatusCodes.Status400BadRequest, "Invalid page number")); + + var sut = new EventService(eventRepositoryMock.Object, organizerServiceMock.Object, addressServiceMock.Object, + dateTimeServiceMock.Object, paginationServiceMock.Object); + + // Act + var result = sut.GetOrganizerEvents(organizer, page, pageSize); + + // Assert + Assert.False(result.IsSuccess); + Assert.Equal(StatusCodes.Status400BadRequest, result.StatusCode); + Assert.Equal("Invalid page number", result.ErrorMsg); + } + + // Helper methods + private Event CreateSampleEvent(string name) + { + return new Event + { + Id = Guid.NewGuid(), + Name = name, + Description = $"Description of {name}", + StartDate = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc), + EndDate = new DateTime(1970, 1, 2, 0, 0, 0, DateTimeKind.Utc), + MinimumAge = 18, + EventStatus = EventStatus.TicketsAvailable, + Categories = new List { new Category { CategoryName = "Test" } }, + Address = new Address + { + Country = "United States", + City = "New York", + PostalCode = "10001", + Street = "Main St", + HouseNumber = 123, + FlatNumber = null + } + }; + } + + private GetEventResponseDto CreateSampleEventResponseDto(string name) + { + return new GetEventResponseDto( + name, + $"Description of {name}", + new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc), + new DateTime(1970, 1, 2, 0, 0, 0, DateTimeKind.Utc), + 18, + [new GetEventResponseCategoryDto("Test")], + EventStatus.TicketsAvailable, + new GetEventResponseAddressDto("United States", "New York", "10001", "Main St", 123, null) + ); + } } \ No newline at end of file From c1fdb6d17eb408ba37aed0ad00dce033b769c93f Mon Sep 17 00:00:00 2001 From: kTrzcinskii Date: Thu, 3 Apr 2025 20:53:22 +0200 Subject: [PATCH 08/14] Move helpers to `Utils` class --- .../Events/Services/EventServiceTests.cs | 53 +++---------------- TickAPI/TickAPI.Tests/Events/Utils.cs | 47 ++++++++++++++++ 2 files changed, 54 insertions(+), 46 deletions(-) create mode 100644 TickAPI/TickAPI.Tests/Events/Utils.cs diff --git a/TickAPI/TickAPI.Tests/Events/Services/EventServiceTests.cs b/TickAPI/TickAPI.Tests/Events/Services/EventServiceTests.cs index e46c6ed..d9e0d7d 100644 --- a/TickAPI/TickAPI.Tests/Events/Services/EventServiceTests.cs +++ b/TickAPI/TickAPI.Tests/Events/Services/EventServiceTests.cs @@ -163,9 +163,9 @@ public void GetOrganizerEvents_WhenPaginationSucceeds_ShouldReturnPaginatedEvent IsVerified = true, Events = new List { - CreateSampleEvent("Event 1"), - CreateSampleEvent("Event 2"), - CreateSampleEvent("Event 3") + Utils.CreateSampleEvent("Event 1"), + Utils.CreateSampleEvent("Event 2"), + Utils.CreateSampleEvent("Event 3") } }; int page = 0; @@ -195,8 +195,8 @@ public void GetOrganizerEvents_WhenPaginationSucceeds_ShouldReturnPaginatedEvent .Returns(new PaginatedData( new List { - CreateSampleEventResponseDto("Event 1"), - CreateSampleEventResponseDto("Event 2") + Utils.CreateSampleEventResponseDto("Event 1"), + Utils.CreateSampleEventResponseDto("Event 2") }, page, pageSize, @@ -234,8 +234,8 @@ public void GetOrganizerEvents_WhenPaginationFails_ShouldPropagateError() IsVerified = true, Events = new List { - CreateSampleEvent("Event 1"), - CreateSampleEvent("Event 2") + Utils.CreateSampleEvent("Event 1"), + Utils.CreateSampleEvent("Event 2") } }; int page = 2; // Invalid page @@ -262,43 +262,4 @@ public void GetOrganizerEvents_WhenPaginationFails_ShouldPropagateError() Assert.Equal(StatusCodes.Status400BadRequest, result.StatusCode); Assert.Equal("Invalid page number", result.ErrorMsg); } - - // Helper methods - private Event CreateSampleEvent(string name) - { - return new Event - { - Id = Guid.NewGuid(), - Name = name, - Description = $"Description of {name}", - StartDate = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc), - EndDate = new DateTime(1970, 1, 2, 0, 0, 0, DateTimeKind.Utc), - MinimumAge = 18, - EventStatus = EventStatus.TicketsAvailable, - Categories = new List { new Category { CategoryName = "Test" } }, - Address = new Address - { - Country = "United States", - City = "New York", - PostalCode = "10001", - Street = "Main St", - HouseNumber = 123, - FlatNumber = null - } - }; - } - - private GetEventResponseDto CreateSampleEventResponseDto(string name) - { - return new GetEventResponseDto( - name, - $"Description of {name}", - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc), - new DateTime(1970, 1, 2, 0, 0, 0, DateTimeKind.Utc), - 18, - [new GetEventResponseCategoryDto("Test")], - EventStatus.TicketsAvailable, - new GetEventResponseAddressDto("United States", "New York", "10001", "Main St", 123, null) - ); - } } \ No newline at end of file diff --git a/TickAPI/TickAPI.Tests/Events/Utils.cs b/TickAPI/TickAPI.Tests/Events/Utils.cs new file mode 100644 index 0000000..252568a --- /dev/null +++ b/TickAPI/TickAPI.Tests/Events/Utils.cs @@ -0,0 +1,47 @@ +using TickAPI.Addresses.Models; +using TickAPI.Categories.Models; +using TickAPI.Events.DTOs.Response; +using TickAPI.Events.Models; + +namespace TickAPI.Tests.Events; + +public static class Utils +{ + public static Event CreateSampleEvent(string name) + { + return new Event + { + Id = Guid.NewGuid(), + Name = name, + Description = $"Description of {name}", + StartDate = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc), + EndDate = new DateTime(1970, 1, 2, 0, 0, 0, DateTimeKind.Utc), + MinimumAge = 18, + EventStatus = EventStatus.TicketsAvailable, + Categories = new List { new Category { CategoryName = "Test" } }, + Address = new Address + { + Country = "United States", + City = "New York", + PostalCode = "10001", + Street = "Main St", + HouseNumber = 123, + FlatNumber = null + } + }; + } + + public static GetEventResponseDto CreateSampleEventResponseDto(string name) + { + return new GetEventResponseDto( + name, + $"Description of {name}", + new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc), + new DateTime(1970, 1, 2, 0, 0, 0, DateTimeKind.Utc), + 18, + [new GetEventResponseCategoryDto("Test")], + EventStatus.TicketsAvailable, + new GetEventResponseAddressDto("United States", "New York", "10001", "Main St", 123, null) + ); + } +} From ca3ea72c67fc81c335a62c8494df14eddfc58279 Mon Sep 17 00:00:00 2001 From: kTrzcinskii Date: Thu, 3 Apr 2025 20:54:59 +0200 Subject: [PATCH 09/14] Add tests for `GetOrganizerEvents` --- .../Controllers/EventControllerTests.cs | 236 ++++++++++++++++-- 1 file changed, 221 insertions(+), 15 deletions(-) diff --git a/TickAPI/TickAPI.Tests/Events/Controllers/EventControllerTests.cs b/TickAPI/TickAPI.Tests/Events/Controllers/EventControllerTests.cs index 17c9e7d..1b60012 100644 --- a/TickAPI/TickAPI.Tests/Events/Controllers/EventControllerTests.cs +++ b/TickAPI/TickAPI.Tests/Events/Controllers/EventControllerTests.cs @@ -6,11 +6,13 @@ using Microsoft.AspNetCore.Mvc; using TickAPI.Addresses.DTOs.Request; using TickAPI.Common.Claims.Abstractions; +using TickAPI.Common.Pagination.Responses; using TickAPI.Events.Controllers; using TickAPI.Events.Abstractions; using TickAPI.Common.Results.Generic; using TickAPI.Events.DTOs.Response; using TickAPI.Organizers.Abstractions; +using TickAPI.Organizers.Models; namespace TickAPI.Tests.Events.Controllers; @@ -19,14 +21,14 @@ public class EventControllerTests [Fact] public async Task CreateEvent_WhenDataIsValid_ShouldReturnSuccess() { - //arrange - string name = "Concert"; - string description = "Description of a concert"; + // Arrange + const string name = "Concert"; + const string description = "Description of a concert"; DateTime startDate = new DateTime(2025, 5, 1); DateTime endDate = new DateTime(2025, 6, 1); uint? minimumAge = 18; - string email = "123@mail.com"; - EventStatus eventStatus = EventStatus.TicketsAvailable; + const string email = "123@mail.com"; + const EventStatus eventStatus = EventStatus.TicketsAvailable; Guid id = Guid.NewGuid(); CreateAddressDto createAddress = new CreateAddressDto("United States", "New York", "Main st", 20, null, "00-000"); CreateEventDto eventDto = new CreateEventDto(name, description, startDate, endDate, minimumAge, eventStatus, createAddress); @@ -57,10 +59,10 @@ public async Task CreateEvent_WhenDataIsValid_ShouldReturnSuccess() sut.ControllerContext = controllerContext; - // act + // Act var res = await sut.CreateEvent(eventDto); - // assert + // Assert var result = Assert.IsType>(res); var objectResult = Assert.IsType(result.Result); Assert.Equal(200, objectResult.StatusCode); @@ -70,14 +72,13 @@ public async Task CreateEvent_WhenDataIsValid_ShouldReturnSuccess() [Fact] public async Task CreateEvent_WhenMissingEmailClaims_ShouldReturnBadRequest() { - //arrange - string name = "Concert"; - string description = "Description of a concert"; + // Arrange + const string name = "Concert"; + const string description = "Description of a concert"; DateTime startDate = new DateTime(2025, 5, 1); DateTime endDate = new DateTime(2025, 6, 1); uint? minimumAge = 18; - string email = "123@mail.com"; - EventStatus eventStatus = EventStatus.TicketsAvailable; + const EventStatus eventStatus = EventStatus.TicketsAvailable; CreateAddressDto createAddress = new CreateAddressDto("United States", "New York", "Main st", 20, null, "00-000"); var eventServiceMock = new Mock(); @@ -96,14 +97,219 @@ public async Task CreateEvent_WhenMissingEmailClaims_ShouldReturnBadRequest() } }; - // act + // Act var res = await sut.CreateEvent(new CreateEventDto(name, description, startDate, endDate, minimumAge, eventStatus, createAddress)); - // assert + // Assert var result = Assert.IsType>(res); var objectResult = Assert.IsType(result.Result); Assert.Equal(StatusCodes.Status400BadRequest, objectResult.StatusCode); Assert.Equal("missing email claim", objectResult.Value); - + } + + [Fact] + public async Task GetOrganizerEvents_WhenAllOperationsSucceed_ShouldReturnOkWithPaginatedData() + { + // Arrange + const string email = "organizer@example.com"; + const int page = 0; + const int pageSize = 10; + + var claims = new List + { + new Claim(ClaimTypes.Email, email) + }; + + var controllerContext = new ControllerContext + { + HttpContext = new DefaultHttpContext + { + User = new ClaimsPrincipal(new ClaimsIdentity(claims)) + } + }; + + var claimsServiceMock = new Mock(); + claimsServiceMock + .Setup(m => m.GetEmailFromClaims(controllerContext.HttpContext.User.Claims)) + .Returns(Result.Success(email)); + + var organizer = new Organizer { Email = email, IsVerified = true }; + + var organizerServiceMock = new Mock(); + organizerServiceMock + .Setup(m => m.GetOrganizerByEmailAsync(email)) + .ReturnsAsync(Result.Success(organizer)); + + var paginatedData = new PaginatedData( + new List + { + Utils.CreateSampleEventResponseDto("Event 1"), + Utils.CreateSampleEventResponseDto("Event 2") + }, + page, + pageSize, + false, + false, + new PaginationDetails(0, 2) + ); + + var eventServiceMock = new Mock(); + eventServiceMock + .Setup(m => m.GetOrganizerEvents(organizer, page, pageSize)) + .Returns(Result>.Success(paginatedData)); + + var sut = new EventController(eventServiceMock.Object, claimsServiceMock.Object, organizerServiceMock.Object); + sut.ControllerContext = controllerContext; + + // Act + var response = await sut.GetOrganizerEvents(pageSize, page); + + // Assert + var result = Assert.IsType>>(response); + var okResult = Assert.IsType(result.Result); + Assert.Equal(StatusCodes.Status200OK, okResult.StatusCode); + + var returnedPaginatedData = Assert.IsType>(okResult.Value); + Assert.Equal(2, returnedPaginatedData.Data.Count); + Assert.Equal(paginatedData.Data[0], returnedPaginatedData.Data[0]); + Assert.Equal(paginatedData.Data[1], returnedPaginatedData.Data[1]); + Assert.Equal(page, returnedPaginatedData.PageNumber); + Assert.Equal(pageSize, returnedPaginatedData.PageSize); + Assert.False(returnedPaginatedData.HasNextPage); + Assert.False(returnedPaginatedData.HasPreviousPage); + } + + [Fact] + public async Task GetOrganizerEvents_WhenEmailClaimIsMissing_ShouldReturnBadRequest() + { + // Arrange + const int page = 0; + const int pageSize = 10; + const string errorMessage = "Missing email claim"; + + var claimsServiceMock = new Mock(); + claimsServiceMock + .Setup(m => m.GetEmailFromClaims(It.IsAny>())) + .Returns(Result.Failure(StatusCodes.Status400BadRequest, errorMessage)); + + var eventServiceMock = new Mock(); + var organizerServiceMock = new Mock(); + + var sut = new EventController(eventServiceMock.Object, claimsServiceMock.Object, organizerServiceMock.Object); + sut.ControllerContext = new ControllerContext + { + HttpContext = new DefaultHttpContext + { + User = new ClaimsPrincipal(new ClaimsIdentity()) + } + }; + + // Act + var response = await sut.GetOrganizerEvents(pageSize, page); + + // Assert + var result = Assert.IsType>>(response); + var objectResult = Assert.IsType(result.Result); + Assert.Equal(StatusCodes.Status400BadRequest, objectResult.StatusCode); + Assert.Equal(errorMessage, objectResult.Value); + } + + [Fact] + public async Task GetOrganizerEvents_WhenOrganizerIsNotFound_ShouldReturnNotFound() + { + // Arrange + const string email = "organizer@example.com"; + const int page = 0; + const int pageSize = 10; + const string errorMessage = "Organizer not found"; + + var claims = new List + { + new Claim(ClaimTypes.Email, email) + }; + + var controllerContext = new ControllerContext + { + HttpContext = new DefaultHttpContext + { + User = new ClaimsPrincipal(new ClaimsIdentity(claims)) + } + }; + + var claimsServiceMock = new Mock(); + claimsServiceMock + .Setup(m => m.GetEmailFromClaims(controllerContext.HttpContext.User.Claims)) + .Returns(Result.Success(email)); + + var organizerServiceMock = new Mock(); + organizerServiceMock + .Setup(m => m.GetOrganizerByEmailAsync(email)) + .ReturnsAsync(Result.Failure(StatusCodes.Status404NotFound, errorMessage)); + + var eventServiceMock = new Mock(); + + var sut = new EventController(eventServiceMock.Object, claimsServiceMock.Object, organizerServiceMock.Object); + sut.ControllerContext = controllerContext; + + // Act + var response = await sut.GetOrganizerEvents(pageSize, page); + + // Assert + var result = Assert.IsType>>(response); + var objectResult = Assert.IsType(result.Result); + Assert.Equal(StatusCodes.Status404NotFound, objectResult.StatusCode); + Assert.Equal(errorMessage, objectResult.Value); + } + + [Fact] + public async Task GetOrganizerEvents_WhenPaginationFails_ShouldReturnBadRequest() + { + // Arrange + const string email = "organizer@example.com"; + const int page = -1; // Invalid page + const int pageSize = 10; + const string errorMessage = "Invalid page number"; + + var claims = new List + { + new Claim(ClaimTypes.Email, email) + }; + + var controllerContext = new ControllerContext + { + HttpContext = new DefaultHttpContext + { + User = new ClaimsPrincipal(new ClaimsIdentity(claims)) + } + }; + + var claimsServiceMock = new Mock(); + claimsServiceMock + .Setup(m => m.GetEmailFromClaims(controllerContext.HttpContext.User.Claims)) + .Returns(Result.Success(email)); + + var organizer = new Organizer { Email = email, IsVerified = true }; + + var organizerServiceMock = new Mock(); + organizerServiceMock + .Setup(m => m.GetOrganizerByEmailAsync(email)) + .ReturnsAsync(Result.Success(organizer)); + + var eventServiceMock = new Mock(); + eventServiceMock + .Setup(m => m.GetOrganizerEvents(organizer, page, pageSize)) + .Returns(Result>.Failure(StatusCodes.Status400BadRequest, errorMessage)); + + var sut = new EventController(eventServiceMock.Object, claimsServiceMock.Object, organizerServiceMock.Object); + sut.ControllerContext = controllerContext; + + // Act + var response = await sut.GetOrganizerEvents(pageSize, page); + + // Assert + var result = Assert.IsType>>(response); + var objectResult = Assert.IsType(result.Result); + Assert.Equal(StatusCodes.Status400BadRequest, objectResult.StatusCode); + Assert.Equal(errorMessage, objectResult.Value); } } \ No newline at end of file From 68f78870eb0eafd99696b5301303e3d3a44098e7 Mon Sep 17 00:00:00 2001 From: kTrzcinskii Date: Thu, 3 Apr 2025 20:57:08 +0200 Subject: [PATCH 10/14] Remove unnecessary import --- TickAPI/TickAPI.Tests/Events/Services/EventServiceTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/TickAPI/TickAPI.Tests/Events/Services/EventServiceTests.cs b/TickAPI/TickAPI.Tests/Events/Services/EventServiceTests.cs index d9e0d7d..7c3272a 100644 --- a/TickAPI/TickAPI.Tests/Events/Services/EventServiceTests.cs +++ b/TickAPI/TickAPI.Tests/Events/Services/EventServiceTests.cs @@ -4,7 +4,6 @@ using TickAPI.Addresses.Abstractions; using TickAPI.Addresses.DTOs.Request; using TickAPI.Addresses.Models; -using TickAPI.Categories.Models; using TickAPI.Common.Pagination.Abstractions; using TickAPI.Common.Pagination.Responses; using TickAPI.Events.Models; From 3b7f975ebcacce74435b8bf526632c3d6f557f10 Mon Sep 17 00:00:00 2001 From: kTrzcinskii Date: Thu, 3 Apr 2025 21:02:01 +0200 Subject: [PATCH 11/14] Add tests for `GetOrganizerEventsPaginationDetails` --- .../Controllers/EventControllerTests.cs | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/TickAPI/TickAPI.Tests/Events/Controllers/EventControllerTests.cs b/TickAPI/TickAPI.Tests/Events/Controllers/EventControllerTests.cs index 1b60012..326878c 100644 --- a/TickAPI/TickAPI.Tests/Events/Controllers/EventControllerTests.cs +++ b/TickAPI/TickAPI.Tests/Events/Controllers/EventControllerTests.cs @@ -312,4 +312,110 @@ public async Task GetOrganizerEvents_WhenPaginationFails_ShouldReturnBadRequest( Assert.Equal(StatusCodes.Status400BadRequest, objectResult.StatusCode); Assert.Equal(errorMessage, objectResult.Value); } + + [Fact] + public async Task GetOrganizerEventsPaginationDetails_WhenAllOperationsSucceed_ShouldReturnOkWithPaginationDetails() + { + // Arrange + const string email = "organizer@example.com"; + const int pageSize = 10; + + var claims = new List + { + new Claim(ClaimTypes.Email, email) + }; + + var controllerContext = new ControllerContext + { + HttpContext = new DefaultHttpContext + { + User = new ClaimsPrincipal(new ClaimsIdentity(claims)) + } + }; + + var claimsServiceMock = new Mock(); + claimsServiceMock + .Setup(m => m.GetEmailFromClaims(controllerContext.HttpContext.User.Claims)) + .Returns(Result.Success(email)); + + var organizer = new Organizer { Email = email, IsVerified = true }; + + var organizerServiceMock = new Mock(); + organizerServiceMock + .Setup(m => m.GetOrganizerByEmailAsync(email)) + .ReturnsAsync(Result.Success(organizer)); + + var paginationDetails = new PaginationDetails(2, 25); + + var eventServiceMock = new Mock(); + eventServiceMock + .Setup(m => m.GetOrganizerEventsPaginationDetails(organizer, pageSize)) + .Returns(Result.Success(paginationDetails)); + + var sut = new EventController(eventServiceMock.Object, claimsServiceMock.Object, organizerServiceMock.Object); + sut.ControllerContext = controllerContext; + + // Act + var response = await sut.GetOrganizerEventsPaginationDetails(pageSize); + + // Assert + var result = Assert.IsType>(response); + var okResult = Assert.IsType(result.Result); + Assert.Equal(StatusCodes.Status200OK, okResult.StatusCode); + + var returnedPaginationDetails = Assert.IsType(okResult.Value); + Assert.Equal(2, returnedPaginationDetails.MaxPageNumber); + Assert.Equal(25, returnedPaginationDetails.AllElementsCount); + } + + [Fact] + public async Task GetOrganizerEventsPaginationDetails_WhenPaginationDetailsFails_ShouldReturnBadRequest() + { + // Arrange + const string email = "organizer@example.com"; + const int pageSize = -1; + const string errorMessage = "Invalid page size"; + + var claims = new List + { + new Claim(ClaimTypes.Email, email) + }; + + var controllerContext = new ControllerContext + { + HttpContext = new DefaultHttpContext + { + User = new ClaimsPrincipal(new ClaimsIdentity(claims)) + } + }; + + var claimsServiceMock = new Mock(); + claimsServiceMock + .Setup(m => m.GetEmailFromClaims(controllerContext.HttpContext.User.Claims)) + .Returns(Result.Success(email)); + + var organizer = new Organizer { Email = email, IsVerified = true }; + + var organizerServiceMock = new Mock(); + organizerServiceMock + .Setup(m => m.GetOrganizerByEmailAsync(email)) + .ReturnsAsync(Result.Success(organizer)); + + var eventServiceMock = new Mock(); + eventServiceMock + .Setup(m => m.GetOrganizerEventsPaginationDetails(organizer, pageSize)) + .Returns(Result.Failure(StatusCodes.Status400BadRequest, errorMessage)); + + var sut = new EventController(eventServiceMock.Object, claimsServiceMock.Object, organizerServiceMock.Object); + sut.ControllerContext = controllerContext; + + // Act + var response = await sut.GetOrganizerEventsPaginationDetails(pageSize); + + // Assert + var result = Assert.IsType>(response); + var objectResult = Assert.IsType(result.Result); + Assert.Equal(StatusCodes.Status400BadRequest, objectResult.StatusCode); + Assert.Equal(errorMessage, objectResult.Value); + } } \ No newline at end of file From b32e0991543a1e17c4a64f5caae19e5cf6b2a8d6 Mon Sep 17 00:00:00 2001 From: kTrzcinskii Date: Sat, 5 Apr 2025 23:32:24 +0200 Subject: [PATCH 12/14] Use `MapData` instead of manually mapping paginated data --- .../TickAPI/Categories/Services/CategoryService.cs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/TickAPI/TickAPI/Categories/Services/CategoryService.cs b/TickAPI/TickAPI/Categories/Services/CategoryService.cs index dbbfcd8..ee26d68 100644 --- a/TickAPI/TickAPI/Categories/Services/CategoryService.cs +++ b/TickAPI/TickAPI/Categories/Services/CategoryService.cs @@ -20,21 +20,15 @@ public CategoryService(ICategoryRepository categoryRepository, IPaginationServi public async Task>> GetCategoriesResponsesAsync(int pageSize, int page) { - var categoriesAllResponse = await _categoryRepository.GetCategoriesAsync(); - List categories = new List(); + var categoriesAllResponse = await _categoryRepository.GetCategoriesAsync(); var categoriesPaginated = _paginationService.Paginate(categoriesAllResponse, pageSize, page); if (!categoriesPaginated.IsSuccess) { return Result>.PropagateError(categoriesPaginated); } - foreach (var category in categoriesPaginated.Value.Data) - { - categories.Add(new GetCategoryResponseDto(category.CategoryName)); - } + var categoriesResponse = _paginationService.MapData(categoriesPaginated.Value!, (c) => new GetCategoryResponseDto(c.CategoryName)); - return Result>.Success(new PaginatedData(categories, categoriesPaginated.Value.PageNumber - ,categoriesPaginated.Value.PageSize, categoriesPaginated.Value.HasNextPage, categoriesPaginated.Value.HasPreviousPage, - categoriesPaginated.Value.PaginationDetails)); + return Result>.Success(categoriesResponse); } } \ No newline at end of file From 265b487784d4677159f86dbc9d79e535c4e33827 Mon Sep 17 00:00:00 2001 From: kTrzcinskii Date: Sat, 5 Apr 2025 23:42:03 +0200 Subject: [PATCH 13/14] Remove redundant type specifier --- TickAPI/TickAPI/Categories/Services/CategoryService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TickAPI/TickAPI/Categories/Services/CategoryService.cs b/TickAPI/TickAPI/Categories/Services/CategoryService.cs index ee26d68..f94c78d 100644 --- a/TickAPI/TickAPI/Categories/Services/CategoryService.cs +++ b/TickAPI/TickAPI/Categories/Services/CategoryService.cs @@ -21,7 +21,7 @@ public CategoryService(ICategoryRepository categoryRepository, IPaginationServi public async Task>> GetCategoriesResponsesAsync(int pageSize, int page) { var categoriesAllResponse = await _categoryRepository.GetCategoriesAsync(); - var categoriesPaginated = _paginationService.Paginate(categoriesAllResponse, pageSize, page); + var categoriesPaginated = _paginationService.Paginate(categoriesAllResponse, pageSize, page); if (!categoriesPaginated.IsSuccess) { return Result>.PropagateError(categoriesPaginated); From 64d9faca6b801caefe530b55e3759d5bada10be7 Mon Sep 17 00:00:00 2001 From: kTrzcinskii Date: Sun, 6 Apr 2025 00:27:03 +0200 Subject: [PATCH 14/14] Fix typos --- .../Categories/DTOs/Response/GetCategoryResponseDto.cs | 4 +--- .../Events/DTOs/Response/GetEventResponseAddressDto.cs | 2 +- TickAPI/TickAPI/Events/DTOs/Response/GetEventResponseDto.cs | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/TickAPI/TickAPI/Categories/DTOs/Response/GetCategoryResponseDto.cs b/TickAPI/TickAPI/Categories/DTOs/Response/GetCategoryResponseDto.cs index 9ece0df..c2376db 100644 --- a/TickAPI/TickAPI/Categories/DTOs/Response/GetCategoryResponseDto.cs +++ b/TickAPI/TickAPI/Categories/DTOs/Response/GetCategoryResponseDto.cs @@ -1,6 +1,4 @@ -using TickAPI.Common.Pagination.Responses; - -namespace TickAPI.Categories.DTOs.Response; +namespace TickAPI.Categories.DTOs.Response; public record GetCategoryResponseDto( string CategoryName diff --git a/TickAPI/TickAPI/Events/DTOs/Response/GetEventResponseAddressDto.cs b/TickAPI/TickAPI/Events/DTOs/Response/GetEventResponseAddressDto.cs index 0f6f14f..8ade67d 100644 --- a/TickAPI/TickAPI/Events/DTOs/Response/GetEventResponseAddressDto.cs +++ b/TickAPI/TickAPI/Events/DTOs/Response/GetEventResponseAddressDto.cs @@ -4,7 +4,7 @@ public record GetEventResponseAddressDto( string Country, string City, string PostalCode, - string? Stree, + string? Street, uint? HouseNumber, uint? FlatNumber ); diff --git a/TickAPI/TickAPI/Events/DTOs/Response/GetEventResponseDto.cs b/TickAPI/TickAPI/Events/DTOs/Response/GetEventResponseDto.cs index cafb0cc..41d1698 100644 --- a/TickAPI/TickAPI/Events/DTOs/Response/GetEventResponseDto.cs +++ b/TickAPI/TickAPI/Events/DTOs/Response/GetEventResponseDto.cs @@ -7,7 +7,7 @@ public record GetEventResponseDto( string Description, DateTime StartDate, DateTime EndDate, - uint? Minimumage, + uint? MinimumAge, List Categories, EventStatus Status, GetEventResponseAddressDto Addres