From b7362c2cbab0761923faa1d6b0dede0d5e8fdb8d Mon Sep 17 00:00:00 2001 From: DmitryLukyanov Date: Tue, 3 Nov 2020 23:26:32 +0300 Subject: [PATCH] CSHARP-3241: Zstandard compression key in the code must be kept in sync with the spec. --- .../Core/Compression/CompressorTypeMapper.cs | 53 +++++++++++++++++++ .../Core/Configuration/ConnectionString.cs | 2 +- .../Core/Connections/IsMasterHelper.cs | 3 +- .../Core/Connections/IsMasterResult.cs | 2 +- .../Configuration/ConnectionStringTests.cs | 7 +++ .../Connections/ConnectionDescriptionTests.cs | 6 +-- .../Connections/ConnectionInitializerTests.cs | 21 ++++++-- .../Core/Connections/IsMasterHelperTests.cs | 5 +- .../Core/Connections/IsMasterResultTests.cs | 5 ++ .../ConnectionStringTestRunner.cs | 2 +- 10 files changed, 94 insertions(+), 12 deletions(-) create mode 100644 src/MongoDB.Driver.Core/Core/Compression/CompressorTypeMapper.cs diff --git a/src/MongoDB.Driver.Core/Core/Compression/CompressorTypeMapper.cs b/src/MongoDB.Driver.Core/Core/Compression/CompressorTypeMapper.cs new file mode 100644 index 00000000000..9d20fb47f19 --- /dev/null +++ b/src/MongoDB.Driver.Core/Core/Compression/CompressorTypeMapper.cs @@ -0,0 +1,53 @@ +/* Copyright 2020–present MongoDB Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; + +namespace MongoDB.Driver.Core.Compression +{ + internal static class CompressorTypeMapper + { + public static string ToServerName(CompressorType compressorType) + { + switch (compressorType) + { + case CompressorType.Noop: + return "noop"; + case CompressorType.Zlib: + return "zlib"; + case CompressorType.Snappy: + return "snappy"; + case CompressorType.ZStandard: + return "zstd"; + default: + throw new ArgumentOutOfRangeException(nameof(compressorType)); + } + } + + public static bool TryFromServerName(string serverName, out CompressorType compressorType) + { + compressorType = default; + switch (serverName.ToLowerInvariant()) + { + case "noop": compressorType = CompressorType.Noop; break; + case "zlib": compressorType = CompressorType.Zlib; break; + case "snappy": compressorType = CompressorType.Snappy; break; + case "zstd": compressorType = CompressorType.ZStandard; break; + default: return false; + } + return true; + } + } +} diff --git a/src/MongoDB.Driver.Core/Core/Configuration/ConnectionString.cs b/src/MongoDB.Driver.Core/Core/Configuration/ConnectionString.cs index 26d52d4a3f0..38eff17c43f 100644 --- a/src/MongoDB.Driver.Core/Core/Configuration/ConnectionString.cs +++ b/src/MongoDB.Driver.Core/Core/Configuration/ConnectionString.cs @@ -1380,7 +1380,7 @@ public void SaveCompressors(string[] compressorNames) foreach (var compressor in compressorNames) { // NOTE: the 'noop' is also expected by the server - if (!Enum.TryParse(compressor, true, out CompressorType compressorType) || !CompressorSource.IsCompressorSupported(compressorType)) + if (!CompressorTypeMapper.TryFromServerName(compressor, out CompressorType compressorType) || !CompressorSource.IsCompressorSupported(compressorType)) { // Keys that aren't supported by a driver MUST be ignored. continue; diff --git a/src/MongoDB.Driver.Core/Core/Connections/IsMasterHelper.cs b/src/MongoDB.Driver.Core/Core/Connections/IsMasterHelper.cs index c2e3413b66f..860792aab49 100644 --- a/src/MongoDB.Driver.Core/Core/Connections/IsMasterHelper.cs +++ b/src/MongoDB.Driver.Core/Core/Connections/IsMasterHelper.cs @@ -21,6 +21,7 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Core.Authentication; +using MongoDB.Driver.Core.Compression; using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; @@ -37,7 +38,7 @@ internal static BsonDocument AddClientDocumentToCommand(BsonDocument command, Bs internal static BsonDocument AddCompressorsToCommand(BsonDocument command, IEnumerable compressors) { - var compressorsArray = new BsonArray(compressors.Select(x => x.Type.ToString().ToLowerInvariant())); + var compressorsArray = new BsonArray(compressors.Select(x => CompressorTypeMapper.ToServerName(x.Type))); return command.Add("compression", compressorsArray); } diff --git a/src/MongoDB.Driver.Core/Core/Connections/IsMasterResult.cs b/src/MongoDB.Driver.Core/Core/Connections/IsMasterResult.cs index 1537d9ca7df..784808578ef 100644 --- a/src/MongoDB.Driver.Core/Core/Connections/IsMasterResult.cs +++ b/src/MongoDB.Driver.Core/Core/Connections/IsMasterResult.cs @@ -57,7 +57,7 @@ public IReadOnlyList Compressions .AsBsonArray .Select(x => { - return Enum.TryParse(x.AsString, true, out var compressorType) + return CompressorTypeMapper.TryFromServerName(x.AsString, out var compressorType) ? compressorType // we can have such a case only due to the server bug : throw new NotSupportedException($"The unsupported compressor name: '{x}'."); diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Configuration/ConnectionStringTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Configuration/ConnectionStringTests.cs index 936d9829f13..c0ea9f5700c 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Configuration/ConnectionStringTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Configuration/ConnectionStringTests.cs @@ -446,7 +446,14 @@ public void When_authSource_is_specified(string connectionString, string authSou } [Theory] + [InlineData("mongodb://localhost?compressors=noop", CompressorType.Noop)] + [InlineData("mongodb://localhost?compressors=nooP", CompressorType.Noop)] [InlineData("mongodb://localhost?compressors=zlib", CompressorType.Zlib)] + [InlineData("mongodb://localhost?compressors=Zlib", CompressorType.Zlib)] + [InlineData("mongodb://localhost?compressors=snappy", CompressorType.Snappy)] + [InlineData("mongodb://localhost?compressors=Snappy", CompressorType.Snappy)] + [InlineData("mongodb://localhost?compressors=zstd", CompressorType.ZStandard)] + [InlineData("mongodb://localhost?compressors=Zstd", CompressorType.ZStandard)] public void When_compressor_is_specified(string connectionString, CompressorType compressor) { var subject = new ConnectionString(connectionString); diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Connections/ConnectionDescriptionTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Connections/ConnectionDescriptionTests.cs index 68e12cc1106..514b223e600 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Connections/ConnectionDescriptionTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Connections/ConnectionDescriptionTests.cs @@ -31,13 +31,13 @@ public class ConnectionDescriptionTests "{ ok: 1, version: \"2.6.3\" }" )); - private static readonly IEnumerable __compressors = new[] { CompressorType.Zlib }; + private static readonly IEnumerable __compressors = new[] { CompressorType.Zlib, CompressorType.ZStandard }; private static readonly ConnectionId __connectionId = new ConnectionId( new ServerId(new ClusterId(), new DnsEndPoint("localhost", 27017))); private static readonly IsMasterResult __isMasterResult = new IsMasterResult(BsonDocument.Parse( - "{ ok: 1, maxWriteBatchSize: 10, maxBsonObjectSize: 20, maxMessageSizeBytes: 30, compression: ['zlib'] }" + "{ ok: 1, maxWriteBatchSize: 10, maxBsonObjectSize: 20, maxMessageSizeBytes: 30, compression: ['zlib', 'zstd'] }" )); private static readonly IsMasterResult __isMasterResultWithoutCompression = new IsMasterResult(BsonDocument.Parse( @@ -57,7 +57,7 @@ public void AvailableCompressors_should_return_expected_result() { var subject = new ConnectionDescription(__connectionId, __isMasterResult, __buildInfoResult); - subject.AvailableCompressors.Count.Should().Be(1); + subject.AvailableCompressors.Count.Should().Be(2); subject.AvailableCompressors.Should().Equal(__compressors); } diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Connections/ConnectionInitializerTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Connections/ConnectionInitializerTests.cs index 54557caddf7..c7e17dcb9b8 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Connections/ConnectionInitializerTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Connections/ConnectionInitializerTests.cs @@ -161,10 +161,12 @@ public void InitializeConnection_should_throw_an_ArgumentNullException_if_the_co [Theory] [ParameterAttributeData] - public void InitializeConnectionA_should_build_the_ConnectionDescription_correctly([Values(false, true)] bool async) + public void InitializeConnectionA_should_build_the_ConnectionDescription_correctly( + [Values("noop", "zlib", "snappy", "zstd")] string compressorType, + [Values(false, true)] bool async) { var isMasterReply = MessageHelper.BuildReply( - RawBsonDocumentHelper.FromJson("{ ok: 1, compression: ['zlib'] }")); + RawBsonDocumentHelper.FromJson($"{{ ok: 1, compression: ['{compressorType}'] }}")); var buildInfoReply = MessageHelper.BuildReply( RawBsonDocumentHelper.FromJson("{ ok: 1, version: \"2.6.3\" }")); var gleReply = MessageHelper.BuildReply( @@ -188,7 +190,20 @@ public void InitializeConnectionA_should_build_the_ConnectionDescription_correct result.ServerVersion.Should().Be(new SemanticVersion(2, 6, 3)); result.ConnectionId.ServerValue.Should().Be(10); result.AvailableCompressors.Count.Should().Be(1); - result.AvailableCompressors.Should().Contain(CompressorType.Zlib); + result.AvailableCompressors.Should().Contain(ToCompressorTypeEnum(compressorType)); + + CompressorType ToCompressorTypeEnum(string ct) + { + switch (ct) + { + case "noop": return CompressorType.Noop; + case "zlib": return CompressorType.Zlib; + case "snappy": return CompressorType.Snappy; + case "zstd": return CompressorType.ZStandard; + default: + throw new InvalidOperationException($"Unexpected compression {compressorType}."); + } + } } private IAuthenticator CreateAuthenticator(string authenticatorType, UsernamePasswordCredential credentials) diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Connections/IsMasterHelperTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Connections/IsMasterHelperTests.cs index 5ff0eaec2d6..7b9238a43f7 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Connections/IsMasterHelperTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Connections/IsMasterHelperTests.cs @@ -71,7 +71,8 @@ public void AddCompressorsToCommand_with_compressors_should_return_expected_resu new CompressorType[] { }, new [] { CompressorType.Zlib }, new [] { CompressorType.Snappy}, - new [] { CompressorType.Zlib, CompressorType.Snappy })] + new [] { CompressorType.Zlib, CompressorType.Snappy }, + new [] { CompressorType.ZStandard, CompressorType.Snappy })] CompressorType[] compressorsParameters) { var command = IsMasterHelper.CreateCommand(); @@ -81,7 +82,7 @@ public void AddCompressorsToCommand_with_compressors_should_return_expected_resu .ToArray(); var result = IsMasterHelper.AddCompressorsToCommand(command, compressors); - var expectedCompressions = string.Join(",", compressorsParameters.Select(c => $"'{c.ToString().ToLowerInvariant()}'")); + var expectedCompressions = string.Join(",", compressorsParameters.Select(c => $"'{CompressorTypeMapper.ToServerName(c)}'")); result.Should().Be(BsonDocument.Parse($"{{ isMaster : 1, compression: [{expectedCompressions}] }}")); } } diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Connections/IsMasterResultTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Connections/IsMasterResultTests.cs index d682a10a1ef..51fefdb8cb3 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Connections/IsMasterResultTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Connections/IsMasterResultTests.cs @@ -29,8 +29,13 @@ public class IsMasterResultTests { [Theory] [InlineData("{ compression : ['zlib'] }", new[] { CompressorType.Zlib })] + [InlineData("{ compression : ['Zlib'] }", new[] { CompressorType.Zlib })] [InlineData("{ compression : ['zlib', 'snappy'] }", new[] { CompressorType.Zlib, CompressorType.Snappy })] + [InlineData("{ compression : ['zlib', 'snAppy'] }", new[] { CompressorType.Zlib, CompressorType.Snappy })] [InlineData("{ compression : ['noop'] }", new[] { CompressorType.Noop })] + [InlineData("{ compression : ['nOop'] }", new[] { CompressorType.Noop })] + [InlineData("{ compression : ['zstd'] }", new[] { CompressorType.ZStandard})] + [InlineData("{ compression : ['zsTd'] }", new[] { CompressorType.ZStandard })] [InlineData("{ compression : [] }", new CompressorType[0])] [InlineData("{ }", new CompressorType[0])] public void Compression_should_parse_document_correctly(string json, CompressorType[] expectedCompression) diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/ConnectionStringTestRunner.cs b/tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/ConnectionStringTestRunner.cs index 8d966b71607..a26f528f2b3 100644 --- a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/ConnectionStringTestRunner.cs +++ b/tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/ConnectionStringTestRunner.cs @@ -91,7 +91,7 @@ private void AssertOptions(ConnectionString connectionString, BsonDocument defin connectionString.AuthSource.Should().Be(expectedOption.Value.AsString); break; case "compressors": - var compressors = new BsonArray(connectionString.Compressors.Select(c => c.Type.ToString().ToLowerInvariant())); + var compressors = new BsonArray(connectionString.Compressors.Select(c => CompressorTypeMapper.ToServerName(c.Type))); var expectedCompressors = RemoveUnsupportedCompressors(expectedOption.Value.AsBsonArray); compressors.Should().Be(expectedCompressors); break;