Skip to content

Commit

Permalink
Added Run Controller and Service (leaderboardsgg#71)
Browse files Browse the repository at this point in the history
Also moved some stuff around
  • Loading branch information
RageCage64 authored Mar 26, 2022
1 parent fd1e43d commit 6089632
Show file tree
Hide file tree
Showing 14 changed files with 175 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using LeaderboardBackend.Models.Entities;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using LeaderboardBackend.Test.Helpers;

namespace LeaderboardBackend.Test.Controllers;

Expand All @@ -32,11 +33,9 @@ public async Task GetCategory_NotFound_CategoryDoesNotExist()
.Setup(x => x.GetCategory(It.IsAny<long>()))
.Returns(Task.FromResult<Category?>(null));

ActionResult<Category> response = await _controller.GetCategory((long)1);
ActionResult<Category> response = await _controller.GetCategory(1);

NotFoundResult? actual = response.Result as NotFoundResult;
Assert.NotNull(actual);
Assert.AreEqual(404, actual!.StatusCode);
ObjectResultHelpers.AssertResponseNotFound(response);
}

[Test]
Expand All @@ -47,8 +46,8 @@ public async Task GetCategory_Ok_CategoryExists()
.Returns(Task.FromResult<Category?>(new Category { Id = 1 }));

ActionResult<Category> response = await _controller.GetCategory(1);
Category? category = Helpers.GetValueFromObjectResult<OkObjectResult, Category>(response.Result);

Category? category = ObjectResultHelpers.GetValueFromObjectResult<Category, OkObjectResult>(response);
Assert.NotNull(category);
Assert.AreEqual(1, category!.Id);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using LeaderboardBackend.Controllers;
using LeaderboardBackend.Models.Entities;
using LeaderboardBackend.Services;
using LeaderboardBackend.Test.Helpers;
using Microsoft.AspNetCore.Mvc;
using Moq;
using NUnit.Framework;
Expand Down Expand Up @@ -36,10 +37,7 @@ public async Task GetLeaderboard_NotFound_LeaderboardDoesNotExist()
.Returns(Task.FromResult<Leaderboard?>(null));

ActionResult<Leaderboard> response = await _controller.GetLeaderboard(1);
var actual = response.Result as NotFoundResult;

Assert.NotNull(actual);
Assert.AreEqual(404, actual!.StatusCode);
ObjectResultHelpers.AssertResponseNotFound(response);
}

[Test]
Expand All @@ -50,10 +48,10 @@ public async Task GetLeaderboard_Ok_LeaderboardExists()
.Returns(Task.FromResult<Leaderboard?>(_defaultLeaderboard));

ActionResult<Leaderboard> response = await _controller.GetLeaderboard(1);
Leaderboard? leaderboard = Helpers.GetValueFromObjectResult<OkObjectResult, Leaderboard>(response);

Leaderboard? leaderboard = ObjectResultHelpers.GetValueFromObjectResult<Leaderboard, OkObjectResult>(response);
Assert.NotNull(leaderboard);
Assert.AreEqual(1, leaderboard!.Id);
Assert.AreEqual(_defaultLeaderboard, leaderboard);
}

[Test]
Expand All @@ -70,8 +68,8 @@ public async Task GetLeaderboards_Ok_ListExists()
.Returns(Task.FromResult(mockList));

ActionResult<List<Leaderboard>> response = await _controller.GetLeaderboards(new long[] { 1, 2 });
List<Leaderboard>? leaderboards = Helpers.GetValueFromObjectResult<OkObjectResult, List<Leaderboard>>(response);

List<Leaderboard>? leaderboards = ObjectResultHelpers.GetValueFromObjectResult<List<Leaderboard>, OkObjectResult>(response);
Assert.NotNull(leaderboards);
Assert.AreEqual(new ulong[] { 1, 2 }, leaderboards!.Select(l => l.Id));
}
Expand Down
30 changes: 30 additions & 0 deletions LeaderboardBackend.Test/Controllers/RunsControllerTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using LeaderboardBackend.Controllers;
using LeaderboardBackend.Models.Entities;
using LeaderboardBackend.Services;
using LeaderboardBackend.Services.Impl;
using LeaderboardBackend.Test.Helpers;
using Microsoft.AspNetCore.Mvc;
using NUnit.Framework;
using System;
using System.Threading.Tasks;

namespace LeaderboardBackend.Test.Controllers;

