RLMObjectServerTests.mm 112 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2016 Realm Inc.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. //
  17. ////////////////////////////////////////////////////////////////////////////
  18. #import "RLMSyncTestCase.h"
  19. #import "RLMTestUtils.h"
  20. #import "RLMSyncSessionRefreshHandle+ObjectServerTests.h"
  21. #import "RLMSyncUser+ObjectServerTests.h"
  22. #import "RLMSyncUtil_Private.h"
  23. #import "RLMRealm+Sync.h"
  24. #import "RLMRealmConfiguration_Private.h"
  25. #import "RLMRealmUtil.hpp"
  26. #import "RLMRealm_Dynamic.h"
  27. #pragma mark - Test objects
  28. @interface PartialSyncObjectA : RLMObject
  29. @property NSInteger number;
  30. @property NSString *string;
  31. + (instancetype)objectWithNumber:(NSInteger)number string:(NSString *)string;
  32. @end
  33. @interface PartialSyncObjectB : RLMObject
  34. @property NSInteger number;
  35. @property NSString *firstString;
  36. @property NSString *secondString;
  37. + (instancetype)objectWithNumber:(NSInteger)number firstString:(NSString *)first secondString:(NSString *)second;
  38. @end
  39. @implementation PartialSyncObjectA
  40. + (instancetype)objectWithNumber:(NSInteger)number string:(NSString *)string {
  41. PartialSyncObjectA *object = [[PartialSyncObjectA alloc] init];
  42. object.number = number;
  43. object.string = string;
  44. return object;
  45. }
  46. @end
  47. @implementation PartialSyncObjectB
  48. + (instancetype)objectWithNumber:(NSInteger)number firstString:(NSString *)first secondString:(NSString *)second {
  49. PartialSyncObjectB *object = [[PartialSyncObjectB alloc] init];
  50. object.number = number;
  51. object.firstString = first;
  52. object.secondString = second;
  53. return object;
  54. }
  55. @end
  56. @implementation PersonObject
  57. + (NSDictionary *)linkingObjectsProperties {
  58. return @{@"parents": [RLMPropertyDescriptor descriptorWithClass:PersonObject.class propertyName:@"children"]};
  59. }
  60. @end
  61. @interface RLMObjectServerTests : RLMSyncTestCase
  62. @end
  63. @implementation RLMObjectServerTests
  64. #pragma mark - Authentication and Tokens
  65. /// Valid username/password credentials should be able to log in a user. Using the same credentials should return the
  66. /// same user object.
  67. - (void)testUsernamePasswordAuthentication {
  68. RLMSyncUser *firstUser = [self logInUserForCredentials:[RLMSyncTestCase basicCredentialsWithName:NSStringFromSelector(_cmd)
  69. register:YES]
  70. server:[RLMSyncTestCase authServerURL]];
  71. RLMSyncUser *secondUser = [self logInUserForCredentials:[RLMSyncTestCase basicCredentialsWithName:NSStringFromSelector(_cmd)
  72. register:NO]
  73. server:[RLMSyncTestCase authServerURL]];
  74. // Two users created with the same credential should resolve to the same actual user.
  75. XCTAssertTrue([firstUser.identity isEqualToString:secondUser.identity]);
  76. // Authentication server property should be properly set.
  77. XCTAssertEqualObjects(firstUser.authenticationServer, [RLMSyncTestCase authServerURL]);
  78. XCTAssertFalse(firstUser.isAdmin);
  79. }
  80. /// A valid admin token should be able to log in a user.
  81. - (void)testAdminTokenAuthentication {
  82. RLMSyncCredentials *credentials = [RLMSyncCredentials credentialsWithAccessToken:self.adminToken identity:@"test"];
  83. XCTAssertNotNil(credentials);
  84. RLMSyncUser *user = [self logInUserForCredentials:credentials server:[RLMObjectServerTests authServerURL]];
  85. XCTAssertTrue(user.isAdmin);
  86. }
  87. /// An invalid username/password credential should not be able to log in a user and a corresponding error should be generated.
  88. - (void)testInvalidPasswordAuthentication {
  89. [self logInUserForCredentials:[RLMSyncTestCase basicCredentialsWithName:NSStringFromSelector(_cmd) register:YES]
  90. server:[RLMSyncTestCase authServerURL]];
  91. RLMSyncCredentials *credentials = [RLMSyncCredentials credentialsWithUsername:NSStringFromSelector(_cmd)
  92. password:@"INVALID_PASSWORD"
  93. register:NO];
  94. XCTestExpectation *expectation = [self expectationWithDescription:@""];
  95. [RLMSyncUser logInWithCredentials:credentials
  96. authServerURL:[RLMObjectServerTests authServerURL]
  97. onCompletion:^(RLMSyncUser *user, NSError *error) {
  98. XCTAssertNil(user);
  99. XCTAssertNotNil(error);
  100. XCTAssertEqual(error.domain, RLMSyncAuthErrorDomain);
  101. XCTAssertEqual(error.code, RLMSyncAuthErrorInvalidCredential);
  102. XCTAssertNotNil(error.localizedDescription);
  103. [expectation fulfill];
  104. }];
  105. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  106. }
  107. /// A non-existsing user should not be able to log in and a corresponding error should be generated.
  108. - (void)testNonExistingUsernameAuthentication {
  109. RLMSyncCredentials *credentials = [RLMSyncTestCase basicCredentialsWithName:NSStringFromSelector(_cmd)
  110. register:NO];
  111. XCTestExpectation *expectation = [self expectationWithDescription:@""];
  112. [RLMSyncUser logInWithCredentials:credentials
  113. authServerURL:[RLMObjectServerTests authServerURL]
  114. onCompletion:^(RLMSyncUser *user, NSError *error) {
  115. XCTAssertNil(user);
  116. XCTAssertNotNil(error);
  117. XCTAssertEqual(error.domain, RLMSyncAuthErrorDomain);
  118. XCTAssertEqual(error.code, RLMSyncAuthErrorInvalidCredential);
  119. XCTAssertNotNil(error.localizedDescription);
  120. [expectation fulfill];
  121. }];
  122. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  123. }
  124. /// Registering a user with existing username should return corresponding error.
  125. - (void)testExistingUsernameRegistration {
  126. RLMSyncCredentials *credentials = [RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  127. register:YES];
  128. [self logInUserForCredentials:credentials server:[RLMSyncTestCase authServerURL]];
  129. XCTestExpectation *expectation = [self expectationWithDescription:@""];
  130. [RLMSyncUser logInWithCredentials:credentials
  131. authServerURL:[RLMObjectServerTests authServerURL]
  132. onCompletion:^(RLMSyncUser *user, NSError *error) {
  133. XCTAssertNil(user);
  134. XCTAssertNotNil(error);
  135. XCTAssertEqual(error.domain, RLMSyncAuthErrorDomain);
  136. XCTAssertEqual(error.code, RLMSyncAuthErrorInvalidCredential);
  137. XCTAssertNotNil(error.localizedDescription);
  138. [expectation fulfill];
  139. }];
  140. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  141. }
  142. /// Errors reported in RLMSyncManager.errorHandler shouldn't contain sync error domain errors as underlying error
  143. - (void)testSyncErrorHandlerErrorDomain {
  144. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  145. register:YES]
  146. server:[RLMObjectServerTests authServerURL]];
  147. XCTAssertNotNil(user);
  148. NSURL *realmURL = [NSURL URLWithString:@"realm://127.0.0.1:9080/THE_PATH_USER_DONT_HAVE_ACCESS_TO/test"];
  149. RLMRealmConfiguration *c = [user configurationWithURL:realmURL fullSynchronization:true];
  150. NSError *error = nil;
  151. __attribute__((objc_precise_lifetime)) RLMRealm *realm = [RLMRealm realmWithConfiguration:c error:&error];
  152. XCTAssertNil(error);
  153. XCTAssertTrue(realm.isEmpty);
  154. XCTestExpectation *expectation = [self expectationWithDescription:@""];
  155. [RLMSyncManager sharedManager].errorHandler = ^(__unused NSError *error,
  156. __unused RLMSyncSession *session) {
  157. XCTAssertTrue([error.domain isEqualToString:RLMSyncErrorDomain]);
  158. XCTAssertFalse([[error.userInfo[kRLMSyncUnderlyingErrorKey] domain] isEqualToString:RLMSyncErrorDomain]);
  159. [expectation fulfill];
  160. };
  161. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  162. }
  163. /// The pre-emptive token refresh subsystem should function, and properly refresh the token.
  164. - (void)testPreemptiveTokenRefresh {
  165. // Prepare the test.
  166. __block NSInteger refreshCount = 0;
  167. __block NSInteger errorCount = 0;
  168. [RLMSyncManager sharedManager].errorHandler = ^(__unused NSError *error,
  169. __unused RLMSyncSession *session) {
  170. errorCount++;
  171. };
  172. __block XCTestExpectation *ex;
  173. [RLMSyncSessionRefreshHandle calculateFireDateUsingTestLogic:YES
  174. blockOnRefreshCompletion:^(BOOL success) {
  175. XCTAssertTrue(success);
  176. refreshCount++;
  177. [ex fulfill];
  178. }];
  179. // Open the Realm.
  180. NSURL *url = REALM_URL();
  181. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  182. register:true]
  183. server:[RLMObjectServerTests authServerURL]];
  184. __attribute__((objc_precise_lifetime)) RLMRealm *realm = [self openRealmForURL:url user:user];
  185. ex = [self expectationWithDescription:@"Timer fired"];
  186. [self waitForExpectationsWithTimeout:10 handler:nil];
  187. XCTAssertTrue(errorCount == 0);
  188. XCTAssertTrue(refreshCount > 0);
  189. }
  190. #pragma mark - Users
  191. /// `[RLMSyncUser all]` should be updated once a user is logged in.
  192. - (void)testBasicUserPersistence {
  193. XCTAssertNil([RLMSyncUser currentUser]);
  194. XCTAssertEqual([[RLMSyncUser allUsers] count], 0U);
  195. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  196. register:YES]
  197. server:[RLMObjectServerTests authServerURL]];
  198. XCTAssertNotNil(user);
  199. XCTAssertEqual([[RLMSyncUser allUsers] count], 1U);
  200. XCTAssertEqualObjects([RLMSyncUser allUsers], @{user.identity: user});
  201. XCTAssertEqualObjects([RLMSyncUser currentUser], user);
  202. RLMSyncUser *user2 = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:[NSStringFromSelector(_cmd) stringByAppendingString:@"2"]
  203. register:YES]
  204. server:[RLMObjectServerTests authServerURL]];
  205. XCTAssertEqual([[RLMSyncUser allUsers] count], 2U);
  206. NSDictionary *dict2 = @{user.identity: user, user2.identity: user2};
  207. XCTAssertEqualObjects([RLMSyncUser allUsers], dict2);
  208. RLMAssertThrowsWithReasonMatching([RLMSyncUser currentUser], @"currentUser cannot be called if more that one valid, logged-in user exists");
  209. }
  210. /// `[RLMSyncUser currentUser]` should become nil if the user is logged out.
  211. - (void)testCurrentUserLogout {
  212. XCTAssertNil([RLMSyncUser currentUser]);
  213. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  214. register:YES]
  215. server:[RLMObjectServerTests authServerURL]];
  216. XCTAssertNotNil(user);
  217. XCTAssertEqualObjects([RLMSyncUser currentUser], user);
  218. [user logOut];
  219. XCTAssertNil([RLMSyncUser currentUser]);
  220. }
  221. /// A sync user should return a session when asked for it based on the path.
  222. - (void)testUserGetSessionForValidURL {
  223. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  224. register:YES]
  225. server:[RLMObjectServerTests authServerURL]];
  226. NSURL *url = REALM_URL();
  227. [self openRealmForURL:url user:user immediatelyBlock:^{
  228. RLMSyncSession *session = [user sessionForURL:url];
  229. XCTAssertNotNil(session);
  230. }];
  231. // Check session existence after binding.
  232. RLMSyncSession *session = [user sessionForURL:url];
  233. XCTAssertNotNil(session);
  234. }
  235. /// A sync user should return nil when asked for a URL that doesn't exist.
  236. - (void)testUserGetSessionForInvalidURL {
  237. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  238. register:YES]
  239. server:[RLMObjectServerTests authServerURL]];
  240. RLMSyncSession *badSession = [user sessionForURL:[NSURL URLWithString:@"realm://127.0.0.1:9080/noSuchRealm"]];
  241. XCTAssertNil(badSession);
  242. }
  243. /// A sync user should be able to successfully change their own password.
  244. - (void)testUserChangePassword {
  245. NSString *userName = NSStringFromSelector(_cmd);
  246. NSString *firstPassword = @"a";
  247. NSString *secondPassword = @"b";
  248. // Successfully create user, change its password, log out,
  249. // then fail to change password again due to being logged out.
  250. {
  251. RLMSyncCredentials *creds = [RLMSyncCredentials credentialsWithUsername:userName password:firstPassword
  252. register:YES];
  253. RLMSyncUser *user = [self logInUserForCredentials:creds
  254. server:[RLMObjectServerTests authServerURL]];
  255. XCTestExpectation *ex = [self expectationWithDescription:@"change password callback invoked"];
  256. [user changePassword:secondPassword completion:^(NSError * _Nullable error) {
  257. XCTAssertNil(error);
  258. [ex fulfill];
  259. }];
  260. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  261. [user logOut];
  262. ex = [self expectationWithDescription:@"change password callback invoked"];
  263. [user changePassword:@"fail" completion:^(NSError * _Nullable error) {
  264. XCTAssertNotNil(error);
  265. [ex fulfill];
  266. }];
  267. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  268. }
  269. // Fail to log in with original password.
  270. {
  271. RLMSyncCredentials *creds = [RLMSyncCredentials credentialsWithUsername:userName password:firstPassword
  272. register:NO];
  273. XCTestExpectation *ex = [self expectationWithDescription:@"login callback invoked"];
  274. [RLMSyncUser logInWithCredentials:creds
  275. authServerURL:[RLMObjectServerTests authServerURL]
  276. onCompletion:^(RLMSyncUser *user, NSError *error) {
  277. XCTAssertNil(user);
  278. XCTAssertNotNil(error);
  279. XCTAssertEqual(error.domain, RLMSyncAuthErrorDomain);
  280. XCTAssertEqual(error.code, RLMSyncAuthErrorInvalidCredential);
  281. XCTAssertNotNil(error.localizedDescription);
  282. [ex fulfill];
  283. }];
  284. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  285. }
  286. // Successfully log in with new password.
  287. {
  288. RLMSyncCredentials *creds = [RLMSyncCredentials credentialsWithUsername:userName password:secondPassword
  289. register:NO];
  290. RLMSyncUser *user = [self logInUserForCredentials:creds server:[RLMObjectServerTests authServerURL]];
  291. XCTAssertNotNil(user);
  292. XCTAssertEqualObjects(RLMSyncUser.currentUser, user);
  293. [user logOut];
  294. XCTAssertNil(RLMSyncUser.currentUser);
  295. }
  296. }
  297. /// A sync admin user should be able to successfully change another user's password.
  298. - (void)testOtherUserChangePassword {
  299. // Create admin user.
  300. NSURL *url = [RLMObjectServerTests authServerURL];
  301. RLMSyncUser *adminUser = [self createAdminUserForURL:url username:[[NSUUID UUID] UUIDString]];
  302. NSString *username = NSStringFromSelector(_cmd);
  303. NSString *firstPassword = @"a";
  304. NSString *secondPassword = @"b";
  305. NSString *nonAdminUserID = nil;
  306. // Successfully create user.
  307. {
  308. RLMSyncCredentials *creds = [RLMSyncCredentials credentialsWithUsername:username
  309. password:firstPassword
  310. register:YES];
  311. RLMSyncUser *user = [self logInUserForCredentials:creds server:url];
  312. nonAdminUserID = user.identity;
  313. [user logOut];
  314. }
  315. // Fail to change password from non-admin user.
  316. {
  317. NSString *username2 = [NSString stringWithFormat:@"%@_2", username];
  318. RLMSyncCredentials *creds2 = [RLMSyncCredentials credentialsWithUsername:username2
  319. password:@"a"
  320. register:YES];
  321. RLMSyncUser *user2 = [self logInUserForCredentials:creds2 server:url];
  322. XCTestExpectation *ex = [self expectationWithDescription:@"change password callback invoked"];
  323. [user2 changePassword:@"foobar" forUserID:nonAdminUserID completion:^(NSError *error) {
  324. XCTAssertNotNil(error);
  325. [ex fulfill];
  326. }];
  327. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  328. }
  329. // Change password from admin user.
  330. {
  331. XCTestExpectation *ex = [self expectationWithDescription:@"change password callback invoked"];
  332. [adminUser changePassword:secondPassword forUserID:nonAdminUserID completion:^(NSError *error) {
  333. XCTAssertNil(error);
  334. [ex fulfill];
  335. }];
  336. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  337. }
  338. // Fail to log in with original password.
  339. {
  340. RLMSyncCredentials *creds = [RLMSyncCredentials credentialsWithUsername:username
  341. password:firstPassword
  342. register:NO];
  343. XCTestExpectation *ex = [self expectationWithDescription:@"login callback invoked"];
  344. [RLMSyncUser logInWithCredentials:creds
  345. authServerURL:[RLMObjectServerTests authServerURL]
  346. onCompletion:^(RLMSyncUser *user, NSError *error) {
  347. XCTAssertNil(user);
  348. XCTAssertNotNil(error);
  349. XCTAssertEqual(error.domain, RLMSyncAuthErrorDomain);
  350. XCTAssertEqual(error.code, RLMSyncAuthErrorInvalidCredential);
  351. XCTAssertNotNil(error.localizedDescription);
  352. [ex fulfill];
  353. }];
  354. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  355. }
  356. // Successfully log in with new password.
  357. {
  358. RLMSyncCredentials *creds = [RLMSyncCredentials credentialsWithUsername:username
  359. password:secondPassword
  360. register:NO];
  361. RLMSyncUser *user = [self logInUserForCredentials:creds server:[RLMObjectServerTests authServerURL]];
  362. XCTAssertNotNil(user);
  363. [user logOut];
  364. }
  365. }
  366. - (void)testRequestPasswordResetForRegisteredUser {
  367. NSString *userName = [NSStringFromSelector(_cmd) stringByAppendingString:@"@example.com"];
  368. RLMSyncCredentials *creds = [RLMSyncCredentials credentialsWithUsername:userName password:@"a" register:YES];
  369. [[self logInUserForCredentials:creds server:[RLMObjectServerTests authServerURL]] logOut];
  370. XCTestExpectation *ex = [self expectationWithDescription:@"callback invoked"];
  371. [RLMSyncUser requestPasswordResetForAuthServer:[RLMObjectServerTests authServerURL] userEmail:userName completion:^(NSError *error) {
  372. XCTAssertNil(error);
  373. [ex fulfill];
  374. }];
  375. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  376. NSString *token = [self emailForAddress:userName];
  377. XCTAssertNotNil(token);
  378. // Use the password reset token
  379. ex = [self expectationWithDescription:@"callback invoked"];
  380. [RLMSyncUser completePasswordResetForAuthServer:[RLMObjectServerTests authServerURL] token:token password:@"new password"
  381. completion:^(NSError *error) {
  382. XCTAssertNil(error);
  383. [ex fulfill];
  384. }];
  385. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  386. // Should now be able to log in with the new password
  387. {
  388. RLMSyncCredentials *creds = [RLMSyncCredentials credentialsWithUsername:userName
  389. password:@"new password"
  390. register:NO];
  391. RLMSyncUser *user = [self logInUserForCredentials:creds server:[RLMObjectServerTests authServerURL]];
  392. XCTAssertNotNil(user);
  393. [user logOut];
  394. }
  395. // Reusing the token should fail
  396. ex = [self expectationWithDescription:@"callback invoked"];
  397. [RLMSyncUser completePasswordResetForAuthServer:[RLMObjectServerTests authServerURL] token:token password:@"new password 2"
  398. completion:^(NSError *error) {
  399. XCTAssertNotNil(error);
  400. [ex fulfill];
  401. }];
  402. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  403. }
  404. - (void)testRequestPasswordResetForNonexistentUser {
  405. NSString *userName = [NSStringFromSelector(_cmd) stringByAppendingString:@"@example.com"];
  406. XCTestExpectation *ex = [self expectationWithDescription:@"callback invoked"];
  407. [RLMSyncUser requestPasswordResetForAuthServer:[RLMObjectServerTests authServerURL] userEmail:userName completion:^(NSError *error) {
  408. // Not an error even though the user doesn't exist
  409. XCTAssertNil(error);
  410. [ex fulfill];
  411. }];
  412. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  413. // Should not have sent an email to the non-registered user
  414. XCTAssertNil([self emailForAddress:userName]);
  415. }
  416. - (void)testRequestPasswordResetWithBadAuthURL {
  417. NSString *userName = [NSStringFromSelector(_cmd) stringByAppendingString:@"@example.com"];
  418. XCTestExpectation *ex = [self expectationWithDescription:@"callback invoked"];
  419. NSURL *badAuthUrl = [[RLMObjectServerTests authServerURL] URLByAppendingPathComponent:@"/bad"];
  420. [RLMSyncUser requestPasswordResetForAuthServer:badAuthUrl userEmail:userName completion:^(NSError *error) {
  421. XCTAssertNotNil(error);
  422. XCTAssertEqualObjects(error.userInfo[@"statusCode"], @404);
  423. [ex fulfill];
  424. }];
  425. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  426. }
  427. - (void)testRequestConfirmEmailForRegisteredUser {
  428. NSString *userName = [NSStringFromSelector(_cmd) stringByAppendingString:@"@example.com"];
  429. RLMSyncCredentials *creds = [RLMSyncCredentials credentialsWithUsername:userName password:@"a" register:YES];
  430. [[self logInUserForCredentials:creds server:[RLMObjectServerTests authServerURL]] logOut];
  431. // This token is sent by ROS upon user registration
  432. NSString *registrationToken = [self emailForAddress:userName];
  433. XCTAssertNotNil(registrationToken);
  434. XCTestExpectation *ex = [self expectationWithDescription:@"callback invoked"];
  435. [RLMSyncUser requestEmailConfirmationForAuthServer:[RLMObjectServerTests authServerURL]
  436. userEmail:userName completion:^(NSError *error) {
  437. XCTAssertNil(error);
  438. [ex fulfill];
  439. }];
  440. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  441. // This token should have been created when requestEmailConfirmationForAuthServer was called
  442. NSString *token = [self emailForAddress:userName];
  443. XCTAssertNotNil(token);
  444. XCTAssertNotEqual(token, registrationToken);
  445. // Use the token
  446. ex = [self expectationWithDescription:@"callback invoked"];
  447. [RLMSyncUser confirmEmailForAuthServer:[RLMObjectServerTests authServerURL] token:token
  448. completion:^(NSError *error) {
  449. XCTAssertNil(error);
  450. [ex fulfill];
  451. }];
  452. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  453. // Reusing the token should fail
  454. ex = [self expectationWithDescription:@"callback invoked"];
  455. [RLMSyncUser confirmEmailForAuthServer:[RLMObjectServerTests authServerURL] token:token
  456. completion:^(NSError *error) {
  457. XCTAssertNotNil(error);
  458. [ex fulfill];
  459. }];
  460. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  461. }
  462. - (void)testRequestConfirmEmailForNonexistentUser {
  463. NSString *userName = [NSStringFromSelector(_cmd) stringByAppendingString:@"@example.com"];
  464. XCTestExpectation *ex = [self expectationWithDescription:@"callback invoked"];
  465. [RLMSyncUser requestEmailConfirmationForAuthServer:[RLMObjectServerTests authServerURL]
  466. userEmail:userName completion:^(NSError *error) {
  467. // Not an error even though the user doesn't exist
  468. XCTAssertNil(error);
  469. [ex fulfill];
  470. }];
  471. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  472. // Should not have sent an email to the non-registered user
  473. XCTAssertNil([self emailForAddress:userName]);
  474. }
  475. /// A sync admin user should be able to retrieve information about other users.
  476. - (void)testRetrieveUserInfo {
  477. NSString *nonAdminUsername = @"meela@realm.example.org";
  478. NSString *adminUsername = @"jyaku";
  479. NSString *pw = @"p";
  480. NSURL *server = [RLMObjectServerTests authServerURL];
  481. // Create a non-admin user.
  482. RLMSyncCredentials *c1 = [RLMSyncCredentials credentialsWithUsername:nonAdminUsername password:pw register:YES];
  483. RLMSyncUser *nonAdminUser = [self logInUserForCredentials:c1 server:server];
  484. // Create an admin user.
  485. __unused RLMSyncUser *adminUser = [self createAdminUserForURL:server username:adminUsername];
  486. // Create another admin user.
  487. RLMSyncUser *userDoingLookups = [self createAdminUserForURL:server username:[[NSUUID UUID] UUIDString]];
  488. // Get the non-admin user's info.
  489. XCTestExpectation *ex1 = [self expectationWithDescription:@"should be able to get info about non-admin user"];
  490. [userDoingLookups retrieveInfoForUser:nonAdminUsername
  491. identityProvider:RLMIdentityProviderUsernamePassword
  492. completion:^(RLMSyncUserInfo *info, NSError *err) {
  493. XCTAssertNil(err);
  494. XCTAssertNotNil(info);
  495. XCTAssertGreaterThan([info.accounts count], ((NSUInteger) 0));
  496. RLMSyncUserAccountInfo *acctInfo = [info.accounts firstObject];
  497. XCTAssertEqualObjects(acctInfo.providerUserIdentity, nonAdminUsername);
  498. XCTAssertEqualObjects(acctInfo.provider, RLMIdentityProviderUsernamePassword);
  499. XCTAssertFalse(info.isAdmin);
  500. [ex1 fulfill];
  501. }];
  502. [self waitForExpectationsWithTimeout:10 handler:nil];
  503. // Get the admin user's info.
  504. XCTestExpectation *ex2 = [self expectationWithDescription:@"should be able to get info about admin user"];
  505. [userDoingLookups retrieveInfoForUser:adminUsername
  506. identityProvider:RLMIdentityProviderDebug
  507. completion:^(RLMSyncUserInfo *info, NSError *err) {
  508. XCTAssertNil(err);
  509. XCTAssertNotNil(info);
  510. XCTAssertGreaterThan([info.accounts count], ((NSUInteger) 0));
  511. RLMSyncUserAccountInfo *acctInfo = [info.accounts firstObject];
  512. XCTAssertEqualObjects(acctInfo.providerUserIdentity, adminUsername);
  513. XCTAssertEqualObjects(acctInfo.provider, RLMIdentityProviderDebug);
  514. XCTAssertTrue(info.isAdmin);
  515. [ex2 fulfill];
  516. }];
  517. [self waitForExpectationsWithTimeout:10 handler:nil];
  518. // Get invalid user's info.
  519. XCTestExpectation *ex3 = [self expectationWithDescription:@"should fail for non-existent user"];
  520. [userDoingLookups retrieveInfoForUser:@"invalid_user@realm.example.org"
  521. identityProvider:RLMIdentityProviderUsernamePassword
  522. completion:^(RLMSyncUserInfo *info, NSError *err) {
  523. XCTAssertNotNil(err);
  524. XCTAssertEqualObjects(err.domain, RLMSyncAuthErrorDomain);
  525. XCTAssertEqual(err.code, RLMSyncAuthErrorUserDoesNotExist);
  526. XCTAssertNil(info);
  527. [ex3 fulfill];
  528. }];
  529. [self waitForExpectationsWithTimeout:10 handler:nil];
  530. // Get info using user without admin privileges.
  531. XCTestExpectation *ex4 = [self expectationWithDescription:@"should fail for user without admin privileges"];
  532. [nonAdminUser retrieveInfoForUser:adminUsername
  533. identityProvider:RLMIdentityProviderUsernamePassword
  534. completion:^(RLMSyncUserInfo *info, NSError *err) {
  535. XCTAssertNotNil(err);
  536. XCTAssertEqualObjects(err.domain, RLMSyncAuthErrorDomain);
  537. // FIXME: Shouldn't this be RLMSyncAuthErrorAccessDeniedOrInvalidPath?
  538. XCTAssertEqual(err.code, RLMSyncAuthErrorUserDoesNotExist);
  539. XCTAssertNil(info);
  540. [ex4 fulfill];
  541. }];
  542. [self waitForExpectationsWithTimeout:10 handler:nil];
  543. }
  544. /// The login queue argument should be respected.
  545. - (void)testLoginQueueForSuccessfulLogin {
  546. // Make global queue
  547. dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  548. RLMSyncCredentials *c1 = [RLMSyncCredentials credentialsWithUsername:[[NSUUID UUID] UUIDString]
  549. password:@"p"
  550. register:YES];
  551. XCTestExpectation *ex1 = [self expectationWithDescription:@"User logs in successfully on background queue"];
  552. [RLMSyncUser logInWithCredentials:c1
  553. authServerURL:[RLMObjectServerTests authServerURL]
  554. timeout:30.0
  555. callbackQueue:queue
  556. onCompletion:^(RLMSyncUser *user, __unused NSError *error) {
  557. XCTAssertNotNil(user);
  558. XCTAssertFalse([NSThread isMainThread]);
  559. [ex1 fulfill];
  560. }];
  561. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  562. RLMSyncCredentials *c2 = [RLMSyncCredentials credentialsWithUsername:[[NSUUID UUID] UUIDString]
  563. password:@"p"
  564. register:YES];
  565. XCTestExpectation *ex2 = [self expectationWithDescription:@"User logs in successfully on main queue"];
  566. [RLMSyncUser logInWithCredentials:c2
  567. authServerURL:[RLMObjectServerTests authServerURL]
  568. timeout:30.0
  569. callbackQueue:dispatch_get_main_queue()
  570. onCompletion:^(RLMSyncUser *user, __unused NSError *error) {
  571. XCTAssertNotNil(user);
  572. XCTAssertTrue([NSThread isMainThread]);
  573. [ex2 fulfill];
  574. }];
  575. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  576. }
  577. /// The login queue argument should be respected.
  578. - (void)testLoginQueueForFailedLogin {
  579. // Make global queue
  580. dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  581. RLMSyncCredentials *c1 = [RLMSyncCredentials credentialsWithUsername:[[NSUUID UUID] UUIDString]
  582. password:@"p"
  583. register:NO];
  584. XCTestExpectation *ex1 = [self expectationWithDescription:@"Error returned on background queue"];
  585. [RLMSyncUser logInWithCredentials:c1
  586. authServerURL:[RLMObjectServerTests authServerURL]
  587. timeout:30.0
  588. callbackQueue:queue
  589. onCompletion:^(__unused RLMSyncUser *user, NSError *error) {
  590. XCTAssertNotNil(error);
  591. XCTAssertFalse([NSThread isMainThread]);
  592. [ex1 fulfill];
  593. }];
  594. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  595. RLMSyncCredentials *c2 = [RLMSyncCredentials credentialsWithUsername:[[NSUUID UUID] UUIDString]
  596. password:@"p"
  597. register:NO];
  598. XCTestExpectation *ex2 = [self expectationWithDescription:@"Error returned on main queue"];
  599. [RLMSyncUser logInWithCredentials:c2
  600. authServerURL:[RLMObjectServerTests authServerURL]
  601. timeout:30.0
  602. callbackQueue:dispatch_get_main_queue()
  603. onCompletion:^(__unused RLMSyncUser *user, NSError *error) {
  604. XCTAssertNotNil(error);
  605. XCTAssertTrue([NSThread isMainThread]);
  606. [ex2 fulfill];
  607. }];
  608. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  609. }
  610. - (void)testUserExpirationCallback {
  611. NSString *username = NSStringFromSelector(_cmd);
  612. RLMSyncCredentials *credentials = [RLMSyncCredentials credentialsWithUsername:username
  613. password:@"a"
  614. register:YES];
  615. RLMSyncUser *user = [self logInUserForCredentials:credentials
  616. server:[RLMObjectServerTests authServerURL]];
  617. XCTestExpectation *ex = [self expectationWithDescription:@"callback should fire"];
  618. // Set a callback on the user
  619. __weak RLMSyncUser *weakUser = user;
  620. user.errorHandler = ^(RLMSyncUser *u, NSError *error) {
  621. XCTAssertEqualObjects(u.identity, weakUser.identity);
  622. // Make sure we get the right error.
  623. XCTAssertEqualObjects(error.domain, RLMSyncAuthErrorDomain);
  624. XCTAssertEqual(error.code, RLMSyncAuthErrorAccessDeniedOrInvalidPath);
  625. [ex fulfill];
  626. };
  627. // Screw up the token on the user using a debug API
  628. [self manuallySetRefreshTokenForUser:user value:@"not_a_real_refresh_token"];
  629. // Try to log in a Realm; this will cause our errorHandler block defined above to be fired.
  630. __attribute__((objc_precise_lifetime)) RLMRealm *r = [self immediatelyOpenRealmForURL:REALM_URL() user:user];
  631. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  632. XCTAssertTrue(user.state == RLMSyncUserStateLoggedOut);
  633. }
  634. #pragma mark - Basic Sync
  635. /// It should be possible to successfully open a Realm configured for sync with an access token.
  636. - (void)testOpenRealmWithAdminToken {
  637. // FIXME (tests): opening a Realm with the access token, then opening a Realm at the same virtual path
  638. // with normal credentials, causes Realms to fail to bind with a "bad virtual path" error.
  639. RLMSyncCredentials *credentials = [RLMSyncCredentials credentialsWithAccessToken:self.adminToken identity:@"test"];
  640. XCTAssertNotNil(credentials);
  641. RLMSyncUser *user = [self logInUserForCredentials:credentials
  642. server:[RLMObjectServerTests authServerURL]];
  643. NSURL *url = [NSURL URLWithString:@"realm://127.0.0.1:9080/testSyncWithAdminToken"];
  644. RLMRealmConfiguration *c = [user configurationWithURL:url fullSynchronization:YES];
  645. NSError *error = nil;
  646. RLMRealm *realm = [RLMRealm realmWithConfiguration:c error:&error];
  647. XCTAssertNil(error);
  648. XCTAssertTrue(realm.isEmpty);
  649. }
  650. /// It should be possible to successfully open a Realm configured for sync with a normal user.
  651. - (void)testOpenRealmWithNormalCredentials {
  652. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  653. register:YES]
  654. server:[RLMObjectServerTests authServerURL]];
  655. NSURL *url = REALM_URL();
  656. RLMRealm *realm = [self openRealmForURL:url user:user];
  657. XCTAssertTrue(realm.isEmpty);
  658. }
  659. /// If client B adds objects to a synced Realm, client A should see those objects.
  660. - (void)testAddObjects {
  661. NSURL *url = REALM_URL();
  662. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  663. register:self.isParent]
  664. server:[RLMObjectServerTests authServerURL]];
  665. RLMRealm *realm = [self openRealmForURL:url user:user];
  666. if (self.isParent) {
  667. CHECK_COUNT(0, SyncObject, realm);
  668. RLMRunChildAndWait();
  669. [self waitForDownloadsForUser:user realms:@[realm] realmURLs:@[url] expectedCounts:@[@3]];
  670. } else {
  671. // Add objects.
  672. [self addSyncObjectsToRealm:realm descriptions:@[@"child-1", @"child-2", @"child-3"]];
  673. [self waitForUploadsForRealm:realm];
  674. CHECK_COUNT(3, SyncObject, realm);
  675. }
  676. }
  677. /// If client B deletes objects from a synced Realm, client A should see the effects of that deletion.
  678. - (void)testDeleteObjects {
  679. NSURL *url = REALM_URL();
  680. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  681. register:self.isParent]
  682. server:[RLMObjectServerTests authServerURL]];
  683. RLMRealm *realm = [self openRealmForURL:url user:user];
  684. if (self.isParent) {
  685. // Add objects.
  686. [self addSyncObjectsToRealm:realm descriptions:@[@"parent-1", @"parent-2", @"parent-3"]];
  687. [self waitForUploadsForRealm:realm];
  688. CHECK_COUNT(3, SyncObject, realm);
  689. RLMRunChildAndWait();
  690. [self waitForDownloadsForRealm:realm];
  691. CHECK_COUNT(0, SyncObject, realm);
  692. } else {
  693. [self waitForDownloadsForRealm:realm];
  694. CHECK_COUNT(3, SyncObject, realm);
  695. [realm beginWriteTransaction];
  696. [realm deleteAllObjects];
  697. [realm commitWriteTransaction];
  698. [self waitForUploadsForRealm:realm];
  699. CHECK_COUNT(0, SyncObject, realm);
  700. }
  701. }
  702. #pragma mark - Encryption
  703. /// If client B encrypts its synced Realm, client A should be able to access that Realm with a different encryption key.
  704. - (void)testEncryptedSyncedRealm {
  705. NSURL *url = REALM_URL();
  706. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  707. register:self.isParent]
  708. server:[RLMObjectServerTests authServerURL]];
  709. NSData *key = RLMGenerateKey();
  710. RLMRealm *realm = [self openRealmForURL:url user:user encryptionKey:key
  711. stopPolicy:RLMSyncStopPolicyAfterChangesUploaded immediatelyBlock:nil];
  712. if (self.isParent) {
  713. CHECK_COUNT(0, SyncObject, realm);
  714. RLMRunChildAndWait();
  715. [self waitForDownloadsForUser:user realms:@[realm] realmURLs:@[url] expectedCounts:@[@3]];
  716. } else {
  717. // Add objects.
  718. [self addSyncObjectsToRealm:realm descriptions:@[@"child-1", @"child-2", @"child-3"]];
  719. [self waitForUploadsForRealm:realm];
  720. CHECK_COUNT(3, SyncObject, realm);
  721. }
  722. }
  723. /// If an encrypted synced Realm is re-opened with the wrong key, throw an exception.
  724. - (void)testEncryptedSyncedRealmWrongKey {
  725. NSURL *url = REALM_URL();
  726. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  727. register:self.isParent]
  728. server:[RLMObjectServerTests authServerURL]];
  729. if (self.isParent) {
  730. NSString *path;
  731. @autoreleasepool {
  732. RLMRealm *realm = [self openRealmForURL:url user:user encryptionKey:RLMGenerateKey()
  733. stopPolicy:RLMSyncStopPolicyImmediately immediatelyBlock:nil];
  734. path = realm.configuration.pathOnDisk;
  735. CHECK_COUNT(0, SyncObject, realm);
  736. RLMRunChildAndWait();
  737. [self waitForDownloadsForUser:user realms:@[realm] realmURLs:@[url] expectedCounts:@[@3]];
  738. }
  739. RLMRealmConfiguration *c = [RLMRealmConfiguration defaultConfiguration];
  740. c.fileURL = [NSURL fileURLWithPath:path];
  741. RLMAssertThrowsWithError([RLMRealm realmWithConfiguration:c error:nil],
  742. @"Unable to open a realm at path",
  743. RLMErrorFileAccess,
  744. @"invalid mnemonic");
  745. c.encryptionKey = RLMGenerateKey();
  746. RLMAssertThrowsWithError([RLMRealm realmWithConfiguration:c error:nil],
  747. @"Unable to open a realm at path",
  748. RLMErrorFileAccess,
  749. @"Realm file decryption failed");
  750. } else {
  751. RLMRealm *realm = [self openRealmForURL:url user:user encryptionKey:RLMGenerateKey()
  752. stopPolicy:RLMSyncStopPolicyImmediately immediatelyBlock:nil];
  753. [self addSyncObjectsToRealm:realm descriptions:@[@"child-1", @"child-2", @"child-3"]];
  754. [self waitForUploadsForRealm:realm];
  755. CHECK_COUNT(3, SyncObject, realm);
  756. }
  757. }
  758. #pragma mark - Multiple Realm Sync
  759. /// If a client opens multiple Realms, there should be one session object for each Realm that was opened.
  760. - (void)testMultipleRealmsSessions {
  761. NSURL *urlA = CUSTOM_REALM_URL(@"a");
  762. NSURL *urlB = CUSTOM_REALM_URL(@"b");
  763. NSURL *urlC = CUSTOM_REALM_URL(@"c");
  764. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  765. register:self.isParent]
  766. server:[RLMObjectServerTests authServerURL]];
  767. // Open three Realms.
  768. __attribute__((objc_precise_lifetime)) RLMRealm *realmealmA = [self openRealmForURL:urlA user:user];
  769. __attribute__((objc_precise_lifetime)) RLMRealm *realmealmB = [self openRealmForURL:urlB user:user];
  770. __attribute__((objc_precise_lifetime)) RLMRealm *realmealmC = [self openRealmForURL:urlC user:user];
  771. // Make sure there are three active sessions for the user.
  772. XCTAssert(user.allSessions.count == 3, @"Expected 3 sessions, but didn't get 3 sessions");
  773. XCTAssertNotNil([user sessionForURL:urlA], @"Expected to get a session for URL A");
  774. XCTAssertNotNil([user sessionForURL:urlB], @"Expected to get a session for URL B");
  775. XCTAssertNotNil([user sessionForURL:urlC], @"Expected to get a session for URL C");
  776. XCTAssertTrue([user sessionForURL:urlA].state == RLMSyncSessionStateActive, @"Expected active session for URL A");
  777. XCTAssertTrue([user sessionForURL:urlB].state == RLMSyncSessionStateActive, @"Expected active session for URL B");
  778. XCTAssertTrue([user sessionForURL:urlC].state == RLMSyncSessionStateActive, @"Expected active session for URL C");
  779. }
  780. /// A client should be able to open multiple Realms and add objects to each of them.
  781. - (void)testMultipleRealmsAddObjects {
  782. NSURL *urlA = CUSTOM_REALM_URL(@"a");
  783. NSURL *urlB = CUSTOM_REALM_URL(@"b");
  784. NSURL *urlC = CUSTOM_REALM_URL(@"c");
  785. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  786. register:self.isParent]
  787. server:[RLMObjectServerTests authServerURL]];
  788. RLMRealm *realmA = [self openRealmForURL:urlA user:user];
  789. RLMRealm *realmB = [self openRealmForURL:urlB user:user];
  790. RLMRealm *realmC = [self openRealmForURL:urlC user:user];
  791. if (self.isParent) {
  792. [self waitForDownloadsForRealm:realmA];
  793. [self waitForDownloadsForRealm:realmB];
  794. [self waitForDownloadsForRealm:realmC];
  795. CHECK_COUNT(0, SyncObject, realmA);
  796. CHECK_COUNT(0, SyncObject, realmB);
  797. CHECK_COUNT(0, SyncObject, realmC);
  798. RLMRunChildAndWait();
  799. [self waitForDownloadsForUser:user
  800. realms:@[realmA, realmB, realmC]
  801. realmURLs:@[urlA, urlB, urlC]
  802. expectedCounts:@[@3, @2, @5]];
  803. } else {
  804. // Add objects.
  805. [self addSyncObjectsToRealm:realmA
  806. descriptions:@[@"child-A1", @"child-A2", @"child-A3"]];
  807. [self addSyncObjectsToRealm:realmB
  808. descriptions:@[@"child-B1", @"child-B2"]];
  809. [self addSyncObjectsToRealm:realmC
  810. descriptions:@[@"child-C1", @"child-C2", @"child-C3", @"child-C4", @"child-C5"]];
  811. [self waitForUploadsForRealm:realmA];
  812. [self waitForUploadsForRealm:realmB];
  813. [self waitForUploadsForRealm:realmC];
  814. CHECK_COUNT(3, SyncObject, realmA);
  815. CHECK_COUNT(2, SyncObject, realmB);
  816. CHECK_COUNT(5, SyncObject, realmC);
  817. }
  818. }
  819. /// A client should be able to open multiple Realms and delete objects from each of them.
  820. - (void)testMultipleRealmsDeleteObjects {
  821. NSURL *urlA = CUSTOM_REALM_URL(@"a");
  822. NSURL *urlB = CUSTOM_REALM_URL(@"b");
  823. NSURL *urlC = CUSTOM_REALM_URL(@"c");
  824. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  825. register:self.isParent]
  826. server:[RLMObjectServerTests authServerURL]];
  827. RLMRealm *realmA = [self openRealmForURL:urlA user:user];
  828. RLMRealm *realmB = [self openRealmForURL:urlB user:user];
  829. RLMRealm *realmC = [self openRealmForURL:urlC user:user];
  830. if (self.isParent) {
  831. [self waitForDownloadsForRealm:realmA];
  832. [self waitForDownloadsForRealm:realmB];
  833. [self waitForDownloadsForRealm:realmC];
  834. // Add objects.
  835. [self addSyncObjectsToRealm:realmA
  836. descriptions:@[@"parent-A1", @"parent-A2", @"parent-A3", @"parent-A4"]];
  837. [self addSyncObjectsToRealm:realmB
  838. descriptions:@[@"parent-B1", @"parent-B2", @"parent-B3", @"parent-B4", @"parent-B5"]];
  839. [self addSyncObjectsToRealm:realmC
  840. descriptions:@[@"parent-C1", @"parent-C2"]];
  841. [self waitForUploadsForRealm:realmA];
  842. [self waitForUploadsForRealm:realmB];
  843. [self waitForUploadsForRealm:realmC];
  844. CHECK_COUNT(4, SyncObject, realmA);
  845. CHECK_COUNT(5, SyncObject, realmB);
  846. CHECK_COUNT(2, SyncObject, realmC);
  847. RLMRunChildAndWait();
  848. [self waitForDownloadsForUser:user
  849. realms:@[realmA, realmB, realmC]
  850. realmURLs:@[urlA, urlB, urlC]
  851. expectedCounts:@[@0, @0, @0]];
  852. } else {
  853. // Delete all the objects from the Realms.
  854. [self waitForDownloadsForRealm:realmA];
  855. [self waitForDownloadsForRealm:realmB];
  856. [self waitForDownloadsForRealm:realmC];
  857. CHECK_COUNT(4, SyncObject, realmA);
  858. CHECK_COUNT(5, SyncObject, realmB);
  859. CHECK_COUNT(2, SyncObject, realmC);
  860. [realmA beginWriteTransaction];
  861. [realmA deleteAllObjects];
  862. [realmA commitWriteTransaction];
  863. [realmB beginWriteTransaction];
  864. [realmB deleteAllObjects];
  865. [realmB commitWriteTransaction];
  866. [realmC beginWriteTransaction];
  867. [realmC deleteAllObjects];
  868. [realmC commitWriteTransaction];
  869. [self waitForUploadsForRealm:realmA];
  870. [self waitForUploadsForRealm:realmB];
  871. [self waitForUploadsForRealm:realmC];
  872. CHECK_COUNT(0, SyncObject, realmA);
  873. CHECK_COUNT(0, SyncObject, realmB);
  874. CHECK_COUNT(0, SyncObject, realmC);
  875. }
  876. }
  877. #pragma mark - Session Lifetime
  878. /// When a session opened by a Realm goes out of scope, it should stay alive long enough to finish any waiting uploads.
  879. - (void)testUploadChangesWhenRealmOutOfScope {
  880. const NSInteger OBJECT_COUNT = 10000;
  881. NSURL *url = REALM_URL();
  882. // Log in the user.
  883. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  884. register:self.isParent]
  885. server:[RLMObjectServerTests authServerURL]];
  886. if (self.isParent) {
  887. // Open the Realm in an autorelease pool so that it is destroyed as soon as possible.
  888. @autoreleasepool {
  889. RLMRealm *realm = [self openRealmForURL:url user:user];
  890. [realm beginWriteTransaction];
  891. for (NSInteger i=0; i<OBJECT_COUNT; i++) {
  892. [realm addObject:[[SyncObject alloc] initWithValue:@[[NSString stringWithFormat:@"parent-%@", @(i+1)]]]];
  893. }
  894. [realm commitWriteTransaction];
  895. CHECK_COUNT(OBJECT_COUNT, SyncObject, realm);
  896. }
  897. // Run the sub-test. (Give the upload a bit of time to start.)
  898. // NOTE: This sleep should be fine because:
  899. // - There is currently no API that allows asynchronous coordination for waiting for an upload to begin.
  900. // - A delay longer than the specified one will not affect the outcome of the test.
  901. sleep(2);
  902. RLMRunChildAndWait();
  903. } else {
  904. RLMRealm *realm = [self openRealmForURL:url user:user];
  905. // Wait for download to complete.
  906. [self waitForDownloadsForRealm:realm];
  907. CHECK_COUNT(OBJECT_COUNT, SyncObject, realm);
  908. }
  909. }
  910. #pragma mark - Logging Back In
  911. /// A Realm that was opened before a user logged out should be able to resume uploading if the user logs back in.
  912. - (void)testLogBackInSameRealmUpload {
  913. NSURL *url = REALM_URL();
  914. // Log in the user.
  915. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  916. register:self.isParent]
  917. server:[RLMObjectServerTests authServerURL]];
  918. RLMRealm *realm = [self openRealmForURL:url user:user];
  919. if (self.isParent) {
  920. [self addSyncObjectsToRealm:realm descriptions:@[@"parent-1"]];
  921. CHECK_COUNT(1, SyncObject, realm);
  922. [self waitForUploadsForRealm:realm];
  923. // Log out the user.
  924. [user logOut];
  925. // Log the user back in.
  926. user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  927. register:NO]
  928. server:[RLMObjectServerTests authServerURL]];
  929. [self addSyncObjectsToRealm:realm descriptions:@[@"parent-2", @"parent-3"]];
  930. [self waitForUploadsForRealm:realm];
  931. CHECK_COUNT(3, SyncObject, realm);
  932. RLMRunChildAndWait();
  933. } else {
  934. [self waitForDownloadsForRealm:realm];
  935. CHECK_COUNT(3, SyncObject, realm);
  936. }
  937. }
  938. /// A Realm that was opened before a user logged out should be able to resume downloading if the user logs back in.
  939. - (void)testLogBackInSameRealmDownload {
  940. NSURL *url = REALM_URL();
  941. // Log in the user.
  942. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  943. register:self.isParent]
  944. server:[RLMObjectServerTests authServerURL]];
  945. RLMRealm *realm = [self openRealmForURL:url user:user];
  946. if (self.isParent) {
  947. [self addSyncObjectsToRealm:realm descriptions:@[@"parent-1"]];
  948. CHECK_COUNT(1, SyncObject, realm);
  949. [self waitForUploadsForRealm:realm];
  950. // Log out the user.
  951. [user logOut];
  952. // Log the user back in.
  953. user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  954. register:NO]
  955. server:[RLMObjectServerTests authServerURL]];
  956. RLMRunChildAndWait();
  957. [self waitForDownloadsForRealm:realm];
  958. CHECK_COUNT(3, SyncObject, realm);
  959. } else {
  960. [self waitForDownloadsForRealm:realm];
  961. [self addSyncObjectsToRealm:realm descriptions:@[@"child-1", @"child-2"]];
  962. [self waitForUploadsForRealm:realm];
  963. CHECK_COUNT(3, SyncObject, realm);
  964. }
  965. }
  966. /// A Realm that was opened while a user was logged out should be able to start uploading if the user logs back in.
  967. - (void)testLogBackInDeferredRealmUpload {
  968. NSURL *url = REALM_URL();
  969. // Log in the user.
  970. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  971. register:self.isParent]
  972. server:[RLMObjectServerTests authServerURL]];
  973. NSError *error = nil;
  974. if (self.isParent) {
  975. // Semaphore for knowing when the Realm is successfully opened for sync.
  976. dispatch_semaphore_t sema = dispatch_semaphore_create(0);
  977. RLMRealmConfiguration *config = [user configurationWithURL:url fullSynchronization:true];
  978. [user logOut];
  979. // Open a Realm after the user's been logged out.
  980. [self primeSyncManagerWithSemaphore:sema];
  981. RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:&error];
  982. XCTAssertNil(error, @"Error when opening Realm: %@", error);
  983. [self addSyncObjectsToRealm:realm descriptions:@[@"parent-1"]];
  984. CHECK_COUNT(1, SyncObject, realm);
  985. user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  986. register:NO]
  987. server:[RLMObjectServerTests authServerURL]];
  988. // Wait for the Realm's session to be bound.
  989. WAIT_FOR_SEMAPHORE(sema, 30);
  990. [self addSyncObjectsToRealm:realm descriptions:@[@"parent-2", @"parent-3"]];
  991. [self waitForUploadsForRealm:realm];
  992. CHECK_COUNT(3, SyncObject, realm);
  993. RLMRunChildAndWait();
  994. } else {
  995. RLMRealm *realm = [self openRealmForURL:url user:user];
  996. XCTAssertNil(error, @"Error when opening Realm: %@", error);
  997. [self waitForDownloadsForRealm:realm];
  998. CHECK_COUNT(3, SyncObject, realm);
  999. }
  1000. }
  1001. /// A Realm that was opened while a user was logged out should be able to start downloading if the user logs back in.
  1002. - (void)testLogBackInDeferredRealmDownload {
  1003. NSURL *url = REALM_URL();
  1004. // Log in the user.
  1005. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  1006. register:self.isParent]
  1007. server:[RLMObjectServerTests authServerURL]];
  1008. NSError *error = nil;
  1009. if (self.isParent) {
  1010. dispatch_semaphore_t sema = dispatch_semaphore_create(0);
  1011. RLMRunChildAndWait();
  1012. RLMRealmConfiguration *config = [user configurationWithURL:url fullSynchronization:true];
  1013. [user logOut];
  1014. // Open a Realm after the user's been logged out.
  1015. [self primeSyncManagerWithSemaphore:sema];
  1016. RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:&error];
  1017. XCTAssertNil(error, @"Error when opening Realm: %@", error);
  1018. [self addSyncObjectsToRealm:realm descriptions:@[@"parent-1"]];
  1019. CHECK_COUNT(1, SyncObject, realm);
  1020. user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  1021. register:NO]
  1022. server:[RLMObjectServerTests authServerURL]];
  1023. // Wait for the Realm's session to be bound.
  1024. WAIT_FOR_SEMAPHORE(sema, 30);
  1025. [self waitForDownloadsForUser:user realms:@[realm] realmURLs:@[url] expectedCounts:@[@4]];
  1026. } else {
  1027. RLMRealm *realm = [self openRealmForURL:url user:user];
  1028. XCTAssertNil(error, @"Error when opening Realm: %@", error);
  1029. [self addSyncObjectsToRealm:realm descriptions:@[@"child-1", @"child-2", @"child-3"]];
  1030. [self waitForUploadsForRealm:realm];
  1031. CHECK_COUNT(3, SyncObject, realm);
  1032. }
  1033. }
  1034. /// After logging back in, a Realm whose path has been opened for the first time should properly upload changes.
  1035. - (void)testLogBackInOpenFirstTimePathUpload {
  1036. NSURL *url = REALM_URL();
  1037. // Log in the user.
  1038. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  1039. register:self.isParent]
  1040. server:[RLMObjectServerTests authServerURL]];
  1041. // Now run a basic multi-client test.
  1042. if (self.isParent) {
  1043. // Log out the user.
  1044. [user logOut];
  1045. // Log the user back in.
  1046. user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  1047. register:NO]
  1048. server:[RLMObjectServerTests authServerURL]];
  1049. // Open the Realm (for the first time).
  1050. RLMRealm *realm = [self openRealmForURL:url user:user];
  1051. [self addSyncObjectsToRealm:realm descriptions:@[@"child-1", @"child-2"]];
  1052. [self waitForUploadsForRealm:realm];
  1053. CHECK_COUNT(2, SyncObject, realm);
  1054. RLMRunChildAndWait();
  1055. } else {
  1056. RLMRealm *realm = [self openRealmForURL:url user:user];
  1057. // Add objects.
  1058. [self waitForDownloadsForRealm:realm];
  1059. CHECK_COUNT(2, SyncObject, realm);
  1060. }
  1061. }
  1062. /// After logging back in, a Realm whose path has been opened for the first time should properly download changes.
  1063. - (void)testLogBackInOpenFirstTimePathDownload {
  1064. NSURL *url = REALM_URL();
  1065. // Log in the user.
  1066. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  1067. register:self.isParent]
  1068. server:[RLMObjectServerTests authServerURL]];
  1069. // Now run a basic multi-client test.
  1070. if (self.isParent) {
  1071. // Log out the user.
  1072. [user logOut];
  1073. // Log the user back in.
  1074. user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  1075. register:NO]
  1076. server:[RLMObjectServerTests authServerURL]];
  1077. // Open the Realm (for the first time).
  1078. RLMRealm *realm = [self openRealmForURL:url user:user];
  1079. // Run the sub-test.
  1080. RLMRunChildAndWait();
  1081. [self waitForDownloadsForRealm:realm];
  1082. CHECK_COUNT(2, SyncObject, realm);
  1083. } else {
  1084. RLMRealm *realm = [self openRealmForURL:url user:user];
  1085. // Add objects.
  1086. [self waitForDownloadsForRealm:realm];
  1087. [self addSyncObjectsToRealm:realm descriptions:@[@"child-1", @"child-2"]];
  1088. [self waitForUploadsForRealm:realm];
  1089. CHECK_COUNT(2, SyncObject, realm);
  1090. }
  1091. }
  1092. /// If a client logs in, connects, logs out, and logs back in, sync should properly upload changes for a new
  1093. /// `RLMRealm` that is opened for the same path as a previously-opened Realm.
  1094. - (void)testLogBackInReopenRealmUpload {
  1095. NSURL *url = REALM_URL();
  1096. // Log in the user.
  1097. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  1098. register:self.isParent]
  1099. server:[RLMObjectServerTests authServerURL]];
  1100. // Open the Realm
  1101. RLMRealm *realm = [self openRealmForURL:url user:user];
  1102. if (self.isParent) {
  1103. [self addSyncObjectsToRealm:realm descriptions:@[@"parent-1"]];
  1104. [self waitForUploadsForRealm:realm];
  1105. CHECK_COUNT(1, SyncObject, realm);
  1106. // Log out the user.
  1107. [user logOut];
  1108. // Log the user back in.
  1109. user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  1110. register:NO]
  1111. server:[RLMObjectServerTests authServerURL]];
  1112. // Open the Realm again.
  1113. realm = [self immediatelyOpenRealmForURL:url user:user];
  1114. [self addSyncObjectsToRealm:realm descriptions:@[@"child-1", @"child-2", @"child-3", @"child-4"]];
  1115. CHECK_COUNT(5, SyncObject, realm);
  1116. [self waitForUploadsForRealm:realm];
  1117. RLMRunChildAndWait();
  1118. } else {
  1119. [self waitForDownloadsForRealm:realm];
  1120. CHECK_COUNT(5, SyncObject, realm);
  1121. }
  1122. }
  1123. /// If a client logs in, connects, logs out, and logs back in, sync should properly download changes for a new
  1124. /// `RLMRealm` that is opened for the same path as a previously-opened Realm.
  1125. - (void)testLogBackInReopenRealmDownload {
  1126. NSURL *url = REALM_URL();
  1127. // Log in the user.
  1128. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  1129. register:self.isParent]
  1130. server:[RLMObjectServerTests authServerURL]];
  1131. // Open the Realm
  1132. RLMRealm *realm = [self openRealmForURL:url user:user];
  1133. if (self.isParent) {
  1134. [self addSyncObjectsToRealm:realm descriptions:@[@"parent-1"]];
  1135. [self waitForUploadsForRealm:realm];
  1136. XCTAssert([SyncObject allObjectsInRealm:realm].count == 1, @"Expected 1 item");
  1137. // Log out the user.
  1138. [user logOut];
  1139. // Log the user back in.
  1140. user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  1141. register:NO]
  1142. server:[RLMObjectServerTests authServerURL]];
  1143. // Run the sub-test.
  1144. RLMRunChildAndWait();
  1145. // Open the Realm again and get the items.
  1146. realm = [self immediatelyOpenRealmForURL:url user:user];
  1147. [self waitForDownloadsForUser:user realms:@[realm] realmURLs:@[url] expectedCounts:@[@5]];
  1148. } else {
  1149. // Add objects.
  1150. [self waitForDownloadsForRealm:realm];
  1151. CHECK_COUNT(1, SyncObject, realm);
  1152. [self addSyncObjectsToRealm:realm descriptions:@[@"child-1", @"child-2", @"child-3", @"child-4"]];
  1153. [self waitForUploadsForRealm:realm];
  1154. CHECK_COUNT(5, SyncObject, realm);
  1155. }
  1156. }
  1157. #pragma mark - Session suspend and resume
  1158. - (void)testSuspendAndResume {
  1159. NSURL *urlA = CUSTOM_REALM_URL(@"a");
  1160. NSURL *urlB = CUSTOM_REALM_URL(@"b");
  1161. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  1162. register:self.isParent]
  1163. server:[RLMObjectServerTests authServerURL]];
  1164. RLMRealm *realmA = [self openRealmForURL:urlA user:user];
  1165. RLMRealm *realmB = [self openRealmForURL:urlB user:user];
  1166. if (self.isParent) {
  1167. [self waitForDownloadsForRealm:realmA];
  1168. [self waitForDownloadsForRealm:realmB];
  1169. CHECK_COUNT(0, SyncObject, realmA);
  1170. CHECK_COUNT(0, SyncObject, realmB);
  1171. // Suspend the session for realm A and then add an object to each Realm
  1172. RLMSyncSession *sessionA = [RLMSyncSession sessionForRealm:realmA];
  1173. [sessionA suspend];
  1174. [self addSyncObjectsToRealm:realmA descriptions:@[@"child-A1"]];
  1175. [self addSyncObjectsToRealm:realmB descriptions:@[@"child-B1"]];
  1176. [self waitForUploadsForRealm:realmB];
  1177. RLMRunChildAndWait();
  1178. // A should still be 1 since it's suspended. If it wasn't suspended, it
  1179. // should have downloaded before B due to the ordering in the child.
  1180. [self waitForDownloadsForRealm:realmB];
  1181. CHECK_COUNT(1, SyncObject, realmA);
  1182. CHECK_COUNT(3, SyncObject, realmB);
  1183. // A should see the other two from the child after resuming
  1184. [sessionA resume];
  1185. [self waitForDownloadsForRealm:realmA];
  1186. CHECK_COUNT(3, SyncObject, realmA);
  1187. } else {
  1188. // Child shouldn't see the object in A
  1189. [self waitForDownloadsForRealm:realmA];
  1190. [self waitForDownloadsForRealm:realmB];
  1191. CHECK_COUNT(0, SyncObject, realmA);
  1192. CHECK_COUNT(1, SyncObject, realmB);
  1193. [self addSyncObjectsToRealm:realmA descriptions:@[@"child-A2", @"child-A3"]];
  1194. [self waitForUploadsForRealm:realmA];
  1195. [self addSyncObjectsToRealm:realmB descriptions:@[@"child-B2", @"child-B3"]];
  1196. [self waitForUploadsForRealm:realmB];
  1197. CHECK_COUNT(2, SyncObject, realmA);
  1198. CHECK_COUNT(3, SyncObject, realmB);
  1199. }
  1200. }
  1201. #pragma mark - Client reset
  1202. /// Ensure that a client reset error is propagated up to the binding successfully.
  1203. - (void)testClientReset {
  1204. NSURL *url = REALM_URL();
  1205. NSString *sessionName = NSStringFromSelector(_cmd);
  1206. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:sessionName
  1207. register:true]
  1208. server:[RLMObjectServerTests authServerURL]];
  1209. // Open the Realm
  1210. __attribute__((objc_precise_lifetime)) RLMRealm *realm = [self openRealmForURL:url user:user];
  1211. __block NSError *theError = nil;
  1212. XCTestExpectation *ex = [self expectationWithDescription:@"Waiting for error handler to be called..."];
  1213. [RLMSyncManager sharedManager].errorHandler = ^void(NSError *error, RLMSyncSession *session) {
  1214. // Make sure we're actually looking at the right session.
  1215. XCTAssertTrue([[session.realmURL absoluteString] rangeOfString:sessionName].location != NSNotFound);
  1216. theError = error;
  1217. [ex fulfill];
  1218. };
  1219. [user simulateClientResetErrorForSession:url];
  1220. [self waitForExpectationsWithTimeout:10 handler:nil];
  1221. XCTAssertNotNil(theError);
  1222. XCTAssertTrue(theError.code == RLMSyncErrorClientResetError);
  1223. NSString *pathValue = [theError rlmSync_clientResetBackedUpRealmPath];
  1224. XCTAssertNotNil(pathValue);
  1225. // Sanity check the recovery path.
  1226. NSString *recoveryPath = @"io.realm.object-server-recovered-realms/recovered_realm";
  1227. XCTAssertTrue([pathValue rangeOfString:recoveryPath].location != NSNotFound);
  1228. XCTAssertNotNil([theError rlmSync_errorActionToken]);
  1229. }
  1230. /// Test manually initiating client reset.
  1231. - (void)testClientResetManualInitiation {
  1232. NSURL *url = REALM_URL();
  1233. NSString *sessionName = NSStringFromSelector(_cmd);
  1234. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:sessionName
  1235. register:true]
  1236. server:[RLMObjectServerTests authServerURL]];
  1237. __block NSError *theError = nil;
  1238. @autoreleasepool {
  1239. __attribute__((objc_precise_lifetime)) RLMRealm *realm = [self openRealmForURL:url user:user];
  1240. XCTestExpectation *ex = [self expectationWithDescription:@"Waiting for error handler to be called..."];
  1241. [RLMSyncManager sharedManager].errorHandler = ^void(NSError *error, RLMSyncSession *session) {
  1242. // Make sure we're actually looking at the right session.
  1243. XCTAssertTrue([[session.realmURL absoluteString] rangeOfString:sessionName].location != NSNotFound);
  1244. theError = error;
  1245. [ex fulfill];
  1246. };
  1247. [user simulateClientResetErrorForSession:url];
  1248. [self waitForExpectationsWithTimeout:10 handler:nil];
  1249. XCTAssertNotNil(theError);
  1250. }
  1251. // At this point the Realm should be invalidated and client reset should be possible.
  1252. NSString *pathValue = [theError rlmSync_clientResetBackedUpRealmPath];
  1253. XCTAssertFalse([[NSFileManager defaultManager] fileExistsAtPath:pathValue]);
  1254. [RLMSyncSession immediatelyHandleError:[theError rlmSync_errorActionToken]];
  1255. XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:pathValue]);
  1256. }
  1257. #pragma mark - Progress Notifications
  1258. - (void)testStreamingDownloadNotifier {
  1259. const NSInteger NUMBER_OF_BIG_OBJECTS = 2;
  1260. NSURL *url = REALM_URL();
  1261. // Log in the user.
  1262. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  1263. register:self.isParent]
  1264. server:[RLMObjectServerTests authServerURL]];
  1265. __block NSInteger callCount = 0;
  1266. __block NSUInteger transferred = 0;
  1267. __block NSUInteger transferrable = 0;
  1268. // Open the Realm
  1269. RLMRealm *realm = [self openRealmForURL:url user:user];
  1270. if (self.isParent) {
  1271. __block BOOL hasBeenFulfilled = NO;
  1272. // Register a notifier.
  1273. RLMSyncSession *session = [user sessionForURL:url];
  1274. XCTAssertNotNil(session);
  1275. XCTestExpectation *ex = [self expectationWithDescription:@"streaming-download-notifier"];
  1276. RLMProgressNotificationToken *token = [session addProgressNotificationForDirection:RLMSyncProgressDirectionDownload
  1277. mode:RLMSyncProgressModeReportIndefinitely
  1278. block:^(NSUInteger xfr, NSUInteger xfb) {
  1279. // Make sure the values are increasing, and update our stored copies.
  1280. XCTAssert(xfr >= transferred);
  1281. XCTAssert(xfb >= transferrable);
  1282. transferred = xfr;
  1283. transferrable = xfb;
  1284. callCount++;
  1285. if (transferrable > 0 && transferred >= transferrable && !hasBeenFulfilled) {
  1286. [ex fulfill];
  1287. hasBeenFulfilled = YES;
  1288. }
  1289. }];
  1290. // Wait for the child process to upload everything.
  1291. RLMRunChildAndWait();
  1292. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  1293. [token invalidate];
  1294. // The notifier should have been called at least twice: once at the beginning and at least once
  1295. // to report progress.
  1296. XCTAssert(callCount > 1);
  1297. XCTAssert(transferred >= transferrable,
  1298. @"Transferred (%@) needs to be greater than or equal to transferrable (%@)",
  1299. @(transferred), @(transferrable));
  1300. } else {
  1301. // Write lots of data to the Realm, then wait for it to be uploaded.
  1302. [realm beginWriteTransaction];
  1303. for (NSInteger i=0; i<NUMBER_OF_BIG_OBJECTS; i++) {
  1304. [realm addObject:[HugeSyncObject object]];
  1305. }
  1306. [realm commitWriteTransaction];
  1307. [self waitForUploadsForRealm:realm];
  1308. CHECK_COUNT(NUMBER_OF_BIG_OBJECTS, HugeSyncObject, realm);
  1309. }
  1310. }
  1311. - (void)testStreamingUploadNotifier {
  1312. const NSInteger NUMBER_OF_BIG_OBJECTS = 2;
  1313. NSURL *url = REALM_URL();
  1314. // Log in the user.
  1315. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  1316. register:self.isParent]
  1317. server:[RLMObjectServerTests authServerURL]];
  1318. __block NSInteger callCount = 0;
  1319. __block NSUInteger transferred = 0;
  1320. __block NSUInteger transferrable = 0;
  1321. __block BOOL hasBeenFulfilled = NO;
  1322. // Open the Realm
  1323. RLMRealm *realm = [self openRealmForURL:url user:user];
  1324. // Register a notifier.
  1325. RLMSyncSession *session = [user sessionForURL:url];
  1326. XCTAssertNotNil(session);
  1327. XCTestExpectation *ex = [self expectationWithDescription:@"streaming-upload-expectation"];
  1328. RLMProgressNotificationToken *token = [session addProgressNotificationForDirection:RLMSyncProgressDirectionUpload
  1329. mode:RLMSyncProgressModeReportIndefinitely
  1330. block:^(NSUInteger xfr, NSUInteger xfb) {
  1331. // Make sure the values are
  1332. // increasing, and update our
  1333. // stored copies.
  1334. XCTAssert(xfr >= transferred);
  1335. XCTAssert(xfb >= transferrable);
  1336. transferred = xfr;
  1337. transferrable = xfb;
  1338. callCount++;
  1339. if (transferred > 0
  1340. && transferred >= transferrable
  1341. && !hasBeenFulfilled) {
  1342. [ex fulfill];
  1343. hasBeenFulfilled = YES;
  1344. }
  1345. }];
  1346. // Upload lots of data
  1347. [realm beginWriteTransaction];
  1348. for (NSInteger i=0; i<NUMBER_OF_BIG_OBJECTS; i++) {
  1349. [realm addObject:[HugeSyncObject object]];
  1350. }
  1351. [realm commitWriteTransaction];
  1352. // Wait for upload to begin and finish
  1353. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  1354. [token invalidate];
  1355. // The notifier should have been called at least twice: once at the beginning and at least once
  1356. // to report progress.
  1357. XCTAssert(callCount > 1);
  1358. XCTAssert(transferred >= transferrable,
  1359. @"Transferred (%@) needs to be greater than or equal to transferrable (%@)",
  1360. @(transferred), @(transferrable));
  1361. }
  1362. #pragma mark - Download Realm
  1363. - (void)testDownloadRealm {
  1364. const NSInteger NUMBER_OF_BIG_OBJECTS = 2;
  1365. NSURL *url = REALM_URL();
  1366. // Log in the user.
  1367. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  1368. register:self.isParent]
  1369. server:[RLMObjectServerTests authServerURL]];
  1370. if (self.isParent) {
  1371. // Wait for the child process to upload everything.
  1372. RLMRunChildAndWait();
  1373. XCTestExpectation *ex = [self expectationWithDescription:@"download-realm"];
  1374. RLMRealmConfiguration *c = [user configurationWithURL:url fullSynchronization:true];
  1375. XCTAssertFalse([[NSFileManager defaultManager] fileExistsAtPath:c.pathOnDisk isDirectory:nil]);
  1376. [RLMRealm asyncOpenWithConfiguration:c
  1377. callbackQueue:dispatch_get_main_queue()
  1378. callback:^(RLMRealm * _Nullable realm, NSError * _Nullable error) {
  1379. XCTAssertNil(error);
  1380. CHECK_COUNT(NUMBER_OF_BIG_OBJECTS, HugeSyncObject, realm);
  1381. [ex fulfill];
  1382. }];
  1383. NSUInteger (^fileSize)(NSString *) = ^NSUInteger(NSString *path) {
  1384. NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil];
  1385. if (attributes)
  1386. return [(NSNumber *)attributes[NSFileSize] unsignedLongLongValue];
  1387. return 0;
  1388. };
  1389. NSUInteger sizeBefore = fileSize(c.pathOnDisk);
  1390. @autoreleasepool {
  1391. // We have partial transaction logs but no data
  1392. XCTAssertGreaterThan(sizeBefore, 0U);
  1393. XCTAssertTrue([[RLMRealm realmWithConfiguration:c error:nil] isEmpty]);
  1394. }
  1395. XCTAssertNil(RLMGetAnyCachedRealmForPath(c.pathOnDisk.UTF8String));
  1396. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  1397. XCTAssertGreaterThan(fileSize(c.pathOnDisk), sizeBefore);
  1398. XCTAssertNil(RLMGetAnyCachedRealmForPath(c.pathOnDisk.UTF8String));
  1399. } else {
  1400. RLMRealm *realm = [self openRealmForURL:url user:user];
  1401. // Write lots of data to the Realm, then wait for it to be uploaded.
  1402. [realm beginWriteTransaction];
  1403. for (NSInteger i=0; i<NUMBER_OF_BIG_OBJECTS; i++) {
  1404. [realm addObject:[HugeSyncObject object]];
  1405. }
  1406. [realm commitWriteTransaction];
  1407. [self waitForUploadsForRealm:realm];
  1408. CHECK_COUNT(NUMBER_OF_BIG_OBJECTS, HugeSyncObject, realm);
  1409. }
  1410. }
  1411. - (void)testDownloadAlreadyOpenRealm {
  1412. const NSInteger NUMBER_OF_BIG_OBJECTS = 2;
  1413. NSURL *url = REALM_URL();
  1414. // Log in the user.
  1415. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  1416. register:self.isParent]
  1417. server:[RLMObjectServerTests authServerURL]];
  1418. if (!self.isParent) {
  1419. RLMRealm *realm = [self openRealmForURL:url user:user];
  1420. // Write lots of data to the Realm, then wait for it to be uploaded.
  1421. [realm beginWriteTransaction];
  1422. for (NSInteger i = 0; i < NUMBER_OF_BIG_OBJECTS; i++) {
  1423. [realm addObject:[HugeSyncObject object]];
  1424. }
  1425. [realm commitWriteTransaction];
  1426. [self waitForUploadsForRealm:realm];
  1427. CHECK_COUNT(NUMBER_OF_BIG_OBJECTS, HugeSyncObject, realm);
  1428. return;
  1429. }
  1430. // Wait for the child process to upload everything.
  1431. RLMRunChildAndWait();
  1432. XCTestExpectation *ex = [self expectationWithDescription:@"download-realm"];
  1433. RLMRealmConfiguration *c = [user configurationWithURL:url fullSynchronization:true];
  1434. XCTAssertFalse([[NSFileManager defaultManager] fileExistsAtPath:c.pathOnDisk isDirectory:nil]);
  1435. RLMRealm *realm = [RLMRealm realmWithConfiguration:c error:nil];
  1436. CHECK_COUNT(0, HugeSyncObject, realm);
  1437. [RLMRealm asyncOpenWithConfiguration:c
  1438. callbackQueue:dispatch_get_main_queue()
  1439. callback:^(RLMRealm * _Nullable realm, NSError * _Nullable error) {
  1440. XCTAssertNil(error);
  1441. CHECK_COUNT(NUMBER_OF_BIG_OBJECTS, HugeSyncObject, realm);
  1442. [ex fulfill];
  1443. }];
  1444. auto fileSize = ^NSUInteger(NSString *path) {
  1445. NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil];
  1446. return [(NSNumber *)attributes[NSFileSize] unsignedLongLongValue];
  1447. };
  1448. NSUInteger sizeBefore = fileSize(c.pathOnDisk);
  1449. XCTAssertGreaterThan(sizeBefore, 0U);
  1450. XCTAssertNotNil(RLMGetAnyCachedRealmForPath(c.pathOnDisk.UTF8String));
  1451. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  1452. XCTAssertGreaterThan(fileSize(c.pathOnDisk), sizeBefore);
  1453. XCTAssertNotNil(RLMGetAnyCachedRealmForPath(c.pathOnDisk.UTF8String));
  1454. CHECK_COUNT(NUMBER_OF_BIG_OBJECTS, HugeSyncObject, realm);
  1455. }
  1456. - (void)testDownloadWhileOpeningRealm {
  1457. const NSInteger NUMBER_OF_BIG_OBJECTS = 2;
  1458. NSURL *url = REALM_URL();
  1459. // Log in the user.
  1460. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  1461. register:self.isParent]
  1462. server:[RLMObjectServerTests authServerURL]];
  1463. if (self.isParent) {
  1464. // Wait for the child process to upload everything.
  1465. RLMRunChildAndWait();
  1466. XCTestExpectation *ex = [self expectationWithDescription:@"download-realm"];
  1467. RLMRealmConfiguration *c = [user configurationWithURL:url fullSynchronization:true];
  1468. XCTAssertFalse([[NSFileManager defaultManager] fileExistsAtPath:c.pathOnDisk isDirectory:nil]);
  1469. [RLMRealm asyncOpenWithConfiguration:c
  1470. callbackQueue:dispatch_get_main_queue()
  1471. callback:^(RLMRealm * _Nullable realm, NSError * _Nullable error) {
  1472. XCTAssertNil(error);
  1473. CHECK_COUNT(NUMBER_OF_BIG_OBJECTS, HugeSyncObject, realm);
  1474. [ex fulfill];
  1475. }];
  1476. RLMRealm *realm = [RLMRealm realmWithConfiguration:c error:nil];
  1477. CHECK_COUNT(0, HugeSyncObject, realm);
  1478. XCTAssertNotNil(RLMGetAnyCachedRealmForPath(c.pathOnDisk.UTF8String));
  1479. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  1480. CHECK_COUNT(NUMBER_OF_BIG_OBJECTS, HugeSyncObject, realm);
  1481. XCTAssertNotNil(RLMGetAnyCachedRealmForPath(c.pathOnDisk.UTF8String));
  1482. } else {
  1483. RLMRealm *realm = [self openRealmForURL:url user:user];
  1484. // Write lots of data to the Realm, then wait for it to be uploaded.
  1485. [realm beginWriteTransaction];
  1486. for (NSInteger i=0; i<NUMBER_OF_BIG_OBJECTS; i++) {
  1487. [realm addObject:[HugeSyncObject object]];
  1488. }
  1489. [realm commitWriteTransaction];
  1490. [self waitForUploadsForRealm:realm];
  1491. CHECK_COUNT(NUMBER_OF_BIG_OBJECTS, HugeSyncObject, realm);
  1492. }
  1493. }
  1494. - (void)testDownloadCancelsOnAuthError {
  1495. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  1496. register:self.isParent]
  1497. server:[RLMObjectServerTests authServerURL]];
  1498. auto c = [user configurationWithURL:[NSURL URLWithString:@"realm://127.0.0.1:9080/invalid"] fullSynchronization:true];
  1499. auto ex = [self expectationWithDescription:@"async open"];
  1500. [RLMRealm asyncOpenWithConfiguration:c callbackQueue:dispatch_get_main_queue()
  1501. callback:^(RLMRealm *realm, NSError *error) {
  1502. XCTAssertNil(realm);
  1503. XCTAssertNotNil(error);
  1504. [ex fulfill];
  1505. }];
  1506. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  1507. }
  1508. #pragma mark - Compact on Launch
  1509. - (void)testCompactOnLaunch {
  1510. NSURL *url = REALM_URL();
  1511. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  1512. register:YES]
  1513. server:[RLMObjectServerTests authServerURL]];
  1514. NSString *path;
  1515. // Create a large object and then delete it in the next transaction so that
  1516. // the file is bloated
  1517. @autoreleasepool {
  1518. auto realm = [self openRealmForURL:url user:user];
  1519. [realm beginWriteTransaction];
  1520. [realm addObject:[HugeSyncObject object]];
  1521. [realm commitWriteTransaction];
  1522. [self waitForUploadsForRealm:realm];
  1523. [realm beginWriteTransaction];
  1524. [realm deleteAllObjects];
  1525. [realm commitWriteTransaction];
  1526. [self waitForUploadsForRealm:realm];
  1527. [self waitForDownloadsForRealm:realm];
  1528. path = realm.configuration.pathOnDisk;
  1529. }
  1530. auto fileManager = NSFileManager.defaultManager;
  1531. auto initialSize = [[fileManager attributesOfItemAtPath:path error:nil][NSFileSize] unsignedLongLongValue];
  1532. // Reopen the file with a shouldCompactOnLaunch block and verify that it is
  1533. // actually compacted
  1534. auto config = [user configurationWithURL:url fullSynchronization:true];
  1535. __block bool blockCalled = false;
  1536. config.shouldCompactOnLaunch = ^(NSUInteger, NSUInteger){
  1537. blockCalled = true;
  1538. return YES;
  1539. };
  1540. @autoreleasepool {
  1541. [RLMRealm realmWithConfiguration:config error:nil];
  1542. }
  1543. XCTAssertTrue(blockCalled);
  1544. auto finalSize = [[fileManager attributesOfItemAtPath:path error:nil][NSFileSize] unsignedLongLongValue];
  1545. XCTAssertLessThan(finalSize, initialSize);
  1546. XCTAssertLessThan(finalSize, 10000U);
  1547. }
  1548. #pragma mark - Offline Client Reset
  1549. - (void)testOfflineClientReset {
  1550. NSError *error;
  1551. RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
  1552. register:YES]
  1553. server:[RLMObjectServerTests authServerURL]];
  1554. NSURL *sourceFileURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"sync-1.x" withExtension:@"realm"];
  1555. NSString *fileName = [NSString stringWithFormat:@"%@.realm", [NSUUID new]];
  1556. NSURL *fileURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:fileName]];
  1557. [NSFileManager.defaultManager copyItemAtURL:sourceFileURL toURL:fileURL error:&error];
  1558. XCTAssertNil(error);
  1559. if (error) {
  1560. return;
  1561. }
  1562. RLMRealmConfiguration *configuration = [user configurationWithURL:REALM_URL() fullSynchronization:true];
  1563. RLMSyncConfiguration *syncConfig = configuration.syncConfiguration;
  1564. syncConfig.customFileURL = fileURL;
  1565. configuration.syncConfiguration = syncConfig;
  1566. RLMRealm *realm = [RLMRealm realmWithConfiguration:configuration error:&error];
  1567. XCTAssertNil(realm);
  1568. XCTAssertEqualObjects(error.domain, RLMErrorDomain);
  1569. XCTAssertEqual(error.code, RLMErrorIncompatibleSyncedFile);
  1570. RLMRealmConfiguration *backupConfiguration = error.userInfo[RLMBackupRealmConfigurationErrorKey];
  1571. XCTAssertNotNil(backupConfiguration);
  1572. // Open the backup Realm with a schema subset since it was created using the schema from .NET's unit tests.
  1573. // The Person class is declared in SwiftObjectServerTests.swift.
  1574. backupConfiguration.objectClasses = @[NSClassFromString(@"Person")];
  1575. error = nil;
  1576. RLMRealm *backupRealm = [RLMRealm realmWithConfiguration:backupConfiguration error:&error];
  1577. XCTAssertNotNil(backupRealm);
  1578. XCTAssertNil(error);
  1579. RLMResults *people = [backupRealm allObjects:@"Person"];
  1580. XCTAssertEqual(people.count, 1u);
  1581. XCTAssertEqualObjects([people[0] valueForKey:@"FirstName"], @"John");
  1582. XCTAssertEqualObjects([people[0] valueForKey:@"LastName"], @"Smith");
  1583. error = nil;
  1584. realm = [RLMRealm realmWithConfiguration:configuration error:&error];
  1585. XCTAssertNotNil(realm);
  1586. XCTAssertNil(error);
  1587. }
  1588. #pragma clang diagnostic push
  1589. #pragma clang diagnostic ignored "-Wdeprecated"
  1590. - (void)testAutomaticSyncConfiguration {
  1591. NSURL *server = [RLMObjectServerTests authServerURL];
  1592. // Automatic configuration should throw when there are no logged-in users.
  1593. XCTAssertThrows([RLMSyncConfiguration automaticConfiguration]);
  1594. RLMSyncCredentials *credsA = [RLMObjectServerTests basicCredentialsWithName:@"a" register:YES];
  1595. RLMSyncUser *userA = [self logInUserForCredentials:credsA server:server];
  1596. // Now that there's a logged-in user, we should be able to retrieve the configuration.
  1597. RLMRealmConfiguration *configuration = [RLMSyncConfiguration automaticConfiguration];
  1598. XCTAssert(configuration);
  1599. @autoreleasepool {
  1600. // And open it successfully.
  1601. RLMRealm *realm = [self openRealmWithConfiguration:configuration];
  1602. [self waitForDownloadsForRealm:realm];
  1603. }
  1604. RLMSyncCredentials *credsB = [RLMObjectServerTests basicCredentialsWithName:@"b" register:YES];
  1605. RLMSyncUser *userB = [self logInUserForCredentials:credsB server:server];
  1606. // Automatic configuration should throw since there's more than one logged-in user.
  1607. XCTAssertThrows([RLMSyncConfiguration automaticConfiguration]);
  1608. // It should still be possible to explicitly retrieve an automatic configuration for a user.
  1609. RLMRealmConfiguration *configurationA = [RLMSyncConfiguration automaticConfigurationForUser:userA];
  1610. XCTAssert(configurationA);
  1611. XCTAssertEqualObjects(configuration.syncConfiguration, configurationA.syncConfiguration);
  1612. RLMRealmConfiguration *configurationB = [RLMSyncConfiguration automaticConfigurationForUser:userB];
  1613. XCTAssert(configurationB);
  1614. XCTAssertNotEqualObjects(configuration.syncConfiguration, configurationB.syncConfiguration);
  1615. [userB logOut];
  1616. // Now that we're back to a single logged-in user, we should be able to retrieve the configuration.
  1617. configuration = [RLMSyncConfiguration automaticConfiguration];
  1618. XCTAssert(configuration);
  1619. }
  1620. #pragma clang diagnostic pop
  1621. #pragma mark - Partial sync
  1622. - (void)waitForKeyPath:(NSString *)keyPath object:(id)object value:(id)value {
  1623. [self waitForExpectations:@[[[XCTKVOExpectation alloc] initWithKeyPath:keyPath object:object expectedValue:value]] timeout:20.0];
  1624. }
  1625. - (void)testPartialSync {
  1626. // Make credentials.
  1627. NSString *name = NSStringFromSelector(_cmd);
  1628. NSURL *server = [RLMObjectServerTests authServerURL];
  1629. // Log in and populate the Realm.
  1630. @autoreleasepool {
  1631. RLMSyncCredentials *creds = [RLMObjectServerTests basicCredentialsWithName:name register:YES];
  1632. RLMSyncUser *user = [self logInUserForCredentials:creds server:server];
  1633. RLMRealmConfiguration *configuration = [user configuration];
  1634. RLMRealm *realm = [self openRealmWithConfiguration:configuration];
  1635. [realm beginWriteTransaction];
  1636. // FIXME: make this less hideous
  1637. // Add ten of each object
  1638. [realm addObject:[PartialSyncObjectA objectWithNumber:0 string:@"realm"]];
  1639. [realm addObject:[PartialSyncObjectA objectWithNumber:1 string:@""]];
  1640. [realm addObject:[PartialSyncObjectA objectWithNumber:2 string:@""]];
  1641. [realm addObject:[PartialSyncObjectA objectWithNumber:3 string:@""]];
  1642. [realm addObject:[PartialSyncObjectA objectWithNumber:4 string:@"realm"]];
  1643. [realm addObject:[PartialSyncObjectA objectWithNumber:5 string:@"sync"]];
  1644. [realm addObject:[PartialSyncObjectA objectWithNumber:6 string:@"partial"]];
  1645. [realm addObject:[PartialSyncObjectA objectWithNumber:7 string:@"partial"]];
  1646. [realm addObject:[PartialSyncObjectA objectWithNumber:8 string:@"partial"]];
  1647. [realm addObject:[PartialSyncObjectA objectWithNumber:9 string:@"partial"]];
  1648. [realm addObject:[PartialSyncObjectB objectWithNumber:0 firstString:@"" secondString:@""]];
  1649. [realm addObject:[PartialSyncObjectB objectWithNumber:1 firstString:@"" secondString:@""]];
  1650. [realm addObject:[PartialSyncObjectB objectWithNumber:2 firstString:@"" secondString:@""]];
  1651. [realm addObject:[PartialSyncObjectB objectWithNumber:3 firstString:@"" secondString:@""]];
  1652. [realm addObject:[PartialSyncObjectB objectWithNumber:4 firstString:@"" secondString:@""]];
  1653. [realm addObject:[PartialSyncObjectB objectWithNumber:5 firstString:@"" secondString:@""]];
  1654. [realm addObject:[PartialSyncObjectB objectWithNumber:6 firstString:@"" secondString:@""]];
  1655. [realm addObject:[PartialSyncObjectB objectWithNumber:7 firstString:@"" secondString:@""]];
  1656. [realm addObject:[PartialSyncObjectB objectWithNumber:8 firstString:@"" secondString:@""]];
  1657. [realm addObject:[PartialSyncObjectB objectWithNumber:9 firstString:@"" secondString:@""]];
  1658. [realm commitWriteTransaction];
  1659. [self waitForUploadsForRealm:realm];
  1660. }
  1661. // Log back in and do partial sync stuff.
  1662. @autoreleasepool {
  1663. RLMSyncCredentials *creds = [RLMObjectServerTests basicCredentialsWithName:name register:NO];
  1664. RLMSyncUser *user = [self logInUserForCredentials:creds server:server];
  1665. RLMRealmConfiguration *configuration = [user configuration];
  1666. RLMRealm *realm = [self openRealmWithConfiguration:configuration];
  1667. // Perform some partial sync queries
  1668. RLMResults *objects = [PartialSyncObjectA objectsInRealm:realm where:@"number > 5"];
  1669. RLMSyncSubscription *subscription = [objects subscribeWithName:@"query"];
  1670. // Wait for the results to become available.
  1671. [self waitForKeyPath:@"state" object:subscription value:@(RLMSyncSubscriptionStateComplete)];
  1672. // Verify that we got what we're looking for
  1673. XCTAssertEqual(objects.count, 4U);
  1674. for (PartialSyncObjectA *object in objects) {
  1675. XCTAssertGreaterThan(object.number, 5);
  1676. XCTAssertEqualObjects(object.string, @"partial");
  1677. }
  1678. // Verify that we didn't get any other objects
  1679. XCTAssertEqual([PartialSyncObjectA allObjectsInRealm:realm].count, objects.count);
  1680. XCTAssertEqual([PartialSyncObjectB allObjectsInRealm:realm].count, 0u);
  1681. // Create a subscription with the same name but a different query. This should trigger an error.
  1682. RLMResults *objects2 = [PartialSyncObjectA objectsInRealm:realm where:@"number < 5"];
  1683. RLMSyncSubscription *subscription2 = [objects2 subscribeWithName:@"query"];
  1684. // Wait for the error to be reported.
  1685. [self waitForKeyPath:@"state" object:subscription2 value:@(RLMSyncSubscriptionStateError)];
  1686. XCTAssertNotNil(subscription2.error);
  1687. // Unsubscribe from the query, and ensure that it correctly transitions to the invalidated state.
  1688. [subscription unsubscribe];
  1689. [self waitForKeyPath:@"state" object:subscription value:@(RLMSyncSubscriptionStateInvalidated)];
  1690. }
  1691. }
  1692. - (RLMRealm *)partialRealmWithName:(SEL)sel {
  1693. NSString *name = NSStringFromSelector(sel);
  1694. NSURL *server = [RLMObjectServerTests authServerURL];
  1695. RLMSyncCredentials *creds = [RLMObjectServerTests basicCredentialsWithName:name register:YES];
  1696. RLMSyncUser *user = [self logInUserForCredentials:creds server:server];
  1697. RLMRealmConfiguration *configuration = [user configuration];
  1698. return [self openRealmWithConfiguration:configuration];
  1699. }
  1700. - (void)testAllSubscriptionsReportsNewlyCreatedSubscription {
  1701. RLMRealm *realm = [self partialRealmWithName:_cmd];
  1702. XCTAssertEqual(0U, realm.subscriptions.count);
  1703. RLMSyncSubscription *subscription = [[PartialSyncObjectA objectsInRealm:realm where:@"number > 5"]
  1704. subscribeWithName:@"query"];
  1705. // Should still be 0 because the subscription is created asynchronously
  1706. XCTAssertEqual(0U, realm.subscriptions.count);
  1707. [self waitForKeyPath:@"state" object:subscription value:@(RLMSyncSubscriptionStateComplete)];
  1708. XCTAssertEqual(1U, realm.subscriptions.count);
  1709. RLMSyncSubscription *subscription2 = realm.subscriptions.firstObject;
  1710. XCTAssertEqualObjects(@"query", subscription2.name);
  1711. XCTAssertEqual(RLMSyncSubscriptionStateComplete, subscription2.state);
  1712. XCTAssertNil(subscription2.error);
  1713. }
  1714. - (void)testAllSubscriptionsDoesNotReportLocalError {
  1715. RLMRealm *realm = [self partialRealmWithName:_cmd];
  1716. RLMSyncSubscription *subscription1 = [[PartialSyncObjectA objectsInRealm:realm where:@"number > 5"]
  1717. subscribeWithName:@"query"];
  1718. [self waitForKeyPath:@"state" object:subscription1 value:@(RLMSyncSubscriptionStateComplete)];
  1719. RLMSyncSubscription *subscription2 = [[PartialSyncObjectA objectsInRealm:realm where:@"number > 6"]
  1720. subscribeWithName:@"query"];
  1721. [self waitForKeyPath:@"state" object:subscription2 value:@(RLMSyncSubscriptionStateError)];
  1722. XCTAssertEqual(1U, realm.subscriptions.count);
  1723. }
  1724. - (void)testAllSubscriptionsReportsServerError {
  1725. RLMRealm *realm = [self partialRealmWithName:_cmd];
  1726. RLMSyncSubscription *subscription = [[PersonObject objectsInRealm:realm where:@"SUBQUERY(parents, $p1, $p1.age < 31 AND SUBQUERY($p1.parents, $p2, $p2.age > 35 AND $p2.name == 'Michael').@count > 0).@count > 0"]
  1727. subscribeWithName:@"query"];
  1728. XCTAssertEqual(0U, realm.subscriptions.count);
  1729. [self waitForKeyPath:@"state" object:subscription value:@(RLMSyncSubscriptionStateError)];
  1730. XCTAssertEqual(1U, realm.subscriptions.count);
  1731. RLMSyncSubscription *subscription2 = realm.subscriptions.lastObject;
  1732. XCTAssertEqualObjects(@"query", subscription2.name);
  1733. XCTAssertEqual(RLMSyncSubscriptionStateError, subscription2.state);
  1734. XCTAssertNotNil(subscription2.error);
  1735. }
  1736. - (void)testUnsubscribeUsingOriginalSubscriptionObservingFetched {
  1737. RLMRealm *realm = [self partialRealmWithName:_cmd];
  1738. RLMSyncSubscription *original = [[PartialSyncObjectA allObjectsInRealm:realm] subscribeWithName:@"query"];
  1739. [self waitForKeyPath:@"state" object:original value:@(RLMSyncSubscriptionStateComplete)];
  1740. XCTAssertEqual(1U, realm.subscriptions.count);
  1741. RLMSyncSubscription *fetched = realm.subscriptions.firstObject;
  1742. [original unsubscribe];
  1743. [self waitForKeyPath:@"state" object:fetched value:@(RLMSyncSubscriptionStateInvalidated)];
  1744. XCTAssertEqual(0U, realm.subscriptions.count);
  1745. XCTAssertEqual(RLMSyncSubscriptionStateInvalidated, original.state);
  1746. }
  1747. - (void)testUnsubscribeUsingFetchedSubscriptionObservingFetched {
  1748. RLMRealm *realm = [self partialRealmWithName:_cmd];
  1749. RLMSyncSubscription *original = [[PartialSyncObjectA allObjectsInRealm:realm] subscribeWithName:@"query"];
  1750. [self waitForKeyPath:@"state" object:original value:@(RLMSyncSubscriptionStateComplete)];
  1751. XCTAssertEqual(1U, realm.subscriptions.count);
  1752. RLMSyncSubscription *fetched = realm.subscriptions.firstObject;
  1753. [fetched unsubscribe];
  1754. [self waitForKeyPath:@"state" object:fetched value:@(RLMSyncSubscriptionStateInvalidated)];
  1755. XCTAssertEqual(0U, realm.subscriptions.count);
  1756. XCTAssertEqual(RLMSyncSubscriptionStateInvalidated, original.state);
  1757. }
  1758. - (void)testUnsubscribeUsingFetchedSubscriptionObservingOriginal {
  1759. RLMRealm *realm = [self partialRealmWithName:_cmd];
  1760. RLMSyncSubscription *original = [[PartialSyncObjectA allObjectsInRealm:realm] subscribeWithName:@"query"];
  1761. [self waitForKeyPath:@"state" object:original value:@(RLMSyncSubscriptionStateComplete)];
  1762. XCTAssertEqual(1U, realm.subscriptions.count);
  1763. RLMSyncSubscription *fetched = realm.subscriptions.firstObject;
  1764. [fetched unsubscribe];
  1765. [self waitForKeyPath:@"state" object:original value:@(RLMSyncSubscriptionStateInvalidated)];
  1766. XCTAssertEqual(0U, realm.subscriptions.count);
  1767. XCTAssertEqual(RLMSyncSubscriptionStateInvalidated, fetched.state);
  1768. }
  1769. - (void)testSubscriptionWithName {
  1770. RLMRealm *realm = [self partialRealmWithName:_cmd];
  1771. XCTAssertNil([realm subscriptionWithName:@"query"]);
  1772. RLMSyncSubscription *subscription = [[PartialSyncObjectA allObjectsInRealm:realm] subscribeWithName:@"query"];
  1773. XCTAssertNil([realm subscriptionWithName:@"query"]);
  1774. [self waitForKeyPath:@"state" object:subscription value:@(RLMSyncSubscriptionStateComplete)];
  1775. XCTAssertNotNil([realm subscriptionWithName:@"query"]);
  1776. XCTAssertNil([realm subscriptionWithName:@"query2"]);
  1777. RLMSyncSubscription *subscription2 = [realm subscriptionWithName:@"query"];
  1778. XCTAssertEqualObjects(@"query", subscription2.name);
  1779. XCTAssertEqual(RLMSyncSubscriptionStateComplete, subscription2.state);
  1780. XCTAssertNil(subscription2.error);
  1781. [subscription unsubscribe];
  1782. XCTAssertNotNil([realm subscriptionWithName:@"query"]);
  1783. [self waitForKeyPath:@"state" object:subscription value:@(RLMSyncSubscriptionStateInvalidated)];
  1784. XCTAssertNil([realm subscriptionWithName:@"query"]);
  1785. XCTAssertEqual(RLMSyncSubscriptionStateInvalidated, subscription2.state);
  1786. }
  1787. - (void)testSortAndFilterSubscriptions {
  1788. RLMRealm *realm = [self partialRealmWithName:_cmd];
  1789. [self waitForKeyPath:@"state" object:[[PartialSyncObjectA allObjectsInRealm:realm] subscribeWithName:@"query 1"]
  1790. value:@(RLMSyncSubscriptionStateComplete)];
  1791. [self waitForKeyPath:@"state" object:[[PartialSyncObjectA allObjectsInRealm:realm] subscribeWithName:@"query 2"]
  1792. value:@(RLMSyncSubscriptionStateComplete)];
  1793. [self waitForKeyPath:@"state" object:[[PartialSyncObjectB allObjectsInRealm:realm] subscribeWithName:@"query 3"]
  1794. value:@(RLMSyncSubscriptionStateComplete)];
  1795. RLMResults *unsupportedQuery = [PersonObject objectsInRealm:realm where:@"SUBQUERY(parents, $p1, $p1.age < 31 AND SUBQUERY($p1.parents, $p2, $p2.age > 35 AND $p2.name == 'Michael').@count > 0).@count > 0"];
  1796. [self waitForKeyPath:@"state" object:[unsupportedQuery subscribeWithName:@"query 4"]
  1797. value:@(RLMSyncSubscriptionStateError)];
  1798. auto subscriptions = realm.subscriptions;
  1799. XCTAssertEqual(4U, subscriptions.count);
  1800. XCTAssertEqual(0U, ([subscriptions objectsWhere:@"name = %@", @"query 0"].count));
  1801. XCTAssertEqualObjects(@"query 1", ([subscriptions objectsWhere:@"name = %@", @"query 1"].firstObject.name));
  1802. XCTAssertEqual(3U, ([subscriptions objectsWhere:@"status = %@", @(RLMSyncSubscriptionStateComplete)].count));
  1803. XCTAssertEqual(1U, ([subscriptions objectsWhere:@"status = %@", @(RLMSyncSubscriptionStateError)].count));
  1804. XCTAssertThrows([subscriptions sortedResultsUsingKeyPath:@"name" ascending:NO]);
  1805. XCTAssertThrows([subscriptions sortedResultsUsingDescriptors:@[]]);
  1806. XCTAssertThrows([subscriptions distinctResultsUsingKeyPaths:@[@"name"]]);
  1807. }
  1808. #pragma mark - Certificate pinning
  1809. - (void)attemptLoginWithUsername:(NSString *)userName callback:(void (^)(RLMSyncUser *, NSError *))callback {
  1810. NSURL *url = [RLMObjectServerTests secureAuthServerURL];
  1811. RLMSyncCredentials *creds = [RLMObjectServerTests basicCredentialsWithName:userName register:YES];
  1812. XCTestExpectation *expectation = [self expectationWithDescription:@"HTTP login"];
  1813. [RLMSyncUser logInWithCredentials:creds authServerURL:url
  1814. onCompletion:^(RLMSyncUser *user, NSError *error) {
  1815. callback(user, error);
  1816. [expectation fulfill];
  1817. }];
  1818. [self waitForExpectationsWithTimeout:4.0 handler:nil];
  1819. }
  1820. - (void)testHTTPSLoginFailsWithoutCertificate {
  1821. [self attemptLoginWithUsername:NSStringFromSelector(_cmd) callback:^(RLMSyncUser *user, NSError *error) {
  1822. XCTAssertNil(user);
  1823. XCTAssertNotNil(error);
  1824. XCTAssertEqualObjects(error.domain, NSURLErrorDomain);
  1825. XCTAssertEqual(error.code, NSURLErrorServerCertificateUntrusted);
  1826. }];
  1827. }
  1828. static NSURL *certificateURL(NSString *filename) {
  1829. return [NSURL fileURLWithPath:[[[@(__FILE__) stringByDeletingLastPathComponent]
  1830. stringByAppendingPathComponent:@"certificates"]
  1831. stringByAppendingPathComponent:filename]];
  1832. }
  1833. - (void)testHTTPSLoginFailsWithIncorrectCertificate {
  1834. RLMSyncManager.sharedManager.pinnedCertificatePaths = @{@"localhost": certificateURL(@"not-localhost.cer")};
  1835. [self attemptLoginWithUsername:NSStringFromSelector(_cmd) callback:^(RLMSyncUser *user, NSError *error) {
  1836. XCTAssertNil(user);
  1837. XCTAssertNotNil(error);
  1838. XCTAssertEqualObjects(error.domain, NSURLErrorDomain);
  1839. XCTAssertEqual(error.code, NSURLErrorServerCertificateUntrusted);
  1840. }];
  1841. }
  1842. - (void)testHTTPSLoginFailsWithInvalidPathToCertificate {
  1843. RLMSyncManager.sharedManager.pinnedCertificatePaths = @{@"localhost": certificateURL(@"nonexistent.pem")};
  1844. [self attemptLoginWithUsername:NSStringFromSelector(_cmd) callback:^(RLMSyncUser *user, NSError *error) {
  1845. XCTAssertNil(user);
  1846. XCTAssertNotNil(error);
  1847. XCTAssertEqualObjects(error.domain, NSCocoaErrorDomain);
  1848. XCTAssertEqual(error.code, NSFileReadNoSuchFileError);
  1849. }];
  1850. }
  1851. - (void)testHTTPSLoginFailsWithDifferentValidCert {
  1852. RLMSyncManager.sharedManager.pinnedCertificatePaths = @{@"localhost": certificateURL(@"localhost-other.cer")};
  1853. [self attemptLoginWithUsername:NSStringFromSelector(_cmd) callback:^(RLMSyncUser *user, NSError *error) {
  1854. XCTAssertNil(user);
  1855. XCTAssertNotNil(error);
  1856. XCTAssertEqualObjects(error.domain, NSURLErrorDomain);
  1857. XCTAssertEqual(error.code, NSURLErrorServerCertificateUntrusted);
  1858. }];
  1859. }
  1860. - (void)testHTTPSLoginFailsWithFileThatIsNotACert {
  1861. RLMSyncManager.sharedManager.pinnedCertificatePaths = @{@"localhost": certificateURL(@"../test-ros-server.js")};
  1862. [self attemptLoginWithUsername:NSStringFromSelector(_cmd) callback:^(RLMSyncUser *user, NSError *error) {
  1863. XCTAssertNil(user);
  1864. XCTAssertNotNil(error);
  1865. XCTAssertEqualObjects(error.domain, NSOSStatusErrorDomain);
  1866. XCTAssertEqual(error.code, errSecUnknownFormat);
  1867. }];
  1868. }
  1869. - (void)testHTTPSLoginDoesNotUseCertificateForDifferentDomain {
  1870. RLMSyncManager.sharedManager.pinnedCertificatePaths = @{@"example.com": certificateURL(@"localhost.cer")};
  1871. [self attemptLoginWithUsername:NSStringFromSelector(_cmd) callback:^(RLMSyncUser *user, NSError *error) {
  1872. XCTAssertNil(user);
  1873. XCTAssertNotNil(error);
  1874. XCTAssertEqualObjects(error.domain, NSURLErrorDomain);
  1875. XCTAssertEqual(error.code, NSURLErrorServerCertificateUntrusted);
  1876. }];
  1877. }
  1878. - (void)testHTTPSLoginSucceedsWithValidSelfSignedCertificate {
  1879. RLMSyncManager.sharedManager.pinnedCertificatePaths = @{@"localhost": certificateURL(@"localhost.cer")};
  1880. [self attemptLoginWithUsername:NSStringFromSelector(_cmd) callback:^(RLMSyncUser *user, NSError *error) {
  1881. XCTAssertNotNil(user);
  1882. XCTAssertNil(error);
  1883. }];
  1884. }
  1885. - (void)testConfigurationFromUserAutomaticallyUsesCert {
  1886. RLMSyncManager.sharedManager.pinnedCertificatePaths = @{@"localhost": certificateURL(@"localhost.cer")};
  1887. __block RLMSyncUser *user;
  1888. [self attemptLoginWithUsername:NSStringFromSelector(_cmd) callback:^(RLMSyncUser *u, NSError *error) {
  1889. XCTAssertNotNil(u);
  1890. XCTAssertNil(error);
  1891. user = u;
  1892. }];
  1893. RLMRealmConfiguration *config = [user configuration];
  1894. XCTAssertEqualObjects(config.syncConfiguration.realmURL.scheme, @"realms");
  1895. XCTAssertEqualObjects(config.syncConfiguration.pinnedCertificateURL, certificateURL(@"localhost.cer"));
  1896. // Verify that we can actually open the Realm
  1897. auto realm = [self openRealmWithConfiguration:config];
  1898. NSError *error;
  1899. [self waitForUploadsForRealm:realm error:&error];
  1900. XCTAssertNil(error);
  1901. }
  1902. - (void)verifyOpenSucceeds:(RLMRealmConfiguration *)config {
  1903. auto realm = [self openRealmWithConfiguration:config];
  1904. NSError *error;
  1905. [self waitForUploadsForRealm:realm error:&error];
  1906. XCTAssertNil(error);
  1907. }
  1908. - (void)verifyOpenFails:(RLMRealmConfiguration *)config {
  1909. [self openRealmWithConfiguration:config];
  1910. XCTestExpectation *expectation = [self expectationWithDescription:@"wait for error"];
  1911. RLMSyncManager.sharedManager.errorHandler = ^(NSError *error, __unused RLMSyncSession *session) {
  1912. XCTAssertTrue([error.domain isEqualToString:RLMSyncErrorDomain]);
  1913. XCTAssertFalse([[error.userInfo[kRLMSyncUnderlyingErrorKey] domain] isEqualToString:RLMSyncErrorDomain]);
  1914. [expectation fulfill];
  1915. };
  1916. [self waitForExpectationsWithTimeout:20.0 handler:nil];
  1917. }
  1918. - (void)testConfigurationFromInsecureUserAutomaticallyUsesCert {
  1919. RLMSyncManager.sharedManager.pinnedCertificatePaths = @{@"localhost": certificateURL(@"localhost.cer")};
  1920. RLMSyncUser *user = [self logInUserForCredentials:[RLMSyncTestCase basicCredentialsWithName:NSStringFromSelector(_cmd)
  1921. register:YES]
  1922. server:[RLMSyncTestCase authServerURL]];
  1923. RLMRealmConfiguration *config = [user configurationWithURL:[NSURL URLWithString:@"realms://localhost:9443/~/default"]];
  1924. XCTAssertEqualObjects(config.syncConfiguration.realmURL.scheme, @"realms");
  1925. XCTAssertEqualObjects(config.syncConfiguration.pinnedCertificateURL, certificateURL(@"localhost.cer"));
  1926. [self verifyOpenSucceeds:config];
  1927. }
  1928. - (void)testOpenSecureRealmWithNoCert {
  1929. RLMSyncUser *user = [self logInUserForCredentials:[RLMSyncTestCase basicCredentialsWithName:NSStringFromSelector(_cmd)
  1930. register:YES]
  1931. server:[RLMSyncTestCase authServerURL]];
  1932. [self verifyOpenFails:[user configurationWithURL:[NSURL URLWithString:@"realms://localhost:9443/~/default"]]];
  1933. }
  1934. - (void)testOpenSecureRealmWithIncorrectCert {
  1935. RLMSyncManager.sharedManager.pinnedCertificatePaths = @{@"localhost": certificateURL(@"not-localhost.cer")};
  1936. RLMSyncUser *user = [self logInUserForCredentials:[RLMSyncTestCase basicCredentialsWithName:NSStringFromSelector(_cmd)
  1937. register:YES]
  1938. server:[RLMSyncTestCase authServerURL]];
  1939. [self verifyOpenFails:[user configurationWithURL:[NSURL URLWithString:@"realms://localhost:9443/~/default"]]];
  1940. }
  1941. - (void)DISABLE_testOpenSecureRealmWithMissingCertFile {
  1942. // FIXME: this currently crashes inside the sync library
  1943. RLMSyncManager.sharedManager.pinnedCertificatePaths = @{@"localhost": certificateURL(@"nonexistent.pem")};
  1944. RLMSyncUser *user = [self logInUserForCredentials:[RLMSyncTestCase basicCredentialsWithName:NSStringFromSelector(_cmd)
  1945. register:YES]
  1946. server:[RLMSyncTestCase authServerURL]];
  1947. [self verifyOpenFails:[user configurationWithURL:[NSURL URLWithString:@"realms://localhost:9443/~/default"]]];
  1948. }
  1949. @end