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/base/status_with.h"
34 #include "mongo/bson/oid.h"
35 #include "mongo/s/catalog/type_collection.h"
36 #include "mongo/unittest/unittest.h"
37 #include "mongo/util/time_support.h"
38
39 namespace {
40
41 using namespace mongo;
42
43 using unittest::assertGet;
44
TEST(CollectionType,Empty)45 TEST(CollectionType, Empty) {
46 StatusWith<CollectionType> status = CollectionType::fromBSON(BSONObj());
47 ASSERT_FALSE(status.isOK());
48 }
49
TEST(CollectionType,Basic)50 TEST(CollectionType, Basic) {
51 const OID oid = OID::gen();
52 StatusWith<CollectionType> status =
53 CollectionType::fromBSON(BSON(CollectionType::fullNs("db.coll")
54 << CollectionType::epoch(oid)
55 << CollectionType::updatedAt(Date_t::fromMillisSinceEpoch(1))
56 << CollectionType::keyPattern(BSON("a" << 1))
57 << CollectionType::defaultCollation(BSON("locale"
58 << "fr_CA"))
59 << CollectionType::unique(true)));
60 ASSERT_TRUE(status.isOK());
61
62 CollectionType coll = status.getValue();
63 ASSERT_TRUE(coll.validate().isOK());
64 ASSERT(coll.getNs() == NamespaceString{"db.coll"});
65 ASSERT_EQUALS(coll.getEpoch(), oid);
66 ASSERT_EQUALS(coll.getUpdatedAt(), Date_t::fromMillisSinceEpoch(1));
67 ASSERT_BSONOBJ_EQ(coll.getKeyPattern().toBSON(), BSON("a" << 1));
68 ASSERT_BSONOBJ_EQ(coll.getDefaultCollation(),
69 BSON("locale"
70 << "fr_CA"));
71 ASSERT_EQUALS(coll.getUnique(), true);
72 ASSERT_EQUALS(coll.getAllowBalance(), true);
73 ASSERT_EQUALS(coll.getDropped(), false);
74 }
75
TEST(CollectionType,EmptyDefaultCollationFailsToParse)76 TEST(CollectionType, EmptyDefaultCollationFailsToParse) {
77 const OID oid = OID::gen();
78 StatusWith<CollectionType> status =
79 CollectionType::fromBSON(BSON(CollectionType::fullNs("db.coll")
80 << CollectionType::epoch(oid)
81 << CollectionType::updatedAt(Date_t::fromMillisSinceEpoch(1))
82 << CollectionType::keyPattern(BSON("a" << 1))
83 << CollectionType::defaultCollation(BSONObj())
84 << CollectionType::unique(true)));
85 ASSERT_FALSE(status.isOK());
86 }
87
TEST(CollectionType,MissingDefaultCollationParses)88 TEST(CollectionType, MissingDefaultCollationParses) {
89 const OID oid = OID::gen();
90 StatusWith<CollectionType> status =
91 CollectionType::fromBSON(BSON(CollectionType::fullNs("db.coll")
92 << CollectionType::epoch(oid)
93 << CollectionType::updatedAt(Date_t::fromMillisSinceEpoch(1))
94 << CollectionType::keyPattern(BSON("a" << 1))
95 << CollectionType::unique(true)));
96 ASSERT_TRUE(status.isOK());
97
98 CollectionType coll = status.getValue();
99 ASSERT_TRUE(coll.validate().isOK());
100 ASSERT_BSONOBJ_EQ(coll.getDefaultCollation(), BSONObj());
101 }
102
TEST(CollectionType,DefaultCollationSerializesCorrectly)103 TEST(CollectionType, DefaultCollationSerializesCorrectly) {
104 const OID oid = OID::gen();
105 StatusWith<CollectionType> status =
106 CollectionType::fromBSON(BSON(CollectionType::fullNs("db.coll")
107 << CollectionType::epoch(oid)
108 << CollectionType::updatedAt(Date_t::fromMillisSinceEpoch(1))
109 << CollectionType::keyPattern(BSON("a" << 1))
110 << CollectionType::defaultCollation(BSON("locale"
111 << "fr_CA"))
112 << CollectionType::unique(true)));
113 ASSERT_TRUE(status.isOK());
114
115 CollectionType coll = status.getValue();
116 ASSERT_TRUE(coll.validate().isOK());
117 BSONObj serialized = coll.toBSON();
118 ASSERT_BSONOBJ_EQ(serialized["defaultCollation"].Obj(),
119 BSON("locale"
120 << "fr_CA"));
121 }
122
TEST(CollectionType,MissingDefaultCollationIsNotSerialized)123 TEST(CollectionType, MissingDefaultCollationIsNotSerialized) {
124 const OID oid = OID::gen();
125 StatusWith<CollectionType> status =
126 CollectionType::fromBSON(BSON(CollectionType::fullNs("db.coll")
127 << CollectionType::epoch(oid)
128 << CollectionType::updatedAt(Date_t::fromMillisSinceEpoch(1))
129 << CollectionType::keyPattern(BSON("a" << 1))
130 << CollectionType::unique(true)));
131 ASSERT_TRUE(status.isOK());
132
133 CollectionType coll = status.getValue();
134 ASSERT_TRUE(coll.validate().isOK());
135 BSONObj serialized = coll.toBSON();
136 ASSERT_FALSE(serialized["defaultCollation"]);
137 }
138
TEST(CollectionType,EpochCorrectness)139 TEST(CollectionType, EpochCorrectness) {
140 CollectionType coll;
141 coll.setNs(NamespaceString{"db.coll"});
142 coll.setUpdatedAt(Date_t::fromMillisSinceEpoch(1));
143 coll.setKeyPattern(KeyPattern{BSON("a" << 1)});
144 coll.setUnique(false);
145 coll.setDropped(false);
146
147 // Validation will fail because we don't have epoch set. This ensures that if we read a
148 // collection with no epoch, we will write back one with epoch.
149 ASSERT_NOT_OK(coll.validate());
150
151 // We should be allowed to set empty epoch for dropped collections
152 coll.setDropped(true);
153 coll.setEpoch(OID());
154 ASSERT_OK(coll.validate());
155
156 // We should be allowed to set normal epoch for non-dropped collections
157 coll.setDropped(false);
158 coll.setEpoch(OID::gen());
159 ASSERT_OK(coll.validate());
160 }
161
TEST(CollectionType,Pre22Format)162 TEST(CollectionType, Pre22Format) {
163 CollectionType coll = assertGet(CollectionType::fromBSON(BSON("_id"
164 << "db.coll"
165 << "lastmod"
166 << Date_t::fromMillisSinceEpoch(1)
167 << "dropped"
168 << false
169 << "key"
170 << BSON("a" << 1)
171 << "unique"
172 << false)));
173
174 ASSERT(coll.getNs() == NamespaceString{"db.coll"});
175 ASSERT(!coll.getEpoch().isSet());
176 ASSERT_EQUALS(coll.getUpdatedAt(), Date_t::fromMillisSinceEpoch(1));
177 ASSERT_BSONOBJ_EQ(coll.getKeyPattern().toBSON(), BSON("a" << 1));
178 ASSERT_EQUALS(coll.getUnique(), false);
179 ASSERT_EQUALS(coll.getAllowBalance(), true);
180 ASSERT_EQUALS(coll.getDropped(), false);
181 }
182
TEST(CollectionType,InvalidCollectionNamespace)183 TEST(CollectionType, InvalidCollectionNamespace) {
184 const OID oid = OID::gen();
185 StatusWith<CollectionType> result =
186 CollectionType::fromBSON(BSON(CollectionType::fullNs("foo\\bar.coll")
187 << CollectionType::epoch(oid)
188 << CollectionType::updatedAt(Date_t::fromMillisSinceEpoch(1))
189 << CollectionType::keyPattern(BSON("a" << 1))
190 << CollectionType::unique(true)));
191 ASSERT_TRUE(result.isOK());
192 CollectionType collType = result.getValue();
193 ASSERT_FALSE(collType.validate().isOK());
194 }
195
TEST(CollectionType,BadType)196 TEST(CollectionType, BadType) {
197 const OID oid = OID::gen();
198 StatusWith<CollectionType> status = CollectionType::fromBSON(
199 BSON(CollectionType::fullNs() << 1 << CollectionType::epoch(oid)
200 << CollectionType::updatedAt(Date_t::fromMillisSinceEpoch(1))
201 << CollectionType::keyPattern(BSON("a" << 1))
202 << CollectionType::unique(true)));
203
204 ASSERT_FALSE(status.isOK());
205 }
206
207 } // namespace
208