using DorkNet.Server.Services; using Google.Protobuf; using RecRoom.Protobuf; namespace DorkNet.Server.Tests; /// /// A modern RecNet export's subroom save stamps PersistedRoomData /// versions past what the March-2023 client knows (field 1 /// DEPRECATED_RoomPersistenceVersion, max 38; field 30 /// PersistedRoomVersion, max 19 = V19February23BetaRelease). The client /// rejects the whole room with its "update Rec Room to visit this room" /// gate — the ShibuyaCrossing import (Sep-2025 save, version=131) is the /// canonical repro. The CDN serve path clamps both varints down; these /// tests pin the rewrite to exactly those two fields. /// public sealed class RoomBlobVersionClampTests { private static byte[] BuildModernBlob(int deprecatedVersion, int version) { // Emit the wire shape a modern save actually has: version fields // plus payload fields the 2023 schema does and doesn't know about. using var ms = new MemoryStream(); var output = new CodedOutputStream(ms); output.WriteTag(1, WireFormat.WireType.Varint); output.WriteInt32(deprecatedVersion); // Field 2: persistence_views payload (opaque here — the clamp // must copy it verbatim). output.WriteTag(2, WireFormat.WireType.LengthDelimited); output.WriteBytes(ByteString.CopyFrom(0x0A, 0x03, 0x01, 0x02, 0x03)); // Field 6: sub_room_id. output.WriteTag(6, WireFormat.WireType.Varint); output.WriteInt64(19589779); output.WriteTag(30, WireFormat.WireType.Varint); output.WriteInt32(version); // Field 37: unknown-to-2023 varint field from newer schemas. output.WriteTag(37, WireFormat.WireType.Varint); output.WriteInt32(1); output.Flush(); return ms.ToArray(); } [Fact] public void Clamps_modern_versions_to_2023_maxima() { var input = BuildModernBlob(deprecatedVersion: 38, version: 131); var (bytes, changed) = RoomDataBlobService.ClampVersionsFor2023(input); Assert.True(changed); var msg = PersistedRoomData.Parser.ParseFrom(bytes); Assert.Equal(RoomDataBlobService.Client2023MaxPersistedRoomVersion, (int)msg.Version); Assert.Equal(DEPRECATED_RoomPersistenceVersion.LatestRoomPersistenceVersion, msg.DEPRECATEDVersion); } [Fact] public void Preserves_every_non_version_byte() { var input = BuildModernBlob(deprecatedVersion: 38, version: 131); var (bytes, _) = RoomDataBlobService.ClampVersionsFor2023(input); // version=131 is a two-byte varint, 16 is one byte — the output // shrinks by exactly that and matches the input everywhere else. Assert.Equal(input.Length - 1, bytes.Length); var expected = BuildModernBlob( deprecatedVersion: 38, version: RoomDataBlobService.Client2023MaxPersistedRoomVersion); Assert.Equal(expected, bytes); } [Fact] public void Blob_already_at_2023_versions_passes_through_unchanged() { var input = BuildModernBlob( deprecatedVersion: 38, version: RoomDataBlobService.Client2023MaxPersistedRoomVersion); var (bytes, changed) = RoomDataBlobService.ClampVersionsFor2023(input); Assert.False(changed); Assert.Same(input, bytes); } [Fact] public void Malformed_input_is_returned_verbatim() { var input = new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01 }; var (bytes, changed) = RoomDataBlobService.ClampVersionsFor2023(input); Assert.False(changed); Assert.Same(input, bytes); } }