diff options
author | Kyle Suarez <kyle.suarez@mongodb.com> | 2016-11-01 11:16:42 -0400 |
---|---|---|
committer | Kyle Suarez <kyle.suarez@mongodb.com> | 2016-11-01 11:16:42 -0400 |
commit | 96f015892c82929e85f565cfc59cdda1e9d420e0 (patch) | |
tree | 6cf85b62284188472cbb1d4907c3a8c3fa47b6ae | |
parent | 938885a1796d8b7e42748a4cf68c77872d75db98 (diff) | |
download | mongo-96f015892c82929e85f565cfc59cdda1e9d420e0.tar.gz |
SERVER-25599 unit tests for ResolvedView
-rw-r--r-- | src/mongo/db/views/SConscript | 13 | ||||
-rw-r--r-- | src/mongo/db/views/resolved_view.cpp | 52 | ||||
-rw-r--r-- | src/mongo/db/views/resolved_view.h | 2 | ||||
-rw-r--r-- | src/mongo/db/views/resolved_view_test.cpp | 239 |
4 files changed, 279 insertions, 27 deletions
diff --git a/src/mongo/db/views/SConscript b/src/mongo/db/views/SConscript index db73adeba8c..9a70543f300 100644 --- a/src/mongo/db/views/SConscript +++ b/src/mongo/db/views/SConscript @@ -46,3 +46,16 @@ env.CppUnitTest( '$BUILD_DIR/mongo/unittest/unittest', ], ) + +env.CppUnitTest( + target='resolved_view_test', + source=[ + 'resolved_view_test.cpp', + ], + LIBDEPS=[ + 'views', + '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/service_context_noop_init', + '$BUILD_DIR/mongo/unittest/unittest', + ], +) diff --git a/src/mongo/db/views/resolved_view.cpp b/src/mongo/db/views/resolved_view.cpp index b11db8958d2..d5f4faa7f68 100644 --- a/src/mongo/db/views/resolved_view.cpp +++ b/src/mongo/db/views/resolved_view.cpp @@ -1,30 +1,30 @@ /** -* Copyright (C) 2016 MongoDB Inc. -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License, version 3, -* as published by the Free Software Foundation. -* -* 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 -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see <http://www.gnu.org/licenses/>. -* -* 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 GNU Affero General 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. -*/ + * Copyright (C) 2016 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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 + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * 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 GNU Affero General 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. + */ #include "mongo/platform/basic.h" diff --git a/src/mongo/db/views/resolved_view.h b/src/mongo/db/views/resolved_view.h index 93ad3a7e709..a5016ffc4b3 100644 --- a/src/mongo/db/views/resolved_view.h +++ b/src/mongo/db/views/resolved_view.h @@ -45,7 +45,7 @@ class AggregationRequest; class ResolvedView { public: ResolvedView(const NamespaceString& collectionNs, const std::vector<BSONObj>& pipeline) - : _namespace(std::move(collectionNs)), _pipeline(std::move(pipeline)) {} + : _namespace(collectionNs), _pipeline(pipeline) {} /** * Returns whether 'commandResponseObj' contains a CommandOnShardedViewNotSupportedOnMongod diff --git a/src/mongo/db/views/resolved_view_test.cpp b/src/mongo/db/views/resolved_view_test.cpp new file mode 100644 index 00000000000..eebcda01db5 --- /dev/null +++ b/src/mongo/db/views/resolved_view_test.cpp @@ -0,0 +1,239 @@ +/** + * Copyright (C) 2016 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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 + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * 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 GNU Affero General 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. + */ + +#include "mongo/platform/basic.h" + +#include <vector> + +#include "mongo/bson/bsonmisc.h" +#include "mongo/bson/bsonobj.h" +#include "mongo/bson/bsonobjbuilder.h" +#include "mongo/db/namespace_string.h" +#include "mongo/db/pipeline/aggregation_request.h" +#include "mongo/db/views/resolved_view.h" +#include "mongo/unittest/unittest.h" + +namespace mongo { + +// Stub to avoid including the server_options library. +bool isMongos() { + return false; +} + +namespace { + +const NamespaceString viewNss("testdb.testview"); +const NamespaceString backingNss("testdb.testcoll"); +const std::vector<BSONObj> emptyPipeline; + +TEST(ResolvedViewTest, ExpandingCmdObjWithEmptyPipelineOnNoOpViewYieldsEmptyPipeline) { + const ResolvedView resolvedView{backingNss, emptyPipeline}; + BSONObj cmdObj = BSON("aggregate" << viewNss.coll() << "pipeline" << BSONArray()); + + auto result = resolvedView.asExpandedViewAggregation(cmdObj); + ASSERT_OK(result.getStatus()); + + BSONObj expected = BSON( + "aggregate" << backingNss.coll() << "pipeline" << BSONArray() << "cursor" << BSONObj()); + ASSERT_BSONOBJ_EQ(result.getValue(), expected); +} + +TEST(ResolvedViewTest, ExpandingCmdObjWithNonemptyPipelineAppendsToViewPipeline) { + std::vector<BSONObj> viewPipeline{BSON("skip" << 7)}; + const ResolvedView resolvedView{backingNss, viewPipeline}; + BSONObj cmdObj = + BSON("aggregate" << viewNss.coll() << "pipeline" << BSON_ARRAY(BSON("limit" << 3))); + + auto result = resolvedView.asExpandedViewAggregation(cmdObj); + ASSERT_OK(result.getStatus()); + + BSONObj expected = BSON("aggregate" << backingNss.coll() << "pipeline" + << BSON_ARRAY(BSON("skip" << 7) << BSON("limit" << 3)) + << "cursor" + << BSONObj()); + ASSERT_BSONOBJ_EQ(result.getValue(), expected); +} + +TEST(ResolvedViewTest, ExpandingCmdObjFailsIfCmdObjIsNotAValidAggregationCommand) { + const ResolvedView resolvedView{backingNss, emptyPipeline}; + BSONObj badCmdObj = BSON("invalid" << 0); + ASSERT_NOT_OK(resolvedView.asExpandedViewAggregation(badCmdObj).getStatus()); +} + +TEST(ResolvedViewTest, ExpandingAggRequestWithEmptyPipelineOnNoOpViewYieldsEmptyPipeline) { + const ResolvedView resolvedView{backingNss, emptyPipeline}; + AggregationRequest aggRequest(viewNss, {}); + + auto result = resolvedView.asExpandedViewAggregation(aggRequest); + ASSERT_OK(result.getStatus()); + + BSONObj expected = BSON( + "aggregate" << backingNss.coll() << "pipeline" << BSONArray() << "cursor" << BSONObj()); + ASSERT_BSONOBJ_EQ(result.getValue(), expected); +} + +TEST(ResolvedViewTest, ExpandingAggRequestWithNonemptyPipelineAppendsToViewPipeline) { + std::vector<BSONObj> viewPipeline{BSON("skip" << 7)}; + const ResolvedView resolvedView{backingNss, viewPipeline}; + + std::vector<BSONObj> userAggregationPipeline = {BSON("limit" << 3)}; + AggregationRequest aggRequest(viewNss, userAggregationPipeline); + + auto result = resolvedView.asExpandedViewAggregation(aggRequest); + ASSERT_OK(result.getStatus()); + + BSONObj expected = BSON("aggregate" << backingNss.coll() << "pipeline" + << BSON_ARRAY(BSON("skip" << 7) << BSON("limit" << 3)) + << "cursor" + << BSONObj()); + ASSERT_BSONOBJ_EQ(result.getValue(), expected); +} + +TEST(ResolvedViewTest, ExpandingAggRequestPreservesExplain) { + const ResolvedView resolvedView{backingNss, emptyPipeline}; + AggregationRequest aggRequest(viewNss, {}); + aggRequest.setExplain(true); + + auto result = resolvedView.asExpandedViewAggregation(aggRequest); + ASSERT_OK(result.getStatus()); + + BSONObj expected = + BSON("aggregate" << backingNss.coll() << "pipeline" << BSONArray() << "cursor" << BSONObj() + << "explain" + << true); + ASSERT_BSONOBJ_EQ(result.getValue(), expected); +} + +TEST(ResolvedViewTest, ExpandingAggRequestPreservesBypassDocumentValidation) { + const ResolvedView resolvedView{backingNss, emptyPipeline}; + AggregationRequest aggRequest(viewNss, {}); + aggRequest.setBypassDocumentValidation(true); + + auto result = resolvedView.asExpandedViewAggregation(aggRequest); + ASSERT_OK(result.getStatus()); + + BSONObj expected = + BSON("aggregate" << backingNss.coll() << "pipeline" << BSONArray() << "cursor" << BSONObj() + << "bypassDocumentValidation" + << true); + ASSERT_BSONOBJ_EQ(result.getValue(), expected); +} + +TEST(ResolvedViewTest, ExpandingAggRequestPreservesAllowDiskUse) { + const ResolvedView resolvedView{backingNss, emptyPipeline}; + AggregationRequest aggRequest(viewNss, {}); + aggRequest.setAllowDiskUse(true); + + auto result = resolvedView.asExpandedViewAggregation(aggRequest); + ASSERT_OK(result.getStatus()); + + BSONObj expected = + BSON("aggregate" << backingNss.coll() << "pipeline" << BSONArray() << "cursor" << BSONObj() + << "allowDiskUse" + << true); + ASSERT_BSONOBJ_EQ(result.getValue(), expected); +} + +TEST(ResolvedViewTest, FromBSONFailsIfMissingResolvedView) { + BSONObj badCmdResponse = BSON("x" << 1); + ASSERT_THROWS_CODE(ResolvedView::fromBSON(badCmdResponse), UserException, 40248); +} + +TEST(ResolvedViewTest, FromBSONFailsOnResolvedViewBadType) { + BSONObj badCmdResponse = BSON("resolvedView" << 7); + ASSERT_THROWS_CODE(ResolvedView::fromBSON(badCmdResponse), UserException, 40249); +} + +TEST(ResolvedViewTest, FromBSONFailsIfMissingViewNs) { + BSONObj badCmdResponse = BSON("resolvedView" << BSON("pipeline" << BSONArray())); + ASSERT_THROWS_CODE(ResolvedView::fromBSON(badCmdResponse), UserException, 40250); +} + +TEST(ResolvedViewTest, FromBSONFailsOnInvalidViewNsType) { + BSONObj badCmdResponse = BSON("resolvedView" << BSON("ns" << 8)); + ASSERT_THROWS_CODE(ResolvedView::fromBSON(badCmdResponse), UserException, 40250); +} + +TEST(ResolvedViewTest, FromBSONFailsIfMissingPipeline) { + BSONObj badCmdResponse = BSON("resolvedView" << BSON("ns" << backingNss.ns())); + ASSERT_THROWS_CODE(ResolvedView::fromBSON(badCmdResponse), UserException, 40251); +} + +TEST(ResolvedViewTest, FromBSONFailsOnInvalidPipelineType) { + BSONObj badCmdResponse = + BSON("resolvedView" << BSON("ns" << backingNss.ns() << "pipeline" << 7)); + ASSERT_THROWS_CODE(ResolvedView::fromBSON(badCmdResponse), UserException, 40251); +} + +TEST(ResolvedViewTest, FromBSONSuccessfullyParsesEmptyBSONArrayIntoEmptyVector) { + BSONObj cmdResponse = + BSON("resolvedView" << BSON("ns" << backingNss.ns() << "pipeline" << BSONArray())); + const ResolvedView result = ResolvedView::fromBSON(cmdResponse); + ASSERT_EQ(result.getNamespace(), backingNss); + ASSERT(std::equal(emptyPipeline.begin(), + emptyPipeline.end(), + result.getPipeline().begin(), + SimpleBSONObjComparator::kInstance.makeEqualTo())); +} + +TEST(ResolvedViewTest, FromBSONSuccessfullyParsesPopulatedBSONArrayIntoVector) { + BSONObj matchStage = BSON("$match" << BSON("x" << 1)); + BSONObj sortStage = BSON("$sort" << BSON("y" << -1)); + BSONObj limitStage = BSON("$limit" << 7); + + BSONArray pipeline = BSON_ARRAY(matchStage << sortStage << limitStage); + BSONObj cmdResponse = BSON("resolvedView" << BSON("ns" + << "testdb.testcoll" + << "pipeline" + << pipeline)); + + const ResolvedView result = ResolvedView::fromBSON(cmdResponse); + ASSERT_EQ(result.getNamespace(), backingNss); + + std::vector<BSONObj> expectedPipeline{matchStage, sortStage, limitStage}; + ASSERT(std::equal(expectedPipeline.begin(), + expectedPipeline.end(), + result.getPipeline().begin(), + SimpleBSONObjComparator::kInstance.makeEqualTo())); +} + +TEST(ResolvedViewTest, IsResolvedViewErrorResponseDetectsKickbackErrorCodeSuccessfully) { + BSONObj errorResponse = + BSON("ok" << 0 << "code" << ErrorCodes::CommandOnShardedViewNotSupportedOnMongod << "errmsg" + << "This view is sharded and cannot be run on mongod"); + ASSERT(ResolvedView::isResolvedViewErrorResponse(errorResponse)); +} + +TEST(ResolvedViewTest, IsResolvedViewErrorResponseReportsFalseOnNonKickbackErrorCode) { + BSONObj errorResponse = + BSON("ok" << 0 << "code" << ErrorCodes::ViewDepthLimitExceeded << "errmsg" + << "View nesting too deep or view cycle detected"); + ASSERT_FALSE(ResolvedView::isResolvedViewErrorResponse(errorResponse)); +} +} // namespace +} // namespace mongo |