using System.Reflection; using System.Security.Claims; using System.Text.Json; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.EntityFrameworkCore; using RadicadeServer.DB; using RadicadeServer.RecNet.Accounts; using RadicadeServer.RecNet.Enums; using RadicadeServer.RecNet.Enums.Rooms; using RadicadeServer.RecNet.Rooms; using RadicadeServer.RoomServer; using RadicadeServer.Utils; using Xunit; namespace RadicadeServer.Tests; /// /// The Play menu's carousel rows read rooms/carousel/{source}&skip=&take=. The /// client's own template for that feed is paged, and every sibling paged rooms feed answers /// { Results, TotalResults }; this one answered a bare array, which the client reported as /// /// Malformed Response: '[ { "RoomId": 167, "IsDorm": false, ... } ]' /// /// and then as Error refreshing rooms from source CarouselEndpoint - the row stayed empty. /// /// The route assertion is here too: a controller-level test calls the handler by name, so it /// cannot see that the URL the client builds has no route at all (that is what the 404 was /// before this route existed). /// [Collection(DbFactoryCollection.Name)] public class CarouselFeedShapeTests { private const long RoomId = 167; private const long OtherRoomId = 211; private sealed class TestDbContextFactory : IDbContextFactory { private readonly DbContextOptions options = TestDbOptions.Create() .UseInMemoryDatabase(Guid.NewGuid().ToString()) .Options; public RecDBContext CreateDbContext() => new(options); } private static (TestDbContextFactory Factory, RecDBContext Db) CreateContext() { var factory = new TestDbContextFactory(); RoomUtils.DbFactory = factory; return (factory, factory.CreateDbContext()); } private static void SetUser(Controller controller, long accountId) { var identity = new ClaimsIdentity("TestAuth"); identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, accountId.ToString())); controller.ControllerContext = new ControllerContext { HttpContext = new DefaultHttpContext { User = new ClaimsPrincipal(identity) } }; } /// Two public, live rooms - the shape a carousel row draws from. private static async Task SeedRoomsAsync(RecDBContext db) { db.Rooms.AddRange( new RoomData { RoomId = RoomId, Name = "Orientation", CreatorAccountId = 1, Accessibility = Accessibility.Public, State = RoomState.Active, MaxPlayers = 1, CreatedAt = DateTime.UtcNow, }, new RoomData { RoomId = OtherRoomId, Name = "RecCenter", CreatorAccountId = 1, Accessibility = Accessibility.Public, State = RoomState.Active, MaxPlayers = 12, CreatedAt = DateTime.UtcNow, }); await db.SaveChangesAsync(); } private static JsonElement ValueOf(IActionResult result) { var ok = Assert.IsType(result); return JsonSerializer.SerializeToElement(ok.Value); } [Fact] public void SupportsThePagedCarouselRouteTheClientBuilds() { var method = typeof(RoomServerAPI).GetMethod(nameof(RoomServerAPI.GetCarouselRooms))!; var attributes = method .GetCustomAttributes(typeof(HttpMethodAttribute), false) .Cast() .ToList(); Assert.Contains(attributes, x => x.Template == "rooms/carousel/{*source}"); Assert.Contains(attributes, x => x is HttpGetAttribute); } [Fact] public async Task TheCarouselRowAnswersThePagedEnvelopeWithRoomsInIt() { var (_, db) = CreateContext(); await using var _db = db; await SeedRoomsAsync(db); var api = new RoomServerAPI(db); SetUser(api, 1); var value = ValueOf(await api.GetCarouselRooms("foryou", skip: -1, take: 20)); Assert.Equal(JsonValueKind.Object, value.ValueKind); Assert.Equal(2, value.GetProperty("TotalResults").GetInt32()); var results = value.GetProperty("Results").EnumerateArray().ToArray(); Assert.Equal(new long[] { RoomId, OtherRoomId }, results.Select(room => room.GetProperty("RoomId").GetInt64()).ToArray()); } [Fact] public async Task ASourceThatMatchesNothingStillFillsTheRow() { var (_, db) = CreateContext(); await using var _db = db; await SeedRoomsAsync(db); var api = new RoomServerAPI(db); SetUser(api, 1); // "rising" is one of the server's own ordering names, so it matches no room by name or // tag. An empty row is not the answer: the client marks the source failed. var value = ValueOf(await api.GetCarouselRooms("rising&skip=0&take=20", skip: -1, take: 20)); Assert.Equal(2, value.GetProperty("TotalResults").GetInt32()); } }