Files
Radicade/ServerTests/StudioCloudBuildTests.cs
T
2026-10-16 23:33:13 -07:00

154 lines
6.6 KiB
C#

using System.Security.Claims;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging.Abstractions;
using RadicadeServer.API.Studio;
using RadicadeServer.DB;
using Xunit;
namespace RadicadeServer.Tests;
[Collection(DbFactoryCollection.Name)]
public class StudioCloudBuildTests
{
private sealed class TestDbFactory : IDbContextFactory<RecDBContext>
{
private readonly DbContextOptions<RecDBContext> options = TestDbOptions.Create()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
public RecDBContext CreateDbContext() => new(options);
public Task<RecDBContext> CreateDbContextAsync(CancellationToken cancellationToken = default) =>
Task.FromResult(CreateDbContext());
}
private static (StudioAPI Api, StudioBuildWorker Worker) CreateController(long accountId = 2, long? roomOwnerId = null)
{
var identity = new ClaimsIdentity("TestAuth");
identity.AddClaim(new Claim("userId", accountId.ToString()));
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, accountId.ToString()));
var worker = new StudioBuildWorker(
NullLogger<StudioBuildWorker>.Instance,
new ConfigurationBuilder().Build());
var dbFactory = new TestDbFactory();
using (var db = dbFactory.CreateDbContext())
{
db.Rooms.Add(new RadicadeServer.RecNet.Rooms.RoomData
{
RoomId = 99,
Name = "Build Test Room",
CreatorAccountId = roomOwnerId ?? accountId
});
db.SaveChanges();
}
var api = new StudioAPI(NullLogger<StudioAPI>.Instance, worker, dbFactory)
{
ControllerContext = new ControllerContext
{
HttpContext = new DefaultHttpContext { User = new ClaimsPrincipal(identity) }
}
};
return (api, worker);
}
private static async Task<StudioCloudBuild> WaitForBuild(StudioAPI api, string cloudBuildId)
{
for (var attempt = 0; attempt < 100; attempt++)
{
var response = Assert.IsType<OkObjectResult>(await api.CloudBuild(cloudBuildId));
var build = System.Text.Json.JsonSerializer.Deserialize<StudioCloudBuild>(
System.Text.Json.JsonSerializer.Serialize(response.Value));
Assert.NotNull(build);
if (build!.CompletedAt != null) return build;
await Task.Delay(20);
}
throw new TimeoutException("The queued local build did not report a result.");
}
[Fact]
public async Task CreateQueuesBuildAndReturnsTheWorkerFailureForMissingUnityConfiguration()
{
var (controller, worker) = CreateController();
await worker.StartAsync(CancellationToken.None);
try
{
var response = Assert.IsType<OkObjectResult>(await controller.CreateCloudBuild(99, 109, 1, autoPublish: true, buildBranch: null));
var json = System.Text.Json.JsonSerializer.SerializeToElement(response.Value);
Assert.True(json.GetProperty("Success").GetBoolean());
Assert.Null(json.GetProperty("Error").GetString());
var queued = json.GetProperty("Value");
var cloudBuildId = queued.GetProperty("CloudBuildId").GetString();
Assert.False(string.IsNullOrWhiteSpace(cloudBuildId));
Assert.Equal(System.Text.Json.JsonValueKind.Null, queued.GetProperty("CompletedAt").ValueKind);
Assert.Equal(2, queued.GetProperty("CreatedByAccountId").GetInt32());
var completed = await WaitForBuild(controller, cloudBuildId!);
Assert.True(completed.CompletedAt >= completed.StartedAt);
Assert.Contains("Radicade:Studio:UnityPath", completed.Error);
Assert.Empty(completed.UnityAssetId);
var list = Assert.IsType<OkObjectResult>(await controller.CloudBuildsForRoom(99, 109, 0, 30));
var listJson = System.Text.Json.JsonSerializer.SerializeToElement(list.Value);
Assert.Contains(listJson.GetProperty("Results").EnumerateArray(),
item => item.GetProperty("CloudBuildId").GetString() == cloudBuildId);
}
finally
{
await worker.StopAsync(CancellationToken.None);
worker.Dispose();
}
}
[Fact]
public async Task CreateAcceptsStudioUrlEncodedPostFormFields()
{
var (controller, worker) = CreateController();
await worker.StartAsync(CancellationToken.None);
try
{
var context = controller.HttpContext;
context.Request.ContentType = "application/x-www-form-urlencoded";
context.Request.Body = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(
"roomId=99&subRoomId=109&subRoomDataSaveId=1&autoPublish=true&buildBranch=main"));
context.Request.ContentLength = context.Request.Body.Length;
var response = Assert.IsType<OkObjectResult>(await controller.CreateCloudBuild());
var json = System.Text.Json.JsonSerializer.SerializeToElement(response.Value);
Assert.True(json.GetProperty("Success").GetBoolean());
var createdId = json.GetProperty("Value").GetProperty("CloudBuildId").GetString()!;
var completed = await WaitForBuild(controller, createdId);
Assert.Contains("Radicade:Studio:UnityPath", completed.Error);
var page = Assert.IsType<OkObjectResult>(await controller.CloudBuildsForRoom(99, 109, 0, 100));
var pageJson = System.Text.Json.JsonSerializer.SerializeToElement(page.Value);
Assert.Contains(pageJson.GetProperty("Results").EnumerateArray(),
build => build.GetProperty("CloudBuildId").GetString() == createdId);
}
finally
{
await worker.StopAsync(CancellationToken.None);
worker.Dispose();
}
}
[Fact]
public async Task CreateRejectsMissingSaveReference()
{
var (controller, worker) = CreateController();
var result = Assert.IsType<BadRequestObjectResult>(await controller.CreateCloudBuild(99, 109, 0));
Assert.NotNull(result.Value);
worker.Dispose();
}
[Fact]
public async Task CreateRejectsAccountsWithoutRoomBuildPermission()
{
var (controller, worker) = CreateController(accountId: 2, roomOwnerId: 7);
var result = Assert.IsType<ForbidResult>(await controller.CreateCloudBuild(99, 109, 1));
Assert.NotNull(result);
worker.Dispose();
}
}