test(capture): fixtures check client game messages, with a synthetic fixture

The fixture check now also reads every recorded client game message with the
struct the server reads it with (GameMessageHandler::CreateReceived) and writes
it again: it must read the whole message and give back the same bits. Game
message fields are decoded with the server's structs in the fixture tests.

A synthetic fixture, built in the test from the server's own structs (login,
position update, a client game message), goes through the export steps
(portable, anonymised, saved, read again) and passes the same checks; recorded
fixtures stay in tests/fixtures-local and are never committed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-29 22:25:28 -05:00
parent 0b58b3ec97
commit b6518caa9c
5 changed files with 174 additions and 11 deletions

View File

@@ -6,6 +6,7 @@
#include <utility>
#include "GameMessages.h"
#include "GameMessageHandler.h"
#include "ActivityMessages.h"
#include "InventoryMessages.h"
#include "ObjectMessages.h"
@@ -94,4 +95,24 @@ namespace GameMessageDecoder {
if (it == Decoders().end()) return std::nullopt;
return it->second(payload);
}
std::optional<bool> RoundTripReceived(MessageType::Game messageId, RakNet::BitStream& payload) {
auto message = GameMessageHandler::CreateReceived(messageId);
if (!message) return std::nullopt;
const auto start = payload.GetReadOffset();
if (!message->Deserialize(payload)) return std::nullopt;
const auto read = payload.GetReadOffset() - start;
// A whole byte or more left over: the struct stopped short of fields the message has
if (payload.GetNumberOfUnreadBits() >= 8) return false;
RakNet::BitStream written;
message->Serialize(written);
if (written.GetNumberOfBitsUsed() != read) return false;
// The bits read, compared with the bits written
payload.SetReadOffset(start);
for (uint32_t bit = 0; bit < read; bit++) {
bool original{}, again{};
if (!payload.Read(original) || !written.Read(again) || original != again) return false;
}
return true;
}
}