using FinalFactory.Rendezvous.Contracts; namespace FinalFactory.Rendezvous.Tests.Contracts; public sealed class ContractLimitTests { [Fact] public void RequiredPlayerFacingTextRejectsEmptyOrWhitespaceValues() { Assert.False(ContractValidation.IsBuildVersionValid(string.Empty)); Assert.False(ContractValidation.IsBuildVersionValid(" ")); Assert.False(ContractValidation.IsDisplayNameValid(string.Empty)); Assert.False(ContractValidation.IsDisplayNameValid(" ")); } [Fact] public void ByteAndCollectionLimitsAcceptTheBoundaryOnly() { Assert.True(ContractValidation.IsHttpRequestSizeValid(ContractLimits.HttpRequestMaxBytes)); Assert.False(ContractValidation.IsHttpRequestSizeValid(ContractLimits.HttpRequestMaxBytes + 1)); Assert.True(ContractValidation.IsBrowserResponseSizeValid(ContractLimits.BrowserResponseMaxBytes)); Assert.False(ContractValidation.IsBrowserResponseSizeValid(ContractLimits.BrowserResponseMaxBytes + 1)); Assert.True(ContractValidation.IsPageSizeValid(ContractLimits.BrowserPageMaxItems)); Assert.False(ContractValidation.IsPageSizeValid(ContractLimits.BrowserPageMaxItems + 1)); Assert.False(ContractValidation.IsPageSizeValid(0)); } [Fact] public void Utf8LimitsCountBytesInsteadOfCharacters() { string atLimit = new('é', ContractLimits.DisplayNameMaxBytes / 2); string overLimit = atLimit + "é"; Assert.True(ContractValidation.IsDisplayNameValid(atLimit)); Assert.False(ContractValidation.IsDisplayNameValid(overLimit)); Assert.True(ContractValidation.IsBuildVersionValid( new string('a', ContractLimits.BuildVersionMaxBytes))); Assert.False(ContractValidation.IsBuildVersionValid( new string('a', ContractLimits.BuildVersionMaxBytes + 1))); } [Fact] public void CapabilityLimitStaysBelowLiteNetLibTokenLimit() { Assert.True(ContractValidation.IsCapabilityValid( new string('a', ContractLimits.UdpCapabilityMaxCharacters - 1))); Assert.True(ContractValidation.IsCapabilityValid( new string('a', ContractLimits.UdpCapabilityMaxCharacters))); Assert.False(ContractValidation.IsCapabilityValid( new string('a', ContractLimits.UdpCapabilityMaxCharacters + 1))); Assert.False(ContractValidation.IsCapabilityValid("not+base64url")); Assert.True( ContractLimits.UdpCapabilityMaxCharacters < ContractLimits.LiteNetLibNatTokenMaxCharacters); } [Fact] public void CapacityAndMetadataLimitsAreBounded() { Assert.True(ContractValidation.IsCapacityValid(new SessionCapacity { CurrentPlayers = ContractLimits.SessionCapacityMaxPlayers, MaximumPlayers = ContractLimits.SessionCapacityMaxPlayers, })); Assert.False(ContractValidation.IsCapacityValid(new SessionCapacity { CurrentPlayers = 0, MaximumPlayers = ContractLimits.SessionCapacityMaxPlayers + 1, })); Dictionary maximumKeys = Enumerable .Range(0, ContractLimits.MetadataMaxKeys) .ToDictionary(index => $"key-{index}", _ => "value", StringComparer.Ordinal); Dictionary tooManyKeys = new(maximumKeys, StringComparer.Ordinal) { ["overflow"] = "value", }; Assert.True(ContractValidation.IsMetadataValid(maximumKeys)); Assert.False(ContractValidation.IsMetadataValid(tooManyKeys)); Assert.False(ContractValidation.IsMetadataValid(new Dictionary { [new string('k', ContractLimits.MetadataKeyMaxBytes + 1)] = "value", })); Assert.False(ContractValidation.IsMetadataValid(new Dictionary { ["key"] = new string('v', ContractLimits.MetadataValueMaxBytes + 1), })); } [Fact] public void MetadataDocumentLimitAcceptsExactlyFourKibibytes() { Dictionary atLimit = Enumerable .Range(0, ContractLimits.MetadataMaxKeys) .ToDictionary(index => $"k{index:00}", _ => string.Empty, StringComparer.Ordinal); for (int index = 0; index < 14; index++) { atLimit[$"k{index:00}"] = new string('v', ContractLimits.MetadataValueMaxBytes); } atLimit["k14"] = new string('v', 223); Dictionary overLimit = new(atLimit, StringComparer.Ordinal) { ["k14"] = new string('v', 224), }; Assert.True(ContractValidation.IsMetadataValid(atLimit)); Assert.False(ContractValidation.IsMetadataValid(overLimit)); } [Fact] public void EndpointValidationIsAddressFamilyAware() { Assert.True(ContractValidation.IsNetworkEndpointValid(new NetworkEndpoint { AddressFamily = AddressFamilyKind.Ipv4, Address = "192.0.2.10", Port = 9050, })); Assert.True(ContractValidation.IsNetworkEndpointValid(new NetworkEndpoint { AddressFamily = AddressFamilyKind.Ipv6, Address = "2001:db8::10", Port = 9050, })); Assert.False(ContractValidation.IsNetworkEndpointValid(new NetworkEndpoint { AddressFamily = AddressFamilyKind.Ipv4, Address = "2001:db8::10", Port = 9050, })); } [Fact] public void ScopeAndOpaqueTextBoundariesAreEnforced() { Assert.True(GameId.TryParse(new string('a', ContractLimits.GameIdMaxCharacters), out _)); Assert.False(GameId.TryParse( new string('a', ContractLimits.GameIdMaxCharacters + 1), out _)); Assert.True(ContractValidation.IsIdempotencyKeyValid( new string('i', ContractLimits.IdempotencyKeyMaxCharacters))); Assert.False(ContractValidation.IsIdempotencyKeyValid( new string('i', ContractLimits.IdempotencyKeyMaxCharacters + 1))); Assert.True(ContractValidation.IsCursorValid(null)); Assert.False(ContractValidation.IsCursorValid("contains whitespace")); } }