internal class RunsControllerTest
{
private RunsController _controller = null!;

[SetUp]
public void Setup()
{
RunService service = new RunService(ApplicationContextFactory.CreateNewContext());
_controller = new RunsController(service);
}

[Test]
public async Task GetRun_NotFound_WhenNotExist()
{
ActionResult<Run> response = await _controller.GetRun(new Guid());
ObjectResultHelpers.AssertResponseNotFound(response);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@
using System.Security.Claims;
using System.Threading.Tasks;
using BCryptNet = BCrypt.Net.BCrypt;
using LeaderboardBackend.Test.Helpers;

namespace LeaderboardBackend.Test.Controllers;

public class UsersControllerTests
public class UsersControllerTest
{
private UsersController _controller = null!;
private Mock<IUserService> _userServiceMock = null!;
Expand Down Expand Up @@ -47,8 +48,8 @@ public async Task GetUserById_NotFound_UserDoesNotExist()
.Returns(Task.FromResult<User?>(null));

ActionResult<User> response = await _controller.GetUserById(defaultUserId);

Helpers.AssertResponseNotFound(response);
ObjectResultHelpers.AssertResponseNotFound(response);
}

[Test]
Expand All @@ -60,8 +61,7 @@ public async Task GetUserById_Ok_UserExists()

ActionResult<User> response = await _controller.GetUserById(defaultUserId);

User? user = Helpers.GetValueFromObjectResult<OkObjectResult, User>(response);

User? user = ObjectResultHelpers.GetValueFromObjectResult<User, OkObjectResult>(response);
Assert.NotNull(user);
Assert.AreEqual(defaultUser, user);
}
Expand All @@ -78,7 +78,8 @@ public async Task Register_BadRequest_PasswordsMismatch()
};

ActionResult<User> response = await _controller.Register(body);
Helpers.AssertResponseBadRequest(response);

ObjectResultHelpers.AssertResponseBadRequest(response);
}

[Test]
Expand All @@ -93,12 +94,11 @@ public async Task Register_OK_PasswordsMatchCreateSuccess()
};

ActionResult<User> response = await _controller.Register(body);
User? user = Helpers.GetValueFromObjectResult<CreatedAtActionResult, User>(response);

User? user = ObjectResultHelpers.GetValueFromObjectResult<User, CreatedAtActionResult>(response);
Assert.NotNull(user);
Assert.AreEqual(defaultUser.Username, user!.Username);
Assert.AreEqual(defaultUser.Email, user!.Email);

// This route creates a new user, and thus does a new password hash.
// Since hashing the password again won't produce the same hash as
// defaultUser, we do a cryptographic verify instead.
Expand All @@ -115,7 +115,7 @@ public async Task Me_Forbid_NoUserInClaims()

ActionResult<User> response = await _controller.Me();

Helpers.AssertResponseForbid(response);
ObjectResultHelpers.AssertResponseForbid(response);
}

[Test]
Expand All @@ -127,8 +127,8 @@ public async Task Me_Ok_UserFoundFromClaims()
.Returns(Task.FromResult<User?>(defaultUser));

ActionResult<User> response = await _controller.Me();
User? user = Helpers.GetValueFromObjectResult<OkObjectResult, User>(response);

User? user = ObjectResultHelpers.GetValueFromObjectResult<User, OkObjectResult>(response);
Assert.NotNull(user);
Assert.AreEqual(defaultUser, user);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
using System;
using Microsoft.AspNetCore.Mvc;
using NUnit.Framework;
using System;

namespace LeaderboardBackend.Test.Controllers;
namespace LeaderboardBackend.Test.Helpers;

internal static class Helpers
internal static class ObjectResultHelpers
{
public static void AssertResponseNotFound<T>(ActionResult<T> response)
{
Expand Down Expand Up @@ -32,7 +32,7 @@ public static void AssertResponseForbid<T>(ActionResult<T> response)
// a Forbidden 403 result.
}

public static TResult? GetValueFromObjectResult<TObjectResult, TResult>(ActionResult<TResult> result) where TObjectResult : ObjectResult
public static TValue? GetValueFromObjectResult<TValue, TObjectResult>(ActionResult<TValue> result) where TObjectResult : ObjectResult
{
TObjectResult? objectResult = null;
try
Expand All @@ -44,6 +44,7 @@ public static void AssertResponseForbid<T>(ActionResult<T> response)
Assert.Fail(ex.Message);
}

return (TResult?)(objectResult?.Value);
return (TValue?)(objectResult?.Value);
}

}
61 changes: 61 additions & 0 deletions LeaderboardBackend/Controllers/RunsController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using Microsoft.AspNetCore.Mvc;
using LeaderboardBackend.Models.Entities;
using LeaderboardBackend.Services;
using Microsoft.AspNetCore.Http;
using LeaderboardBackend.Controllers.Annotations;
using LeaderboardBackend.Models.Requests.Run;

