Skip to content

Commit

Permalink
v3.25.0 (#552)
Browse files Browse the repository at this point in the history
* Fix teamup bug

* Clarify that the regrade permission also applies to reranking games

* Resolve #549

* Various bug fixes for competitive and version 3.25.0

* Pending user names count as any unresolved name request.

* Fix test fails.

* Fix a bug in practice challenge search

* Resolves #543

* Resolves #540

* Fix prac challenge visibility

* Cleanup and fix test

* Fix/improve various session and challenge launch logic

* Fix #539

* Fix promotion bug
  • Loading branch information
sei-bstein authored Dec 2, 2024
1 parent 101ad57 commit 87cd6bd
Show file tree
Hide file tree
Showing 40 changed files with 685 additions and 285 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ await _testContext.WithDataState
state.Add<Data.Game>(fixture, game =>
{
game.IsPublished = true;
game.PlayerMode = PlayerMode.Practice;
game.Specs = state.Build<Data.ChallengeSpec>(fixture, spec =>
{
game.PlayerMode = PlayerMode.Practice;
spec.Tags = tag;
}).ToCollection();
});
Expand Down Expand Up @@ -63,6 +65,9 @@ await _testContext.WithDataState
// note there are no suggested searches in this db
state.Add<Data.Game>(fixture, game =>
{
game.PlayerMode = PlayerMode.Practice;
game.IsPublished = true;
game.Specs = state.Build<Data.ChallengeSpec>(fixture, spec =>
{
game.PlayerMode = PlayerMode.Practice;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using Gameboard.Api.Common;
using Gameboard.Api.Features.Teams;
using Gameboard.Api.Structure;
using ServiceStack;
using StackExchange.Redis;

namespace Gameboard.Api.Tests.Integration.Teams;

Expand Down Expand Up @@ -142,7 +142,7 @@ await _testContext.WithDataState(state =>
await httpClient
.PutAsync($"api/team/{teamId}/manager/{finalCaptainPlayerId}", new PromoteToManagerRequest
{
CurrentManagerPlayerId = initialCaptainPlayerId,
CurrentCaptainId = initialCaptainPlayerId,
NewManagerPlayerId = finalCaptainPlayerId,
TeamId = teamId
}.ToJsonBody());
Expand All @@ -159,4 +159,47 @@ await httpClient
// Users can team up, leave the team, join a different team, then start sessions on the original and the new team
// Admins can start sessions for non-admins
// Non-admins can't start sessions for other teams
[Theory, GbIntegrationAutoData]
public async Task Team_WhenStartingOtherTeamSession_FailsValidation
(
string actingTeamId,
string actingUserId,
string targetPlayerId,
IFixture fixture
)
{
// given two players registered for the same game
await _testContext.WithDataState(state =>
{
state.Add<Data.Game>(fixture, game =>
{
game.Players =
[
// the person who's starting a session
state.Build<Data.Player>(fixture, p =>
{
p.Id = fixture.Create<string>();
p.Role = PlayerRole.Manager;
p.TeamId = actingTeamId;
p.User = state.Build<Data.User>(fixture, u => u.Id = actingUserId);
}),
state.Build<Data.Player>(fixture, p =>
{
p.Id = targetPlayerId;
p.Role = PlayerRole.Manager;
p.TeamId = actingTeamId;
p.User = state.Build<Data.User>(fixture);
})
];
});
});

// when the first player tries to start the second's session
var response = await _testContext
.CreateHttpClientWithActingUser(u => u.Id = actingUserId)
.PutAsync($"api/player/{targetPlayerId}/start", null);

// then the response should have a failure code
response.IsSuccessStatusCode.ShouldBeFalse();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using Gameboard.Api.Features.Users;
using Gameboard.Api.Services;
using MediatR;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;

namespace Gameboard.Api.Tests.Unit;
Expand Down Expand Up @@ -113,7 +112,6 @@ string userId
A.Fake<ILogger<ChallengeService>>(),
A.Fake<IMapper>(),
A.Fake<IMediator>(),
A.Fake<IMemoryCache>(),
A.Fake<INowService>(),
A.Fake<IPracticeService>(),
A.Fake<IUserRolePermissionsService>(),
Expand Down Expand Up @@ -237,7 +235,6 @@ string userId
A.Fake<ILogger<ChallengeService>>(),
A.Fake<IMapper>(),
A.Fake<IMediator>(),
A.Fake<IMemoryCache>(),
A.Fake<INowService>(),
A.Fake<IPracticeService>(),
A.Fake<IUserRolePermissionsService>(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,6 @@ namespace Gameboard.Api.Tests.Unit;

public class PlayerServiceTests
{
[Theory, GameboardAutoData]
public async Task Standings_WhenGameIdIsEmpty_ReturnsEmptyArray(IFixture fixture)
{
// arrange
var sut = fixture.Create<PlayerService>();
var filterParams = A.Fake<PlayerDataFilter>();

// act
var result = await sut.Standings(filterParams);

// assert
result.ShouldBe(Array.Empty<Standing>());
}

[Theory, GameboardAutoData]
public async Task MakeCertificates_WhenScoreZero_ReturnsEmptyArray(IFixture fixture)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
using AutoMapper;
using Gameboard.Api.Common;
using Gameboard.Api.Common.Services;
using Gameboard.Api.Data;
using Gameboard.Api.Features.Challenges;
using Gameboard.Api.Features.Practice;
using Gameboard.Api.Features.Users;
using Microsoft.EntityFrameworkCore;

namespace Gameboard.Api.Tests.Unit;
Expand Down Expand Up @@ -32,9 +32,8 @@ public async Task SearchPracticeChallenges_WithDisabled_ReturnsEmpty(IFixture fi
var sut = GetSutWithResults(fixture, disabledSpec);

// when a query for all challenges is issued
var result = await sut
.BuildQuery(string.Empty, Array.Empty<string>())
.ToArrayAsync(CancellationToken.None);
var query = await sut.BuildQuery(string.Empty, []);
var result = await query.ToArrayAsync(CancellationToken.None);

// then we expect no results
result.Length.ShouldBe(0);
Expand All @@ -53,6 +52,7 @@ public async Task SearchPracticeChallenges_WithEnabled_Returns(IFixture fixture)
Disabled = false,
Game = new Data.Game
{
IsPublished = true,
Name = fixture.Create<string>(),
PlayerMode = PlayerMode.Practice
}
Expand All @@ -61,9 +61,8 @@ public async Task SearchPracticeChallenges_WithEnabled_Returns(IFixture fixture)
var sut = GetSutWithResults(fixture, enabledSpec);

// when a query for all challenges is issued
var result = await sut
.BuildQuery(string.Empty, Array.Empty<string>())
.ToArrayAsync(CancellationToken.None);
var query = await sut.BuildQuery(string.Empty, []);
var result = await query.ToArrayAsync(CancellationToken.None);

// then we expect one result
result.Length.ShouldBe(1);
Expand All @@ -81,8 +80,8 @@ private SearchPracticeChallengesHandler GetSutWithResults(IFixture fixture, para
var sut = new SearchPracticeChallengesHandler
(
A.Fake<IChallengeDocsService>(),
A.Fake<IMapper>(),
A.Fake<IPagingService>(),
A.Fake<IUserRolePermissionsService>(),
A.Fake<IPracticeService>(),
A.Fake<ISlugService>(),
store
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,32 +16,22 @@ namespace Gameboard.Api.Features.Admin;

public record GetGameCenterContextQuery(string GameId) : IRequest<GameCenterContext>;

internal class GetGameCenterContextHandler : IRequestHandler<GetGameCenterContextQuery, GameCenterContext>
internal class GetGameCenterContextHandler
(
EntityExistsValidator<GetGameCenterContextQuery, Data.Game> gameExists,
INowService now,
IStore store,
ITeamService teamService,
TicketService ticketService,
IValidatorService<GetGameCenterContextQuery> validator
) : IRequestHandler<GetGameCenterContextQuery, GameCenterContext>
{
private readonly EntityExistsValidator<GetGameCenterContextQuery, Data.Game> _gameExists;
private readonly INowService _now;
private readonly IStore _store;
private readonly ITeamService _teamService;
private readonly TicketService _ticketService;
private readonly IValidatorService<GetGameCenterContextQuery> _validator;

public GetGameCenterContextHandler
(
EntityExistsValidator<GetGameCenterContextQuery, Data.Game> gameExists,
INowService now,
IStore store,
ITeamService teamService,
TicketService ticketService,
IValidatorService<GetGameCenterContextQuery> validator
)
{
_gameExists = gameExists;
_now = now;
_store = store;
_teamService = teamService;
_ticketService = ticketService;
_validator = validator;
}
private readonly EntityExistsValidator<GetGameCenterContextQuery, Data.Game> _gameExists = gameExists;
private readonly INowService _now = now;
private readonly IStore _store = store;
private readonly ITeamService _teamService = teamService;
private readonly TicketService _ticketService = ticketService;
private readonly IValidatorService<GetGameCenterContextQuery> _validator = validator;

public async Task<GameCenterContext> Handle(GetGameCenterContextQuery request, CancellationToken cancellationToken)
{
Expand Down Expand Up @@ -86,8 +76,11 @@ await _validator
})
.SingleOrDefaultAsync(cancellationToken);

var openTicketCount = await _ticketService
.GetGameOpenTickets(request.GameId)
var gameTotalTicketCount = await _ticketService
.GetGameTicketsQuery(request.GameId)
.CountAsync(cancellationToken);
var gameOpenTicketCount = await _ticketService
.GetGameOpenTicketsQuery(request.GameId)
.CountAsync(cancellationToken);

var topScore = await _store
Expand Down Expand Up @@ -185,7 +178,8 @@ await _validator
// aggregates
ChallengeCount = challengeData?.ChallengeCount ?? 0,
PointsAvailable = challengeData?.PointsAvailable ?? 0,
OpenTicketCount = openTicketCount
OpenTicketCount = gameOpenTicketCount,
TotalTicketCount = gameTotalTicketCount
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public sealed class GameCenterContext

public required int ChallengeCount { get; set; }
public required int OpenTicketCount { get; set; }
public required int TotalTicketCount { get; set; }

public required bool HasScoreboard { get; set; }
public required bool IsExternal { get; set; }
Expand Down
12 changes: 12 additions & 0 deletions src/Gameboard.Api/Features/Challenge/Challenge.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,18 @@ public class ChallengeSummary
public bool IsActive { get; set; }
}

public sealed class ChallengeLaunchCacheEntry
{
public required string TeamId { get; set; }
public required IList<ChallengeLaunchCacheEntrySpec> Specs { get; set; } = [];
}

public sealed class ChallengeLaunchCacheEntrySpec
{
public required string GameId { get; set; }
public required string SpecId { get; set; }
}

public class UserActiveChallenge
{
public required string Id { get; set; }
Expand Down
8 changes: 3 additions & 5 deletions src/Gameboard.Api/Features/Challenge/ChallengeMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,13 @@ public ChallengeMapper()
.ForMember(d => d.LastScoreTime, opt => opt.MapFrom(s => s.Challenge.LastScoreTime))
.ForMember(d => d.Score, opt => opt.MapFrom(s => s.Challenge.Score))
.ForMember(d => d.HasDeployedGamespace, opt => opt.MapFrom(s => s.Vms != null && s.Vms.Any()))
.ForMember(d => d.State, opt => opt.MapFrom(s => JsonSerializer.Serialize(s, JsonOptions)))
;
.ForMember(d => d.State, opt => opt.MapFrom(s => JsonSerializer.Serialize(s, JsonOptions)));

CreateMap<Data.Challenge, Challenge>()
.ForMember(d => d.Score, opt => opt.MapFrom(s => (int)Math.Floor(s.Score)))
.ForMember(d => d.State, opt => opt.MapFrom(s =>
JsonSerializer.Deserialize<GameEngineGameState>(s.State, JsonOptions))
)
;
);

CreateMap<Data.Player, ChallengePlayer>()
.ForMember(cp => cp.IsManager, o => o.MapFrom(p => p.Role == PlayerRole.Manager));
Expand All @@ -67,7 +65,7 @@ public ChallengeMapper()
.ForMember(d => d.Events, o => o.MapFrom(c => c.Events.OrderBy(e => e.Timestamp)))
.ForMember(s => s.Players, o => o.MapFrom(d => new ChallengePlayer[]
{
new ChallengePlayer
new()
{
Id = d.PlayerId,
Name = d.Player.Name,
Expand Down
Loading

0 comments on commit 87cd6bd

Please sign in to comment.