mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-25 23:18:56 -07:00
130 lines
4.6 KiB
C#
130 lines
4.6 KiB
C#
using System;
|
|
using System.IO;
|
|
using RadicadeServer.Images;
|
|
using Xunit;
|
|
|
|
namespace RadicadeServer.Tests;
|
|
|
|
/// <summary>
|
|
/// The image route answers by name, and the name a client asks for is not always the name on
|
|
/// disk. Two real cases drove this: a custom-avatar design is stored as
|
|
/// <c><guid>.bin</c> while its bytes are a PNG, and the default room image ships as
|
|
/// <c>DefaultRoomImage.png</c> while the server asks for the bare <c>DefaultRoomImage</c>.
|
|
/// Both resolved to nothing, and nothing is not "no picture" -- it is an empty 404 body, which
|
|
/// the 2023 client turns into a broken surface (and, for a room image, a fatal load failure).
|
|
/// </summary>
|
|
public class ImageAssetResolutionTests : IDisposable
|
|
{
|
|
private readonly string root = Directory.CreateTempSubdirectory("radicade-images-test-").FullName;
|
|
|
|
public void Dispose()
|
|
{
|
|
try
|
|
{
|
|
Directory.Delete(root, true);
|
|
}
|
|
catch (IOException)
|
|
{
|
|
}
|
|
}
|
|
|
|
private string Write(string name, string content = "x")
|
|
{
|
|
var path = Path.Join(root, name);
|
|
File.WriteAllText(path, content);
|
|
return path;
|
|
}
|
|
|
|
[Fact]
|
|
public void TheRequestedNameIsFoundFirst()
|
|
{
|
|
var expected = Write("photo.png");
|
|
|
|
Assert.Equal(expected, ImageAPI.ResolveAsset("photo.png", new[] { root }));
|
|
}
|
|
|
|
[Fact]
|
|
public void ADesignStoredAsBinIsFoundByTheNameTheClientAsksFor()
|
|
{
|
|
// Exactly what the customizer wrote before designs moved to the image CDN: the bytes
|
|
// are a PNG, the name on disk ends in .bin, and the client asks for that name.
|
|
var expected = Write("a07188bb-9f99-442d-a2ff-2aebad2ca7fe.bin");
|
|
|
|
Assert.Equal(expected, ImageAPI.ResolveAsset("a07188bb-9f99-442d-a2ff-2aebad2ca7fe.bin", new[] { root }));
|
|
}
|
|
|
|
[Fact]
|
|
public void AMissingExtensionIsTriedOnTheSameStem()
|
|
{
|
|
// The default-image case: the server asks for "DefaultRoomImage", the deployment ships
|
|
// "DefaultRoomImage.png", and the extension search used to be skipped for this name.
|
|
var expected = Write("DefaultRoomImage.png");
|
|
|
|
Assert.Equal(expected, ImageAPI.ResolveAsset("DefaultRoomImage", new[] { root }));
|
|
}
|
|
|
|
[Fact]
|
|
public void AnExtensionThatDoesNotDescribeTheContentStillFindsTheFile()
|
|
{
|
|
var expected = Write("design.png");
|
|
|
|
Assert.Equal(expected, ImageAPI.ResolveAsset("design.bin", new[] { root }));
|
|
Assert.Equal(expected, ImageAPI.ResolveAsset("design.jpeg", new[] { root }));
|
|
}
|
|
|
|
[Fact]
|
|
public void RootsAreSearchedInOrder()
|
|
{
|
|
var first = Directory.CreateDirectory(Path.Join(root, "images")).FullName;
|
|
var second = Directory.CreateDirectory(Path.Join(root, "custom")).FullName;
|
|
var expected = Path.Join(first, "shirt.png");
|
|
File.WriteAllText(expected, "x");
|
|
File.WriteAllText(Path.Join(second, "shirt.png"), "x");
|
|
|
|
Assert.Equal(expected, ImageAPI.ResolveAsset("shirt.png", new[] { first, second }));
|
|
}
|
|
|
|
[Fact]
|
|
public void ANamedRootIsPreferredOverALaterRootsVariant()
|
|
{
|
|
var images = Directory.CreateDirectory(Path.Join(root, "images")).FullName;
|
|
var custom = Directory.CreateDirectory(Path.Join(root, "custom")).FullName;
|
|
var expected = Path.Join(custom, "shirt.bin");
|
|
File.WriteAllText(expected, "x");
|
|
File.WriteAllText(Path.Join(images, "shirt.png"), "x");
|
|
|
|
Assert.Equal(expected, ImageAPI.ResolveAsset("shirt.bin", new[] { images, custom }));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("")]
|
|
[InlineData(".")]
|
|
[InlineData("..")]
|
|
[InlineData("../outside.png")]
|
|
[InlineData("..\\outside.png")]
|
|
[InlineData("sub/../outside.png")]
|
|
public void NamesThatDoNotNameAFileInsideARootResolveToNothing(string name)
|
|
{
|
|
Assert.Null(ImageAPI.ResolveAsset(name, new[] { root }));
|
|
}
|
|
|
|
[Fact]
|
|
public void AnUnknownNameResolvesToNothing()
|
|
{
|
|
Assert.Null(ImageAPI.ResolveAsset("definitely-missing.png", new[] { root }));
|
|
Assert.Null(ImageAPI.ResolveAsset("definitely-missing.bin", new[] { root }));
|
|
}
|
|
|
|
[Fact]
|
|
public void TheServedRootsStillIncludeTheDesignRoot()
|
|
{
|
|
// A design written before designs moved to the image CDN only stays reachable while
|
|
// CDN/Custom is still searched, so dropping it here would silently blank old shirts.
|
|
var roots = ImageAPI.AssetRoots();
|
|
|
|
Assert.Contains(roots, r => r.EndsWith(Path.Join("CDN", "Images")));
|
|
Assert.Contains(roots, r => r.EndsWith(Path.Join("CDN", "Custom")));
|
|
Assert.Contains(roots, r => r.EndsWith(Path.Join("CDN", "Storage")));
|
|
}
|
|
}
|