107 lines
4.0 KiB
C#
107 lines
4.0 KiB
C#
using System.Text.Json;
|
|
using FinalFactory.Rendezvous.Contracts;
|
|
|
|
namespace FinalFactory.Rendezvous.Tests.Contracts;
|
|
|
|
public sealed class ContractSerializationTests
|
|
{
|
|
public static TheoryData<string, Type> GoldenJsonVectors => new()
|
|
{
|
|
{ "register-session.json", typeof(RegisterSessionRequest) },
|
|
{ "register-session-response.json", typeof(RegisterSessionResponse) },
|
|
{ "browse-sessions.json", typeof(BrowseSessionsResponse) },
|
|
{ "create-join-response.json", typeof(CreateJoinAttemptResponse) },
|
|
{ "api-error.json", typeof(ApiError) },
|
|
};
|
|
|
|
[Theory]
|
|
[MemberData(nameof(GoldenJsonVectors))]
|
|
public void CanonicalJsonRoundtripsGoldenVectors(string fileName, Type contractType)
|
|
{
|
|
string expected = ContractTestFiles.Read(fileName);
|
|
object? value = JsonSerializer.Deserialize(expected, contractType, ContractJson.Options);
|
|
|
|
Assert.NotNull(value);
|
|
Assert.Equal(expected, JsonSerializer.Serialize(value, contractType, ContractJson.Options));
|
|
}
|
|
|
|
[Fact]
|
|
public void IdentifiersAreSerializedAsStrings()
|
|
{
|
|
SessionListingId id = new(new Guid("00112233-4455-6677-8899-aabbccddeeff"));
|
|
|
|
Assert.Equal(
|
|
"\"00112233-4455-6677-8899-aabbccddeeff\"",
|
|
JsonSerializer.Serialize(id, ContractJson.Options));
|
|
Assert.Equal(id, JsonSerializer.Deserialize<SessionListingId>(
|
|
"\"00112233-4455-6677-8899-aabbccddeeff\"",
|
|
ContractJson.Options));
|
|
}
|
|
|
|
[Fact]
|
|
public void UnknownObjectFieldsAreIgnoredForAdditiveV1Changes()
|
|
{
|
|
const string json = """
|
|
{"contractVersion":1,"idempotencyKey":"join-001","gameId":"space-game","environmentId":"production","listingId":"00112233-4455-6677-8899-aabbccddeeff","protocolVersion":7,"futureField":{"nested":true}}
|
|
""";
|
|
|
|
CreateJoinAttemptRequest? request = JsonSerializer.Deserialize<CreateJoinAttemptRequest>(
|
|
json,
|
|
ContractJson.Options);
|
|
|
|
Assert.NotNull(request);
|
|
Assert.Equal(new GameId("space-game"), request.GameId);
|
|
Assert.Equal(7u, request.ProtocolVersion);
|
|
}
|
|
|
|
[Fact]
|
|
public void MissingNormativeFieldsAreRejectedInsteadOfDefaulted()
|
|
{
|
|
const string missingVersion = """
|
|
{"idempotencyKey":"join-001","gameId":"space-game","environmentId":"production","listingId":"00112233-4455-6677-8899-aabbccddeeff","protocolVersion":7}
|
|
""";
|
|
|
|
Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<CreateJoinAttemptRequest>(
|
|
missingVersion,
|
|
ContractJson.Options));
|
|
}
|
|
|
|
[Fact]
|
|
public void UnknownEnumNamesAndNumericValuesAreRejected()
|
|
{
|
|
Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<ReportConnectionOutcomeRequest>(
|
|
"{\"contractVersion\":1,\"outcome\":\"futureOutcome\",\"elapsedBucket\":\"underOneSecond\"}",
|
|
ContractJson.Options));
|
|
Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<ReportConnectionOutcomeRequest>(
|
|
"{\"contractVersion\":1,\"outcome\":99,\"elapsedBucket\":\"underOneSecond\"}",
|
|
ContractJson.Options));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(0)]
|
|
[InlineData(2)]
|
|
[InlineData(int.MaxValue)]
|
|
public void UnknownContractVersionsFailPredictably(int version)
|
|
{
|
|
Assert.Equal(
|
|
RendezvousErrorCode.UnsupportedContractVersion,
|
|
ContractValidation.ValidateContractVersion(version));
|
|
}
|
|
|
|
[Fact]
|
|
public void GameplayProtocolCompatibilityIsExactAndBuildIndependent()
|
|
{
|
|
Assert.True(ContractValidation.AreProtocolsCompatible(7, 7));
|
|
Assert.False(ContractValidation.AreProtocolsCompatible(7, 8));
|
|
}
|
|
|
|
[Fact]
|
|
public void SharedCanonicalOptionsCannotBeMutatedByConsumers()
|
|
{
|
|
Assert.Equal(9, ContractJson.Options.MaxDepth);
|
|
Assert.True(ContractJson.Options.IsReadOnly);
|
|
Assert.Throws<InvalidOperationException>(() =>
|
|
ContractJson.Options.WriteIndented = true);
|
|
}
|
|
}
|