PageRenderTime 30ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/TestRunner.cs

https://github.com/craiggwilson/mongo-csharp-driver
C# | 293 lines | 244 code | 33 blank | 16 comment | 17 complexity | 8347188f76a600082eb79c61b81d6e42 MD5 | raw file
  1. /* Copyright 2013-present MongoDB Inc.
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. using System;
  16. using System.Collections.Generic;
  17. using System.Linq;
  18. using System.Threading;
  19. using FluentAssertions;
  20. using MongoDB.Bson;
  21. using MongoDB.Bson.TestHelpers;
  22. using MongoDB.Bson.TestHelpers.JsonDrivenTests;
  23. using MongoDB.Driver.Core.Clusters;
  24. using MongoDB.Driver.Core.Configuration;
  25. using MongoDB.Driver.Core.Connections;
  26. using MongoDB.Driver.Core.Events;
  27. using MongoDB.Driver.Core.Helpers;
  28. using MongoDB.Driver.Core.Misc;
  29. using MongoDB.Driver.Core.Servers;
  30. using Moq;
  31. using Xunit;
  32. namespace MongoDB.Driver.Specifications.server_discovery_and_monitoring
  33. {
  34. public class TestRunner
  35. {
  36. private ICluster _cluster;
  37. private IEventSubscriber _eventSubscriber;
  38. private MockClusterableServerFactory _serverFactory;
  39. [Theory]
  40. [ClassData(typeof(TestCaseFactory))]
  41. public void RunTestDefinition(JsonDrivenTestCase testCase)
  42. {
  43. var definition = testCase.Test;
  44. JsonDrivenHelper.EnsureAllFieldsAreValid(definition, "description", "_path", "phases", "uri");
  45. _cluster = BuildCluster(definition);
  46. _cluster.Initialize();
  47. var phases = definition["phases"].AsBsonArray;
  48. foreach (BsonDocument phase in phases)
  49. {
  50. ApplyPhase(phase);
  51. }
  52. }
  53. private void ApplyPhase(BsonDocument phase)
  54. {
  55. JsonDrivenHelper.EnsureAllFieldsAreValid(phase, "outcome", "responses");
  56. var responses = phase["responses"].AsBsonArray;
  57. foreach (BsonArray response in responses)
  58. {
  59. ApplyResponse(response);
  60. }
  61. var outcome = (BsonDocument)phase["outcome"];
  62. VerifyOutcome(outcome);
  63. }
  64. private void ApplyResponse(BsonArray response)
  65. {
  66. if (response.Count != 2)
  67. {
  68. throw new FormatException($"Invalid response count: {response.Count}.");
  69. }
  70. var address = response[0].AsString;
  71. var isMasterDocument = response[1].AsBsonDocument;
  72. JsonDrivenHelper.EnsureAllFieldsAreValid(isMasterDocument, "arbiterOnly", "arbiters", "electionId", "hidden", "hosts", "ismaster", "isreplicaset", "logicalSessionTimeoutMinutes", "maxWireVersion", "me", "minWireVersion", "msg", "ok", "passive", "passives", "primary", "secondary", "setName", "setVersion");
  73. var endPoint = EndPointHelper.Parse(address);
  74. var isMasterResult = new IsMasterResult(isMasterDocument);
  75. var currentServerDescription = _serverFactory.GetServerDescription(endPoint);
  76. var newServerDescription = currentServerDescription.With(
  77. canonicalEndPoint: isMasterResult.Me,
  78. electionId: isMasterResult.ElectionId,
  79. logicalSessionTimeout: isMasterResult.LogicalSessionTimeout,
  80. replicaSetConfig: isMasterResult.GetReplicaSetConfig(),
  81. state: isMasterResult.Wrapped.GetValue("ok", false).ToBoolean() ? ServerState.Connected : ServerState.Disconnected,
  82. type: isMasterResult.ServerType,
  83. wireVersionRange: new Range<int>(isMasterResult.MinWireVersion, isMasterResult.MaxWireVersion));
  84. var currentClusterDescription = _cluster.Description;
  85. _serverFactory.PublishDescription(newServerDescription);
  86. SpinWait.SpinUntil(() => !object.ReferenceEquals(_cluster.Description, currentClusterDescription), 100); // sometimes returns false and that's OK
  87. }
  88. private void VerifyTopology(ICluster cluster, string expectedType)
  89. {
  90. switch (expectedType)
  91. {
  92. case "Single":
  93. cluster.Should().BeOfType<SingleServerCluster>();
  94. break;
  95. case "ReplicaSetWithPrimary":
  96. cluster.Should().BeOfType<MultiServerCluster>();
  97. cluster.Description.Type.Should().Be(ClusterType.ReplicaSet);
  98. cluster.Description.Servers.Should().ContainSingle(x => x.Type == ServerType.ReplicaSetPrimary);
  99. break;
  100. case "ReplicaSetNoPrimary":
  101. cluster.Should().BeOfType<MultiServerCluster>();
  102. cluster.Description.Type.Should().Be(ClusterType.ReplicaSet);
  103. cluster.Description.Servers.Should().NotContain(x => x.Type == ServerType.ReplicaSetPrimary);
  104. break;
  105. case "Sharded":
  106. cluster.Should().BeOfType<MultiServerCluster>();
  107. cluster.Description.Type.Should().Be(ClusterType.Sharded);
  108. break;
  109. case "Unknown":
  110. cluster.Description.Type.Should().Be(ClusterType.Unknown);
  111. break;
  112. default:
  113. throw new FormatException($"Invalid topology type: \"{expectedType}\".");
  114. }
  115. }
  116. private void VerifyOutcome(BsonDocument outcome)
  117. {
  118. JsonDrivenHelper.EnsureAllFieldsAreValid(outcome, "compatible", "logicalSessionTimeoutMinutes", "servers", "setName", "topologyType");
  119. var expectedTopologyType = (string)outcome["topologyType"];
  120. VerifyTopology(_cluster, expectedTopologyType);
  121. var actualDescription = _cluster.Description;
  122. var actualServers = actualDescription.Servers.Select(x => x.EndPoint);
  123. var expectedServers = outcome["servers"].AsBsonDocument.Elements.Select(x => new
  124. {
  125. EndPoint = EndPointHelper.Parse(x.Name),
  126. Description = (BsonDocument)x.Value
  127. });
  128. actualServers.WithComparer(EndPointHelper.EndPointEqualityComparer).Should().BeEquivalentTo(expectedServers.Select(x => x.EndPoint).WithComparer(EndPointHelper.EndPointEqualityComparer));
  129. foreach (var actualServer in actualDescription.Servers)
  130. {
  131. var expectedServer = expectedServers.Single(x => EndPointHelper.EndPointEqualityComparer.Equals(x.EndPoint, actualServer.EndPoint));
  132. VerifyServerDescription(actualServer, expectedServer.Description);
  133. }
  134. if (outcome.Contains("setName"))
  135. {
  136. // TODO: assert something against setName
  137. }
  138. if (outcome.Contains("logicalSessionTimeoutMinutes"))
  139. {
  140. TimeSpan? expectedLogicalSessionTimeout;
  141. switch (outcome["logicalSessionTimeoutMinutes"].BsonType)
  142. {
  143. case BsonType.Null:
  144. expectedLogicalSessionTimeout = null;
  145. break;
  146. case BsonType.Int32:
  147. case BsonType.Int64:
  148. expectedLogicalSessionTimeout = TimeSpan.FromMinutes(outcome["logicalSessionTimeoutMinutes"].ToDouble());
  149. break;
  150. default:
  151. throw new FormatException($"Invalid logicalSessionTimeoutMinutes BSON type: {outcome["setName"].BsonType}.");
  152. }
  153. actualDescription.LogicalSessionTimeout.Should().Be(expectedLogicalSessionTimeout);
  154. }
  155. if (outcome.Contains("compatible"))
  156. {
  157. var expectedIsCompatibleWithDriver = outcome["compatible"].ToBoolean();
  158. actualDescription.IsCompatibleWithDriver.Should().Be(expectedIsCompatibleWithDriver);
  159. }
  160. }
  161. private void VerifyServerDescription(ServerDescription actualDescription, BsonDocument expectedDescription)
  162. {
  163. JsonDrivenHelper.EnsureAllFieldsAreValid(expectedDescription, "electionId", "setName", "setVersion", "type");
  164. var expectedType = (string)expectedDescription["type"];
  165. switch (expectedType)
  166. {
  167. case "RSPrimary":
  168. actualDescription.Type.Should().Be(ServerType.ReplicaSetPrimary);
  169. break;
  170. case "RSSecondary":
  171. actualDescription.Type.Should().Be(ServerType.ReplicaSetSecondary);
  172. break;
  173. case "RSArbiter":
  174. actualDescription.Type.Should().Be(ServerType.ReplicaSetArbiter);
  175. break;
  176. case "RSGhost":
  177. actualDescription.Type.Should().Be(ServerType.ReplicaSetGhost);
  178. break;
  179. case "RSOther":
  180. actualDescription.Type.Should().Be(ServerType.ReplicaSetOther);
  181. break;
  182. case "Mongos":
  183. actualDescription.Type.Should().Be(ServerType.ShardRouter);
  184. break;
  185. case "Standalone":
  186. actualDescription.Type.Should().Be(ServerType.Standalone);
  187. break;
  188. default:
  189. actualDescription.Type.Should().Be(ServerType.Unknown);
  190. break;
  191. }
  192. if (expectedDescription.Contains("setName"))
  193. {
  194. string expectedSetName;
  195. switch (expectedDescription["setName"].BsonType)
  196. {
  197. case BsonType.Null: expectedSetName = null; break;
  198. case BsonType.String: expectedSetName = expectedDescription["setName"].AsString; ; break;
  199. default: throw new FormatException($"Invalid setName BSON type: {expectedDescription["setName"].BsonType}.");
  200. }
  201. actualDescription.ReplicaSetConfig?.Name.Should().Be(expectedSetName);
  202. }
  203. if (expectedDescription.Contains("setVersion"))
  204. {
  205. int? expectedSetVersion;
  206. switch (expectedDescription["setVersion"].BsonType)
  207. {
  208. case BsonType.Null:
  209. expectedSetVersion = null;
  210. break;
  211. case BsonType.Int32:
  212. case BsonType.Int64:
  213. expectedSetVersion = expectedDescription["setVersion"].ToInt32();
  214. break;
  215. default:
  216. throw new FormatException($"Invalid setVersion BSON type: {expectedDescription["setVersion"].BsonType}.");
  217. }
  218. actualDescription.ReplicaSetConfig?.Version.Should().Be(expectedSetVersion);
  219. }
  220. if (expectedDescription.Contains("electionId"))
  221. {
  222. ElectionId expectedElectionId;
  223. switch (expectedDescription["electionId"].BsonType)
  224. {
  225. case BsonType.Null: expectedElectionId = null; break;
  226. case BsonType.ObjectId: expectedElectionId = new ElectionId(expectedDescription["electionId"].AsObjectId); break;
  227. default: throw new FormatException($"Invalid electionId BSON type: {expectedDescription["electionId"].BsonType}.");
  228. }
  229. actualDescription.ElectionId.Should().Be(expectedElectionId);
  230. }
  231. }
  232. private ICluster BuildCluster(BsonDocument definition)
  233. {
  234. var connectionString = new ConnectionString((string)definition["uri"]);
  235. var settings = new ClusterSettings(
  236. endPoints: Optional.Enumerable(connectionString.Hosts),
  237. connectionMode: connectionString.Connect,
  238. replicaSetName: connectionString.ReplicaSet);
  239. _serverFactory = new MockClusterableServerFactory();
  240. _eventSubscriber = new Mock<IEventSubscriber>().Object;
  241. return new ClusterFactory(settings, _serverFactory, _eventSubscriber)
  242. .CreateCluster();
  243. }
  244. private class TestCaseFactory : JsonDrivenTestCaseFactory
  245. {
  246. // private constants
  247. private const string MonitoringPrefix = "MongoDB.Driver.Core.Tests.Specifications.server_discovery_and_monitoring.tests.monitoring.";
  248. protected override string PathPrefix => "MongoDB.Driver.Core.Tests.Specifications.server_discovery_and_monitoring.tests.";
  249. protected override IEnumerable<JsonDrivenTestCase> CreateTestCases(BsonDocument document)
  250. {
  251. var name = GetTestCaseName(document, document, 0);
  252. yield return new JsonDrivenTestCase(name, document, document);
  253. }
  254. protected override bool ShouldReadJsonDocument(string path)
  255. {
  256. return base.ShouldReadJsonDocument(path) && !path.StartsWith(MonitoringPrefix);
  257. }
  258. }
  259. }
  260. }