public void Authenticate_should_not_throw_when_authentication_succeeds( [Values(false, true)] bool useSpeculativeAuthenticate, [Values(false, true)] bool useLongAuthentication, [Values(false, true)] bool async) { var randomStringGenerator = new ConstantRandomStringGenerator(_clientNonce); var subject = new ScramSha256Authenticator(__credential, randomStringGenerator, serverApi: null); var saslStartReply = MessageHelper.BuildReply <RawBsonDocument>(RawBsonDocumentHelper.FromJson( @"{ conversationId : 1," + $" payload : BinData(0,'{ToUtf8Base64(__serverResponse1)}')," + @" done : false, ok : 1 }")); var saslContinueReply = MessageHelper.BuildReply <RawBsonDocument>(RawBsonDocumentHelper.FromJson( @"{ conversationId : 1," + $" payload : BinData(0,'{ToUtf8Base64(__serverResponse2)}')," + $" done : {new BsonBoolean(!useLongAuthentication)}," + @" ok : 1 }")); var saslLastStepReply = MessageHelper.BuildReply <RawBsonDocument>(RawBsonDocumentHelper.FromJson( @"{ conversationId : 1," + $" payload : BinData(0,'{ToUtf8Base64(__serverOptionalFinalResponse)}')," + @" done : true, ok : 1 }")); var connection = new MockConnection(__serverId); var isMasterResult = (BsonDocument)__descriptionQueryWireProtocol.IsMasterResult.Wrapped.Clone(); if (useSpeculativeAuthenticate) { isMasterResult.Add("speculativeAuthenticate", saslStartReply.Documents[0].ToBsonDocument()); } /* set buildInfoResult to 3.4 to force authenticator to use Query Message Wire Protocol because MockConnection * does not support OP_MSG */ connection.Description = new ConnectionDescription( __descriptionQueryWireProtocol.ConnectionId, new IsMasterResult(isMasterResult), new BuildInfoResult(new BsonDocument("version", "3.4"))); BsonDocument isMasterCommand = null; if (useSpeculativeAuthenticate) { // We must call CustomizeIsMasterCommand so that the authenticator thinks its started to speculatively // authenticate isMasterCommand = subject.CustomizeInitialIsMasterCommand(new BsonDocument { { "isMaster", 1 } }); } else { connection.EnqueueReplyMessage(saslStartReply); } connection.EnqueueReplyMessage(saslContinueReply); if (useLongAuthentication) { connection.EnqueueReplyMessage(saslLastStepReply); } var expectedRequestId = RequestMessage.CurrentGlobalRequestId + 1; Exception exception; if (async) { exception = Record.Exception( () => subject.AuthenticateAsync(connection, connection.Description, CancellationToken.None) .GetAwaiter().GetResult()); } else { exception = Record.Exception( () => subject.Authenticate(connection, connection.Description, CancellationToken.None)); } exception.Should().BeNull(); var expectedSentMessageCount = 3 - (useLongAuthentication ? 0 : 1) - (useSpeculativeAuthenticate ? 1 : 0); SpinWait.SpinUntil( () => connection.GetSentMessages().Count >= expectedSentMessageCount, TimeSpan.FromSeconds(5) ).Should().BeTrue(); var sentMessages = MessageHelper.TranslateMessagesToBsonDocuments(connection.GetSentMessages()); sentMessages.Count.Should().Be(expectedSentMessageCount); var actualRequestIds = sentMessages.Select(m => m["requestId"].AsInt32).ToList(); for (var i = 0; i != actualRequestIds.Count; ++i) { actualRequestIds[i].Should().BeInRange(expectedRequestId + i, expectedRequestId + 10 + i); } var expectedMessages = new List <BsonDocument>(); var saslStartMessage = BsonDocument.Parse( @"{ opcode : 'query'," + $" requestId : {actualRequestIds[0]}," + @" database : 'source', collection : '$cmd', batchSize : -1, slaveOk : true, query : { saslStart : 1, mechanism : 'SCRAM-SHA-256'," + $" payload : new BinData(0, '{ToUtf8Base64(__clientRequest1)}')" + @" options : { skipEmptyExchange: true }}}"); if (!useSpeculativeAuthenticate) { expectedMessages.Add(saslStartMessage); } var saslContinueMessage = BsonDocument.Parse( @"{ opcode : 'query'," + $" requestId : {(useSpeculativeAuthenticate ? actualRequestIds[0] : actualRequestIds[1])}," + @" database : 'source', collection : '$cmd', batchSize : -1, slaveOk : true, query : { saslContinue : 1, conversationId : 1, " + $" payload : new BinData(0, \"{ToUtf8Base64(__clientRequest2)}\")}}}}"); expectedMessages.Add(saslContinueMessage); if (useLongAuthentication) { var saslOptionalFinalMessage = BsonDocument.Parse( @"{ opcode : 'query'," + $" requestId : {(useSpeculativeAuthenticate ? actualRequestIds[1] : actualRequestIds[2])}," + @" database : 'source', collection : '$cmd', batchSize : -1, slaveOk : true, query : { saslContinue : 1, conversationId : 1, " + $" payload : new BinData(0, '{ToUtf8Base64(__clientOptionalFinalRequest)}')}}}}"); expectedMessages.Add(saslOptionalFinalMessage); } sentMessages.Should().Equal(expectedMessages); if (useSpeculativeAuthenticate) { isMasterCommand.Should().Contain("speculativeAuthenticate"); var speculativeAuthenticateDocument = isMasterCommand["speculativeAuthenticate"].AsBsonDocument; var expectedSpeculativeAuthenticateDocument = saslStartMessage["query"].AsBsonDocument.Add("db", __credential.Source); speculativeAuthenticateDocument.Should().Be(expectedSpeculativeAuthenticateDocument); } }