namespace LeaderboardBackend.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class RunsController : ControllerBase
{
private readonly IRunService _runService;

public RunsController(IRunService runService)
{
_runService = runService;
}

/// <summary>Gets a Run.</summary>
/// <param name="id">The Run ID. Must parse to a long for this route to be hit.</param>
/// <response code="200">The Run with the provided ID.</response>
/// <response code="404">If no Run is found with the provided ID.</response>
[ApiConventionMethod(typeof(Conventions),
nameof(Conventions.Get))]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[HttpGet("{id}")]
public async Task<ActionResult<Run>> GetRun(Guid id)
{
Run? run = await _runService.GetRun(id);
if (run == null)
{
return NotFound();
}

return Ok(run);
}

/// <summary>Creates a Run.</summary>
[ApiConventionMethod(typeof(Conventions),
nameof(Conventions.Post))]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesDefaultResponseType]
[HttpPost]
public async Task<ActionResult> CreateRun([FromBody] CreateRunRequest request)
{
Run run = new()
{
Played = request.Played,
Submitted = request.Submitted,
Status = request.Status,
};

await _runService.CreateRun(run);
return CreatedAtAction(nameof(GetRun), new { id = run.Id }, run);
}
}
}
10 changes: 5 additions & 5 deletions LeaderboardBackend/Models/Entities/Run.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ namespace LeaderboardBackend.Models.Entities;

public enum RunStatus
{
Created,
Submitted,
Pending,
Approved,
Rejected
CREATED,
SUBMITTED,
PENDING,
APPROVED,
REJECTED
}

public class Run
Expand Down
16 changes: 16 additions & 0 deletions LeaderboardBackend/Models/Requests/Runs/Create.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using LeaderboardBackend.Models.Entities;
using System.ComponentModel.DataAnnotations;

namespace LeaderboardBackend.Models.Requests.Run;

public class CreateRunRequest
{
[Required]
public DateTime Played { get; set; }

[Required]
public DateTime Submitted { get; set; }

[Required]
public RunStatus Status { get; set; }
}
10 changes: 10 additions & 0 deletions LeaderboardBackend/Services/IRunService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using LeaderboardBackend.Models.Entities;

namespace LeaderboardBackend.Services;

public interface IRunService
{
Task<Run?> GetRun(Guid id);

Task CreateRun(Run run);
}
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ namespace LeaderboardBackend.Services
{
public class CategoryService : ICategoryService
{
private ApplicationContext _applicationContext;
private readonly ApplicationContext _applicationContext;

public CategoryService(ApplicationContext applicationContext)
{
_applicationContext = applicationContext;
Expand Down
24 changes: 24 additions & 0 deletions LeaderboardBackend/Services/Impl/RunService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using LeaderboardBackend.Models.Entities;

namespace LeaderboardBackend.Services.Impl;

public class RunService : IRunService
{
private readonly ApplicationContext _applicationContext;

public RunService(ApplicationContext applicationContext)
{
_applicationContext = applicationContext;
}

public async Task<Run?> GetRun(Guid id)
{
return await _applicationContext.Runs.FindAsync(id);
}

public async Task CreateRun(Run run)
{
_applicationContext.Runs.Add(run);
await _applicationContext.SaveChangesAsync();
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using LeaderboardBackend.Models.Entities;
using LeaderboardBackend.Models.Entities;
using Microsoft.EntityFrameworkCore;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
Expand Down Expand Up @@ -28,7 +28,7 @@ public UserService(ApplicationContext applicationContext, IConfiguration config)
{
// We save a username with casing, but match without.
// Effectively you can't have two separate users named e.g. "cool" and "cOoL".
return await _applicationContext.Users.FirstOrDefaultAsync(u => u.Username != null && u.Username.ToLower() == name.ToLower());
return await _applicationContext.Users.SingleOrDefaultAsync(u => u.Username != null && u.Username.ToLower() == name.ToLower());
}

public async Task<User?> GetUserFromClaims(ClaimsPrincipal claims)
Expand Down

0 comments on commit 6089632

Please sign in to comment.