Skip to content

Commit

Permalink
Test for #522 and fixes to pending names in Game Center -> Teams. (#578)
Browse files Browse the repository at this point in the history
  • Loading branch information
sei-bstein authored Jan 6, 2025
1 parent 25aade0 commit 494eee6
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 17 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Gameboard.Api.Common;
using Gameboard.Api.Data;
using Gameboard.Api.Features.Teams;
using Gameboard.Api.Structure;

Expand Down Expand Up @@ -157,8 +158,6 @@ await httpClient
result.SessionBegin.ShouldBeGreaterThan(DateTimeOffset.MinValue);
}

// 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
Expand Down Expand Up @@ -188,7 +187,7 @@ await _testContext.WithDataState(state =>
{
p.Id = targetPlayerId;
p.Role = PlayerRole.Manager;
p.TeamId = actingTeamId;
p.TeamId = fixture.Create<string>();
p.User = state.Build<Data.User>(fixture);
})
];
Expand All @@ -203,4 +202,40 @@ await _testContext.WithDataState(state =>
// then the response should have a failure code
response.IsSuccessStatusCode.ShouldBeFalse();
}

// 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
[Theory, GbIntegrationAutoData]
public async Task Team_WhenAdminStartingOtherTeamSession_Starts
(
string targetPlayerId,
IFixture fixture
)
{
// given two players registered for the same game
await _testContext.WithDataState(state =>
{
state.Add<Data.Game>(fixture, game =>
{
game.Players =
[
state.Build<Data.Player>(fixture, p =>
{
p.Id = targetPlayerId;
p.Role = PlayerRole.Manager;
p.TeamId = fixture.Create<string>();
p.User = state.Build<Data.User>(fixture);
})
];
});
});

// when the first player tries to start the second's session
var response = await _testContext
.CreateHttpClientWithAuthRole(UserRoleKey.Admin)
.PutAsync($"api/player/{targetPlayerId}/start", null);

// then the response should have a failure code
response.IsSuccessStatusCode.ShouldBeTrue();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Gameboard.Api.Services;
using MediatR;
using Microsoft.EntityFrameworkCore;
using ServiceStack;

namespace Gameboard.Api.Features.Admin;

Expand All @@ -19,13 +20,15 @@ internal class GetGameCenterTeamsHandler
(
INowService nowService,
IPagingService pagingService,
PlayerService playerService,
IStore store,
ITeamService teamService,
TicketService ticketService
) : IRequestHandler<GetGameCenterTeamsQuery, GameCenterTeamsResults>
{
private readonly INowService _nowService = nowService;
private readonly IPagingService _pagingService = pagingService;
private readonly PlayerService _playerService = playerService;
private readonly IStore _store = store;
private readonly ITeamService _teamService = teamService;
private readonly TicketService _ticketService = ticketService;
Expand Down Expand Up @@ -92,6 +95,18 @@ public async Task<GameCenterTeamsResults> Handle(GetGameCenterTeamsQuery request
}
}

if (request.Args.HasPendingNames is not null)
{
if (request.Args.HasPendingNames.Value)
{
query = query.Where(_playerService.GetWhereHasPendingNamePredicate());
}
else
{
query = query.Where(_playerService.GetWhereDoesntHavePendingNamePredicate());
}
}

var matchingTeams = await query
.Select(p => new
{
Expand Down Expand Up @@ -124,15 +139,7 @@ public async Task<GameCenterTeamsResults> Handle(GetGameCenterTeamsQuery request
p.WhenCreated
})
.GroupBy(p => p.TeamId)
// have to do pending names filter here because we need all players
.ToDictionaryAsync(gr => gr.Key, gr => gr.ToArray(), cancellationToken);

if (request.Args.HasPendingNames is not null)
{
matchingTeams = matchingTeams
.Where(kv => request.Args.HasPendingNames.Value == kv.Value.Any(p => p.PendingName != p.Name && (p.NameStatus == string.Empty || p.NameStatus == null || p.NameStatus == AppConstants.NameStatusPending)))
.ToDictionary(kv => kv.Key, kv => kv.Value);
}
.ToDictionaryAsync(gr => gr.Key, gr => gr.ToArray(), cancellationToken); ;

var matchingTeamIds = matchingTeams.Keys.ToArray();
var captains = matchingTeams.ToDictionary(kv => kv.Key, kv =>
Expand Down Expand Up @@ -257,9 +264,7 @@ public async Task<GameCenterTeamsResults> Handle(GetGameCenterTeamsQuery request
// last, we check to see if the game has any pending approvals, as we need them for the screen
var pendingNameCount = await _store
.WithNoTracking<Data.Player>()
.Where(p => p.GameId == request.GameId)
.Where(p => p.Name != null && p.Name != string.Empty && p.Name != p.ApprovedName)
.Where(p => p.NameStatus == AppConstants.NameStatusPending || p.NameStatus == null || p.NameStatus == string.Empty)
.Where(_playerService.GetWhereHasPendingNamePredicate())
.CountAsync(cancellationToken);

return new GameCenterTeamsResults
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,8 @@ public IQueryable<CertificateTemplateView> GetTemplatesQuery()
CreatedByUser = new SimpleEntity { Id = t.CreatedByUserId, Name = t.CreatedByUser.ApprovedName },
UseAsPracticeTemplateForGames = t.UseAsPracticeTemplateForGames.Select(g => new SimpleEntity { Id = g.Id, Name = g.Name }),
UseAsTemplateForGames = t.UseAsTemplateForGames.Select(g => new SimpleEntity { Id = g.Id, Name = g.Name })
});
})
.OrderBy(t => t.Name);

private string BuildCertificateHtml(string templateHtml, CertificateHtmlContext htmlContext)
{
Expand Down
18 changes: 17 additions & 1 deletion src/Gameboard.Api/Features/Player/Services/PlayerService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
using AutoMapper;
Expand Down Expand Up @@ -133,6 +134,16 @@ await _store
);
}

public Expression<Func<Data.Player, bool>> GetWhereHasPendingNamePredicate()
{
return p => p.Name != p.ApprovedName && p.Name != null && p.Name != string.Empty && (p.NameStatus == null || p.NameStatus == string.Empty || p.NameStatus == AppConstants.NameStatusPending);
}

public Expression<Func<Data.Player, bool>> GetWhereDoesntHavePendingNamePredicate()
{
return p => !(p.Name != p.ApprovedName && p.Name != null && p.Name != string.Empty && (p.NameStatus == null || p.NameStatus == string.Empty || p.NameStatus == AppConstants.NameStatusPending));
}

/// <summary>
/// Maps a PlayerId to its UserId
/// </summary>
Expand Down Expand Up @@ -182,7 +193,9 @@ public async Task<Player> Update(ChangedPlayer model, User actor)

// if manipulation of the names has caused Name to equal ApprovedName, clear any pending status
if (player.Name == player.ApprovedName && player.NameStatus == AppConstants.NameStatusPending)
{
player.NameStatus = string.Empty;
}

if (prev.Name != player.Name)
{
Expand Down Expand Up @@ -308,7 +321,10 @@ public async Task<Player[]> List(PlayerDataFilter model, bool sudo = false)
q = q.Where(p => !p.Advanced);

if (model.WantsPending)
q = q.Where(p => p.Name != null && p.Name != string.Empty && p.Name != p.ApprovedName);
{
var pendingNamePredicate = GetWhereHasPendingNamePredicate();
q = q.Where(pendingNamePredicate);
}

if (model.WantsDisallowed)
q = q.Where(u => !string.IsNullOrEmpty(u.NameStatus));
Expand Down

0 comments on commit 494eee6

Please sign in to comment.