pEpForiOSTests/NetworkServiceTests.swift
author Xavier Algarra <xavier@pep-project.org>
Tue, 06 Jun 2017 07:49:23 +0200
changeset 2248 75013a586bfd
parent 2238 7cf70731b53f
child 2354 090598342e52
permissions -rw-r--r--
IOS-137 add multiple accounts option with the new login
     1 //
     2 //  NetworkServiceTests.swift
     3 //  pEpForiOS
     4 //
     5 //  Created by hernani on 23/11/16.
     6 //  Copyright © 2016 p≡p Security S.A. All rights reserved.
     7 //
     8 
     9 import XCTest
    10 
    11 import MessageModel
    12 import pEpForiOS
    13 
    14 class NetworkServiceTests: XCTestCase {
    15     
    16     var persistenceSetup: PersistentSetup!
    17 
    18     override func setUp() {
    19         super.setUp()
    20         persistenceSetup = PersistentSetup()
    21     }
    22     
    23     override func tearDown() {
    24         persistenceSetup = nil
    25         CdAccount.sendLayer = nil
    26         super.tearDown()
    27     }
    28 
    29     class NetworkServiceObserver: NetworkServiceDelegate, CustomDebugStringConvertible {
    30         let expSingleAccountSynced: XCTestExpectation?
    31         var expCanceled: XCTestExpectation?
    32         var accountInfo: AccountConnectInfo?
    33 
    34         var debugDescription: String {
    35             return expSingleAccountSynced?.debugDescription ?? "unknown"
    36         }
    37 
    38         let failOnError: Bool
    39 
    40         init(expAccountsSynced: XCTestExpectation? = nil, expCanceled: XCTestExpectation? = nil,
    41              failOnError: Bool = false) {
    42             self.expSingleAccountSynced = expAccountsSynced
    43             self.expCanceled = expCanceled
    44             self.failOnError = failOnError
    45         }
    46 
    47         func didSync(service: NetworkService, accountInfo: AccountConnectInfo,
    48                      errorProtocol: ServiceErrorProtocol) {
    49             Log.info(component: #function, content: "\(self)")
    50             if errorProtocol.hasErrors() && failOnError {
    51                 Log.error(component: #function, error: errorProtocol.error)
    52                 XCTFail()
    53             }
    54             if self.accountInfo == nil {
    55                 self.accountInfo = accountInfo
    56                 expSingleAccountSynced?.fulfill()
    57             }
    58         }
    59 
    60         func didCancel(service: NetworkService) {
    61             expCanceled?.fulfill()
    62         }
    63     }
    64 
    65     class MessageModelObserver: MessageFolderDelegate {
    66         var messages: [Message] {
    67             var messages = [Message]()
    68             for ms in messagesByID.values {
    69                 for m in ms {
    70                     messages.append(m)
    71                 }
    72             }
    73             return messages.sorted { m1, m2 in
    74                 if let d1 = m1.received, let d2 = m2.received {
    75                     return areInIncreasingOrder(d1: d1, d2: d2)
    76                 } else if let d1 = m1.sent, let d2 = m2.sent {
    77                     return areInIncreasingOrder(d1: d1, d2: d2)
    78                 }
    79                 return false
    80             }
    81         }
    82         var messagesByID = [MessageID: [Message]]()
    83         var changedMessagesByID = [MessageID: Message]()
    84 
    85         var hasChangedMessages: Bool {
    86             return !changedMessagesByID.isEmpty
    87         }
    88 
    89         func contains(messageID: MessageID) -> Bool {
    90             return messagesByID[messageID] != nil
    91         }
    92 
    93         func areInIncreasingOrder(d1: Date, d2: Date) -> Bool {
    94             switch d1.compare(d2 as Date) {
    95             case .orderedAscending: return true
    96             default: return false
    97             }
    98         }
    99 
   100         func add(message: Message) {
   101             if let existing = messagesByID[message.uuid] {
   102                 var news = existing
   103                 news.append(message)
   104                 messagesByID[message.uuid] = news
   105             } else {
   106                 messagesByID[message.uuid] = [message]
   107             }
   108         }
   109 
   110         func didChange(messageFolder: MessageFolder) {
   111             if let msg = messageFolder as? Message {
   112                 if msg.isOriginal {
   113                     add(message: msg)
   114                 } else {
   115                     XCTAssertNotNil(messagesByID[msg.messageID])
   116                     add(message: msg)
   117                     changedMessagesByID[msg.messageID] = msg
   118                 }
   119             }
   120         }
   121     }
   122 
   123     class SendLayerObserver: SendLayerDelegate {
   124         let expAccountVerified: XCTestExpectation?
   125         var messageIDs = [String]()
   126 
   127         init(expAccountVerified: XCTestExpectation? = nil) {
   128             self.expAccountVerified = expAccountVerified
   129         }
   130 
   131         func didVerify(cdAccount: CdAccount, error: Error?) {
   132             XCTAssertNil(error)
   133             expAccountVerified?.fulfill()
   134         }
   135 
   136         func didFetch(cdMessage: CdMessage) {
   137             if let msg = cdMessage.message() {
   138                 messageIDs.append(msg.messageID)
   139             } else {
   140                 XCTFail()
   141             }
   142         }
   143 
   144         func didRemove(cdFolder: CdFolder) {
   145             XCTFail()
   146         }
   147 
   148         func didRemove(cdMessage: CdMessage) {
   149             XCTFail()
   150         }
   151     }
   152 
   153     func testSyncOutgoing(useCorrectSmtpAccount: Bool) {
   154         XCTAssertNil(CdAccount.all())
   155         XCTAssertNil(CdFolder.all())
   156         XCTAssertNil(CdMessage.all())
   157 
   158         let modelDelegate = MessageModelObserver()
   159         MessageModelConfig.messageFolderDelegate = modelDelegate
   160 
   161         let sendLayerDelegate = SendLayerObserver()
   162 
   163         let networkService = NetworkService(parentName: #function)
   164         networkService.sleepTimeInSeconds = 2
   165 
   166         // A temp variable is necassary, since the networkServiceDelegate is weak
   167         var del = NetworkServiceObserver(
   168             expAccountsSynced: expectation(description: "expSingleAccountSynced1"),
   169             failOnError: useCorrectSmtpAccount)
   170 
   171         networkService.networkServiceDelegate = del
   172         networkService.sendLayerDelegate = sendLayerDelegate
   173 
   174         let cdAccount = useCorrectSmtpAccount ? TestData().createWorkingCdAccount() :
   175             TestData().createSmtpTimeOutCdAccount()
   176         TestUtil.skipValidation()
   177         Record.saveAndWait()
   178 
   179         networkService.start()
   180 
   181         // Wait for first sync, mainly to have folders
   182         waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
   183             XCTAssertNil(error)
   184         })
   185 
   186         let from = CdIdentity.create()
   187         from.userName = cdAccount.identity?.userName ?? "Unit 004"
   188         from.address = cdAccount.identity?.address ?? "unittest.ios.4@peptest.ch"
   189 
   190         let to = CdIdentity.create()
   191         to.userName = "Unit 001"
   192         to.address = "unittest.ios.1@peptest.ch"
   193 
   194         guard let sentFolder = CdFolder.by(folderType: .sent, account: cdAccount) else {
   195             XCTFail()
   196             return
   197         }
   198         XCTAssertEqual((sentFolder.messages ?? NSSet()).count, 0)
   199 
   200         // Build outgoing emails
   201         var outgoingMails = [CdMessage]()
   202         var outgoingMessageIDs = [String]()
   203         let numMails = 5
   204         for i in 1...numMails {
   205             let message = CdMessage.create()
   206             message.from = from
   207             message.parent = sentFolder
   208             message.shortMessage = "Some subject \(i)"
   209             message.longMessage = "Long message \(i)"
   210             message.longMessageFormatted = "<h1>Long HTML \(i)</h1>"
   211             message.addTo(cdIdentity: to)
   212             let messageID = MessageID.generate()
   213             message.uuid = messageID
   214             outgoingMails.append(message)
   215             outgoingMessageIDs.append(messageID)
   216         }
   217         Record.saveAndWait()
   218 
   219         // Verify outgoing mails
   220         for m in outgoingMails {
   221             XCTAssertEqual(m.parent?.folderType, FolderType.sent.rawValue)
   222             XCTAssertEqual(m.uid, Int32(0))
   223             XCTAssertEqual(m.sendStatus, Int16(SendStatus.none.rawValue))
   224         }
   225 
   226         del = NetworkServiceObserver(
   227             expAccountsSynced: expectation(description: "expSingleAccountSynced2"))
   228         networkService.networkServiceDelegate = del
   229 
   230         // Wait for next sync, to verify outgoing mails
   231         waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
   232             XCTAssertNil(error)
   233         })
   234 
   235         // Check that the sent mails have been deleted
   236         Record.refreshRegisteredObjects(mergeChanges: true)
   237         if useCorrectSmtpAccount {
   238             for m in outgoingMails {
   239                 XCTAssertTrue(m.isDeleted)
   240             }
   241         }
   242 
   243         // Make sure the sent folder will still *not* be synced in the next step
   244         sentFolder.lastLookedAt = Date(
   245             timeIntervalSinceNow: -(networkService.timeIntervalForInterestingFolders + 1))
   246             as NSDate?
   247         Record.saveAndWait()
   248 
   249         // Will the sent folder be synced on next sync?
   250         let accountInfo = AccountConnectInfo(accountID: cdAccount.objectID)
   251         var fis = networkService.currentWorker?.determineInterestingFolders(
   252             accountInfo: accountInfo) ?? []
   253         XCTAssertEqual(fis.count, 1) // still only inbox
   254 
   255         var haveSentFolder = false
   256         for f in fis {
   257             if f.folderType == .sent {
   258                 haveSentFolder = true
   259             }
   260         }
   261         XCTAssertFalse(haveSentFolder)
   262 
   263         // Make sure the sent folder will be synced in the next step
   264         sentFolder.lastLookedAt = Date() as NSDate?
   265         Record.saveAndWait()
   266 
   267         // Will the sent folder be synced on next sync?
   268         fis = networkService.currentWorker?.determineInterestingFolders(
   269             accountInfo: accountInfo) ?? []
   270         XCTAssertGreaterThan(fis.count, 1)
   271 
   272         for f in fis {
   273             if f.folderType == .sent {
   274                 haveSentFolder = true
   275             }
   276         }
   277         XCTAssertTrue(haveSentFolder)
   278 
   279         del = NetworkServiceObserver(
   280             expAccountsSynced: expectation(description: "expSingleAccountSynced3"))
   281         networkService.networkServiceDelegate = del
   282 
   283         // Wait for next sync
   284         waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
   285             Log.info(component: "didSync", content: "expSingleAccountSynced3 timeout?")
   286             XCTAssertNil(error)
   287         })
   288 
   289         TestUtil.checkForUniqueness(uuids: outgoingMessageIDs)
   290         cancelNetworkService(networkService: networkService)
   291     }
   292 
   293     func testSyncOutgoing() {
   294         testSyncOutgoing(useCorrectSmtpAccount: true)
   295     }
   296 
   297     func testSyncOutgoingWithWrongAccount() {
   298         testSyncOutgoing(useCorrectSmtpAccount: false)
   299     }
   300 
   301     func cancelNetworkService(networkService: NetworkService) {
   302         let del = NetworkServiceObserver(
   303             expCanceled: expectation(description: "expCanceled"))
   304         networkService.networkServiceDelegate = del
   305         networkService.cancel()
   306 
   307         // Wait for cancellation
   308         waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
   309             XCTAssertNil(error)
   310         })
   311     }
   312 
   313     func testSyncOneTime() {
   314         XCTAssertNil(CdAccount.all())
   315         XCTAssertNil(CdFolder.all())
   316         XCTAssertNil(CdMessage.all())
   317 
   318         let modelDelegate = MessageModelObserver()
   319         MessageModelConfig.messageFolderDelegate = modelDelegate
   320 
   321         let sendLayerDelegate = SendLayerObserver()
   322 
   323         let networkService = NetworkService(parentName: #function)
   324 
   325         let del = NetworkServiceObserver(
   326             expAccountsSynced: expectation(description: "expSingleAccountSynced"))
   327         networkService.networkServiceDelegate = del
   328 
   329         networkService.sendLayerDelegate = sendLayerDelegate
   330 
   331         _ = TestData().createWorkingCdAccount()
   332         TestUtil.skipValidation()
   333         Record.saveAndWait()
   334 
   335         networkService.start()
   336 
   337         waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
   338             XCTAssertNil(error)
   339         })
   340 
   341         XCTAssertNotNil(del.accountInfo)
   342         XCTAssertNotNil(CdFolder.all())
   343         XCTAssertNotNil(CdMessage.all())
   344 
   345         guard let cdFolder = CdFolder.first(
   346             attributes: ["folderType": FolderType.inbox.rawValue]) else {
   347                 XCTFail()
   348                 return
   349         }
   350         XCTAssertGreaterThan(cdFolder.messages?.count ?? 0, 0)
   351         let allCdMessages = cdFolder.messages?.sortedArray(
   352             using: [NSSortDescriptor(key: "uid", ascending: true)]) as? [CdMessage] ?? []
   353         XCTAssertGreaterThan(allCdMessages.count, 0)
   354         var cdDecryptAgainCount = 0
   355         for cdMsg in allCdMessages {
   356             guard let parentF = cdMsg.parent else {
   357                 XCTFail()
   358                 continue
   359             }
   360             XCTAssertEqual(parentF.folderType, FolderType.inbox.rawValue)
   361             if cdMsg.pEpRating == PEPUtil.pEpRatingNone {
   362                 cdDecryptAgainCount += 1
   363             }
   364         }
   365         XCTAssertGreaterThan(allCdMessages.count, cdDecryptAgainCount)
   366 
   367         let unifiedInbox = Folder.unifiedInbox()
   368 
   369         let unifiedMessageCount = unifiedInbox.messageCount()
   370         XCTAssertGreaterThan(unifiedMessageCount, 0)
   371         for i in 0..<unifiedMessageCount {
   372             guard let msg = unifiedInbox.messageAt(index: i) else {
   373                 XCTFail()
   374                 continue
   375             }
   376             XCTAssertNotNil(msg.shortMessage)
   377             XCTAssertTrue(
   378                 msg.longMessage != nil || msg.longMessageFormatted != nil ||
   379                     msg.attachments.count > 0)
   380             let pEpRating = Int16(msg.pEpRatingInt ?? -1)
   381             XCTAssertNotEqual(pEpRating, PEPUtil.pEpRatingNone)
   382             if !modelDelegate.contains(messageID: msg.messageID) {
   383                 XCTFail()
   384             }
   385         }
   386 
   387         let inbox = Folder.from(cdFolder: cdFolder)
   388         XCTAssertGreaterThanOrEqual(sendLayerDelegate.messageIDs.count, unifiedMessageCount)
   389         XCTAssertEqual(modelDelegate.messages.count, unifiedMessageCount)
   390 
   391         for msg in modelDelegate.messages {
   392             XCTAssertTrue(msg.isOriginal)
   393             XCTAssertTrue(sendLayerDelegate.messageIDs.contains(msg.messageID))
   394             XCTAssertTrue(inbox.contains(message: msg))
   395             if !unifiedInbox.contains(message: msg) {
   396                 XCTFail()
   397             }
   398         }
   399         XCTAssertFalse(modelDelegate.hasChangedMessages)
   400 
   401         cancelNetworkService(networkService: networkService)
   402     }
   403 
   404     func testCancelSyncImmediately() {
   405         XCTAssertNil(CdAccount.all())
   406         XCTAssertNil(CdFolder.all())
   407         XCTAssertNil(CdMessage.all())
   408 
   409         let networkService = NetworkService(parentName: #function)
   410 
   411         _ = TestData().createWorkingCdAccount()
   412         TestUtil.skipValidation()
   413         Record.saveAndWait()
   414 
   415         for _ in 0...10 {
   416             networkService.start()
   417             cancelNetworkService(networkService: networkService)
   418         }
   419 
   420         XCTAssertNil(CdFolder.all())
   421         XCTAssertNil(CdMessage.all())
   422     }
   423 
   424     class Backgrounder: BackgroundTaskProtocol {
   425         let expBackgrounded: XCTestExpectation?
   426         let taskName: String?
   427         var currentTaskID = 1
   428         var taskIDs = [BackgroundTaskID: String]()
   429 
   430         init(taskName: String? = nil, expBackgrounded: XCTestExpectation? = nil) {
   431             self.expBackgrounded = expBackgrounded
   432             self.taskName = taskName
   433         }
   434 
   435         func beginBackgroundTask(taskName: String?,
   436                                  expirationHandler: (() -> Void)?) -> BackgroundTaskID {
   437             let taskID = currentTaskID
   438             taskIDs[taskID] = taskName
   439             currentTaskID += 1
   440             return taskID
   441         }
   442 
   443         func endBackgroundTask(_ taskID: BackgroundTaskID?) {
   444             if let theID = taskID, let theTaskName = taskIDs[theID] {
   445                 if theTaskName == taskName {
   446                     expBackgrounded?.fulfill()
   447                 }
   448             } else {
   449                 XCTFail()
   450             }
   451         }
   452     }
   453 
   454     class MySelfObserver: KickOffMySelfProtocol {
   455         let expMySelfed: XCTestExpectation?
   456         let queue = LimitedOperationQueue()
   457         let backgrounder: Backgrounder
   458 
   459         init(expMySelfed: XCTestExpectation?, expBackgrounded: XCTestExpectation?) {
   460             self.expMySelfed = expMySelfed
   461             backgrounder = Backgrounder(
   462                 taskName: MySelfOperation.taskNameSubOperation, expBackgrounded: expBackgrounded)
   463         }
   464 
   465         func startMySelf() {
   466             let op = MySelfOperation(backgrounder: backgrounder)
   467             op.completionBlock = {
   468                 self.expMySelfed?.fulfill()
   469             }
   470             queue.addOperation(op)
   471         }
   472     }
   473 }