This repository has been archived by the owner. It is now read-only.
Permalink
Cannot retrieve contributors at this time
434 lines (345 sloc)
24 KB
| /* This Source Code Form is subject to the terms of the Mozilla Public | |
| * License, v. 2.0. If a copy of the MPL was not distributed with this | |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
| import Foundation | |
| import Shared | |
| import Deferred | |
| @testable import Sync | |
| @testable import Storage | |
| import XCTest | |
| import SwiftyJSON | |
| class MockStorage: LocalItemSource, MirrorItemSource, SyncableBookmarks { | |
| var local: [GUID: BookmarkMirrorItem] = [:] | |
| var localAdditions: [GUID] = [] | |
| var localDeletions: [GUID] = [] | |
| var lastBufferUpdatedCompletionOpApplied: BufferUpdatedCompletionOp? | |
| // LocalItemSource methods. | |
| func getLocalItemWithGUID(_ guid: GUID) -> Deferred<Maybe<BookmarkMirrorItem>> { | |
| guard let item = self.local[guid] else { | |
| return deferMaybe(DatabaseError(description: "Couldn't find item \(guid).")) | |
| } | |
| return deferMaybe(item) | |
| } | |
| func getLocalItemsWithGUIDs<T: Collection>(_ guids: T) -> Deferred<Maybe<[GUID: BookmarkMirrorItem]>> where T.Iterator.Element == GUID { | |
| var acc: [GUID: BookmarkMirrorItem] = [:] | |
| guids.forEach { guid in | |
| if let item = self.local[guid] { | |
| acc[guid] = item | |
| } | |
| } | |
| return deferMaybe(acc) | |
| } | |
| func prefetchLocalItemsWithGUIDs<T: Collection>(_ guids: T) -> Success where T.Iterator.Element == GUID { | |
| return succeed() | |
| } | |
| // MirrorItemSource methods (not implemented!). | |
| func getMirrorItemWithGUID(_ guid: GUID) -> Deferred<Maybe<BookmarkMirrorItem>> { | |
| return deferMaybe(DatabaseError(description: "Not implemented")) | |
| } | |
| func getMirrorItemsWithGUIDs<T>(_ guids: T) -> Deferred<Maybe<[GUID : BookmarkMirrorItem]>> where T : Collection, T.Iterator.Element == GUID { | |
| return deferMaybe(DatabaseError(description: "Not implemented")) | |
| } | |
| func prefetchMirrorItemsWithGUIDs<T: Collection>(_ guids: T) -> Success where T.Iterator.Element == GUID { | |
| return succeed() | |
| } | |
| // SyncableBookmarks methods (partialy implemented!). | |
| func isUnchanged() -> Deferred<Maybe<Bool>> { | |
| return deferMaybe(DatabaseError(description: "Not implemented")) | |
| } | |
| func getLocalBookmarksModifications(limit: Int) -> Deferred<Maybe<(deletions: [GUID], additions: [BookmarkMirrorItem])>> { | |
| let deletions = Array(self.localDeletions.prefix(limit)) | |
| let additions = Array(self.localAdditions.prefix(limit - deletions.count)).map { self.local[$0]! } | |
| return deferMaybe((deletions: deletions, additions: additions)) | |
| } | |
| func getLocalDeletions() -> Deferred<Maybe<[(GUID, Timestamp)]>> { | |
| return deferMaybe(DatabaseError(description: "Not implemented")) | |
| } | |
| func treesForEdges() -> Deferred<Maybe<(local: BookmarkTree, buffer: BookmarkTree)>> { | |
| return deferMaybe(DatabaseError(description: "Not implemented")) | |
| } | |
| func treeForMirror() -> Deferred<Maybe<BookmarkTree>> { | |
| return deferMaybe(DatabaseError(description: "Not implemented")) | |
| } | |
| func applyLocalOverrideCompletionOp(_ op: LocalOverrideCompletionOp, itemSources: ItemSources) -> Success { | |
| return deferMaybe(DatabaseError(description: "Not implemented")) | |
| } | |
| func applyBufferUpdatedCompletionOp(_ op: BufferUpdatedCompletionOp) -> Success { | |
| self.lastBufferUpdatedCompletionOpApplied = op | |
| return succeed() | |
| } | |
| // Misc methods. | |
| func resetClient() -> Success { | |
| return deferMaybe(DatabaseError(description: "Not implemented")) | |
| } | |
| func onRemovedAccount() -> Success { | |
| return deferMaybe(DatabaseError(description: "Not implemented")) | |
| } | |
| } | |
| class MockBuffer: BookmarkBufferStorage, BufferItemSource { | |
| var buffer: [GUID: BookmarkMirrorItem] = [:] | |
| var children: [GUID: [GUID]] = [:] | |
| // BufferItemSource methods. | |
| func getBufferItemWithGUID(_ guid: GUID) -> Deferred<Maybe<BookmarkMirrorItem>> { | |
| guard let item = self.buffer[guid] else { | |
| return deferMaybe(DatabaseError(description: "Couldn't find item \(guid).")) | |
| } | |
| return deferMaybe(item) | |
| } | |
| func getBufferItemsWithGUIDs<T>(_ guids: T) -> Deferred<Maybe<[GUID : BookmarkMirrorItem]>> where T : Collection, T.Iterator.Element == GUID { | |
| return deferMaybe(DatabaseError(description: "Not implemented")) | |
| } | |
| func getBufferChildrenGUIDsForParent(_ guid: GUID) -> Deferred<Maybe<[GUID]>> { | |
| guard let children = self.children[guid] else { | |
| return deferMaybe(DatabaseError(description: "Couldn't find children for \(guid).")) | |
| } | |
| return deferMaybe(children) | |
| } | |
| func prefetchBufferItemsWithGUIDs<T>(_ guids: T) -> Success where T : Collection, T.Iterator.Element == GUID { | |
| return succeed() | |
| } | |
| // BookmarkBufferStorage methods. | |
| func isEmpty() -> Deferred<Maybe<Bool>> { | |
| return deferMaybe(DatabaseError(description: "Not implemented")) | |
| } | |
| func applyRecords(_ records: [BookmarkMirrorItem]) -> Success { | |
| return deferMaybe(DatabaseError(description: "Not implemented")) | |
| } | |
| func doneApplyingRecordsAfterDownload() -> Success { | |
| return deferMaybe(DatabaseError(description: "Not implemented")) | |
| } | |
| func validate() -> Success { | |
| return deferMaybe(DatabaseError(description: "Not implemented")) | |
| } | |
| func getBufferedDeletions() -> Deferred<Maybe<[(GUID, Timestamp)]>> { | |
| return deferMaybe(DatabaseError(description: "Not implemented")) | |
| } | |
| func applyBufferCompletionOp(_ op: BufferCompletionOp, itemSources: ItemSources) -> Success { | |
| return deferMaybe(DatabaseError(description: "Not implemented")) | |
| } | |
| func synchronousBufferCount() -> Int? { | |
| return nil | |
| } | |
| func getUpstreamRecordCount() -> Deferred<Int?> { | |
| return Deferred(value: nil) | |
| } | |
| } | |
| class TestBookmarksSynchronizer: XCTestCase { | |
| func testBuildMobileRootAndChildrenRecords_noMobileRootInBuffer() { | |
| let delegate = MockSyncDelegate() | |
| let prefs = MockProfilePrefs() | |
| let scratchpad = Scratchpad(b: KeyBundle.random(), persistingTo: prefs) | |
| let buffer = MockBuffer() | |
| let storage = MockStorage() | |
| storage.local[BookmarkRoots.MobileFolderGUID] = BookmarkMirrorItem.folder(BookmarkRoots.MobileFolderGUID, dateAdded: Date.now(), modified: Date.now(), hasDupe: false, parentID: BookmarkRoots.RootGUID, parentName: nil, title: "Mobile Bookmarks", description: nil, children: ["bk1"]) | |
| storage.local["bk1"] = BookmarkMirrorItem.bookmark("bk1", dateAdded: Date.now(), modified: Date.now(), hasDupe: false, parentID: BookmarkRoots.MobileFolderGUID, parentName: nil, title: "Bookmark 1", description: nil, URI: "https://example.com/1", tags: "", keyword: nil) | |
| storage.local["bk1"] = BookmarkMirrorItem.bookmark("bk1", dateAdded: Date.now(), modified: Date.now(), hasDupe: false, parentID: BookmarkRoots.MobileFolderGUID, parentName: nil, title: "Bookmark 1", description: nil, URI: "https://example.com/1", tags: "", keyword: nil) | |
| let bk2 = BookmarkMirrorItem.bookmark("bk2", dateAdded: Date.now(), modified: Date.now(), hasDupe: false, parentID: BookmarkRoots.MobileFolderGUID, parentName: nil, title: "Bookmark 2", description: nil, URI: "https://example.com/2", tags: "", keyword: nil) | |
| let bk3 = BookmarkMirrorItem.bookmark("bk3", dateAdded: Date.now(), modified: Date.now(), hasDupe: false, parentID: BookmarkRoots.MobileFolderGUID, parentName: nil, title: "Bookmark 3", description: nil, URI: "https://example.com/3", tags: "", keyword: nil) | |
| storage.local["bk2"] = bk2 | |
| storage.local["bk3"] = bk3 | |
| let synchronizer = BufferingBookmarksSynchronizer(scratchpad: scratchpad, delegate: delegate, basePrefs: prefs, why: .scheduled) | |
| let (mobileRootRecord, childrenRecords) = synchronizer.buildMobileRootAndChildrenRecords(storage, buffer, additionalChildren: [bk2, bk3], deletedChildren: []).value.successValue! | |
| XCTAssertEqual(mobileRootRecord.id, BookmarkRoots.translateOutgoingRootGUID(BookmarkRoots.MobileFolderGUID)) | |
| XCTAssertEqual(mobileRootRecord.payload.json["title"], "Mobile Bookmarks") | |
| // We are not including bk1 in the call to buildMobileRootRecord() therefore it should NOT be included in the returned record | |
| XCTAssertEqual(mobileRootRecord.payload.json["children"], ["bk2", "bk3"]) | |
| XCTAssertEqual(childrenRecords.count, 2) | |
| XCTAssertEqual(childrenRecords[0].id, "bk2") | |
| XCTAssertEqual(childrenRecords[1].id, "bk3") | |
| } | |
| func testBuildMobileRootAndChildrenRecords_mobileRootInBuffer() { | |
| let delegate = MockSyncDelegate() | |
| let prefs = MockProfilePrefs() | |
| let scratchpad = Scratchpad(b: KeyBundle.random(), persistingTo: prefs) | |
| let buffer = MockBuffer() | |
| buffer.buffer[BookmarkRoots.MobileFolderGUID] = BookmarkMirrorItem.folder(BookmarkRoots.MobileFolderGUID, dateAdded: Date.now(), modified: Date.now(), hasDupe: false, parentID: BookmarkRoots.RootGUID, parentName: nil, title: "Mobile Bookmarks", description: nil, children: ["bk1", "bkmdeleteme"]) | |
| buffer.children[BookmarkRoots.MobileFolderGUID] = ["bk1", "bkmdeleteme"] | |
| buffer.buffer["bk1"] = BookmarkMirrorItem.bookmark("bk1", dateAdded: Date.now(), modified: Date.now(), hasDupe: false, parentID: BookmarkRoots.MobileFolderGUID, parentName: nil, title: "Bookmark 1", description: nil, URI: "https://example.com/1", tags: "", keyword: nil) | |
| buffer.buffer["bkmdeleteme"] = BookmarkMirrorItem.bookmark("bkmdeleteme", dateAdded: Date.now(), modified: Date.now(), hasDupe: false, parentID: BookmarkRoots.MobileFolderGUID, parentName: nil, title: "Bookmark to delete", description: nil, URI: "https://example.com/todelete", tags: "", keyword: nil) | |
| let storage = MockStorage() | |
| storage.local["bk1"] = BookmarkMirrorItem.bookmark("bk1", dateAdded: Date.now(), modified: Date.now(), hasDupe: false, parentID: BookmarkRoots.MobileFolderGUID, parentName: nil, title: "Bookmark 1", description: nil, URI: "https://example.com/1", tags: "", keyword: nil) | |
| let bk2 = BookmarkMirrorItem.bookmark("bk2", dateAdded: Date.now(), modified: Date.now(), hasDupe: false, parentID: BookmarkRoots.MobileFolderGUID, parentName: nil, title: "Bookmark 2", description: nil, URI: "https://example.com/2", tags: "", keyword: nil) | |
| let bk3 = BookmarkMirrorItem.bookmark("bk3", dateAdded: Date.now(), modified: Date.now(), hasDupe: false, parentID: BookmarkRoots.MobileFolderGUID, parentName: nil, title: "Bookmark 3", description: nil, URI: "https://example.com/3", tags: "", keyword: nil) | |
| storage.local["bk2"] = bk2 | |
| storage.local["bk3"] = bk3 | |
| let synchronizer = BufferingBookmarksSynchronizer(scratchpad: scratchpad, delegate: delegate, basePrefs: prefs, why: .scheduled) | |
| let (mobileRootRecord, childrenRecords) = synchronizer.buildMobileRootAndChildrenRecords(storage, buffer, additionalChildren: [bk2, bk3], deletedChildren: ["bkmdeleteme"]).value.successValue! | |
| XCTAssertEqual(mobileRootRecord.id, BookmarkRoots.translateOutgoingRootGUID(BookmarkRoots.MobileFolderGUID)) | |
| XCTAssertEqual(mobileRootRecord.payload.json["title"], "Mobile Bookmarks") | |
| // bk1 is a children of mobile root in the buffer, therefore it should be included here. | |
| XCTAssertEqual(mobileRootRecord.payload.json["children"], ["bk1", "bk2", "bk3"]) | |
| // We are only sending the new/deleted records though! | |
| XCTAssertEqual(childrenRecords.count, 3) | |
| XCTAssertEqual(childrenRecords[0].id, "bk2") | |
| XCTAssertEqual(childrenRecords[1].id, "bk3") | |
| XCTAssertEqual(childrenRecords[2].id, "bkmdeleteme") | |
| XCTAssertTrue(childrenRecords[2].payload.deleted) | |
| } | |
| func testUploadSomeLocalRecords_batched_ok() { | |
| let delegate = MockSyncDelegate() | |
| let prefs = MockProfilePrefs() | |
| let scratchpad = Scratchpad(b: KeyBundle.random(), persistingTo: prefs) | |
| let storage = MockStorage() | |
| let buffer = MockBuffer() | |
| let records = createMobileRootAndChildrenRecords(numChildren: 80) | |
| var numPosts = 0 | |
| let now = Date.now() | |
| var firstPostLastModified: Timestamp? | |
| var secondPostLastModified: Timestamp? | |
| let uploader: BatchUploadFunction = { lines, ifUnmodifiedSince, queryParams in | |
| numPosts += 1 | |
| var headers = [String: Any]() | |
| let result: POSTResult | |
| if numPosts == 1 { | |
| result = POSTResult(success: records.childrenRecords.map { $0.id } + ["mobile"], failed: [:], batchToken: "toktok") | |
| firstPostLastModified = now | |
| headers["X-Last-Modified"] = firstPostLastModified | |
| } else { | |
| XCTAssertEqual(queryParams![0].name, "batch") | |
| XCTAssertEqual(queryParams![0].value, "toktok") | |
| XCTAssertEqual(queryParams![1].name, "commit") | |
| XCTAssertEqual(queryParams![1].value, "true") | |
| result = POSTResult(success: [], failed: [:]) | |
| secondPostLastModified = now + 5000 | |
| headers["X-Last-Modified"] = secondPostLastModified | |
| } | |
| let response = StorageResponse<POSTResult>(value: result, metadata: ResponseMetadata(status: 200, headers: headers)) | |
| return deferMaybe(response) | |
| } | |
| let miniConfig = InfoConfiguration(maxRequestBytes: 1_048_576, maxPostRecords: 100, maxPostBytes: 1_048_576, maxTotalRecords: 250, maxTotalBytes: 104_857_600) | |
| let collectionClient = MockSyncCollectionClient(uploader: uploader, infoConfig: miniConfig, collection: "bookmarks", encrypter: getBookmarksEncrypter()) | |
| let mirrorer = BookmarksMirrorer(storage: buffer, client: collectionClient, basePrefs: prefs, collection: "bookmarks", statsSession: SyncEngineStatsSession(collection: "bookmarks")) | |
| let synchronizer = BufferingBookmarksSynchronizer(scratchpad: scratchpad, delegate: delegate, basePrefs: prefs, why: .scheduled) | |
| synchronizer.uploadSomeLocalRecords(storage, mirrorer, collectionClient, mobileRootRecord: records.mobileRootRecord, childrenRecords: records.childrenRecords).succeeded() | |
| XCTAssertEqual(storage.lastBufferUpdatedCompletionOpApplied?.bufferValuesToMoveFromLocal.count, 81) | |
| XCTAssertEqual(numPosts, 2) | |
| XCTAssertEqual(mirrorer.lastModified / 1000, secondPostLastModified) | |
| } | |
| func testUploadSomeLocalRecords_batched_someFailed() { | |
| let delegate = MockSyncDelegate() | |
| let prefs = MockProfilePrefs() | |
| let scratchpad = Scratchpad(b: KeyBundle.random(), persistingTo: prefs) | |
| let storage = MockStorage() | |
| let buffer = MockBuffer() | |
| let records = createMobileRootAndChildrenRecords(numChildren: 80) | |
| var numPosts = 0 | |
| let uploader: BatchUploadFunction = { lines, ifUnmodifiedSince, queryParams in | |
| numPosts += 1 | |
| var headers = [String: Any]() | |
| headers["X-Last-Modified"] = Date.now() | |
| let result = POSTResult(success: records.childrenRecords.map { $0.id }.dropFirst(20) + ["mobile"], failed: [:], batchToken: "toktok") | |
| let response = StorageResponse<POSTResult>(value: result, metadata: ResponseMetadata(status: 200, headers: headers)) | |
| return deferMaybe(response) | |
| } | |
| let miniConfig = InfoConfiguration(maxRequestBytes: 1_048_576, maxPostRecords: 100, maxPostBytes: 1_048_576, maxTotalRecords: 250, maxTotalBytes: 104_857_600) | |
| let collectionClient = MockSyncCollectionClient(uploader: uploader, infoConfig: miniConfig, collection: "bookmarks", encrypter: getBookmarksEncrypter()) | |
| let mirrorer = BookmarksMirrorer(storage: buffer, client: collectionClient, basePrefs: prefs, collection: "bookmarks", statsSession: SyncEngineStatsSession(collection: "bookmarks")) | |
| let synchronizer = BufferingBookmarksSynchronizer(scratchpad: scratchpad, delegate: delegate, basePrefs: prefs, why: .scheduled) | |
| let error = synchronizer.uploadSomeLocalRecords(storage, mirrorer, collectionClient, mobileRootRecord: records.mobileRootRecord, childrenRecords: records.childrenRecords).value.failureValue! | |
| XCTAssertTrue(error is RecordsFailedToUpload) | |
| XCTAssertNil(storage.lastBufferUpdatedCompletionOpApplied) | |
| XCTAssertEqual(numPosts, 1) | |
| } | |
| func testUploadSomeLocalRecords_nobatch_ok() { | |
| let delegate = MockSyncDelegate() | |
| let prefs = MockProfilePrefs() | |
| let scratchpad = Scratchpad(b: KeyBundle.random(), persistingTo: prefs) | |
| let storage = MockStorage() | |
| let buffer = MockBuffer() | |
| let records = createMobileRootAndChildrenRecords(numChildren: 80) | |
| var numPosts = 0 | |
| let uploader: BatchUploadFunction = { lines, ifUnmodifiedSince, queryParams in | |
| numPosts += 1 | |
| var headers = [String: Any]() | |
| headers["X-Last-Modified"] = Date.now() | |
| let result = POSTResult(success: records.childrenRecords.map { $0.id } + ["mobile"], failed: [:]) | |
| let response = StorageResponse<POSTResult>(value: result, metadata: ResponseMetadata(status: 200, headers: headers)) | |
| return deferMaybe(response) | |
| } | |
| let miniConfig = InfoConfiguration(maxRequestBytes: 1_048_576, maxPostRecords: 100, maxPostBytes: 1_048_576, maxTotalRecords: 250, maxTotalBytes: 104_857_600) | |
| let collectionClient = MockSyncCollectionClient(uploader: uploader, infoConfig: miniConfig, collection: "bookmarks", encrypter: getBookmarksEncrypter()) | |
| let mirrorer = BookmarksMirrorer(storage: buffer, client: collectionClient, basePrefs: prefs, collection: "bookmarks", statsSession: SyncEngineStatsSession(collection: "bookmarks")) | |
| let synchronizer = BufferingBookmarksSynchronizer(scratchpad: scratchpad, delegate: delegate, basePrefs: prefs, why: .scheduled) | |
| synchronizer.uploadSomeLocalRecords(storage, mirrorer, collectionClient, mobileRootRecord: records.mobileRootRecord, childrenRecords: records.childrenRecords).succeeded() | |
| XCTAssertEqual(storage.lastBufferUpdatedCompletionOpApplied?.bufferValuesToMoveFromLocal.count, 81) | |
| XCTAssertEqual(numPosts, 1) | |
| } | |
| func testUploadSomeLocalRecords_nobatch_someFailed() { | |
| let delegate = MockSyncDelegate() | |
| let prefs = MockProfilePrefs() | |
| let scratchpad = Scratchpad(b: KeyBundle.random(), persistingTo: prefs) | |
| let storage = MockStorage() | |
| let buffer = MockBuffer() | |
| let records = createMobileRootAndChildrenRecords(numChildren: 80) | |
| var numPosts = 0 | |
| let uploader: BatchUploadFunction = { lines, ifUnmodifiedSince, queryParams in | |
| numPosts += 1 | |
| var headers = [String: Any]() | |
| headers["X-Last-Modified"] = Date.now() | |
| let result = POSTResult(success: records.childrenRecords.map { $0.id }.dropFirst(20) + ["mobile"], failed: [:]) | |
| let response = StorageResponse<POSTResult>(value: result, metadata: ResponseMetadata(status: 200, headers: headers)) | |
| return deferMaybe(response) | |
| } | |
| let miniConfig = InfoConfiguration(maxRequestBytes: 1_048_576, maxPostRecords: 100, maxPostBytes: 1_048_576, maxTotalRecords: 250, maxTotalBytes: 104_857_600) | |
| let collectionClient = MockSyncCollectionClient(uploader: uploader, infoConfig: miniConfig, collection: "bookmarks", encrypter: getBookmarksEncrypter()) | |
| let mirrorer = BookmarksMirrorer(storage: buffer, client: collectionClient, basePrefs: prefs, collection: "bookmarks", statsSession: SyncEngineStatsSession(collection: "bookmarks")) | |
| let synchronizer = BufferingBookmarksSynchronizer(scratchpad: scratchpad, delegate: delegate, basePrefs: prefs, why: .scheduled) | |
| synchronizer.uploadSomeLocalRecords(storage, mirrorer, collectionClient, mobileRootRecord: records.mobileRootRecord, childrenRecords: records.childrenRecords).succeeded() | |
| XCTAssertEqual(storage.lastBufferUpdatedCompletionOpApplied?.bufferValuesToMoveFromLocal.count, 61) | |
| XCTAssertEqual(numPosts, 1) | |
| } | |
| func testUploadSomeLocalRecords_tooManyRecords() { | |
| let delegate = MockSyncDelegate() | |
| let prefs = MockProfilePrefs() | |
| let scratchpad = Scratchpad(b: KeyBundle.random(), persistingTo: prefs) | |
| let storage = MockStorage() | |
| let buffer = MockBuffer() | |
| let uploader: BatchUploadFunction = { _, _, _ in | |
| // Should never happen | |
| XCTFail() | |
| return deferMaybe(NSError()) | |
| } | |
| let miniConfig = InfoConfiguration(maxRequestBytes: 1_048_576, maxPostRecords: 100, maxPostBytes: 1_048_576, maxTotalRecords: 250, maxTotalBytes: 104_857_600) | |
| let collectionClient = MockSyncCollectionClient(uploader: uploader, infoConfig: miniConfig, collection: "bookmarks", encrypter: getBookmarksEncrypter()) | |
| let mirrorer = BookmarksMirrorer(storage: buffer, client: collectionClient, basePrefs: prefs, collection: "bookmarks", statsSession: SyncEngineStatsSession(collection: "bookmarks")) | |
| let records = createMobileRootAndChildrenRecords(numChildren: 120) | |
| let synchronizer = BufferingBookmarksSynchronizer(scratchpad: scratchpad, delegate: delegate, basePrefs: prefs, why: .scheduled) | |
| let error = synchronizer.uploadSomeLocalRecords(storage, mirrorer, collectionClient, mobileRootRecord: records.mobileRootRecord, childrenRecords: records.childrenRecords).value.failureValue! | |
| XCTAssertTrue(error is TooManyRecordsError) | |
| } | |
| func testUploadSomeLocalRecords_tooBig() { | |
| let delegate = MockSyncDelegate() | |
| let prefs = MockProfilePrefs() | |
| let scratchpad = Scratchpad(b: KeyBundle.random(), persistingTo: prefs) | |
| let storage = MockStorage() | |
| let buffer = MockBuffer() | |
| let uploader: BatchUploadFunction = { _, _, _ in | |
| // Should never happen | |
| XCTFail() | |
| return deferMaybe(NSError()) | |
| } | |
| let miniConfig = InfoConfiguration(maxRequestBytes: 1_048_576, maxPostRecords: 100, maxPostBytes: 5000, maxTotalRecords: 250, maxTotalBytes: 104_857_600) | |
| let collectionClient = MockSyncCollectionClient(uploader: uploader, infoConfig: miniConfig, collection: "bookmarks", encrypter: getBookmarksEncrypter()) | |
| let mirrorer = BookmarksMirrorer(storage: buffer, client: collectionClient, basePrefs: prefs, collection: "bookmarks", statsSession: SyncEngineStatsSession(collection: "bookmarks")) | |
| let records = createMobileRootAndChildrenRecords(numChildren: 80) | |
| let synchronizer = BufferingBookmarksSynchronizer(scratchpad: scratchpad, delegate: delegate, basePrefs: prefs, why: .scheduled) | |
| let error = synchronizer.uploadSomeLocalRecords(storage, mirrorer, collectionClient, mobileRootRecord: records.mobileRootRecord, childrenRecords: records.childrenRecords).value.failureValue! | |
| XCTAssertTrue(error is TooManyRecordsError) | |
| } | |
| func getBookmarksEncrypter() -> RecordEncrypter<BookmarkBasePayload> { | |
| let serializer: (Record<BookmarkBasePayload>) -> JSON? = { $0.payload.json } | |
| let factory: (String) -> BookmarkBasePayload = { BookmarkBasePayload($0) } | |
| return RecordEncrypter(serializer: serializer, factory: factory) | |
| } | |
| func createMobileRootAndChildrenRecords(numChildren: Int) -> (mobileRootRecord: Record<BookmarkBasePayload>, childrenRecords: [Record<BookmarkBasePayload>]) { | |
| var childrenRecords: [Record<BookmarkBasePayload>] = [] | |
| for i in 0...numChildren { | |
| let item = BookmarkMirrorItem.bookmark("bk1\(i)", dateAdded: Date.now(), modified: Date.now(), hasDupe: false, parentID: BookmarkRoots.MobileFolderGUID, parentName: nil, title: "bk1\(i)", description: nil, URI: "https://example.com/\(i)", tags: "", keyword: nil) | |
| let record = Record<BookmarkBasePayload>(id: item.guid, payload: item.asPayload()) | |
| childrenRecords.append(record) | |
| } | |
| let mobileRoot = BookmarkMirrorItem.folder(BookmarkRoots.MobileFolderGUID, dateAdded: Date.now(), modified: Date.now(), hasDupe: false, parentID: BookmarkRoots.RootGUID, parentName: nil, title: "Mobile Bookmarks", description: nil, children: []) | |
| let mobileRootRecord = Record<BookmarkBasePayload>(id: BookmarkRoots.translateOutgoingRootGUID(BookmarkRoots.MobileFolderGUID), payload: mobileRoot.asPayloadWithChildren(childrenRecords.map { $0.id })) | |
| return (mobileRootRecord: mobileRootRecord, childrenRecords: childrenRecords) | |
| } | |
| } |