using System.Security.Claims; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; using RadicadeServer.DB; using RadicadeServer.RecNet; 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 "Similar Rooms" rail (GET rooms/rooms/{id}/similar). /// /// The route answered a BARE ARRAY, which the client's list DTO cannot parse: its rows are objects /// (PagedResultsDTO<RoomDTO>), so the response came back as /// Malformed Response: '[]' in Player.log and the rail rendered empty. An unknown room was a /// 404 on top of that, which stalls the screen the rail sits on rather than emptying one row. /// [Collection(DbFactoryCollection.Name)] public class SimilarRoomsTests { private sealed class TestDbContextFactory : IDbContextFactory { private readonly DbContextOptions options; public TestDbContextFactory() { var root = new InMemoryDatabaseRoot(); options = TestDbOptions.Create() .UseInMemoryDatabase(Guid.NewGuid().ToString(), root) .Options; } public RecDBContext CreateDbContext() => new(options); public RecDBContext Create() => new(options); } private static (TestDbContextFactory Factory, RecDBContext Db) NewContext() { var factory = new TestDbContextFactory(); RoomUtils.DbFactory = factory; MatchmakingUtils.DbFactory = factory; return (factory, factory.Create()); } private static V2_RoomsAPI NewApi(RecDBContext db, long callerId = 2) { var identity = new ClaimsIdentity("TestAuth"); identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, callerId.ToString())); return new V2_RoomsAPI(db) { ControllerContext = new ControllerContext { HttpContext = new DefaultHttpContext { User = new ClaimsPrincipal(identity) } } }; } private static RoomData Room(long id, string name, string tags, Accessibility accessibility = Accessibility.Public) => new() { RoomId = id, Name = name, CreatorAccountId = 2, Accessibility = accessibility, Tags = tags, State = RoomState.Active, }; [Fact] public async Task SimilarRoomsAnswerAPagedObjectAndNotABareArray() { var (_, db) = NewContext(); db.Rooms.Add(Room(10, "Source", "import,0|horror,0")); db.Rooms.Add(Room(11, "SharesTag", "import,0")); db.Rooms.Add(Room(12, "Unrelated", "art,0")); await db.SaveChangesAsync(); var result = await NewApi(db).GetSimilarRooms(10, 0, 100); var ok = Assert.IsType(result); var page = Assert.IsType(ok.Value); Assert.Equal(1, page.TotalResults); Assert.Single(page.Results); Assert.Equal(11, page.Results[0].RoomId); } [Fact] public async Task AnUnknownRoomAnswersAnEmptyPageRatherThan404() { var (_, db) = NewContext(); var result = await NewApi(db).GetSimilarRooms(99999, 0, 100); var ok = Assert.IsType(result); var page = Assert.IsType(ok.Value); Assert.Equal(0, page.TotalResults); Assert.Empty(page.Results); } [Fact] public async Task PaginationPagesTheRowsAndCountsThemAll() { var (_, db) = NewContext(); db.Rooms.Add(Room(20, "Source", "import,0")); for (var id = 21; id < 25; id++) db.Rooms.Add(Room(id, "Shares" + id, "import,0")); await db.SaveChangesAsync(); var result = await NewApi(db).GetSimilarRooms(20, 1, 2); var page = Assert.IsType(Assert.IsType(result).Value); Assert.Equal(4, page.TotalResults); Assert.Equal(2, page.Results.Count); } [Fact] public async Task RoomsTheCallerMayNotSeeAreNeverInTheRail() { var (_, db) = NewContext(); db.Rooms.Add(Room(30, "Source", "import,0")); db.Rooms.Add(Room(31, "Hidden", "import,0", Accessibility.Private)); await db.SaveChangesAsync(); var page = Assert.IsType( Assert.IsType(await NewApi(db).GetSimilarRooms(30, 0, 100)).Value); Assert.Equal(0, page.TotalResults); } }