FSTLevelDBKeyTests.mm 15 KB

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