Files
Rendezvous/tests/FinalFactory.Rendezvous.Tests/Contracts/OpenApiCompatibilityTests.cs
KyuubiYoru 06c4ecf8f3
quality-gate / quality (push) Failing after 1m47s
quality-gate / container (push) Has been skipped
feat(browser): stream bounded live session updates (#26)
2026-07-16 23:25:48 +02:00

252 lines
11 KiB
C#

using System.Text.Json;
namespace FinalFactory.Rendezvous.Tests.Contracts;
public sealed class OpenApiCompatibilityTests
{
private static readonly string[] ExpectedPaths =
[
"/health/live",
"/health/ready",
"/v1/join-attempts",
"/v1/join-attempts/{attemptId}",
"/v1/join-attempts/{attemptId}/outcome",
"/v1/operator/drain",
"/v1/operator/keys/revoke",
"/v1/operator/listings/revoke",
"/v1/operator/principals/revoke",
"/v1/operator/status",
"/v1/sessions",
"/v1/sessions/stream",
"/v1/sessions/{listingId}",
"/v1/sessions/{listingId}/join-attempts",
"/v1/sessions/{listingId}/renew",
];
private static readonly string[] ExpectedListingProperties =
[
"buildVersion",
"capacity",
"contractVersion",
"dedicatedFallback",
"displayName",
"environmentId",
"gameId",
"listingId",
"metadata",
"protocolVersion",
"publisherTrustMode",
"regionId",
"visibility",
];
[Fact]
public void GeneratedOpenApiContainsTheFrozenVersionedSurface()
{
using JsonDocument document = JsonDocument.Parse(
File.ReadAllText(ContractTestFiles.OpenApiDocument));
JsonElement root = document.RootElement;
Assert.Equal("3.1.1", root.GetProperty("openapi").GetString());
string[] paths = root.GetProperty("paths")
.EnumerateObject()
.Select(static item => item.Name)
.Order(StringComparer.Ordinal)
.ToArray();
Assert.Equal(ExpectedPaths, paths);
JsonElement schemas = root.GetProperty("components").GetProperty("schemas");
Assert.Equal("string", schemas.GetProperty("GameId").GetProperty("type").GetString());
Assert.Equal("uuid", schemas.GetProperty("SessionListingId").GetProperty("format").GetString());
string[] listingProperties = schemas.GetProperty("SessionListing")
.GetProperty("properties")
.EnumerateObject()
.Select(static item => item.Name)
.Order(StringComparer.Ordinal)
.ToArray();
Assert.Equal(ExpectedListingProperties, listingProperties);
Assert.DoesNotContain(listingProperties, static property =>
property.Contains("token", StringComparison.OrdinalIgnoreCase)
|| property.Contains("playerId", StringComparison.OrdinalIgnoreCase));
JsonElement streamProperties = schemas.GetProperty("SessionStreamEvent")
.GetProperty("properties");
Assert.True(streamProperties.TryGetProperty("contractVersion", out _));
Assert.True(streamProperties.TryGetProperty("kind", out _));
Assert.True(streamProperties.TryGetProperty("cursor", out _));
Assert.True(streamProperties.TryGetProperty("session", out _));
Assert.True(streamProperties.TryGetProperty("listingId", out _));
Assert.DoesNotContain(streamProperties.EnumerateObject(), static property =>
property.Name.Contains("token", StringComparison.OrdinalIgnoreCase)
|| property.Name.Contains("capability", StringComparison.OrdinalIgnoreCase)
|| property.Name.Contains("ticket", StringComparison.OrdinalIgnoreCase)
|| property.Name.Contains("endpoint", StringComparison.OrdinalIgnoreCase));
Assert.True(root.GetProperty("paths")
.GetProperty("/v1/sessions/stream")
.GetProperty("get")
.GetProperty("responses")
.GetProperty("200")
.GetProperty("content")
.TryGetProperty("text/event-stream", out _));
JsonElement dedicatedFallback = schemas.GetProperty("SessionListing")
.GetProperty("properties")
.GetProperty("dedicatedFallback");
JsonElement fallbackReference = Assert.Single(
dedicatedFallback.GetProperty("oneOf").EnumerateArray(),
static schema => schema.TryGetProperty("$ref", out _));
Assert.Equal(
"#/components/schemas/NetworkEndpoint",
fallbackReference.GetProperty("$ref").GetString());
JsonElement outcomeReportProperties = schemas.GetProperty("ReportConnectionOutcomeRequest")
.GetProperty("properties");
Assert.True(outcomeReportProperties.TryGetProperty("elapsedBucket", out _));
Assert.True(outcomeReportProperties.TryGetProperty("elapsedMilliseconds", out _));
Assert.True(outcomeReportProperties.TryGetProperty("diagnosticCode", out _));
string[] outcomeNames = schemas.GetProperty("ConnectionOutcomeKind")
.GetProperty("enum")
.EnumerateArray()
.Select(static value => value.GetString()!)
.ToArray();
Assert.Contains("timedOut", outcomeNames);
Assert.Contains("staleHost", outcomeNames);
Assert.Contains("transportFailed", outcomeNames);
Assert.Contains("punchTimedOut", outcomeNames);
Assert.Contains("directConnectTimedOut", outcomeNames);
Assert.Contains("transportError", outcomeNames);
JsonElement publisherBearer = root.GetProperty("components")
.GetProperty("securitySchemes")
.GetProperty("PublisherBearer");
Assert.Equal("http", publisherBearer.GetProperty("type").GetString());
Assert.Equal("bearer", publisherBearer.GetProperty("scheme").GetString());
JsonElement attemptCapability = root.GetProperty("components")
.GetProperty("securitySchemes")
.GetProperty("JoinAttemptCapability");
Assert.Equal("apiKey", attemptCapability.GetProperty("type").GetString());
Assert.Equal(
"X-Rendezvous-Client-Punch-Capability",
attemptCapability.GetProperty("name").GetString());
JsonElement operatorBearer = root.GetProperty("components")
.GetProperty("securitySchemes")
.GetProperty("OperatorBearer");
Assert.Equal("http", operatorBearer.GetProperty("type").GetString());
Assert.Equal("bearer", operatorBearer.GetProperty("scheme").GetString());
(string Path, string Method)[] publisherOperations =
[
("/v1/sessions", "post"),
("/v1/sessions/{listingId}", "put"),
("/v1/sessions/{listingId}", "delete"),
("/v1/sessions/{listingId}/renew", "post"),
];
foreach ((string operationPath, string method) in publisherOperations)
{
JsonElement security = root.GetProperty("paths")
.GetProperty(operationPath)
.GetProperty(method)
.GetProperty("security");
Assert.True(security[0].TryGetProperty("PublisherBearer", out _));
}
(string Path, string Method)[] operatorOperations =
[
("/v1/operator/status", "get"),
("/v1/operator/listings/revoke", "post"),
("/v1/operator/principals/revoke", "post"),
("/v1/operator/keys/revoke", "post"),
("/v1/operator/drain", "post"),
];
foreach ((string operationPath, string method) in operatorOperations)
{
JsonElement security = root.GetProperty("paths")
.GetProperty(operationPath)
.GetProperty(method)
.GetProperty("security");
Assert.True(security[0].TryGetProperty("OperatorBearer", out _));
}
JsonElement cancelParameters = root.GetProperty("paths")
.GetProperty("/v1/join-attempts/{attemptId}")
.GetProperty("delete")
.GetProperty("parameters");
JsonElement cancelCapability = Assert.Single(cancelParameters.EnumerateArray(), static parameter =>
parameter.GetProperty("in").GetString() == "header"
&& parameter.GetProperty("name").GetString()
== "X-Rendezvous-Client-Punch-Capability");
Assert.True(cancelCapability.GetProperty("required").GetBoolean());
foreach ((string operationPath, string method) in new[]
{
("/v1/join-attempts/{attemptId}", "delete"),
("/v1/join-attempts/{attemptId}/outcome", "post"),
})
{
JsonElement security = root.GetProperty("paths")
.GetProperty(operationPath)
.GetProperty(method)
.GetProperty("security");
Assert.True(security[0].TryGetProperty("JoinAttemptCapability", out _));
}
JsonElement hostPollParameters = root.GetProperty("paths")
.GetProperty("/v1/sessions/{listingId}/join-attempts")
.GetProperty("get")
.GetProperty("parameters");
JsonElement leaseToken = Assert.Single(hostPollParameters.EnumerateArray(), static parameter =>
parameter.GetProperty("in").GetString() == "header"
&& parameter.GetProperty("name").GetString() == "X-Rendezvous-Lease-Token");
Assert.True(leaseToken.GetProperty("required").GetBoolean());
int overloadContracts = 0;
foreach (JsonProperty pathItem in root.GetProperty("paths").EnumerateObject())
{
foreach (JsonProperty operation in pathItem.Value.EnumerateObject().Where(
static item => item.Name is "get" or "post" or "put" or "delete"))
{
JsonElement responses = operation.Value.GetProperty("responses");
foreach (JsonProperty response in responses.EnumerateObject())
{
JsonElement correlation = response.Value.GetProperty("headers")
.GetProperty("X-Rendezvous-Correlation-ID");
Assert.Equal(
"string",
correlation.GetProperty("schema").GetProperty("type").GetString());
}
if (!responses.TryGetProperty("429", out JsonElement overloaded))
{
continue;
}
overloadContracts++;
JsonElement retryAfter = overloaded.GetProperty("headers")
.GetProperty("Retry-After");
Assert.Equal(
"integer",
retryAfter.GetProperty("schema").GetProperty("type").GetString());
}
}
Assert.Equal(18, overloadContracts);
(string Path, string Method)[] bodyOperations =
[
("/v1/sessions", "post"),
("/v1/sessions/{listingId}/renew", "post"),
("/v1/sessions/{listingId}", "put"),
("/v1/sessions/{listingId}", "delete"),
("/v1/join-attempts", "post"),
("/v1/join-attempts/{attemptId}/outcome", "post"),
("/v1/operator/listings/revoke", "post"),
("/v1/operator/principals/revoke", "post"),
("/v1/operator/keys/revoke", "post"),
("/v1/operator/drain", "post"),
];
foreach ((string operationPath, string method) in bodyOperations)
{
Assert.True(root.GetProperty("paths")
.GetProperty(operationPath)
.GetProperty(method)
.GetProperty("responses")
.TryGetProperty("413", out _));
}
}
}