summaryrefslogtreecommitdiff
path: root/src/mongo/s/append_raw_responses_test.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/s/append_raw_responses_test.cpp')
-rw-r--r--src/mongo/s/append_raw_responses_test.cpp541
1 files changed, 541 insertions, 0 deletions
diff --git a/src/mongo/s/append_raw_responses_test.cpp b/src/mongo/s/append_raw_responses_test.cpp
new file mode 100644
index 00000000000..791949a8672
--- /dev/null
+++ b/src/mongo/s/append_raw_responses_test.cpp
@@ -0,0 +1,541 @@
+/**
+ * Copyright (C) 2018-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kSharding
+
+#include "mongo/unittest/unittest.h"
+
+#include "mongo/client/remote_command_targeter_mock.h"
+#include "mongo/db/commands.h"
+#include "mongo/rpc/get_status_from_command_result.h"
+#include "mongo/s/async_requests_sender.h"
+#include "mongo/s/catalog/sharding_catalog_client_mock.h"
+#include "mongo/s/catalog/type_shard.h"
+#include "mongo/s/cluster_commands_helpers.h"
+#include "mongo/s/shard_server_test_fixture.h"
+
+namespace mongo {
+namespace {
+
+// Have two each of ignorable and non-ignorable errors, so we can test shards returning different
+// ignorable and different non-ignorable errors.
+const ErrorCodes::Error kIgnorableError1{ErrorCodes::NamespaceNotFound};
+const ErrorCodes::Error kIgnorableError2{ErrorCodes::NamespaceNotFound};
+const ErrorCodes::Error kNonIgnorableError1{ErrorCodes::HostUnreachable};
+const ErrorCodes::Error kNonIgnorableError2{ErrorCodes::HostUnreachable};
+
+const Status kIgnorableError1Status{kIgnorableError1, "dummy"};
+const Status kIgnorableError2Status{kIgnorableError2, "dummy"};
+const Status kNonIgnorableError1Status{kNonIgnorableError1, "dummy"};
+const Status kNonIgnorableError2Status{kNonIgnorableError2, "dummy"};
+
+const Status kWriteConcernError1Status{ErrorCodes::WriteConcernFailed, "dummy"};
+const Status kWriteConcernError2Status{ErrorCodes::UnsatisfiableWriteConcern, "dummy"};
+
+executor::RemoteCommandResponse kOkResponse{BSON("ok" << 1), Milliseconds(0)};
+
+executor::RemoteCommandResponse makeErrorResponse(const Status& errorStatus) {
+ invariant(!errorStatus.isOK());
+ BSONObjBuilder res;
+ CommandHelpers::appendCommandStatusNoThrow(res, errorStatus);
+ return {res.obj(), Milliseconds(0)};
+}
+
+executor::RemoteCommandResponse makeWriteConcernErrorResponse(
+ const Status& writeConcernErrorStatus) {
+ invariant(!writeConcernErrorStatus.isOK());
+ BSONObjBuilder res;
+ WriteConcernErrorDetail wcError;
+ wcError.setStatus(writeConcernErrorStatus);
+ res.append("ok", 1);
+ res.append("writeConcernError", wcError.toBSON());
+ return {res.obj(), Milliseconds(0)};
+}
+
+HostAndPort makeHostAndPort(const ShardId& shardId) {
+ return HostAndPort(str::stream() << shardId << ":123");
+}
+
+} // namespace
+
+class AppendRawResponsesTest : public ShardServerTestFixture {
+protected:
+ /**
+ * Runs 'appendRawResponses' and asserts that the top-level status matches 'expectedStatus', the
+ * 'raw' sub-object contains the shards in 'expectedShardsInRawSubObj', and the writeConcern
+ * status matches 'expectedWriteConcernStatus'.
+ */
+ void runAppendRawResponsesExpect(
+ const std::vector<AsyncRequestsSender::Response>& shardResponses,
+ const Status& expectedStatus,
+ const std::vector<ShardId>& expectedShardsInRawSubObj,
+ const Status& expectedWriteConcernStatus = Status::OK()) {
+ BSONObjBuilder result;
+ std::string errmsg;
+
+ const auto ok = appendRawResponses(operationContext(),
+ &errmsg,
+ &result,
+ shardResponses,
+ {kIgnorableError1, kIgnorableError2});
+
+ if (!errmsg.empty()) {
+ CommandHelpers::appendSimpleCommandStatus(result, ok, errmsg);
+ }
+ const auto resultObj = result.obj();
+
+ // Check the top-level status.
+ if (expectedStatus.isOK()) {
+ ASSERT_FALSE(resultObj.hasField("code"));
+ ASSERT_FALSE(resultObj.hasField("codeName"));
+ ASSERT(errmsg.empty());
+ } else {
+ ASSERT_EQ(expectedStatus.code(), resultObj.getField("code").Int());
+ ASSERT_EQ(ErrorCodes::errorString(expectedStatus.code()),
+ resultObj.getField("codeName").String());
+ ASSERT_EQ(expectedStatus.reason(), errmsg);
+ }
+
+ // Check the 'raw' sub-object.
+ const auto rawSubObj = resultObj.getField("raw").Obj();
+ ASSERT_EQ(rawSubObj.nFields(), int(expectedShardsInRawSubObj.size()));
+ for (const auto& shardId : expectedShardsInRawSubObj) {
+ const auto shardConnStr =
+ ConnectionString::forReplicaSet(shardId.toString(), {makeHostAndPort(shardId)})
+ .toString();
+ ASSERT(rawSubObj.hasField(shardConnStr));
+ }
+
+ // Check for a writeConcern error.
+ if (expectedWriteConcernStatus.isOK()) {
+ ASSERT(resultObj.getField("writeConcernError").eoo());
+ } else {
+ ASSERT_EQ(expectedWriteConcernStatus,
+ getWriteConcernStatusFromCommandResult(resultObj));
+ }
+ }
+
+ void setUp() override {
+ ShardServerTestFixture::setUp();
+
+ for (const auto& shardId : kShardIdList) {
+ auto shardTargeter = RemoteCommandTargeterMock::get(
+ uassertStatusOK(shardRegistry()->getShard(operationContext(), shardId))
+ ->getTargeter());
+ shardTargeter->setConnectionStringReturnValue(
+ ConnectionString::forReplicaSet(shardId.toString(), {makeHostAndPort(shardId)}));
+ }
+ }
+
+ std::unique_ptr<ShardingCatalogClient> makeShardingCatalogClient(
+ std::unique_ptr<DistLockManager> distLockManager) override {
+
+ class StaticCatalogClient final : public ShardingCatalogClientMock {
+ public:
+ StaticCatalogClient(std::vector<ShardId> shardIds)
+ : ShardingCatalogClientMock(nullptr), _shardIds(std::move(shardIds)) {}
+
+ StatusWith<repl::OpTimeWith<std::vector<ShardType>>> getAllShards(
+ OperationContext* opCtx, repl::ReadConcernLevel readConcern) override {
+ std::vector<ShardType> shardTypes;
+ for (const auto& shardId : _shardIds) {
+ const ConnectionString cs = ConnectionString::forReplicaSet(
+ shardId.toString(), {makeHostAndPort(shardId)});
+ ShardType sType;
+ sType.setName(cs.getSetName());
+ sType.setHost(cs.toString());
+ shardTypes.push_back(std::move(sType));
+ };
+ return repl::OpTimeWith<std::vector<ShardType>>(shardTypes);
+ }
+
+ private:
+ const std::vector<ShardId> _shardIds;
+ };
+
+ return stdx::make_unique<StaticCatalogClient>(kShardIdList);
+ }
+
+ const ShardId kShard1{"s1"};
+ const ShardId kShard2{"s2"};
+ const ShardId kShard3{"s3"};
+ const ShardId kShard4{"s4"};
+ const ShardId kShard5{"s5"};
+ const std::vector<ShardId> kShardIdList{kShard1, kShard2, kShard3, kShard4, kShard5};
+};
+
+//
+// No errors
+//
+
+TEST_F(AppendRawResponsesTest, AllShardsReturnSuccess) {
+ std::vector<AsyncRequestsSender::Response> shardResponses{
+ {kShard1, kOkResponse, makeHostAndPort(kShard1)},
+ {kShard2, kOkResponse, makeHostAndPort(kShard2)},
+ {kShard3, kOkResponse, makeHostAndPort(kShard3)}};
+
+ runAppendRawResponsesExpect(shardResponses, Status::OK(), {kShard1, kShard2, kShard3});
+}
+
+//
+// Only write concern errors
+//
+
+TEST_F(AppendRawResponsesTest, AllShardsReturnSuccessOneWithWriteConcernError) {
+ std::vector<AsyncRequestsSender::Response> shardResponses{
+ {kShard1,
+ makeWriteConcernErrorResponse(kWriteConcernError1Status),
+ makeHostAndPort(kShard1)},
+ {kShard2, kOkResponse, makeHostAndPort(kShard2)},
+ {kShard3, kOkResponse, makeHostAndPort(kShard3)}};
+
+ runAppendRawResponsesExpect(
+ shardResponses, Status::OK(), {kShard1, kShard2, kShard3}, kWriteConcernError1Status);
+}
+
+TEST_F(AppendRawResponsesTest, AllShardsReturnSuccessMoreThanOneWithWriteConcernError) {
+ std::vector<AsyncRequestsSender::Response> shardResponses{
+ {kShard1,
+ makeWriteConcernErrorResponse(kWriteConcernError1Status),
+ makeHostAndPort(kShard1)},
+ {kShard2,
+ makeWriteConcernErrorResponse(kWriteConcernError2Status),
+ makeHostAndPort(kShard2)},
+ {kShard3, kOkResponse, makeHostAndPort(kShard3)}};
+
+ // The first writeConcern error is reported.
+ runAppendRawResponsesExpect(
+ shardResponses, Status::OK(), {kShard1, kShard2, kShard3}, kWriteConcernError1Status);
+}
+
+TEST_F(AppendRawResponsesTest, AllShardsReturnSuccessAllWithWriteConcernError) {
+ std::vector<AsyncRequestsSender::Response> shardResponses{
+ {kShard1,
+ makeWriteConcernErrorResponse(kWriteConcernError1Status),
+ makeHostAndPort(kShard1)},
+ {kShard2,
+ makeWriteConcernErrorResponse(kWriteConcernError2Status),
+ makeHostAndPort(kShard2)},
+ {kShard3,
+ makeWriteConcernErrorResponse(kWriteConcernError1Status),
+ makeHostAndPort(kShard3)}};
+
+ // The first writeConcern error is reported.
+ runAppendRawResponsesExpect(
+ shardResponses, Status::OK(), {kShard1, kShard2, kShard3}, kWriteConcernError1Status);
+}
+
+//
+// Errors *sending* the requests.
+//
+
+TEST_F(AppendRawResponsesTest, AllAttemptsToSendRequestsReturnSameIgnorableError) {
+ std::vector<AsyncRequestsSender::Response> shardResponses{
+ {kShard1, kIgnorableError1Status, boost::none},
+ {kShard2, kIgnorableError1Status, boost::none},
+ {kShard3, kIgnorableError1Status, boost::none}};
+
+ // The ignorable error gets promoted to a non-ignorable error.
+ runAppendRawResponsesExpect(
+ shardResponses, kIgnorableError1Status, {kShard1, kShard2, kShard3});
+}
+
+TEST_F(AppendRawResponsesTest,
+ SomeShardsReturnSuccessRestOfAttemptsToSendRequestsReturnSameIgnorableError) {
+ std::vector<AsyncRequestsSender::Response> shardResponses{
+ {kShard1, kOkResponse, makeHostAndPort(kShard1)},
+ {kShard2, kOkResponse, makeHostAndPort(kShard2)},
+ {kShard3, kIgnorableError1Status, boost::none},
+ {kShard4, kIgnorableError1Status, boost::none}};
+
+ // The ignorable errors are ignored.
+ runAppendRawResponsesExpect(shardResponses, Status::OK(), {kShard1, kShard2});
+}
+
+TEST_F(AppendRawResponsesTest, AttemptsToSendRequestsReturnDifferentIgnorableErrors) {
+ std::vector<AsyncRequestsSender::Response> shardResponses{
+ {kShard1, kIgnorableError1Status, boost::none},
+ {kShard2, kIgnorableError2Status, boost::none},
+ {kShard3, kIgnorableError1Status, boost::none}};
+
+ // The *first* ignorable error gets promoted to a non-ignorable error.
+ runAppendRawResponsesExpect(
+ shardResponses, kIgnorableError1Status, {kShard1, kShard2, kShard3});
+}
+
+TEST_F(AppendRawResponsesTest,
+ SomeShardsReturnSuccessRestOfAttemptsToSendRequestsReturnDifferentIgnorableErrors) {
+ std::vector<AsyncRequestsSender::Response> shardResponses{
+ {kShard1, kOkResponse, makeHostAndPort(kShard1)},
+ {kShard2, kOkResponse, makeHostAndPort(kShard2)},
+ {kShard3, kIgnorableError1Status, boost::none},
+ {kShard4, kIgnorableError2Status, boost::none}};
+
+ // The ignorable errors are ignored.
+ runAppendRawResponsesExpect(shardResponses, Status::OK(), {kShard1, kShard2});
+}
+
+TEST_F(AppendRawResponsesTest, AllAttemptsToSendRequestsReturnSameNonIgnorableError) {
+ std::vector<AsyncRequestsSender::Response> shardResponses{
+ {kShard1, kNonIgnorableError1Status, boost::none},
+ {kShard2, kNonIgnorableError1Status, boost::none},
+ {kShard3, kNonIgnorableError1Status, boost::none}};
+
+ // The non-ignorable error is returned.
+ runAppendRawResponsesExpect(
+ shardResponses, kNonIgnorableError1Status, {kShard1, kShard2, kShard3});
+}
+
+TEST_F(AppendRawResponsesTest,
+ SomeShardsReturnSuccessRestOfAttemptsToSendRequestsReturnSameNonIgnorableError) {
+ std::vector<AsyncRequestsSender::Response> shardResponses{
+ {kShard1, kOkResponse, makeHostAndPort(kShard1)},
+ {kShard2, kOkResponse, makeHostAndPort(kShard2)},
+ {kShard3, kNonIgnorableError1Status, boost::none},
+ {kShard4, kNonIgnorableError1Status, boost::none}};
+
+ // The non-ignorable error is returned.
+ runAppendRawResponsesExpect(
+ shardResponses, kNonIgnorableError1Status, {kShard1, kShard2, kShard3, kShard4});
+}
+
+TEST_F(AppendRawResponsesTest, AttemptsToSendRequestsReturnDifferentNonIgnorableErrors) {
+ std::vector<AsyncRequestsSender::Response> shardResponses{
+ {kShard1, kNonIgnorableError1Status, boost::none},
+ {kShard2, kNonIgnorableError2Status, boost::none},
+ {kShard3, kNonIgnorableError1Status, boost::none}};
+
+ // The first non-ignorable error is returned.
+ runAppendRawResponsesExpect(
+ shardResponses, kNonIgnorableError1Status, {kShard1, kShard2, kShard3});
+}
+
+TEST_F(AppendRawResponsesTest,
+ SomeShardsReturnSuccessRestOfAttemptsToSendRequestsReturnDifferentNonIgnorableErrors) {
+ std::vector<AsyncRequestsSender::Response> shardResponses{
+ {kShard1, kOkResponse, makeHostAndPort(kShard1)},
+ {kShard2, kOkResponse, makeHostAndPort(kShard2)},
+ {kShard3, kNonIgnorableError1Status, boost::none},
+ {kShard4, kNonIgnorableError2Status, boost::none}};
+
+ // The first non-ignorable error is returned.
+ runAppendRawResponsesExpect(
+ shardResponses, kNonIgnorableError1Status, {kShard1, kShard2, kShard3, kShard4});
+}
+
+TEST_F(AppendRawResponsesTest, AllAttemptsToSendRequestsReturnErrorsSomeIgnorableSomeNonIgnorable) {
+ std::vector<AsyncRequestsSender::Response> shardResponses{
+ {kShard1, kIgnorableError1Status, boost::none},
+ {kShard2, kIgnorableError2Status, boost::none},
+ {kShard3, kNonIgnorableError1Status, boost::none},
+ {kShard4, kNonIgnorableError2Status, boost::none}};
+
+ // The first non-ignorable error is returned.
+ runAppendRawResponsesExpect(shardResponses, kNonIgnorableError1Status, {kShard3, kShard4});
+}
+
+TEST_F(
+ AppendRawResponsesTest,
+ SomeShardsReturnSuccessRestOfAttemptsToSendRequestsReturnErrorsSomeIgnorableSomeNonIgnorable) {
+ std::vector<AsyncRequestsSender::Response> shardResponses{
+ {kShard1, kIgnorableError1Status, boost::none},
+ {kShard2, kOkResponse, makeHostAndPort(kShard2)},
+ {kShard3, kNonIgnorableError1Status, boost::none},
+ {kShard4, kNonIgnorableError2Status, boost::none}};
+
+ // The first non-ignorable error is returned.
+ runAppendRawResponsesExpect(
+ shardResponses, kNonIgnorableError1Status, {kShard2, kShard3, kShard4});
+}
+
+//
+// Errors *processing* the requests.
+//
+
+TEST_F(AppendRawResponsesTest, AllShardsReturnSameIgnorableError) {
+ std::vector<AsyncRequestsSender::Response> shardResponses{
+ {kShard1, makeErrorResponse(kIgnorableError1Status), makeHostAndPort(kShard1)},
+ {kShard2, makeErrorResponse(kIgnorableError1Status), makeHostAndPort(kShard2)},
+ {kShard3, makeErrorResponse(kIgnorableError1Status), makeHostAndPort(kShard3)}};
+
+ // The ignorable error gets promoted to a non-ignorable error.
+ runAppendRawResponsesExpect(
+ shardResponses, kIgnorableError1Status, {kShard1, kShard2, kShard3});
+}
+
+TEST_F(AppendRawResponsesTest, SomeShardsReturnSuccessRestReturnSameIgnorableError) {
+ std::vector<AsyncRequestsSender::Response> shardResponses{
+ {kShard1, kOkResponse, makeHostAndPort(kShard1)},
+ {kShard2, kOkResponse, makeHostAndPort(kShard2)},
+ {kShard3, makeErrorResponse(kIgnorableError1Status), makeHostAndPort(kShard3)},
+ {kShard4, makeErrorResponse(kIgnorableError1Status), makeHostAndPort(kShard4)}};
+
+ // The ignorable errors are ignored.
+ runAppendRawResponsesExpect(shardResponses, Status::OK(), {kShard1, kShard2});
+}
+
+TEST_F(AppendRawResponsesTest, ShardsReturnDifferentIgnorableErrors) {
+ std::vector<AsyncRequestsSender::Response> shardResponses{
+ {kShard1, makeErrorResponse(kIgnorableError1Status), makeHostAndPort(kShard1)},
+ {kShard2, makeErrorResponse(kIgnorableError2Status), makeHostAndPort(kShard2)},
+ {kShard3, makeErrorResponse(kIgnorableError1Status), makeHostAndPort(kShard3)}};
+
+ // The *first* ignorable error gets promoted to a non-ignorable error.
+ runAppendRawResponsesExpect(
+ shardResponses, kIgnorableError1Status, {kShard1, kShard2, kShard3});
+}
+
+TEST_F(AppendRawResponsesTest, SomeShardsReturnSuccessRestReturnDifferentIgnorableErrors) {
+ std::vector<AsyncRequestsSender::Response> shardResponses{
+ {kShard1, kOkResponse, makeHostAndPort(kShard1)},
+ {kShard2, kOkResponse, makeHostAndPort(kShard2)},
+ {kShard3, makeErrorResponse(kIgnorableError1Status), makeHostAndPort(kShard3)},
+ {kShard4, makeErrorResponse(kIgnorableError2Status), makeHostAndPort(kShard4)}};
+
+ // The ignorable errors are ignored.
+ runAppendRawResponsesExpect(shardResponses, Status::OK(), {kShard1, kShard2});
+}
+
+TEST_F(AppendRawResponsesTest, AllShardsReturnSameNonIgnorableError) {
+ std::vector<AsyncRequestsSender::Response> shardResponses{
+ {kShard1, makeErrorResponse(kNonIgnorableError1Status), makeHostAndPort(kShard1)},
+ {kShard2, makeErrorResponse(kNonIgnorableError1Status), makeHostAndPort(kShard2)},
+ {kShard3, makeErrorResponse(kNonIgnorableError1Status), makeHostAndPort(kShard3)}};
+
+ // The non-ignorable error is returned.
+ runAppendRawResponsesExpect(
+ shardResponses, kNonIgnorableError1Status, {kShard1, kShard2, kShard3});
+}
+
+TEST_F(AppendRawResponsesTest, SomeShardsReturnSuccessRestReturnSameNonIgnorableError) {
+ std::vector<AsyncRequestsSender::Response> shardResponses{
+ {kShard1, kOkResponse, makeHostAndPort(kShard1)},
+ {kShard2, kOkResponse, makeHostAndPort(kShard2)},
+ {kShard3, makeErrorResponse(kNonIgnorableError1Status), makeHostAndPort(kShard3)},
+ {kShard4, makeErrorResponse(kNonIgnorableError1Status), makeHostAndPort(kShard4)}};
+
+ // The non-ignorable error is returned.
+ runAppendRawResponsesExpect(
+ shardResponses, kNonIgnorableError1Status, {kShard1, kShard2, kShard3, kShard4});
+}
+
+TEST_F(AppendRawResponsesTest, ShardsReturnDifferentNonIgnorableErrors) {
+ std::vector<AsyncRequestsSender::Response> shardResponses{
+ {kShard1, makeErrorResponse(kNonIgnorableError1Status), makeHostAndPort(kShard1)},
+ {kShard2, makeErrorResponse(kNonIgnorableError2Status), makeHostAndPort(kShard2)},
+ {kShard3, makeErrorResponse(kNonIgnorableError1Status), makeHostAndPort(kShard3)}};
+
+ // The first non-ignorable error is returned.
+ runAppendRawResponsesExpect(
+ shardResponses, kNonIgnorableError1Status, {kShard1, kShard2, kShard3});
+}
+
+TEST_F(AppendRawResponsesTest, SomeShardsReturnSuccessRestReturnDifferentNonIgnorableErrors) {
+ std::vector<AsyncRequestsSender::Response> shardResponses{
+ {kShard1, kOkResponse, makeHostAndPort(kShard1)},
+ {kShard2, kOkResponse, makeHostAndPort(kShard2)},
+ {kShard3, makeErrorResponse(kNonIgnorableError1Status), makeHostAndPort(kShard3)},
+ {kShard4, makeErrorResponse(kNonIgnorableError2Status), makeHostAndPort(kShard4)}};
+
+ // The first non-ignorable error is returned.
+ runAppendRawResponsesExpect(
+ shardResponses, kNonIgnorableError1Status, {kShard1, kShard2, kShard3, kShard4});
+}
+
+TEST_F(AppendRawResponsesTest, AllShardsReturnErrorsSomeIgnorableSomeNonIgnorable) {
+ std::vector<AsyncRequestsSender::Response> shardResponses{
+ {kShard1, makeErrorResponse(kIgnorableError1Status), makeHostAndPort(kShard1)},
+ {kShard2, makeErrorResponse(kIgnorableError2Status), makeHostAndPort(kShard2)},
+ {kShard3, makeErrorResponse(kNonIgnorableError1Status), makeHostAndPort(kShard3)},
+ {kShard4, makeErrorResponse(kNonIgnorableError2Status), makeHostAndPort(kShard4)}};
+
+ // The first non-ignorable error is returned.
+ runAppendRawResponsesExpect(shardResponses, kNonIgnorableError1Status, {kShard3, kShard4});
+}
+
+TEST_F(AppendRawResponsesTest,
+ SomeShardsReturnSuccessRestReturnErrorsSomeIgnorableSomeNonIgnorable) {
+ std::vector<AsyncRequestsSender::Response> shardResponses{
+ {kShard1, makeErrorResponse(kIgnorableError1Status), makeHostAndPort(kShard1)},
+ {kShard2, kOkResponse, makeHostAndPort(kShard2)},
+ {kShard3, makeErrorResponse(kNonIgnorableError1Status), makeHostAndPort(kShard3)},
+ {kShard4, makeErrorResponse(kNonIgnorableError2Status), makeHostAndPort(kShard4)}};
+
+ // The first non-ignorable error is returned.
+ runAppendRawResponsesExpect(
+ shardResponses, kNonIgnorableError1Status, {kShard2, kShard3, kShard4});
+}
+
+// Mix of errors *sending* and *processing* the requests.
+
+TEST_F(AppendRawResponsesTest,
+ AllShardsReturnErrorsMixOfErrorsSendingRequestsAndErrorsProcessingRequests) {
+ std::vector<AsyncRequestsSender::Response> shardResponses{
+ {kShard1, kIgnorableError1Status, boost::none},
+ {kShard2, makeErrorResponse(kIgnorableError2Status), makeHostAndPort(kShard2)},
+ {kShard3, kNonIgnorableError1Status, boost::none},
+ {kShard4, makeErrorResponse(kNonIgnorableError2Status), makeHostAndPort(kShard4)}};
+
+ // The first non-ignorable error is returned.
+ runAppendRawResponsesExpect(shardResponses, kNonIgnorableError1Status, {kShard3, kShard4});
+}
+
+TEST_F(
+ AppendRawResponsesTest,
+ SomeShardsReturnSuccessRestReturnErrorsMixOfErrorsSendingRequestsAndErrorsProcessingRequests) {
+ std::vector<AsyncRequestsSender::Response> shardResponses{
+ {kShard1, kIgnorableError1Status, boost::none},
+ {kShard2, makeErrorResponse(kIgnorableError2Status), makeHostAndPort(kShard2)},
+ {kShard3, kNonIgnorableError1Status, boost::none},
+ {kShard4, makeErrorResponse(kNonIgnorableError2Status), makeHostAndPort(kShard4)},
+ {kShard5, kOkResponse, HostAndPort("e:1")}};
+
+ // The first non-ignorable error is returned.
+ runAppendRawResponsesExpect(
+ shardResponses, kNonIgnorableError1Status, {kShard3, kShard4, kShard5});
+}
+
+// Mix of errors sending or processing the requests *and* writeConcern error.
+
+TEST_F(AppendRawResponsesTest, SomeShardsReturnSuccessWithWriteConcernErrorRestReturnMixOfErrors) {
+ std::vector<AsyncRequestsSender::Response> shardResponses{
+ {kShard1, kIgnorableError1Status, boost::none},
+ {kShard2, makeErrorResponse(kIgnorableError2Status), makeHostAndPort(kShard2)},
+ {kShard3, kNonIgnorableError1Status, boost::none},
+ {kShard4, makeErrorResponse(kNonIgnorableError2Status), makeHostAndPort(kShard4)},
+ {kShard5,
+ makeWriteConcernErrorResponse(kWriteConcernError1Status),
+ makeHostAndPort(kShard5)}};
+
+ // The first non-ignorable error is returned, and no writeConcern error is reported at the top
+ // level.
+ runAppendRawResponsesExpect(
+ shardResponses, kNonIgnorableError1Status, {kShard3, kShard4, kShard5});
+}
+
+} // namespace mongo