SwiftObjectServerTests.swift 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  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 XCTest
  19. import RealmSwift
  20. // Used by testOfflineClientReset
  21. // The naming here is nonstandard as the sync-1.x.realm test file comes from the .NET unit tests.
  22. // swiftlint:disable identifier_name
  23. @objc(Person)
  24. class Person: Object {
  25. @objc dynamic var FirstName: String?
  26. @objc dynamic var LastName: String?
  27. override class func shouldIncludeInDefaultSchema() -> Bool { return false }
  28. }
  29. class SwiftObjectServerTests: SwiftSyncTestCase {
  30. /// It should be possible to successfully open a Realm configured for sync.
  31. func testBasicSwiftSync() {
  32. let url = URL(string: "realm://127.0.0.1:9080/~/testBasicSync")!
  33. do {
  34. let user = try synchronouslyLogInUser(for: basicCredentials(register: true), server: authURL)
  35. let realm = try synchronouslyOpenRealm(url: url, user: user)
  36. XCTAssert(realm.isEmpty, "Freshly synced Realm was not empty...")
  37. } catch {
  38. XCTFail("Got an error: \(error)")
  39. }
  40. }
  41. /// If client B adds objects to a Realm, client A should see those new objects.
  42. func testSwiftAddObjects() {
  43. do {
  44. let user = try synchronouslyLogInUser(for: basicCredentials(register: isParent), server: authURL)
  45. let realm = try synchronouslyOpenRealm(url: realmURL, user: user)
  46. if isParent {
  47. waitForDownloads(for: realm)
  48. checkCount(expected: 0, realm, SwiftSyncObject.self)
  49. executeChild()
  50. waitForDownloads(for: realm)
  51. checkCount(expected: 3, realm, SwiftSyncObject.self)
  52. } else {
  53. // Add objects
  54. try realm.write {
  55. realm.add(SwiftSyncObject(value: ["child-1"]))
  56. realm.add(SwiftSyncObject(value: ["child-2"]))
  57. realm.add(SwiftSyncObject(value: ["child-3"]))
  58. }
  59. waitForUploads(for: realm)
  60. checkCount(expected: 3, realm, SwiftSyncObject.self)
  61. }
  62. } catch {
  63. XCTFail("Got an error: \(error) (process: \(isParent ? "parent" : "child"))")
  64. }
  65. }
  66. /// If client B removes objects from a Realm, client A should see those changes.
  67. func testSwiftDeleteObjects() {
  68. do {
  69. let user = try synchronouslyLogInUser(for: basicCredentials(register: isParent), server: authURL)
  70. let realm = try synchronouslyOpenRealm(url: realmURL, user: user)
  71. if isParent {
  72. try realm.write {
  73. realm.add(SwiftSyncObject(value: ["child-1"]))
  74. realm.add(SwiftSyncObject(value: ["child-2"]))
  75. realm.add(SwiftSyncObject(value: ["child-3"]))
  76. }
  77. waitForUploads(for: realm)
  78. checkCount(expected: 3, realm, SwiftSyncObject.self)
  79. executeChild()
  80. waitForDownloads(for: realm)
  81. checkCount(expected: 0, realm, SwiftSyncObject.self)
  82. } else {
  83. try realm.write {
  84. realm.deleteAll()
  85. }
  86. waitForUploads(for: realm)
  87. checkCount(expected: 0, realm, SwiftSyncObject.self)
  88. }
  89. } catch {
  90. XCTFail("Got an error: \(error) (process: \(isParent ? "parent" : "child"))")
  91. }
  92. }
  93. func testConnectionState() {
  94. let user = try! synchronouslyLogInUser(for: basicCredentials(register: true), server: authURL)
  95. let realm = try! synchronouslyOpenRealm(url: realmURL, user: user)
  96. let session = realm.syncSession!
  97. func wait(forState desiredState: SyncSession.ConnectionState) {
  98. let ex = expectation(description: "Wait for connection state: \(desiredState)")
  99. let token = session.observe(\SyncSession.connectionState, options: .initial) { s, _ in
  100. if s.connectionState == desiredState {
  101. ex.fulfill()
  102. }
  103. }
  104. waitForExpectations(timeout: 2.0)
  105. token.invalidate()
  106. }
  107. wait(forState: .connected)
  108. session.suspend()
  109. wait(forState: .disconnected)
  110. session.resume()
  111. wait(forState: .connecting)
  112. wait(forState: .connected)
  113. }
  114. // MARK: - Client reset
  115. func testClientReset() {
  116. do {
  117. let user = try synchronouslyLogInUser(for: basicCredentials(register: isParent), server: authURL)
  118. let realm = try synchronouslyOpenRealm(url: realmURL, user: user)
  119. var theError: SyncError?
  120. let ex = expectation(description: "Waiting for error handler to be called...")
  121. SyncManager.shared.errorHandler = { (error, session) in
  122. if let error = error as? SyncError {
  123. theError = error
  124. } else {
  125. XCTFail("Error \(error) was not a sync error. Something is wrong.")
  126. }
  127. ex.fulfill()
  128. }
  129. user.simulateClientResetError(forSession: realmURL)
  130. waitForExpectations(timeout: 10, handler: nil)
  131. XCTAssertNotNil(theError)
  132. XCTAssertTrue(theError!.code == SyncError.Code.clientResetError)
  133. let resetInfo = theError!.clientResetInfo()
  134. XCTAssertNotNil(resetInfo)
  135. XCTAssertTrue(resetInfo!.0.contains("io.realm.object-server-recovered-realms/recovered_realm"))
  136. XCTAssertNotNil(realm)
  137. } catch {
  138. XCTFail("Got an error: \(error) (process: \(isParent ? "parent" : "child"))")
  139. }
  140. }
  141. func testClientResetManualInitiation() {
  142. do {
  143. let user = try synchronouslyLogInUser(for: basicCredentials(register: isParent), server: authURL)
  144. var theError: SyncError?
  145. try autoreleasepool {
  146. let realm = try synchronouslyOpenRealm(url: realmURL, user: user)
  147. let ex = expectation(description: "Waiting for error handler to be called...")
  148. SyncManager.shared.errorHandler = { (error, session) in
  149. if let error = error as? SyncError {
  150. theError = error
  151. } else {
  152. XCTFail("Error \(error) was not a sync error. Something is wrong.")
  153. }
  154. ex.fulfill()
  155. }
  156. user.simulateClientResetError(forSession: realmURL)
  157. waitForExpectations(timeout: 10, handler: nil)
  158. XCTAssertNotNil(theError)
  159. XCTAssertNotNil(realm)
  160. }
  161. let (path, errorToken) = theError!.clientResetInfo()!
  162. XCTAssertFalse(FileManager.default.fileExists(atPath: path))
  163. SyncSession.immediatelyHandleError(errorToken)
  164. XCTAssertTrue(FileManager.default.fileExists(atPath: path))
  165. } catch {
  166. XCTFail("Got an error: \(error) (process: \(isParent ? "parent" : "child"))")
  167. }
  168. }
  169. // MARK: - Progress notifiers
  170. let bigObjectCount = 2
  171. func populateRealm(user: SyncUser, url: URL) {
  172. let realm = try! synchronouslyOpenRealm(url: realmURL, user: user)
  173. try! realm.write {
  174. for _ in 0..<bigObjectCount {
  175. realm.add(SwiftHugeSyncObject())
  176. }
  177. }
  178. waitForUploads(for: realm)
  179. checkCount(expected: bigObjectCount, realm, SwiftHugeSyncObject.self)
  180. }
  181. func testStreamingDownloadNotifier() {
  182. let user = try! synchronouslyLogInUser(for: basicCredentials(register: isParent), server: authURL)
  183. if !isParent {
  184. populateRealm(user: user, url: realmURL)
  185. return
  186. }
  187. var callCount = 0
  188. var transferred = 0
  189. var transferrable = 0
  190. let realm = try! synchronouslyOpenRealm(url: realmURL, user: user)
  191. let session = realm.syncSession
  192. XCTAssertNotNil(session)
  193. let ex = expectation(description: "streaming-downloads-expectation")
  194. var hasBeenFulfilled = false
  195. let token = session!.addProgressNotification(for: .download, mode: .reportIndefinitely) { p in
  196. callCount += 1
  197. XCTAssert(p.transferredBytes >= transferred)
  198. XCTAssert(p.transferrableBytes >= transferrable)
  199. transferred = p.transferredBytes
  200. transferrable = p.transferrableBytes
  201. if p.transferredBytes > 0 && p.isTransferComplete && !hasBeenFulfilled {
  202. ex.fulfill()
  203. hasBeenFulfilled = true
  204. }
  205. }
  206. // Wait for the child process to upload all the data.
  207. executeChild()
  208. waitForExpectations(timeout: 10.0, handler: nil)
  209. token!.invalidate()
  210. XCTAssert(callCount > 1)
  211. XCTAssert(transferred >= transferrable)
  212. }
  213. func testStreamingUploadNotifier() {
  214. do {
  215. var transferred = 0
  216. var transferrable = 0
  217. let user = try synchronouslyLogInUser(for: basicCredentials(register: isParent), server: authURL)
  218. let realm = try synchronouslyOpenRealm(url: realmURL, user: user)
  219. let session = realm.syncSession
  220. XCTAssertNotNil(session)
  221. var ex = expectation(description: "initial upload")
  222. let token = session!.addProgressNotification(for: .upload, mode: .reportIndefinitely) { p in
  223. XCTAssert(p.transferredBytes >= transferred)
  224. XCTAssert(p.transferrableBytes >= transferrable)
  225. transferred = p.transferredBytes
  226. transferrable = p.transferrableBytes
  227. if p.transferredBytes > 0 && p.isTransferComplete {
  228. ex.fulfill()
  229. }
  230. }
  231. waitForExpectations(timeout: 10.0, handler: nil)
  232. ex = expectation(description: "write transaction upload")
  233. try realm.write {
  234. for _ in 0..<bigObjectCount {
  235. realm.add(SwiftHugeSyncObject())
  236. }
  237. }
  238. waitForExpectations(timeout: 10.0, handler: nil)
  239. token!.invalidate()
  240. XCTAssert(transferred >= transferrable)
  241. } catch {
  242. XCTFail("Got an error: \(error) (process: \(isParent ? "parent" : "child"))")
  243. }
  244. }
  245. // MARK: - Download Realm
  246. func testDownloadRealm() {
  247. let user = try! synchronouslyLogInUser(for: basicCredentials(register: isParent), server: authURL)
  248. if !isParent {
  249. populateRealm(user: user, url: realmURL)
  250. return
  251. }
  252. // Wait for the child process to upload everything.
  253. executeChild()
  254. let ex = expectation(description: "download-realm")
  255. let config = user.configuration(realmURL: realmURL, fullSynchronization: true)
  256. let pathOnDisk = ObjectiveCSupport.convert(object: config).pathOnDisk
  257. XCTAssertFalse(FileManager.default.fileExists(atPath: pathOnDisk))
  258. Realm.asyncOpen(configuration: config) { realm, error in
  259. XCTAssertNil(error)
  260. self.checkCount(expected: self.bigObjectCount, realm!, SwiftHugeSyncObject.self)
  261. ex.fulfill()
  262. }
  263. func fileSize(path: String) -> Int {
  264. if let attr = try? FileManager.default.attributesOfItem(atPath: path) {
  265. return attr[.size] as! Int
  266. }
  267. return 0
  268. }
  269. XCTAssertFalse(RLMHasCachedRealmForPath(pathOnDisk))
  270. waitForExpectations(timeout: 10.0, handler: nil)
  271. XCTAssertGreaterThan(fileSize(path: pathOnDisk), 0)
  272. XCTAssertFalse(RLMHasCachedRealmForPath(pathOnDisk))
  273. }
  274. func testDownloadRealmToCustomPath() {
  275. let user = try! synchronouslyLogInUser(for: basicCredentials(register: isParent), server: authURL)
  276. if !isParent {
  277. populateRealm(user: user, url: realmURL)
  278. return
  279. }
  280. // Wait for the child process to upload everything.
  281. executeChild()
  282. let ex = expectation(description: "download-realm")
  283. let customFileURL = realmURLForFile("copy")
  284. var config = user.configuration(realmURL: realmURL, fullSynchronization: true)
  285. config.fileURL = customFileURL
  286. let pathOnDisk = ObjectiveCSupport.convert(object: config).pathOnDisk
  287. XCTAssertEqual(pathOnDisk, customFileURL.path)
  288. XCTAssertFalse(FileManager.default.fileExists(atPath: pathOnDisk))
  289. Realm.asyncOpen(configuration: config) { realm, error in
  290. XCTAssertNil(error)
  291. self.checkCount(expected: self.bigObjectCount, realm!, SwiftHugeSyncObject.self)
  292. ex.fulfill()
  293. }
  294. func fileSize(path: String) -> Int {
  295. if let attr = try? FileManager.default.attributesOfItem(atPath: path) {
  296. return attr[.size] as! Int
  297. }
  298. return 0
  299. }
  300. XCTAssertFalse(RLMHasCachedRealmForPath(pathOnDisk))
  301. waitForExpectations(timeout: 10.0, handler: nil)
  302. XCTAssertGreaterThan(fileSize(path: pathOnDisk), 0)
  303. XCTAssertFalse(RLMHasCachedRealmForPath(pathOnDisk))
  304. }
  305. func testCancelDownloadRealm() {
  306. let user = try! synchronouslyLogInUser(for: basicCredentials(register: isParent), server: authURL)
  307. if !isParent {
  308. populateRealm(user: user, url: realmURL)
  309. return
  310. }
  311. // Wait for the child process to upload everything.
  312. executeChild()
  313. // Use a serial queue for asyncOpen to ensure that the first one adds
  314. // the completion block before the second one cancels it
  315. RLMSetAsyncOpenQueue(DispatchQueue(label: "io.realm.asyncOpen"))
  316. let ex = expectation(description: "async open")
  317. let config = user.configuration(realmURL: realmURL, fullSynchronization: true)
  318. Realm.asyncOpen(configuration: config) { _, error in
  319. XCTAssertNotNil(error)
  320. ex.fulfill()
  321. }
  322. let task = Realm.asyncOpen(configuration: config) { _, _ in
  323. XCTFail("Cancelled completion handler was called")
  324. }
  325. task.cancel()
  326. waitForExpectations(timeout: 10.0, handler: nil)
  327. }
  328. func testAsyncOpenProgress() {
  329. let user = try! synchronouslyLogInUser(for: basicCredentials(register: isParent), server: authURL)
  330. if !isParent {
  331. populateRealm(user: user, url: realmURL)
  332. return
  333. }
  334. // Wait for the child process to upload everything.
  335. executeChild()
  336. let ex1 = expectation(description: "async open")
  337. let ex2 = expectation(description: "download progress")
  338. let config = user.configuration(realmURL: realmURL, fullSynchronization: true)
  339. let task = Realm.asyncOpen(configuration: config) { _, error in
  340. XCTAssertNil(error)
  341. ex1.fulfill()
  342. }
  343. task.addProgressNotification { progress in
  344. if progress.isTransferComplete {
  345. ex2.fulfill()
  346. }
  347. }
  348. waitForExpectations(timeout: 10.0, handler: nil)
  349. }
  350. func testAsyncOpenTimeout() {
  351. let syncTimeoutOptions = SyncTimeoutOptions()
  352. syncTimeoutOptions.connectTimeout = 3000
  353. SyncManager.shared.timeoutOptions = syncTimeoutOptions
  354. // The server proxy adds a 2 second delay, so a 3 second timeout should succeed
  355. autoreleasepool {
  356. let user = try! synchronouslyLogInUser(for: basicCredentials(register: true), server: slowConnectAuthURL)
  357. let config = user.configuration(cancelAsyncOpenOnNonFatalErrors: true)
  358. let ex = expectation(description: "async open")
  359. Realm.asyncOpen(configuration: config) { _, error in
  360. XCTAssertNil(error)
  361. ex.fulfill()
  362. }
  363. waitForExpectations(timeout: 10.0, handler: nil)
  364. user.logOut()
  365. }
  366. self.resetSyncManager()
  367. self.setupSyncManager()
  368. // and a 1 second timeout should fail
  369. autoreleasepool {
  370. let user = try! synchronouslyLogInUser(for: basicCredentials(register: true), server: slowConnectAuthURL)
  371. let config = user.configuration(cancelAsyncOpenOnNonFatalErrors: true)
  372. syncTimeoutOptions.connectTimeout = 1000
  373. SyncManager.shared.timeoutOptions = syncTimeoutOptions
  374. let ex = expectation(description: "async open")
  375. Realm.asyncOpen(configuration: config) { _, error in
  376. XCTAssertNotNil(error)
  377. if let error = error as NSError? {
  378. XCTAssertEqual(error.code, Int(ETIMEDOUT))
  379. XCTAssertEqual(error.domain, NSPOSIXErrorDomain)
  380. }
  381. ex.fulfill()
  382. }
  383. waitForExpectations(timeout: 4.0, handler: nil)
  384. }
  385. }
  386. // MARK: - Administration
  387. func testRetrieveUserInfo() {
  388. let adminUsername = "jyaku.swift"
  389. let nonAdminUsername = "meela.swift@realm.example.org"
  390. let password = "p"
  391. let server = SwiftObjectServerTests.authServerURL()
  392. // Create a non-admin user.
  393. _ = logInUser(for: .init(username: nonAdminUsername, password: password, register: true),
  394. server: server)
  395. // Create an admin user.
  396. let adminUser = createAdminUser(for: server, username: adminUsername)
  397. // Look up information about the non-admin user from the admin user.
  398. let ex = expectation(description: "Should be able to look up user information")
  399. adminUser.retrieveInfo(forUser: nonAdminUsername, identityProvider: .usernamePassword) { (userInfo, err) in
  400. XCTAssertNil(err)
  401. XCTAssertNotNil(userInfo)
  402. guard let userInfo = userInfo else {
  403. return
  404. }
  405. let account = userInfo.accounts.first!
  406. XCTAssertEqual(account.providerUserIdentity, nonAdminUsername)
  407. XCTAssertEqual(account.provider, Provider.usernamePassword)
  408. XCTAssertFalse(userInfo.isAdmin)
  409. ex.fulfill()
  410. }
  411. waitForExpectations(timeout: 10.0, handler: nil)
  412. }
  413. // MARK: - Authentication
  414. func testInvalidCredentials() {
  415. do {
  416. let username = "testInvalidCredentialsUsername"
  417. let credentials = SyncCredentials.usernamePassword(username: username,
  418. password: "THIS_IS_A_PASSWORD",
  419. register: true)
  420. _ = try synchronouslyLogInUser(for: credentials, server: authURL)
  421. // Now log in the same user, but with a bad password.
  422. let ex = expectation(description: "wait for user login")
  423. let credentials2 = SyncCredentials.usernamePassword(username: username, password: "NOT_A_VALID_PASSWORD")
  424. SyncUser.logIn(with: credentials2, server: authURL) { user, error in
  425. XCTAssertNil(user)
  426. XCTAssertTrue(error is SyncAuthError)
  427. let castError = error as! SyncAuthError
  428. XCTAssertEqual(castError.code, SyncAuthError.invalidCredential)
  429. ex.fulfill()
  430. }
  431. waitForExpectations(timeout: 2.0, handler: nil)
  432. } catch {
  433. XCTFail("Got an error: \(error) (process: \(isParent ? "parent" : "child"))")
  434. }
  435. }
  436. // MARK: - User-specific functionality
  437. func testUserExpirationCallback() {
  438. do {
  439. let user = try synchronouslyLogInUser(for: basicCredentials(), server: authURL)
  440. // Set a callback on the user
  441. var blockCalled = false
  442. let ex = expectation(description: "Error callback should fire upon receiving an error")
  443. user.errorHandler = { (u, error) in
  444. XCTAssertEqual(u.identity, user.identity)
  445. XCTAssertEqual(error.code, .accessDeniedOrInvalidPath)
  446. blockCalled = true
  447. ex.fulfill()
  448. }
  449. // Screw up the token on the user.
  450. manuallySetRefreshToken(for: user, value: "not-a-real-token")
  451. // Try to open a Realm with the user; this will cause our errorHandler block defined above to be fired.
  452. XCTAssertFalse(blockCalled)
  453. _ = try immediatelyOpenRealm(url: realmURL, user: user)
  454. waitForExpectations(timeout: 10.0, handler: nil)
  455. XCTAssertEqual(user.state, .loggedOut)
  456. } catch {
  457. XCTFail("Got an error: \(error) (process: \(isParent ? "parent" : "child"))")
  458. }
  459. }
  460. // MARK: - Offline client reset
  461. func testOfflineClientReset() {
  462. let user = try! synchronouslyLogInUser(for: basicCredentials(), server: authURL)
  463. let sourceFileURL = Bundle(for: type(of: self)).url(forResource: "sync-1.x", withExtension: "realm")!
  464. let fileName = "\(UUID()).realm"
  465. let fileURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(fileName)
  466. try! FileManager.default.copyItem(at: sourceFileURL, to: fileURL)
  467. let syncConfig = ObjectiveCSupport.convert(object: user.configuration(realmURL: realmURL, fullSynchronization: true).syncConfiguration!)
  468. syncConfig.customFileURL = fileURL
  469. let config = Realm.Configuration(syncConfiguration: ObjectiveCSupport.convert(object: syncConfig))
  470. do {
  471. _ = try Realm(configuration: config)
  472. } catch let e as Realm.Error where e.code == .incompatibleSyncedFile {
  473. var backupConfiguration = e.backupConfiguration
  474. XCTAssertNotNil(backupConfiguration)
  475. // Open the backup Realm with a schema subset since it was created using the schema from .NET's unit tests.
  476. backupConfiguration!.objectTypes = [Person.self]
  477. let backupRealm = try! Realm(configuration: backupConfiguration!)
  478. let people = backupRealm.objects(Person.self)
  479. XCTAssertEqual(people.count, 1)
  480. XCTAssertEqual(people[0].FirstName, "John")
  481. XCTAssertEqual(people[0].LastName, "Smith")
  482. // Verify that we can now successfully open the original synced Realm.
  483. _ = try! Realm(configuration: config)
  484. } catch {
  485. fatalError("Unexpected error: \(error)")
  486. }
  487. }
  488. // MARK: - Certificate Pinning
  489. func testSecureConnectionToLocalhostWithDefaultSecurity() {
  490. let user = try! synchronouslyLogInUser(for: basicCredentials(), server: authURL)
  491. let config = user.configuration(realmURL: URL(string: "realms://localhost:9443/~/default"),
  492. serverValidationPolicy: .system)
  493. let ex = expectation(description: "Waiting for error handler to be called")
  494. SyncManager.shared.errorHandler = { (error, session) in
  495. ex.fulfill()
  496. }
  497. _ = try! Realm(configuration: config)
  498. self.waitForExpectations(timeout: 4.0)
  499. }
  500. func testSecureConnectionToLocalhostWithValidationDisabled() {
  501. let user = try! synchronouslyLogInUser(for: basicCredentials(), server: authURL)
  502. let config = user.configuration(realmURL: URL(string: "realms://localhost:9443/~/default"),
  503. serverValidationPolicy: .none)
  504. SyncManager.shared.errorHandler = { (error, session) in
  505. XCTFail("Unexpected connection failure: \(error)")
  506. }
  507. let realm = try! Realm(configuration: config)
  508. self.waitForUploads(for: realm)
  509. }
  510. func testSecureConnectionToLocalhostWithPinnedCertificate() {
  511. let user = try! synchronouslyLogInUser(for: basicCredentials(), server: authURL)
  512. let certURL = URL(string: #file)!
  513. .deletingLastPathComponent()
  514. .appendingPathComponent("certificates")
  515. .appendingPathComponent("localhost.cer")
  516. let config = user.configuration(realmURL: URL(string: "realms://localhost:9443/~/default"),
  517. serverValidationPolicy: .pinCertificate(path: certURL))
  518. SyncManager.shared.errorHandler = { (error, session) in
  519. XCTFail("Unexpected connection failure: \(error)")
  520. }
  521. let realm = try! Realm(configuration: config)
  522. self.waitForUploads(for: realm)
  523. }
  524. func testSecureConnectionToLocalhostWithIncorrectPinnedCertificate() {
  525. let user = try! synchronouslyLogInUser(for: basicCredentials(), server: authURL)
  526. let certURL = URL(string: #file)!
  527. .deletingLastPathComponent()
  528. .appendingPathComponent("certificates")
  529. .appendingPathComponent("localhost-other.cer")
  530. let config = user.configuration(realmURL: URL(string: "realms://localhost:9443/~/default"),
  531. serverValidationPolicy: .pinCertificate(path: certURL))
  532. let ex = expectation(description: "Waiting for error handler to be called")
  533. SyncManager.shared.errorHandler = { (error, session) in
  534. ex.fulfill()
  535. }
  536. _ = try! Realm(configuration: config)
  537. self.waitForExpectations(timeout: 4.0)
  538. }
  539. private func realmURLForFile(_ fileName: String) -> URL {
  540. let testDir = RLMRealmPathForFile("realm-object-server")
  541. let directory = URL(fileURLWithPath: testDir, isDirectory: true)
  542. return directory.appendingPathComponent(fileName, isDirectory: false)
  543. }
  544. }