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);
}
}