FSTLevelDBKeyTests.mm 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. /*
  2. * Copyright 2017 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import "Firestore/Source/Local/FSTLevelDBKey.h"
  17. #import <XCTest/XCTest.h>
  18. #import "Firestore/Source/Model/FSTPath.h"
  19. #import "Firestore/Example/Tests/Util/FSTHelpers.h"
  20. NS_ASSUME_NONNULL_BEGIN
  21. @interface FSTLevelDBKeyTests : XCTestCase
  22. @end
  23. // I can't believe I have to write this...
  24. bool StartsWith(const std::string &value, const std::string &prefix) {
  25. return prefix.size() <= value.size() && std::equal(prefix.begin(), prefix.end(), value.begin());
  26. }
  27. static std::string RemoteDocKey(NSString *pathString) {
  28. return [FSTLevelDBRemoteDocumentKey keyWithDocumentKey:FSTTestDocKey(pathString)];
  29. }
  30. static std::string RemoteDocKeyPrefix(NSString *pathString) {
  31. return [FSTLevelDBRemoteDocumentKey keyPrefixWithResourcePath:FSTTestPath(pathString)];
  32. }
  33. static std::string DocMutationKey(NSString *userID, NSString *key, FSTBatchID batchID) {
  34. return [FSTLevelDBDocumentMutationKey keyWithUserID:userID
  35. documentKey:FSTTestDocKey(key)
  36. batchID:batchID];
  37. }
  38. static std::string TargetDocKey(FSTTargetID targetID, NSString *key) {
  39. return [FSTLevelDBTargetDocumentKey keyWithTargetID:targetID documentKey:FSTTestDocKey(key)];
  40. }
  41. static std::string DocTargetKey(NSString *key, FSTTargetID targetID) {
  42. return [FSTLevelDBDocumentTargetKey keyWithDocumentKey:FSTTestDocKey(key) targetID:targetID];
  43. }
  44. /**
  45. * Asserts that the description for given key is equal to the expected description.
  46. *
  47. * @param key A StringView of a textual key
  48. * @param key An NSString that [FSTLevelDBKey descriptionForKey:] is expected to produce.
  49. */
  50. #define FSTAssertExpectedKeyDescription(key, expectedDescription) \
  51. XCTAssertEqualObjects([FSTLevelDBKey descriptionForKey:(key)], (expectedDescription))
  52. #define FSTAssertKeyLessThan(left, right) \
  53. do { \
  54. std::string leftKey = (left); \
  55. std::string rightKey = (right); \
  56. XCTAssertLessThan(leftKey.compare(right), 0, @"Expected %@ to be less than %@", \
  57. [FSTLevelDBKey descriptionForKey:leftKey], \
  58. [FSTLevelDBKey descriptionForKey:rightKey]); \
  59. } while (0)
  60. @implementation FSTLevelDBKeyTests
  61. - (void)testMutationKeyPrefixing {
  62. auto tableKey = [FSTLevelDBMutationKey keyPrefix];
  63. auto emptyUserKey = [FSTLevelDBMutationKey keyPrefixWithUserID:""];
  64. auto fooUserKey = [FSTLevelDBMutationKey keyPrefixWithUserID:"foo"];
  65. auto foo2Key = [FSTLevelDBMutationKey keyWithUserID:"foo" batchID:2];
  66. XCTAssertTrue(StartsWith(emptyUserKey, tableKey));
  67. // This is critical: prefixes of the a value don't convert into prefixes of the key.
  68. XCTAssertTrue(StartsWith(fooUserKey, tableKey));
  69. XCTAssertFalse(StartsWith(fooUserKey, emptyUserKey));
  70. // However whole segments in common are prefixes.
  71. XCTAssertTrue(StartsWith(foo2Key, tableKey));
  72. XCTAssertTrue(StartsWith(foo2Key, fooUserKey));
  73. }
  74. - (void)testMutationKeyEncodeDecodeCycle {
  75. FSTLevelDBMutationKey *key = [[FSTLevelDBMutationKey alloc] init];
  76. std::string user("foo");
  77. NSArray<NSNumber *> *batchIds = @[ @0, @1, @100, @(INT_MAX - 1), @(INT_MAX) ];
  78. for (NSNumber *batchIDNumber in batchIds) {
  79. FSTBatchID batchID = [batchIDNumber intValue];
  80. auto encoded = [FSTLevelDBMutationKey keyWithUserID:user batchID:batchID];
  81. BOOL ok = [key decodeKey:encoded];
  82. XCTAssertTrue(ok);
  83. XCTAssertEqual(key.userID, user);
  84. XCTAssertEqual(key.batchID, batchID);
  85. }
  86. }
  87. - (void)testMutationKeyDescription {
  88. FSTAssertExpectedKeyDescription([FSTLevelDBMutationKey keyPrefix], @"[mutation: incomplete key]");
  89. FSTAssertExpectedKeyDescription([FSTLevelDBMutationKey keyPrefixWithUserID:@"user1"],
  90. @"[mutation: userID=user1 incomplete key]");
  91. auto key = [FSTLevelDBMutationKey keyWithUserID:@"user1" batchID:42];
  92. FSTAssertExpectedKeyDescription(key, @"[mutation: userID=user1 batchID=42]");
  93. FSTAssertExpectedKeyDescription(key + " extra",
  94. @"[mutation: userID=user1 batchID=42 invalid "
  95. @"key=<hW11dGF0aW9uAAGNdXNlcjEAAYqqgCBleHRyYQ==>]");
  96. // Truncate the key so that it's missing its terminator.
  97. key.resize(key.size() - 1);
  98. FSTAssertExpectedKeyDescription(key, @"[mutation: userID=user1 batchID=42 incomplete key]");
  99. }
  100. - (void)testDocumentMutationKeyPrefixing {
  101. auto tableKey = [FSTLevelDBDocumentMutationKey keyPrefix];
  102. auto emptyUserKey = [FSTLevelDBDocumentMutationKey keyPrefixWithUserID:""];
  103. auto fooUserKey = [FSTLevelDBDocumentMutationKey keyPrefixWithUserID:"foo"];
  104. FSTDocumentKey *documentKey = FSTTestDocKey(@"foo/bar");
  105. auto foo2Key =
  106. [FSTLevelDBDocumentMutationKey keyWithUserID:"foo" documentKey:documentKey batchID:2];
  107. XCTAssertTrue(StartsWith(emptyUserKey, tableKey));
  108. // While we want a key with whole segments in common be considered a prefix it's vital that
  109. // partial segments in common not be prefixes.
  110. XCTAssertTrue(StartsWith(fooUserKey, tableKey));
  111. // Here even though "" is a prefix of "foo" that prefix is within a segment so keys derived from
  112. // those segments cannot be prefixes of each other.
  113. XCTAssertFalse(StartsWith(fooUserKey, emptyUserKey));
  114. XCTAssertFalse(StartsWith(emptyUserKey, fooUserKey));
  115. // However whole segments in common are prefixes.
  116. XCTAssertTrue(StartsWith(foo2Key, tableKey));
  117. XCTAssertTrue(StartsWith(foo2Key, fooUserKey));
  118. }
  119. - (void)testDocumentMutationKeyEncodeDecodeCycle {
  120. FSTLevelDBDocumentMutationKey *key = [[FSTLevelDBDocumentMutationKey alloc] init];
  121. std::string user("foo");
  122. NSArray<FSTDocumentKey *> *documentKeys = @[ FSTTestDocKey(@"a/b"), FSTTestDocKey(@"a/b/c/d") ];
  123. NSArray<NSNumber *> *batchIds = @[ @0, @1, @100, @(INT_MAX - 1), @(INT_MAX) ];
  124. for (NSNumber *batchIDNumber in batchIds) {
  125. for (FSTDocumentKey *documentKey in documentKeys) {
  126. FSTBatchID batchID = [batchIDNumber intValue];
  127. auto encoded = [FSTLevelDBDocumentMutationKey keyWithUserID:user
  128. documentKey:documentKey
  129. batchID:batchID];
  130. BOOL ok = [key decodeKey:encoded];
  131. XCTAssertTrue(ok);
  132. XCTAssertEqual(key.userID, user);
  133. XCTAssertEqualObjects(key.documentKey, documentKey);
  134. XCTAssertEqual(key.batchID, batchID);
  135. }
  136. }
  137. }
  138. - (void)testDocumentMutationKeyOrdering {
  139. // Different user:
  140. FSTAssertKeyLessThan(DocMutationKey(@"1", @"foo/bar", 0), DocMutationKey(@"10", @"foo/bar", 0));
  141. FSTAssertKeyLessThan(DocMutationKey(@"1", @"foo/bar", 0), DocMutationKey(@"2", @"foo/bar", 0));
  142. // Different paths:
  143. FSTAssertKeyLessThan(DocMutationKey(@"1", @"foo/bar", 0), DocMutationKey(@"1", @"foo/baz", 0));
  144. FSTAssertKeyLessThan(DocMutationKey(@"1", @"foo/bar", 0), DocMutationKey(@"1", @"foo/bar2", 0));
  145. FSTAssertKeyLessThan(DocMutationKey(@"1", @"foo/bar", 0),
  146. DocMutationKey(@"1", @"foo/bar/suffix/key", 0));
  147. FSTAssertKeyLessThan(DocMutationKey(@"1", @"foo/bar/suffix/key", 0),
  148. DocMutationKey(@"1", @"foo/bar2", 0));
  149. // Different batchID:
  150. FSTAssertKeyLessThan(DocMutationKey(@"1", @"foo/bar", 0), DocMutationKey(@"1", @"foo/bar", 1));
  151. }
  152. - (void)testDocumentMutationKeyDescription {
  153. FSTAssertExpectedKeyDescription([FSTLevelDBDocumentMutationKey keyPrefix],
  154. @"[document_mutation: incomplete key]");
  155. FSTAssertExpectedKeyDescription([FSTLevelDBDocumentMutationKey keyPrefixWithUserID:@"user1"],
  156. @"[document_mutation: userID=user1 incomplete key]");
  157. auto key = [FSTLevelDBDocumentMutationKey keyPrefixWithUserID:@"user1"
  158. resourcePath:FSTTestPath(@"foo/bar")];
  159. FSTAssertExpectedKeyDescription(key,
  160. @"[document_mutation: userID=user1 key=foo/bar incomplete key]");
  161. key = [FSTLevelDBDocumentMutationKey keyWithUserID:@"user1"
  162. documentKey:FSTTestDocKey(@"foo/bar")
  163. batchID:42];
  164. FSTAssertExpectedKeyDescription(key, @"[document_mutation: userID=user1 key=foo/bar batchID=42]");
  165. }
  166. - (void)testTargetGlobalKeyEncodeDecodeCycle {
  167. FSTLevelDBTargetGlobalKey *key = [[FSTLevelDBTargetGlobalKey alloc] init];
  168. auto encoded = [FSTLevelDBTargetGlobalKey key];
  169. BOOL ok = [key decodeKey:encoded];
  170. XCTAssertTrue(ok);
  171. }
  172. - (void)testTargetGlobalKeyDescription {
  173. FSTAssertExpectedKeyDescription([FSTLevelDBTargetGlobalKey key], @"[target_global:]");
  174. }
  175. - (void)testTargetKeyEncodeDecodeCycle {
  176. FSTLevelDBTargetKey *key = [[FSTLevelDBTargetKey alloc] init];
  177. FSTTargetID targetID = 42;
  178. auto encoded = [FSTLevelDBTargetKey keyWithTargetID:42];
  179. BOOL ok = [key decodeKey:encoded];
  180. XCTAssertTrue(ok);
  181. XCTAssertEqual(key.targetID, targetID);
  182. }
  183. - (void)testTargetKeyDescription {
  184. FSTAssertExpectedKeyDescription([FSTLevelDBTargetKey keyWithTargetID:42],
  185. @"[target: targetID=42]");
  186. }
  187. - (void)testQueryTargetKeyEncodeDecodeCycle {
  188. FSTLevelDBQueryTargetKey *key = [[FSTLevelDBQueryTargetKey alloc] init];
  189. std::string canonicalID("foo");
  190. FSTTargetID targetID = 42;
  191. auto encoded = [FSTLevelDBQueryTargetKey keyWithCanonicalID:canonicalID targetID:42];
  192. BOOL ok = [key decodeKey:encoded];
  193. XCTAssertTrue(ok);
  194. XCTAssertEqual(key.canonicalID, canonicalID);
  195. XCTAssertEqual(key.targetID, targetID);
  196. }
  197. - (void)testQueryKeyDescription {
  198. FSTAssertExpectedKeyDescription([FSTLevelDBQueryTargetKey keyWithCanonicalID:"foo" targetID:42],
  199. @"[query_target: canonicalID=foo targetID=42]");
  200. }
  201. - (void)testTargetDocumentKeyEncodeDecodeCycle {
  202. FSTLevelDBTargetDocumentKey *key = [[FSTLevelDBTargetDocumentKey alloc] init];
  203. auto encoded =
  204. [FSTLevelDBTargetDocumentKey keyWithTargetID:42 documentKey:FSTTestDocKey(@"foo/bar")];
  205. BOOL ok = [key decodeKey:encoded];
  206. XCTAssertTrue(ok);
  207. XCTAssertEqual(key.targetID, 42);
  208. XCTAssertEqualObjects(key.documentKey, FSTTestDocKey(@"foo/bar"));
  209. }
  210. - (void)testTargetDocumentKeyOrdering {
  211. // Different targetID:
  212. FSTAssertKeyLessThan(TargetDocKey(1, @"foo/bar"), TargetDocKey(2, @"foo/bar"));
  213. FSTAssertKeyLessThan(TargetDocKey(2, @"foo/bar"), TargetDocKey(10, @"foo/bar"));
  214. FSTAssertKeyLessThan(TargetDocKey(10, @"foo/bar"), TargetDocKey(100, @"foo/bar"));
  215. FSTAssertKeyLessThan(TargetDocKey(42, @"foo/bar"), TargetDocKey(100, @"foo/bar"));
  216. // Different paths:
  217. FSTAssertKeyLessThan(TargetDocKey(1, @"foo/bar"), TargetDocKey(1, @"foo/baz"));
  218. FSTAssertKeyLessThan(TargetDocKey(1, @"foo/bar"), TargetDocKey(1, @"foo/bar2"));
  219. FSTAssertKeyLessThan(TargetDocKey(1, @"foo/bar"), TargetDocKey(1, @"foo/bar/suffix/key"));
  220. FSTAssertKeyLessThan(TargetDocKey(1, @"foo/bar/suffix/key"), TargetDocKey(1, @"foo/bar2"));
  221. }
  222. - (void)testTargetDocumentKeyDescription {
  223. auto key = [FSTLevelDBTargetDocumentKey keyWithTargetID:42 documentKey:FSTTestDocKey(@"foo/bar")];
  224. XCTAssertEqualObjects([FSTLevelDBKey descriptionForKey:key],
  225. @"[target_document: targetID=42 key=foo/bar]");
  226. }
  227. - (void)testDocumentTargetKeyEncodeDecodeCycle {
  228. FSTLevelDBDocumentTargetKey *key = [[FSTLevelDBDocumentTargetKey alloc] init];
  229. auto encoded =
  230. [FSTLevelDBDocumentTargetKey keyWithDocumentKey:FSTTestDocKey(@"foo/bar") targetID:42];
  231. BOOL ok = [key decodeKey:encoded];
  232. XCTAssertTrue(ok);
  233. XCTAssertEqualObjects(key.documentKey, FSTTestDocKey(@"foo/bar"));
  234. XCTAssertEqual(key.targetID, 42);
  235. }
  236. - (void)testDocumentTargetKeyDescription {
  237. auto key = [FSTLevelDBDocumentTargetKey keyWithDocumentKey:FSTTestDocKey(@"foo/bar") targetID:42];
  238. XCTAssertEqualObjects([FSTLevelDBKey descriptionForKey:key],
  239. @"[document_target: key=foo/bar targetID=42]");
  240. }
  241. - (void)testDocumentTargetKeyOrdering {
  242. // Different paths:
  243. FSTAssertKeyLessThan(DocTargetKey(@"foo/bar", 1), DocTargetKey(@"foo/baz", 1));
  244. FSTAssertKeyLessThan(DocTargetKey(@"foo/bar", 1), DocTargetKey(@"foo/bar2", 1));
  245. FSTAssertKeyLessThan(DocTargetKey(@"foo/bar", 1), DocTargetKey(@"foo/bar/suffix/key", 1));
  246. FSTAssertKeyLessThan(DocTargetKey(@"foo/bar/suffix/key", 1), DocTargetKey(@"foo/bar2", 1));
  247. // Different targetID:
  248. FSTAssertKeyLessThan(DocTargetKey(@"foo/bar", 1), DocTargetKey(@"foo/bar", 2));
  249. FSTAssertKeyLessThan(DocTargetKey(@"foo/bar", 2), DocTargetKey(@"foo/bar", 10));
  250. FSTAssertKeyLessThan(DocTargetKey(@"foo/bar", 10), DocTargetKey(@"foo/bar", 100));
  251. FSTAssertKeyLessThan(DocTargetKey(@"foo/bar", 42), DocTargetKey(@"foo/bar", 100));
  252. }
  253. - (void)testRemoteDocumentKeyPrefixing {
  254. auto tableKey = [FSTLevelDBRemoteDocumentKey keyPrefix];
  255. XCTAssertTrue(StartsWith(RemoteDocKey(@"foo/bar"), tableKey));
  256. // This is critical: foo/bar2 should not contain foo/bar.
  257. XCTAssertFalse(StartsWith(RemoteDocKey(@"foo/bar2"), RemoteDocKey(@"foo/bar")));
  258. // Prefixes must be encoded specially
  259. XCTAssertFalse(StartsWith(RemoteDocKey(@"foo/bar/baz/quu"), RemoteDocKey(@"foo/bar")));
  260. XCTAssertTrue(StartsWith(RemoteDocKey(@"foo/bar/baz/quu"), RemoteDocKeyPrefix(@"foo/bar")));
  261. XCTAssertTrue(StartsWith(RemoteDocKeyPrefix(@"foo/bar/baz/quu"), RemoteDocKeyPrefix(@"foo/bar")));
  262. XCTAssertTrue(StartsWith(RemoteDocKeyPrefix(@"foo/bar/baz"), RemoteDocKeyPrefix(@"foo/bar")));
  263. XCTAssertTrue(StartsWith(RemoteDocKeyPrefix(@"foo/bar"), RemoteDocKeyPrefix(@"foo")));
  264. }
  265. - (void)testRemoteDocumentKeyOrdering {
  266. FSTAssertKeyLessThan(RemoteDocKey(@"foo/bar"), RemoteDocKey(@"foo/bar2"));
  267. FSTAssertKeyLessThan(RemoteDocKey(@"foo/bar"), RemoteDocKey(@"foo/bar/suffix/key"));
  268. }
  269. - (void)testRemoteDocumentKeyEncodeDecodeCycle {
  270. FSTLevelDBRemoteDocumentKey *key = [[FSTLevelDBRemoteDocumentKey alloc] init];
  271. NSArray<NSString *> *paths = @[ @"foo/bar", @"foo/bar2", @"foo/bar/baz/quux" ];
  272. for (NSString *path in paths) {
  273. auto encoded = RemoteDocKey(path);
  274. BOOL ok = [key decodeKey:encoded];
  275. XCTAssertTrue(ok);
  276. XCTAssertEqualObjects(key.documentKey, FSTTestDocKey(path));
  277. }
  278. }
  279. - (void)testRemoteDocumentKeyDescription {
  280. FSTAssertExpectedKeyDescription(
  281. [FSTLevelDBRemoteDocumentKey keyWithDocumentKey:FSTTestDocKey(@"foo/bar/baz/quux")],
  282. @"[remote_document: key=foo/bar/baz/quux]");
  283. }
  284. @end
  285. #undef FSTAssertExpectedKeyDescription
  286. NS_ASSUME_NONNULL_END