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/bson/bsonmisc.h"
34 #include "mongo/bson/bsonobjbuilder.h"
35 #include "mongo/bson/bsontypes.h"
36 #include "mongo/stdx/unordered_set.h"
37 #include "mongo/unittest/unittest.h"
38 #include "mongo/util/uuid.h"
39 
40 namespace mongo {
41 namespace {
42 
TEST(UUIDTest,UUIDCollisionTest)43 TEST(UUIDTest, UUIDCollisionTest) {
44     // Generate some UUIDs and check that they do not collide.
45     // NOTE: if this test fails, it is not necessarily a bug. However, if it
46     // begins to fail often and on specific platforms, we should investigate
47     // the quality of our entropy on those systems.
48     stdx::unordered_set<UUID, UUID::Hash> uuids;
49     for (int i = 0; i < 10000; i++) {
50         ASSERT(uuids.emplace(UUID::gen()).second);
51     }
52 }
53 
TEST(UUIDTest,isUUIDStringTest)54 TEST(UUIDTest, isUUIDStringTest) {
55     // Several valid strings
56     ASSERT(UUID::isUUIDString("00000000-0000-4000-8000-000000000000"));
57     ASSERT(UUID::isUUIDString("01234567-9abc-4def-9012-3456789abcde"));
58     ASSERT(UUID::isUUIDString("dddddddd-eeee-4fff-aaaa-bbbbbbbbbbbb"));
59     ASSERT(UUID::isUUIDString("A9A9A9A9-BEDF-4DD9-B001-222345716283"));
60 
61     // No version or variant set
62     ASSERT(UUID::isUUIDString("00000000-0000-0000-0000-000000000000"));
63 
64     // Mixed casing is weird, but technically legal
65     ASSERT(UUID::isUUIDString("abcdefAB-CDEF-4000-AaAa-FDFfdffd9991"));
66 
67     // Wrong number of Hyphens
68     ASSERT(!UUID::isUUIDString("00000000-0000-4000-8000-0000000000-00"));
69     ASSERT(!UUID::isUUIDString("000000000000-4000-8000-000000000000"));
70     ASSERT(!UUID::isUUIDString("00000000000040008000000000000000"));
71 
72     // Hyphens in the wrong places
73     ASSERT(!UUID::isUUIDString("dddddd-ddeeee-4fff-aaaa-bbbbbbbbbbbb"));
74     ASSERT(!UUID::isUUIDString("ddddddd-deeee-4fff-aaaa-bbbbbbbbbbbb"));
75     ASSERT(!UUID::isUUIDString("d-d-d-dddddeeee4fffaaaa-bbbbbbbbbbbb"));
76 
77     // Illegal characters
78     ASSERT(!UUID::isUUIDString("samsamsa-sams-4sam-8sam-samsamsamsam"));
79 
80     // Too short
81     ASSERT(!UUID::isUUIDString("A9A9A9A9-BEDF-4DD9-B001"));
82     ASSERT(!UUID::isUUIDString("dddddddd-eeee-4fff-aaaa-bbbbbbbbbbb"));
83 
84     // Too long
85     ASSERT(!UUID::isUUIDString("01234567-9abc-4def-9012-3456789abcdef"));
86     ASSERT(!UUID::isUUIDString("0123004567-9abc-4def-9012-3456789abcdef0000"));
87 }
88 
TEST(UUIDTest,toAndFromString)89 TEST(UUIDTest, toAndFromString) {
90     // String -> UUID -> string
91     auto s1 = "00000000-0000-4000-8000-000000000000";
92     auto uuid1Res = UUID::parse(s1);
93     ASSERT_OK(uuid1Res);
94     auto uuid1 = uuid1Res.getValue();
95     ASSERT(UUID::isUUIDString(s1));
96     ASSERT(UUID::isUUIDString(uuid1.toString()));
97     ASSERT_EQUALS(uuid1.toString(), s1);
98 
99     // UUID -> string -> UUID
100     auto uuid2 = UUID::gen();
101     auto s2 = uuid2.toString();
102     ASSERT(UUID::isUUIDString(s2));
103 
104     auto uuid2FromStringRes = UUID::parse(s2);
105     ASSERT_OK(uuid2FromStringRes);
106     auto uuid2FromString = uuid2FromStringRes.getValue();
107     ASSERT_EQUALS(uuid2FromString, uuid2);
108     ASSERT_EQUALS(uuid2FromString.toString(), s2);
109 
110     // Two UUIDs constructed from the same string are equal
111     auto s3 = "01234567-9abc-4def-9012-3456789abcde";
112     auto uuid3Res = UUID::parse(s3);
113     auto uuid3AgainRes = UUID::parse(s3);
114     ASSERT_OK(uuid3Res);
115     ASSERT_OK(uuid3AgainRes);
116     ASSERT_EQUALS(uuid3Res.getValue(), uuid3AgainRes.getValue());
117 
118     // Two UUIDs constructed from differently cased string are equal
119     auto sLower = "00000000-aaaa-4000-8000-000000000000";
120     auto sUpper = "00000000-AAAA-4000-8000-000000000000";
121     auto uuidLowerRes = UUID::parse(sLower);
122     auto uuidUpperRes = UUID::parse(sUpper);
123     ASSERT_OK(uuidLowerRes);
124     ASSERT_OK(uuidUpperRes);
125     auto uuidLower = uuidLowerRes.getValue();
126     auto uuidUpper = uuidUpperRes.getValue();
127     ASSERT_EQUALS(uuidLower, uuidUpper);
128 
129     // Casing is not preserved on round trip, both become lowercase
130     ASSERT_EQUALS(uuidLower.toString(), uuidUpper.toString());
131     ASSERT_EQUALS(uuidLower.toString(), sLower);
132     ASSERT_EQUALS(uuidUpper.toString(), sLower);
133     ASSERT_NOT_EQUALS(uuidUpper.toString(), sUpper);
134 
135     // UUIDs constructed from different strings are not equal
136     auto s4 = "01234567-9abc-4def-9012-3456789abcde";
137     auto s5 = "01234567-0000-4def-9012-3456789abcde";
138     ASSERT_NOT_EQUALS(UUID::parse(s4).getValue(), UUID::parse(s5).getValue());
139 
140     // UUIDs cannot be constructed from invalid strings
141     ASSERT_EQUALS(ErrorCodes::InvalidUUID, UUID::parse("00000000000040008000000000000000"));
142     ASSERT_EQUALS(ErrorCodes::InvalidUUID, UUID::parse("d-d-d-dddddeeee4fffaaaa-bbbbbbbbbbbb"));
143     ASSERT_EQUALS(ErrorCodes::InvalidUUID, UUID::parse("samsamsa-sams-4sam-8sam-samsamsamsam"));
144 }
145 
TEST(UUIDTest,toAndFromCDR)146 TEST(UUIDTest, toAndFromCDR) {
147     UUID uuid = UUID::gen();
148     ConstDataRange cdr = uuid.toCDR();
149     UUID roundtripped = UUID::fromCDR(cdr);
150     ASSERT_EQUALS(roundtripped, uuid);
151 }
152 
TEST(UUIDTest,toAndFromBSON)153 TEST(UUIDTest, toAndFromBSON) {
154     // UUID -> BSON -> UUID
155     UUID uuid = UUID::gen();
156     auto uuidBSON = uuid.toBSON();
157     auto uuid2Res = UUID::parse(uuidBSON.getField("uuid"));
158     ASSERT_OK(uuid2Res);
159     auto uuid2 = uuid2Res.getValue();
160     ASSERT_EQUALS(uuid, uuid2Res);
161 
162     // BSON -> UUID -> BSON
163     uint8_t uuidBytes[] = {0, 0, 0, 0, 0, 0, 0x40, 0, 0x80, 0, 0, 0, 0, 0, 0, 0};
164     auto bson = BSON("uuid" << BSONBinData(uuidBytes, 16, newUUID));
165     auto uuidFromBSON = UUID::parse(bson.getField("uuid"));
166     ASSERT_OK(uuidFromBSON);
167 
168     auto uuidBSON2 = uuid2.toBSON();
169     ASSERT_EQUALS(uuidBSON.woCompare(uuidBSON2), 0);
170 
171     // UUIDs cannot be constructed from invalid BSON elements
172     auto bson2 = BSON("uuid"
173                       << "sam");
174     ASSERT_EQUALS(ErrorCodes::InvalidUUID, UUID::parse(bson2.getField("uuid")));
175     auto bson3 = BSON("uuid"
176                       << "dddddddd-eeee-4fff-aaaa-bbbbbbbbbbbb");
177     ASSERT_EQUALS(ErrorCodes::InvalidUUID, UUID::parse(bson3.getField("uuid")));
178     auto bson4 = BSON("uuid" << 14);
179     ASSERT_EQUALS(ErrorCodes::InvalidUUID, UUID::parse(bson4.getField("uuid")));
180 }
181 
TEST(UUIDTest,toBSONUsingBSONMacro)182 TEST(UUIDTest, toBSONUsingBSONMacro) {
183     auto uuid = UUID::gen();
184     auto bson = BSON("myuuid" << uuid);
185 
186     BSONObjBuilder bob;
187     uuid.appendToBuilder(&bob, "myuuid");
188     auto expectedBson = bob.obj();
189 
190     ASSERT_BSONOBJ_EQ(expectedBson, bson);
191 }
192 
193 }  // namespace
194 }  // namespace mongo
195