diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SlotsController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SlotsController.cs index 7afaf6a1..57c07031 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SlotsController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SlotsController.cs @@ -351,31 +351,30 @@ public class SlotsController : ControllerBase PaginationData pageData = this.Request.GetPaginationData(); - Dictionary playersBySlotId = new(); + List busiestSlots = RoomHelper.Rooms.Where(r => r.Slot.SlotType == SlotType.User) + .GroupBy(r => r.Slot.SlotId) + .OrderByDescending(kvp => kvp.Count()) + .Select(kvp => kvp.Key) + .AsQueryable() + .ApplyPagination(pageData) + .ToList(); - foreach (Room room in RoomHelper.Rooms) + pageData.TotalElements = busiestSlots.Count; + + List slots = new(); + + Expression> filterQuery = this.FilterFromRequest(token).Build(); + + foreach (int slotId in busiestSlots) { - // TODO: support developer slotTypes? - if (room.Slot.SlotType != SlotType.User) continue; - - if (!playersBySlotId.TryGetValue(room.Slot.SlotId, out int playerCount)) - playersBySlotId.Add(room.Slot.SlotId, 0); - - playerCount += room.PlayerIds.Count; - - playersBySlotId.Remove(room.Slot.SlotId); - playersBySlotId.Add(room.Slot.SlotId, playerCount); + SlotBase? slot = await this.database.Slots.Where(s => s.SlotId == slotId) + .Where(filterQuery) + .Select(s => SlotBase.CreateFromEntity(s, token)) + .FirstOrDefaultAsync(); + if (slot == null) continue; + slots.Add(slot); } - pageData.TotalElements = playersBySlotId.Count; - - List orderedPlayersBySlotId = playersBySlotId.OrderByDescending(kvp => kvp.Value).Select(kvp => kvp.Key).ToList(); - - SlotQueryBuilder queryBuilder = this.FilterFromRequest(token); - queryBuilder.AddFilter(0, new SlotIdFilter(orderedPlayersBySlotId)); - - List slots = await this.database.GetSlots(token, queryBuilder, pageData, new SlotSortBuilder()); - return this.Ok(new GenericSlotResponse(slots, pageData)); } } \ No newline at end of file diff --git a/ProjectLighthouse.Tests.GameApiTests/Unit/Controllers/SlotControllerTests.cs b/ProjectLighthouse.Tests.GameApiTests/Unit/Controllers/SlotControllerTests.cs index fd1eaff2..ca59ecbc 100644 --- a/ProjectLighthouse.Tests.GameApiTests/Unit/Controllers/SlotControllerTests.cs +++ b/ProjectLighthouse.Tests.GameApiTests/Unit/Controllers/SlotControllerTests.cs @@ -1,13 +1,16 @@ using System.Collections; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Database; +using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots; using LBPUnion.ProjectLighthouse.Tests.Helpers; using LBPUnion.ProjectLighthouse.Types.Entities.Level; using LBPUnion.ProjectLighthouse.Types.Entities.Profile; using LBPUnion.ProjectLighthouse.Types.Entities.Token; using LBPUnion.ProjectLighthouse.Types.Levels; +using LBPUnion.ProjectLighthouse.Types.Matchmaking.Rooms; using LBPUnion.ProjectLighthouse.Types.Serialization; using LBPUnion.ProjectLighthouse.Types.Users; using Microsoft.AspNetCore.Mvc; @@ -377,4 +380,139 @@ public class SlotControllerTests } #endregion + #region BusiestLevels + + // Rather than trying to mock a singleton + // we just make the unit tests take turns + private static readonly Mutex roomMutex = new(false); + + private static async Task AddRoom(int slotId, SlotType type, params int[] playerIds) + { + await RoomHelper.Rooms.AddAsync(new Room + { + PlayerIds = new List(playerIds), + Slot = new RoomSlot + { + SlotId = slotId, + SlotType = type, + }, + }); + } + + [Fact] + public async Task BusiestLevels_ShouldReturnSlots_OrderedByRoomCount() + { + roomMutex.WaitOne(); + try + { + DatabaseContext db = await MockHelper.GetTestDatabase(new[] + { + new List + { + new() + { + SlotId = 1, + Type = SlotType.User, + }, + new() + { + SlotId = 2, + Type = SlotType.User, + }, + new() + { + SlotId = 3, + Type = SlotType.User, + }, + new() + { + SlotId = 4, + Type = SlotType.Developer, + InternalSlotId = 10, + }, + }, + }); + SlotsController controller = new(db); + controller.SetupTestController(); + + await AddRoom(1, SlotType.User, 1); + await AddRoom(2, SlotType.User, 2); + await AddRoom(2, SlotType.User, 3); + await AddRoom(3, SlotType.User, 4); + await AddRoom(3, SlotType.User, 5); + await AddRoom(3, SlotType.User, 6); + + await AddRoom(10, SlotType.Developer, 7); + + IActionResult result = await controller.BusiestLevels(); + GenericSlotResponse slotResponse = result.CastTo(); + Assert.Equal(3, slotResponse.Slots.Count); + Assert.IsType(slotResponse.Slots[0]); + Assert.Equal(3, ((GameUserSlot)slotResponse.Slots[0]).SlotId); + Assert.IsType(slotResponse.Slots[1]); + Assert.Equal(2, ((GameUserSlot)slotResponse.Slots[1]).SlotId); + Assert.IsType(slotResponse.Slots[2]); + Assert.Equal(1, ((GameUserSlot)slotResponse.Slots[2]).SlotId); + } + finally + { + roomMutex.ReleaseMutex(); + } + } + + [Fact] + public async Task BusiestLevels_ShouldNotIncludeDeveloperSlots() + { + roomMutex.WaitOne(); + try + { + DatabaseContext db = await MockHelper.GetTestDatabase(new[] + { + new List + { + new() + { + SlotId = 4, + Type = SlotType.Developer, + InternalSlotId = 10, + }, + }, + }); + SlotsController controller = new(db); + controller.SetupTestController(); + + await AddRoom(10, SlotType.Developer, 1); + + IActionResult result = await controller.BusiestLevels(); + GenericSlotResponse slotResponse = result.CastTo(); + Assert.Empty(slotResponse.Slots); + } + finally + { + roomMutex.ReleaseMutex(); + } + } + + [Fact] + public async Task BusiestLevels_ShouldNotIncludeInvalidSlots() + { + roomMutex.WaitOne(); + try + { + DatabaseContext db = await MockHelper.GetTestDatabase(); + SlotsController controller = new(db); + controller.SetupTestController(); + + await AddRoom(1, SlotType.User, 1); + + IActionResult result = await controller.BusiestLevels(); + GenericSlotResponse slotResponse = result.CastTo(); + Assert.Empty(slotResponse.Slots); + } + finally + { + roomMutex.ReleaseMutex(); + } + } + #endregion } \ No newline at end of file diff --git a/ProjectLighthouse.Tests/Helpers/MockHelper.cs b/ProjectLighthouse.Tests/Helpers/MockHelper.cs index efac1356..ced0fc68 100644 --- a/ProjectLighthouse.Tests/Helpers/MockHelper.cs +++ b/ProjectLighthouse.Tests/Helpers/MockHelper.cs @@ -7,6 +7,7 @@ using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Database; +using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Types.Entities.Profile; using LBPUnion.ProjectLighthouse.Types.Entities.Token; using LBPUnion.ProjectLighthouse.Types.Users; @@ -53,6 +54,8 @@ public static class MockHelper public static async Task GetTestDatabase(IEnumerable sets, [CallerMemberName] string caller = "", [CallerLineNumber] int lineNum = 0) { + await RoomHelper.Rooms.RemoveAllAsync(); + Dictionary setDict = new(); foreach (IList list in sets) { @@ -77,7 +80,6 @@ public static class MockHelper }; } - DbContextOptions options = new DbContextOptionsBuilder() .UseInMemoryDatabase($"{caller}_{lineNum}") .Options; @@ -101,6 +103,8 @@ public static class MockHelper [CallerMemberName] string caller = "", [CallerLineNumber] int lineNum = 0 ) { + await RoomHelper.Rooms.RemoveAllAsync(); + users ??= new List { GetUnitTestUser(),