CultLib/tests/GameCult.Mesh.Tests/CultMeshCdnTests.cs
2026-06-28 07:54:00 +01:00

123 lines
4.9 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FluentAssertions;
using GameCult.Caching;
using GameCult.Networking;
using NUnit.Framework;
namespace GameCult.Mesh.Tests;
public sealed class CultMeshCdnTests
{
[Test]
public async Task Artifact_PacksPublishesAndReassembles_FromCultCache()
{
var cache = new CultCache();
var payload = Encoding.UTF8.GetBytes("abcdefghijklmnopqrstuvwxyz");
var artifact = CultMesh.PackCdnArtifact(
"aetheria/ui/icons/minimap-pack",
payload,
new CultMeshCdnPackOptions
{
ChunkSizeBytes = 8,
Version = "2026.06.27",
MimeType = "application/octet-stream",
Tags = ["aetheria", "ui"],
Metadata = new Dictionary<string, string>
{
["channel"] = "dev"
}
});
artifact.Manifest.Kind.Should().Be(CultMeshCdnArtifactKinds.Asset);
artifact.Manifest.SizeBytes.Should().Be(payload.Length);
artifact.Manifest.Chunks.Should().HaveCount(4);
artifact.Manifest.Chunks.Select(chunk => chunk.Offset).Should().Equal(0, 8, 16, 24);
artifact.Manifest.Chunks.Select(chunk => chunk.SizeBytes).Should().Equal(8, 8, 8, 2);
var manifest = await CultMesh.PublishCdnArtifactAsync(cache, artifact);
var manifestKey = CultMeshCdnArtifactManifest.CreateRecordKey(manifest);
cache.Get<CultMeshCdnArtifactManifest>(manifestKey).Should().NotBeNull();
var roundTrip = CultMesh.ReadCdnArtifact(cache, manifest);
roundTrip.Should().Equal(payload);
}
[Test]
public async Task BuildArtifact_Replicates_ThroughCultNetRawSnapshot()
{
var sourceCache = new CultCache();
var targetCache = new CultCache();
var registry = CultMesh.CreateCdnDocumentRegistry(sourceCache.Registry);
var payload = Encoding.UTF8.GetBytes("starbridge-build-update");
var artifact = CultMesh.PackCdnArtifact(
"starbridge/windows/client",
payload,
new CultMeshCdnPackOptions
{
ChunkSizeBytes = 7,
Kind = CultMeshCdnArtifactKinds.Build,
Version = "0.4.0-preview",
Tags = ["starbridge", "build"],
Metadata = new Dictionary<string, string>
{
["platform"] = "windows-x64"
}
});
var manifest = await CultMesh.PublishCdnArtifactAsync(sourceCache, artifact);
var manifestSchemaId = sourceCache.Registry.GetRequired<CultMeshCdnArtifactManifest>().SchemaId;
var chunkSchemaId = sourceCache.Registry.GetRequired<CultMeshCdnArtifactChunk>().SchemaId;
var recordKeys = artifact.RecordKeys.Select(key => key.Value).ToArray();
var request = registry.CreateSnapshotRequest(
"cdn-snapshot-request",
schemaIds: [manifestSchemaId, chunkSchemaId],
recordKeys: recordKeys);
var response = registry.CreateRawSnapshotResponse(sourceCache, "cdn-snapshot", request);
response.Documents.Should().HaveCount(artifact.Chunks.Count + 1);
response.Documents.Select(document => document.RecordKey).Should().BeEquivalentTo(recordKeys);
await registry.ApplyRawSnapshotResponseAsync(targetCache, response);
var replicatedManifest = targetCache.Get<CultMeshCdnArtifactManifest>(
CultMeshCdnArtifactManifest.CreateRecordKey(manifest));
replicatedManifest.Should().NotBeNull();
replicatedManifest!.Kind.Should().Be(CultMeshCdnArtifactKinds.Build);
replicatedManifest.Metadata["platform"].Should().Be("windows-x64");
var roundTrip = CultMesh.ReadCdnArtifact(targetCache, replicatedManifest);
roundTrip.Should().Equal(payload);
}
[Test]
public async Task ArtifactRead_RejectsTamperedChunkPayload()
{
var cache = new CultCache();
var artifact = CultMesh.PackCdnArtifact(
"aetheria/gravity/layer0",
[1, 1, 2, 3, 5, 8],
new CultMeshCdnPackOptions
{
ChunkSizeBytes = 3,
Metadata = new Dictionary<string, string>
{
["channels"] = "r32"
}
});
var manifest = await CultMesh.PublishCdnArtifactAsync(cache, artifact);
var firstChunk = artifact.Chunks[0];
firstChunk.Payload[0] = 99;
await cache.UpsertAsync(
firstChunk,
new CultRecordHandle<CultMeshCdnArtifactChunk>(
CultMeshCdnArtifactChunk.CreateRecordKey(firstChunk)));
Action read = () => CultMesh.ReadCdnArtifact(cache, manifest);
read.Should().Throw<InvalidDataException>()
.WithMessage("*chunk hash*");
}
}