RLMObjectServerTests.mm 124 KB

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