-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Fixed a bug that could cause score denormalization to choke without a rerank. * Add unit tests for team cumulative time calculation * Restored oprhaned team advance feature
- Loading branch information
1 parent
23cb932
commit 25aade0
Showing
10 changed files
with
249 additions
and
7 deletions.
There are no files selected for viewing
114 changes: 114 additions & 0 deletions
114
src/Gameboard.Api.Tests.Unit/Tests/Features/Teams/CumulativeTimeCalculatorTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
using Gameboard.Api.Features.Teams; | ||
|
||
namespace Gameboard.Api.Tests.Unit; | ||
|
||
public class CumulativeTimeCalculatorTests | ||
{ | ||
[Theory, GameboardAutoData] | ||
public void CalculateCumulativeTimeMs_WithOneChallenge_EqualsSolveTime | ||
( | ||
int scoreTimeOffset, | ||
IFixture fixture | ||
) | ||
{ | ||
// given a challenge with fixed start and end | ||
var startTime = fixture.Create<DateTimeOffset>(); | ||
var teamTime = new TeamChallengeTime | ||
{ | ||
TeamId = fixture.Create<string>(), | ||
ChallengeId = fixture.Create<string>(), | ||
StartTime = startTime, | ||
LastScoreTime = startTime.AddMilliseconds(scoreTimeOffset) | ||
}; | ||
|
||
// when it's calculated | ||
var sut = new CumulativeTimeCalculator(); | ||
var result = sut.CalculativeCumulativeTimeMs([teamTime]); | ||
|
||
// then | ||
result.ShouldBe(scoreTimeOffset); | ||
} | ||
|
||
[Theory, GameboardAutoData] | ||
public void CalculateCumulativeTimeMs_WithTwoChallenge_EqualsSolveTime | ||
( | ||
int scoreTimeOffset1, | ||
int scoreTimeOffset2, | ||
DateTimeOffset startTime1, | ||
DateTimeOffset startTime2, | ||
IFixture fixture | ||
) | ||
{ | ||
// given a challenge with fixed start and end | ||
var teamTimes = new TeamChallengeTime[] | ||
{ | ||
new() | ||
{ | ||
TeamId = fixture.Create<string>(), | ||
ChallengeId = fixture.Create<string>(), | ||
StartTime = startTime1, | ||
LastScoreTime = startTime1.AddMilliseconds(scoreTimeOffset1) | ||
}, | ||
new() | ||
{ | ||
TeamId = fixture.Create<string>(), | ||
ChallengeId = fixture.Create<string>(), | ||
StartTime = startTime2, | ||
LastScoreTime = startTime2.AddMilliseconds(scoreTimeOffset2) | ||
} | ||
}; | ||
|
||
// when it's calculated | ||
var sut = new CumulativeTimeCalculator(); | ||
var result = sut.CalculativeCumulativeTimeMs(teamTimes); | ||
|
||
// then | ||
result.ShouldBe(scoreTimeOffset1 + scoreTimeOffset2); | ||
} | ||
|
||
[Theory, GameboardAutoData] | ||
public void CalculateCumulativeTimeMs_WithUnstartedChallenge_YieldsZero(IFixture fixture) | ||
{ | ||
// given a challenge with fixed start and end | ||
var teamTimes = new TeamChallengeTime[] | ||
{ | ||
new() | ||
{ | ||
TeamId = fixture.Create<string>(), | ||
ChallengeId = fixture.Create<string>(), | ||
StartTime = null, | ||
LastScoreTime = fixture.Create<DateTimeOffset>() | ||
} | ||
}; | ||
|
||
// when it's calculated | ||
var sut = new CumulativeTimeCalculator(); | ||
var result = sut.CalculativeCumulativeTimeMs(teamTimes); | ||
|
||
// then | ||
result.ShouldBe(0); | ||
} | ||
|
||
[Theory, GameboardAutoData] | ||
public void CalculateCumulativeTimeMs_WithUnscoredChallenge_YieldsZero(IFixture fixture) | ||
{ | ||
// given a challenge with fixed start and end | ||
var teamTimes = new TeamChallengeTime[] | ||
{ | ||
new() | ||
{ | ||
TeamId = fixture.Create<string>(), | ||
ChallengeId = fixture.Create<string>(), | ||
StartTime = fixture.Create<DateTimeOffset>(), | ||
LastScoreTime = null | ||
} | ||
}; | ||
|
||
// when it's calculated | ||
var sut = new CumulativeTimeCalculator(); | ||
var result = sut.CalculativeCumulativeTimeMs(teamTimes); | ||
|
||
// then | ||
result.ShouldBe(0); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
58 changes: 58 additions & 0 deletions
58
src/Gameboard.Api/Features/Teams/Requests/AdvanceTeams/AdvanceTeams.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Gameboard.Api.Features.Users; | ||
using Gameboard.Api.Services; | ||
using Gameboard.Api.Structure.MediatR; | ||
using MediatR; | ||
|
||
namespace Gameboard.Api.Features.Teams; | ||
|
||
public record AdvanceTeamsCommand(string GameId, bool IncludeScores, IEnumerable<string> TeamIds) : IRequest; | ||
|
||
internal sealed class AdvanceTeamsHandler | ||
( | ||
PlayerService playerService, | ||
ITeamService teamService, | ||
IValidatorService validatorService | ||
) : IRequestHandler<AdvanceTeamsCommand> | ||
{ | ||
private readonly PlayerService _playerService = playerService; | ||
private readonly ITeamService _teamService = teamService; | ||
private readonly IValidatorService _validator = validatorService; | ||
|
||
public async Task Handle(AdvanceTeamsCommand request, CancellationToken cancellationToken) | ||
{ | ||
// sanitize input | ||
var finalTeamIds = request.TeamIds.Distinct().ToArray(); | ||
|
||
await _validator | ||
.Auth(c => c.RequirePermissions(PermissionKey.Teams_Enroll)) | ||
.AddEntityExistsValidator<Data.Game>(request.GameId) | ||
.AddValidator(async ctx => | ||
{ | ||
var captains = await _teamService.ResolveCaptains(request.TeamIds, cancellationToken); | ||
|
||
// ensure all teams are represented | ||
var unreppedTeamIds = finalTeamIds.Where(t => !captains.ContainsKey(t)).ToArray(); | ||
if (unreppedTeamIds.Length > 0) | ||
{ | ||
foreach (var unreppedTeam in unreppedTeamIds) | ||
{ | ||
ctx.AddValidationException(new ResourceNotFound<Team>(unreppedTeam)); | ||
} | ||
} | ||
}) | ||
.Validate(cancellationToken); | ||
|
||
var gameId = await _teamService.GetGameId(request.TeamIds, cancellationToken); | ||
await _playerService.AdvanceTeams(new TeamAdvancement | ||
{ | ||
GameId = gameId, | ||
NextGameId = request.GameId, | ||
TeamIds = request.TeamIds.ToArray(), | ||
WithScores = request.IncludeScores | ||
}); | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
src/Gameboard.Api/Features/Teams/Services/CumulativeTimeCalculator.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
|
||
namespace Gameboard.Api.Features.Teams; | ||
|
||
public interface ICumulativeTimeCalculator | ||
{ | ||
long CalculativeCumulativeTimeMs(IEnumerable<TeamChallengeTime> times); | ||
} | ||
|
||
public class CumulativeTimeCalculator : ICumulativeTimeCalculator | ||
{ | ||
public long CalculativeCumulativeTimeMs(IEnumerable<TeamChallengeTime> times) | ||
=> times | ||
.Where(t => t.StartTime.IsNotEmpty()) | ||
.Where(t => t.LastScoreTime.IsNotEmpty()) | ||
.Sum(t => Math.Max(t.LastScoreTime.Value.ToUnixTimeMilliseconds() - t.StartTime.Value.ToUnixTimeMilliseconds(), 0)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters