mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-25 23:18:56 -07:00
Radicade Public Code
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
using System.Security.Cryptography;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using RadicadeServer;
|
||||
using RadicadeServer.CDN;
|
||||
using RadicadeServer.RecNet.CDN;
|
||||
using RadicadeServer.Storage;
|
||||
using RadicadeServer.API.Studio;
|
||||
using RadicadeServer.RoomServer;
|
||||
using RadicadeServer.DB;
|
||||
using RadicadeServer.RecNet.Rooms;
|
||||
using Xunit;
|
||||
|
||||
namespace RadicadeServer.Tests;
|
||||
|
||||
[Collection(DbFactoryCollection.Name)]
|
||||
public class StudioUploadTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task BatchPreservesOrderAndStoresActualContentsIncludingEmptyFiles()
|
||||
{
|
||||
var previous = DBConfig.CDNStorage;
|
||||
var directory = Path.Combine(Path.GetTempPath(), "studio-upload-" + Guid.NewGuid());
|
||||
DBConfig.CDNStorage = directory;
|
||||
try
|
||||
{
|
||||
var payloads = new[] { new byte[] { 1, 2, 3 }, Array.Empty<byte>() };
|
||||
var boundary = "test";
|
||||
var body = new MemoryStream();
|
||||
foreach (var payload in payloads)
|
||||
{
|
||||
var header = System.Text.Encoding.ASCII.GetBytes(
|
||||
$"--{boundary}\r\nContent-Disposition: form-data; name=\"file\"; filename=\"asset.bin\"\r\nContent-Type: application/octet-stream\r\n\r\n");
|
||||
body.Write(header);
|
||||
body.Write(payload);
|
||||
body.Write(System.Text.Encoding.ASCII.GetBytes("\r\n"));
|
||||
}
|
||||
body.Write(System.Text.Encoding.ASCII.GetBytes($"--{boundary}--\r\n"));
|
||||
body.Position = 0;
|
||||
var context = new DefaultHttpContext();
|
||||
context.Request.ContentType = $"multipart/form-data; boundary={boundary}";
|
||||
context.Request.Body = body;
|
||||
context.Request.ContentLength = body.Length;
|
||||
var controller = new StorageAPI { ControllerContext = new ControllerContext { HttpContext = context } };
|
||||
var response = Assert.IsType<OkObjectResult>(await controller.UploadUnityAssets());
|
||||
var json = System.Text.Json.JsonSerializer.SerializeToElement(response.Value);
|
||||
Assert.Equal(2, json.GetArrayLength());
|
||||
for (var i = 0; i < payloads.Length; i++)
|
||||
{
|
||||
var name = json[i].GetProperty("Filename").GetString()!;
|
||||
Assert.Equal(40, name.Length);
|
||||
Assert.Equal(payloads[i], await File.ReadAllBytesAsync(Path.Combine(directory, name)));
|
||||
Assert.Equal(Convert.ToBase64String(SHA256.HashData(payloads[i])), json[i].GetProperty("Hash").GetString());
|
||||
Assert.IsType<PhysicalFileResult>(new StudioAssetsCDN().Download(name));
|
||||
}
|
||||
Assert.Equal(2, Directory.GetFiles(directory).Length);
|
||||
}
|
||||
finally
|
||||
{
|
||||
DBConfig.CDNStorage = previous;
|
||||
if (Directory.Exists(directory)) Directory.Delete(directory, true);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UploadParsesGzipCompressedMultipartFilesLargerThanDefault16KiBLimit()
|
||||
{
|
||||
var previous = DBConfig.CDNStorage;
|
||||
var directory = Path.Combine(Path.GetTempPath(), "studio-upload-large-" + Guid.NewGuid());
|
||||
DBConfig.CDNStorage = directory;
|
||||
try
|
||||
{
|
||||
const string boundary = "studio-upload-boundary";
|
||||
var payload = Enumerable.Range(0, 32 * 1024).Select(i => (byte)(i % 251)).ToArray();
|
||||
var multipart = System.Text.Encoding.ASCII.GetBytes(
|
||||
new string('p', 20 * 1024) + "\r\n" +
|
||||
$"--{boundary}\r\nContent-Disposition: form-data; name=\"file\"; filename=\"asset.bin\"\r\nContent-Type: application/octet-stream\r\n\r\n")
|
||||
.Concat(payload)
|
||||
.Concat(System.Text.Encoding.ASCII.GetBytes($"\r\n--{boundary}--\r\n"))
|
||||
.ToArray();
|
||||
var body = new MemoryStream();
|
||||
await using (var gzip = new System.IO.Compression.GZipStream(body, System.IO.Compression.CompressionLevel.Optimal, leaveOpen: true))
|
||||
await gzip.WriteAsync(multipart);
|
||||
body.Position = 0;
|
||||
var context = new DefaultHttpContext();
|
||||
context.Request.ContentType = $"multipart/form-data; boundary=\"{boundary}\"";
|
||||
context.Request.Headers["Content-Encoding"] = "gzip";
|
||||
context.Request.Body = body;
|
||||
context.Request.ContentLength = body.Length;
|
||||
var controller = new StorageAPI { ControllerContext = new ControllerContext { HttpContext = context } };
|
||||
|
||||
var response = Assert.IsType<OkObjectResult>(await controller.UploadUnityAssets());
|
||||
var json = System.Text.Json.JsonSerializer.SerializeToElement(response.Value);
|
||||
var filename = json[0].GetProperty("Filename").GetString()!;
|
||||
Assert.Equal(payload, await File.ReadAllBytesAsync(Path.Combine(directory, filename)));
|
||||
}
|
||||
finally
|
||||
{
|
||||
DBConfig.CDNStorage = previous;
|
||||
if (Directory.Exists(directory)) Directory.Delete(directory, true);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CompressedSingleFileUploadReturnsLegacyFilenameResponse()
|
||||
{
|
||||
var previous = DBConfig.CDNStorage;
|
||||
var directory = Path.Combine(Path.GetTempPath(), "studio-upload-single-" + Guid.NewGuid());
|
||||
DBConfig.CDNStorage = directory;
|
||||
try
|
||||
{
|
||||
const string boundary = "legacy-upload-boundary";
|
||||
var payload = Enumerable.Range(0, 4096).Select(i => (byte)(i % 253)).ToArray();
|
||||
var multipart = System.Text.Encoding.ASCII.GetBytes(
|
||||
"--" + boundary + "\r\nContent-Disposition: form-data; name=\"file\"; filename=\"asset.bin\"\r\nContent-Type: application/octet-stream\r\n\r\n")
|
||||
.Concat(payload)
|
||||
.Concat(System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n"))
|
||||
.ToArray();
|
||||
var body = new MemoryStream();
|
||||
await using (var gzip = new System.IO.Compression.GZipStream(body, System.IO.Compression.CompressionLevel.Optimal, leaveOpen: true))
|
||||
await gzip.WriteAsync(multipart);
|
||||
body.Position = 0;
|
||||
var context = new DefaultHttpContext();
|
||||
context.Request.ContentType = $"multipart/form-data; boundary=\"{boundary}\"";
|
||||
context.Request.Headers["Content-Encoding"] = "gzip";
|
||||
context.Request.Body = body;
|
||||
context.Request.ContentLength = body.Length;
|
||||
var controller = new StorageAPI { ControllerContext = new ControllerContext { HttpContext = context } };
|
||||
|
||||
var response = await controller.Upload();
|
||||
var value = Assert.IsType<UploadResponse>(response.Value);
|
||||
Assert.Equal(40, value.filename.Length);
|
||||
Assert.Equal(payload, await File.ReadAllBytesAsync(Path.Combine(directory, value.filename)));
|
||||
}
|
||||
finally
|
||||
{
|
||||
DBConfig.CDNStorage = previous;
|
||||
if (Directory.Exists(directory)) Directory.Delete(directory, true);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RegistryAndGameMetadataExposeBothWindowsBundlesAndRawAssetDownload()
|
||||
{
|
||||
var previous = DBConfig.CDNStorage;
|
||||
var directory = Path.Combine(Path.GetTempPath(), "studio-baked-" + Guid.NewGuid());
|
||||
DBConfig.CDNStorage = directory;
|
||||
Directory.CreateDirectory(directory);
|
||||
try
|
||||
{
|
||||
const string manifestId = "0123456789abcdef0123456789abcdef01234567";
|
||||
await File.WriteAllTextAsync(Path.Combine(directory, manifestId), "{\"Version\":2,\"MainSceneProjectRelativePath\":\"Assets/Room.unity\",\"Files\":[]}");
|
||||
await using var sceneBundle = new MemoryStream(new byte[] { 1, 2, 3, 4 });
|
||||
var sceneUpload = await StudioAssetStorage.Save(sceneBundle, CancellationToken.None);
|
||||
await using var rejectedBundle = new MemoryStream(new byte[] { 5, 6, 7 });
|
||||
var rejectedUpload = await StudioAssetStorage.Save(rejectedBundle, CancellationToken.None);
|
||||
await StudioUnityAssetRegistry.AddBakedAssetAsync(manifestId,
|
||||
new StudioBakedUnityAsset(manifestId, 0, 1, sceneUpload.Filename, rejectedUpload.Filename));
|
||||
|
||||
var api = new UnityAssetsCDN { ControllerContext = new ControllerContext { HttpContext = new DefaultHttpContext() } };
|
||||
var response = Assert.IsType<ContentResult>(await api.GetUnityAsset(manifestId, 0, 1));
|
||||
using var metadata = System.Text.Json.JsonDocument.Parse(response.Content!);
|
||||
Assert.Equal(sceneUpload.Filename, metadata.RootElement.GetProperty("Filename").GetString());
|
||||
Assert.Equal(rejectedUpload.Filename, metadata.RootElement.GetProperty("RejectedDependenciesFilename").GetString());
|
||||
Assert.IsType<PhysicalFileResult>(new DataCDN().GetDataBlob(sceneUpload.Filename));
|
||||
Assert.IsType<PhysicalFileResult>(new DataCDN().GetDataBlob(rejectedUpload.Filename));
|
||||
|
||||
var studio = new StudioAssetsCDN { ControllerContext = new ControllerContext { HttpContext = new DefaultHttpContext() } };
|
||||
Assert.IsType<PhysicalFileResult>(studio.Download(sceneUpload.Filename));
|
||||
}
|
||||
finally
|
||||
{
|
||||
DBConfig.CDNStorage = previous;
|
||||
if (Directory.Exists(directory)) Directory.Delete(directory, true);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("../secret")]
|
||||
[InlineData("..\\secret")]
|
||||
[InlineData("C:secret")]
|
||||
public void DownloadRejectsPaths(string filename) =>
|
||||
Assert.IsType<BadRequestResult>(new StudioAssetsCDN().Download(filename));
|
||||
}
|
||||
Reference in New Issue
Block a user