1 
2 /**
3  *    Copyright (C) 2018-present MongoDB, Inc.
4  *
5  *    This program is free software: you can redistribute it and/or modify
6  *    it under the terms of the Server Side Public License, version 1,
7  *    as published by MongoDB, Inc.
8  *
9  *    This program is distributed in the hope that it will be useful,
10  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *    Server Side Public License for more details.
13  *
14  *    You should have received a copy of the Server Side Public License
15  *    along with this program. If not, see
16  *    <http://www.mongodb.com/licensing/server-side-public-license>.
17  *
18  *    As a special exception, the copyright holders give permission to link the
19  *    code of portions of this program with the OpenSSL library under certain
20  *    conditions as described in each individual source file and distribute
21  *    linked combinations including the program with the OpenSSL library. You
22  *    must comply with the Server Side Public License in all respects for
23  *    all of the code used other than as permitted herein. If you modify file(s)
24  *    with this exception, you may extend this exception to your version of the
25  *    file(s), but you are not obligated to do so. If you do not wish to do so,
26  *    delete this exception statement from your version. If you delete this
27  *    exception statement from all source files in the program, then also delete
28  *    it in the license file.
29  */
30 
31 #include "mongo/platform/basic.h"
32 
33 #include "mongo/db/catalog/capped_utils.h"
34 #include "mongo/db/catalog/collection_catalog_entry.h"
35 #include "mongo/db/client.h"
36 #include "mongo/db/db_raii.h"
37 #include "mongo/db/repl/replication_coordinator.h"
38 #include "mongo/db/repl/replication_coordinator_mock.h"
39 #include "mongo/db/repl/storage_interface_impl.h"
40 #include "mongo/db/service_context_d_test_fixture.h"
41 #include "mongo/stdx/memory.h"
42 #include "mongo/unittest/unittest.h"
43 
44 namespace {
45 
46 using namespace mongo;
47 
48 class CappedUtilsTest : public ServiceContextMongoDTest {
49 private:
50     void setUp() override;
51     void tearDown() override;
52 
53 protected:
54     // Use StorageInterface to access storage features below catalog interface.
55     std::unique_ptr<repl::StorageInterface> _storage;
56 };
57 
setUp()58 void CappedUtilsTest::setUp() {
59     // Set up mongod.
60     ServiceContextMongoDTest::setUp();
61 
62     auto service = getServiceContext();
63 
64     // Set up ReplicationCoordinator and ensure that we are primary.
65     auto replCoord = stdx::make_unique<repl::ReplicationCoordinatorMock>(service);
66     ASSERT_OK(replCoord->setFollowerMode(repl::MemberState::RS_PRIMARY));
67     repl::ReplicationCoordinator::set(service, std::move(replCoord));
68 
69     _storage = stdx::make_unique<repl::StorageInterfaceImpl>();
70 }
71 
tearDown()72 void CappedUtilsTest::tearDown() {
73     _storage = {};
74 
75     // Tear down mongod.
76     ServiceContextMongoDTest::tearDown();
77 }
78 
79 /**
80  * Creates an OperationContext.
81  */
makeOpCtx()82 ServiceContext::UniqueOperationContext makeOpCtx() {
83     return cc().makeOperationContext();
84 }
85 
86 /**
87  * Returns true if collection exists.
88  */
collectionExists(OperationContext * opCtx,const NamespaceString & nss)89 bool collectionExists(OperationContext* opCtx, const NamespaceString& nss) {
90     return AutoGetCollectionForRead(opCtx, nss).getCollection() != nullptr;
91 }
92 
93 /**
94  * Returns collection options.
95  */
getCollectionOptions(OperationContext * opCtx,const NamespaceString & nss)96 CollectionOptions getCollectionOptions(OperationContext* opCtx, const NamespaceString& nss) {
97     AutoGetCollectionForRead autoColl(opCtx, nss);
98     auto collection = autoColl.getCollection();
99     ASSERT_TRUE(collection) << "Unable to get collections options for " << nss
100                             << " because collection does not exist.";
101     auto catalogEntry = collection->getCatalogEntry();
102     return catalogEntry->getCollectionOptions(opCtx);
103 }
104 
105 // Size of capped collection to be passed to convertToCapped() which accepts a double.
106 // According to documentation, the size (in bytes) of capped collection must be a minimum of 4096
107 // and a multiple of 256.
108 const double cappedCollectionSize = 8192.0;
109 
TEST_F(CappedUtilsTest,ConvertToCappedReturnsNamespaceNotFoundIfCollectionIsMissing)110 TEST_F(CappedUtilsTest, ConvertToCappedReturnsNamespaceNotFoundIfCollectionIsMissing) {
111     NamespaceString nss("test.t");
112     auto opCtx = makeOpCtx();
113     ASSERT_FALSE(collectionExists(opCtx.get(), nss));
114     ASSERT_EQUALS(ErrorCodes::NamespaceNotFound, convertToCapped(opCtx.get(), nss, 1000.0));
115 }
116 
TEST_F(CappedUtilsTest,ConvertToCappedUpdatesCollectionOptionsOnSuccess)117 TEST_F(CappedUtilsTest, ConvertToCappedUpdatesCollectionOptionsOnSuccess) {
118     NamespaceString nss("test.t");
119 
120     auto opCtx = makeOpCtx();
121     ASSERT_OK(_storage->createCollection(opCtx.get(), nss, {}));
122     auto options = getCollectionOptions(opCtx.get(), nss);
123     ASSERT_FALSE(options.capped);
124 
125     ASSERT_OK(convertToCapped(opCtx.get(), nss, cappedCollectionSize));
126     options = getCollectionOptions(opCtx.get(), nss);
127     ASSERT_TRUE(options.capped);
128     ASSERT_APPROX_EQUAL(cappedCollectionSize, options.cappedSize, 0.001)
129         << "unexpected capped collection size: " << options.toBSON();
130 }
131 
TEST_F(CappedUtilsTest,ConvertToCappedReturnsNamespaceNotFoundIfCollectionIsDropPending)132 TEST_F(CappedUtilsTest, ConvertToCappedReturnsNamespaceNotFoundIfCollectionIsDropPending) {
133     NamespaceString nss("test.t");
134     repl::OpTime dropOpTime(Timestamp(Seconds(100), 0), 1LL);
135     auto dropPendingNss = nss.makeDropPendingNamespace(dropOpTime);
136 
137     auto opCtx = makeOpCtx();
138     ASSERT_OK(_storage->createCollection(opCtx.get(), dropPendingNss, {}));
139     auto options = getCollectionOptions(opCtx.get(), dropPendingNss);
140     ASSERT_FALSE(options.capped);
141 
142     ASSERT_EQUALS(ErrorCodes::NamespaceNotFound,
143                   convertToCapped(opCtx.get(), dropPendingNss, cappedCollectionSize));
144     options = getCollectionOptions(opCtx.get(), dropPendingNss);
145     ASSERT_FALSE(options.capped);
146 }
147 
148 }  // namespace
149