Skip to content
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
14 changes: 14 additions & 0 deletions ArenaService.Shared/Dtos/PagedSeasonsResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Libplanet.Crypto;

namespace ArenaService.Shared.Dtos;

public class PagedSeasonsResponse
{
public required List<SeasonResponse> Seasons { get; set; }
public required int TotalCount { get; set; }
public required int PageNumber { get; set; }
public required int PageSize { get; set; }
public required int TotalPages { get; set; }
public required bool HasNextPage { get; set; }
public required bool HasPreviousPage { get; set; }
}
21 changes: 21 additions & 0 deletions ArenaService.Shared/Services/SeasonService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ Task<List<Season>> ClassifyByChampionship(
Task<Season?> GetLastSeasonByBlockIndexAsync(long blockIndex);

Task<List<Season>> GetCompletedSeasonsBeforeBlock(long blockIndex);

Task<(List<Season> Seasons, int TotalCount, int TotalPages, bool HasNextPage, bool HasPreviousPage)> GetSeasonsPagedAsync(
int pageNumber,
int pageSize,
Func<IQueryable<Season>, IQueryable<Season>>? includeQuery = null
);
}

public class SeasonService : ISeasonService
Expand Down Expand Up @@ -149,4 +155,19 @@ public async Task<List<Season>> GetCompletedSeasonsBeforeBlock(long blockIndex)
.OrderByDescending(s => s.EndBlock)
.ToList();
}

public async Task<(List<Season> Seasons, int TotalCount, int TotalPages, bool HasNextPage, bool HasPreviousPage)> GetSeasonsPagedAsync(
int pageNumber,
int pageSize,
Func<IQueryable<Season>, IQueryable<Season>>? includeQuery = null
)
{
var seasons = await _seasonRepo.GetSeasonsPagedAsync(pageNumber, pageSize, includeQuery);
var totalCount = await _seasonRepo.GetTotalSeasonsCountAsync();
var totalPages = (int)Math.Ceiling((double)totalCount / pageSize);
var hasNextPage = pageNumber < totalPages;
var hasPreviousPage = pageNumber > 1;

return (seasons, totalCount, totalPages, hasNextPage, hasPreviousPage);
}
}
147 changes: 125 additions & 22 deletions ArenaService.Tests/Controllers/SeasonControllerTests.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,125 @@
// using ArenaService.Controllers;
// using ArenaService.Shared.Dtos;
// using ArenaService.Shared.Models;
// using ArenaService.Shared.Repositories;
// using Microsoft.AspNetCore.Http.HttpResults;
// using Moq;

// namespace ArenaService.Tests.Controllers;

// public class SeasonControllerTests
// {
// private readonly SeasonController _controller;
// private Mock<ISeasonRepository> _repoMock;

// public SeasonControllerTests()
// {
// var repoMock = new Mock<ISeasonRepository>();
// _repoMock = repoMock;
// _controller = new SeasonController(_repoMock.Object);
// }

// }
using ArenaService.Controllers;
using ArenaService.Shared.Constants;
using ArenaService.Shared.Dtos;
using ArenaService.Shared.Models;
using ArenaService.Shared.Models.BattleTicket;
using ArenaService.Shared.Models.RefreshTicket;
using ArenaService.Shared.Models.Ticket;
using ArenaService.Shared.Repositories;
using ArenaService.Shared.Services;
using ArenaService.Options;
using Libplanet.Crypto;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Moq;

namespace ArenaService.Tests.Controllers;

public class SeasonControllerTests
{
private readonly SeasonController _controller;
private readonly Mock<ISeasonRepository> _repoMock;
private readonly Mock<ISeasonService> _serviceMock;
private readonly Address _recipientAddress;

public SeasonControllerTests()
{
_repoMock = new Mock<ISeasonRepository>();
_serviceMock = new Mock<ISeasonService>();
_recipientAddress = new Address("0x0000000000000000000000000000000000000000");

var options = new Mock<IOptions<OpsConfigOptions>>();
options.Setup(x => x.Value).Returns(new OpsConfigOptions
{
RecipientAddress = _recipientAddress.ToString(),
JwtSecretKey = "test-secret-key",
JwtPublicKey = "test-public-key",
ArenaProviderName = "test-provider",
HangfireUsername = "test-user",
HangfirePassword = "test-password"
});

_controller = new SeasonController(_repoMock.Object, _serviceMock.Object, options.Object);
}

[Fact]
public async Task GetSeasonsPaged_ValidParameters_ReturnsOkResult()
{
var seasons = new List<Season>
{
new Season
{
Id = 1,
SeasonGroupId = 1,
ArenaType = ArenaType.CHAMPIONSHIP,
StartBlock = 1000,
EndBlock = 2000,
RoundInterval = 100,
RequiredMedalCount = 10,
TotalPrize = 1000,
BattleTicketPolicy = new BattleTicketPolicy { Id = 1, Name = "Test" },
RefreshTicketPolicy = new RefreshTicketPolicy { Id = 1, Name = "Test" },
Rounds = new List<Round>()
}
};

_serviceMock.Setup(x => x.GetSeasonsPagedAsync(1, 10, It.IsAny<Func<IQueryable<Season>, IQueryable<Season>>>()))
.ReturnsAsync((seasons, 1, 1, false, false));

var result = await _controller.GetSeasonsPaged(1, 10);

var okResult = Assert.IsType<OkObjectResult>(result);
var response = Assert.IsType<PagedSeasonsResponse>(okResult.Value);

Assert.Single(response.Seasons);
Assert.Equal(1, response.TotalCount);
Assert.Equal(1, response.PageNumber);
Assert.Equal(10, response.PageSize);
Assert.Equal(1, response.TotalPages);
Assert.False(response.HasNextPage);
Assert.False(response.HasPreviousPage);
}

[Fact]
public async Task GetSeasonsPaged_InvalidPageNumber_ReturnsBadRequest()
{
var result = await _controller.GetSeasonsPaged(0, 10);

var badRequestResult = Assert.IsType<BadRequestObjectResult>(result);
Assert.Equal("Page number must be greater than 0", badRequestResult.Value);
}

[Fact]
public async Task GetSeasonsPaged_InvalidPageSize_ReturnsBadRequest()
{
var result = await _controller.GetSeasonsPaged(1, 0);

var badRequestResult = Assert.IsType<BadRequestObjectResult>(result);
Assert.Equal("Page size must be between 1 and 100", badRequestResult.Value);
}

[Fact]
public async Task GetSeasonsPaged_PageSizeTooLarge_ReturnsBadRequest()
{
var result = await _controller.GetSeasonsPaged(1, 101);

var badRequestResult = Assert.IsType<BadRequestObjectResult>(result);
Assert.Equal("Page size must be between 1 and 100", badRequestResult.Value);
}

[Fact]
public async Task GetSeasonsPaged_DefaultParameters_ReturnsCorrectPageInfo()
{
var seasons = new List<Season>();
_serviceMock.Setup(x => x.GetSeasonsPagedAsync(1, 10, It.IsAny<Func<IQueryable<Season>, IQueryable<Season>>>()))
.ReturnsAsync((seasons, 0, 0, false, false));

var result = await _controller.GetSeasonsPaged();

var okResult = Assert.IsType<OkObjectResult>(result);
var response = Assert.IsType<PagedSeasonsResponse>(okResult.Value);

Assert.Equal(1, response.PageNumber);
Assert.Equal(10, response.PageSize);
}
}
40 changes: 40 additions & 0 deletions ArenaService/Controllers/SeasonController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,44 @@ public async Task<IActionResult> GetSeasons(long blockIndex)

return Ok(response);
}

[HttpGet]
[SwaggerResponse(StatusCodes.Status200OK, "PagedSeasonsResponse", typeof(PagedSeasonsResponse))]
public async Task<IActionResult> GetSeasonsPaged(
[FromQuery] int pageNumber = 1,
[FromQuery] int pageSize = 10
)
{
if (pageNumber < 1)
{
return BadRequest("Page number must be greater than 0");
}

if (pageSize < 1 || pageSize > 100)
{
return BadRequest("Page size must be between 1 and 100");
}

var (seasons, totalCount, totalPages, hasNextPage, hasPreviousPage) = await _seasonService.GetSeasonsPagedAsync(
pageNumber,
pageSize,
q =>
q.Include(s => s.BattleTicketPolicy)
.Include(s => s.RefreshTicketPolicy)
.Include(s => s.Rounds)
);

var response = new PagedSeasonsResponse
{
Seasons = seasons.Select(s => s.ToResponse()).ToList(),
TotalCount = totalCount,
PageNumber = pageNumber,
PageSize = pageSize,
TotalPages = totalPages,
HasNextPage = hasNextPage,
HasPreviousPage = hasPreviousPage
};

return Ok(response);
}
}
Loading