Comment-out non-functional Account tests. Add Account tests to test plans.

This commit is contained in:
Brent Simmons 2024-05-21 17:29:37 -07:00
parent c35008b4c6
commit d52b52475a
24 changed files with 2301 additions and 2229 deletions

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1530"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AccountTests"
BuildableName = "AccountTests"
BlueprintName = "AccountTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -11,91 +11,91 @@ import Web
@testable import Account
import Secrets
class AccountCredentialsTest: XCTestCase {
private var account: Account!
override func setUp() {
account = TestAccountManager.shared.createAccount(type: .feedbin, transport: TestTransport())
}
override func tearDown() {
TestAccountManager.shared.deleteAccount(account)
}
func testCreateRetrieveDelete() {
// Make sure any left over from failed tests are gone
do {
try account.removeCredentials(type: .basic)
} catch {
XCTFail(error.localizedDescription)
}
var credentials: Credentials? = Credentials(type: .basic, username: "maurice", secret: "hardpasswd")
// Store the credentials
do {
try account.storeCredentials(credentials!)
} catch {
XCTFail(error.localizedDescription)
}
// Retrieve them
credentials = nil
do {
credentials = try account.retrieveCredentials(type: .basic)
} catch {
XCTFail(error.localizedDescription)
}
switch credentials!.type {
case .basic:
XCTAssertEqual("maurice", credentials?.username)
XCTAssertEqual("hardpasswd", credentials?.secret)
default:
XCTFail("Expected \(CredentialsType.basic), received \(credentials!.type)")
}
// Update them
credentials = Credentials(type: .basic, username: "maurice", secret: "easypasswd")
do {
try account.storeCredentials(credentials!)
} catch {
XCTFail(error.localizedDescription)
}
// Retrieve them again
credentials = nil
do {
credentials = try account.retrieveCredentials(type: .basic)
} catch {
XCTFail(error.localizedDescription)
}
switch credentials!.type {
case .basic:
XCTAssertEqual("maurice", credentials?.username)
XCTAssertEqual("easypasswd", credentials?.secret)
default:
XCTFail("Expected \(CredentialsType.basic), received \(credentials!.type)")
}
// Delete them
do {
try account.removeCredentials(type: .basic)
} catch {
XCTFail(error.localizedDescription)
}
// Make sure they are gone
do {
try credentials = account.retrieveCredentials(type: .basic)
} catch {
XCTFail(error.localizedDescription)
}
XCTAssertNil(credentials)
}
}
//class AccountCredentialsTest: XCTestCase {
//
// private var account: Account!
//
// override func setUp() {
// account = TestAccountManager.shared.createAccount(type: .feedbin, transport: TestTransport())
// }
//
// override func tearDown() {
// TestAccountManager.shared.deleteAccount(account)
// }
//
// func testCreateRetrieveDelete() {
//
// // Make sure any left over from failed tests are gone
// do {
// try account.removeCredentials(type: .basic)
// } catch {
// XCTFail(error.localizedDescription)
// }
//
// var credentials: Credentials? = Credentials(type: .basic, username: "maurice", secret: "hardpasswd")
//
// // Store the credentials
// do {
// try account.storeCredentials(credentials!)
// } catch {
// XCTFail(error.localizedDescription)
// }
//
// // Retrieve them
// credentials = nil
// do {
// credentials = try account.retrieveCredentials(type: .basic)
// } catch {
// XCTFail(error.localizedDescription)
// }
//
// switch credentials!.type {
// case .basic:
// XCTAssertEqual("maurice", credentials?.username)
// XCTAssertEqual("hardpasswd", credentials?.secret)
// default:
// XCTFail("Expected \(CredentialsType.basic), received \(credentials!.type)")
// }
//
// // Update them
// credentials = Credentials(type: .basic, username: "maurice", secret: "easypasswd")
// do {
// try account.storeCredentials(credentials!)
// } catch {
// XCTFail(error.localizedDescription)
// }
//
// // Retrieve them again
// credentials = nil
// do {
// credentials = try account.retrieveCredentials(type: .basic)
// } catch {
// XCTFail(error.localizedDescription)
// }
//
// switch credentials!.type {
// case .basic:
// XCTAssertEqual("maurice", credentials?.username)
// XCTAssertEqual("easypasswd", credentials?.secret)
// default:
// XCTFail("Expected \(CredentialsType.basic), received \(credentials!.type)")
// }
//
// // Delete them
// do {
// try account.removeCredentials(type: .basic)
// } catch {
// XCTFail(error.localizedDescription)
// }
//
// // Make sure they are gone
// do {
// try credentials = account.retrieveCredentials(type: .basic)
// } catch {
// XCTFail(error.localizedDescription)
// }
//
// XCTAssertNil(credentials)
// }
//
//}

View File

@ -9,59 +9,59 @@
import XCTest
@testable import Account
class AccountFeedbinFolderContentsSyncTest: XCTestCase {
override func setUp() {
}
override func tearDown() {
}
func testDownloadSync() {
let testTransport = TestTransport()
testTransport.testFiles["https://api.feedbin.com/v2/tags.json"] = "JSON/tags_add.json"
testTransport.testFiles["https://api.feedbin.com/v2/subscriptions.json"] = "JSON/subscriptions_initial.json"
testTransport.testFiles["https://api.feedbin.com/v2/taggings.json"] = "JSON/taggings_initial.json"
let account = TestAccountManager.shared.createAccount(type: .feedbin, transport: testTransport)
// Test initial folders
let initialExpection = self.expectation(description: "Initial contents")
account.refreshAll() { _ in
initialExpection.fulfill()
}
waitForExpectations(timeout: 5, handler: nil)
let folder = account.folders?.filter { $0.name == "Developers" } .first!
XCTAssertEqual(156, folder?.topLevelFeeds.count ?? 0)
XCTAssertEqual(2, account.topLevelFeeds.count)
// Test Adding a Feed to the folder
testTransport.testFiles["https://api.feedbin.com/v2/taggings.json"] = "JSON/taggings_add.json"
let addExpection = self.expectation(description: "Add contents")
account.refreshAll() { _ in
addExpection.fulfill()
}
waitForExpectations(timeout: 5, handler: nil)
XCTAssertEqual(157, folder?.topLevelFeeds.count ?? 0)
XCTAssertEqual(1, account.topLevelFeeds.count)
// Test Deleting some Feeds from the folder
testTransport.testFiles["https://api.feedbin.com/v2/taggings.json"] = "JSON/taggings_delete.json"
let deleteExpection = self.expectation(description: "Delete contents")
account.refreshAll() { _ in
deleteExpection.fulfill()
}
waitForExpectations(timeout: 5, handler: nil)
XCTAssertEqual(153, folder?.topLevelFeeds.count ?? 0)
XCTAssertEqual(5, account.topLevelFeeds.count)
TestAccountManager.shared.deleteAccount(account)
}
}
//class AccountFeedbinFolderContentsSyncTest: XCTestCase {
//
// override func setUp() {
// }
//
// override func tearDown() {
// }
//
// func testDownloadSync() {
//
// let testTransport = TestTransport()
// testTransport.testFiles["https://api.feedbin.com/v2/tags.json"] = "JSON/tags_add.json"
// testTransport.testFiles["https://api.feedbin.com/v2/subscriptions.json"] = "JSON/subscriptions_initial.json"
// testTransport.testFiles["https://api.feedbin.com/v2/taggings.json"] = "JSON/taggings_initial.json"
// let account = TestAccountManager.shared.createAccount(type: .feedbin, transport: testTransport)
//
// // Test initial folders
// let initialExpection = self.expectation(description: "Initial contents")
// account.refreshAll() { _ in
// initialExpection.fulfill()
// }
// waitForExpectations(timeout: 5, handler: nil)
//
// let folder = account.folders?.filter { $0.name == "Developers" } .first!
// XCTAssertEqual(156, folder?.topLevelFeeds.count ?? 0)
// XCTAssertEqual(2, account.topLevelFeeds.count)
//
// // Test Adding a Feed to the folder
// testTransport.testFiles["https://api.feedbin.com/v2/taggings.json"] = "JSON/taggings_add.json"
//
// let addExpection = self.expectation(description: "Add contents")
// account.refreshAll() { _ in
// addExpection.fulfill()
// }
// waitForExpectations(timeout: 5, handler: nil)
//
// XCTAssertEqual(157, folder?.topLevelFeeds.count ?? 0)
// XCTAssertEqual(1, account.topLevelFeeds.count)
//
// // Test Deleting some Feeds from the folder
// testTransport.testFiles["https://api.feedbin.com/v2/taggings.json"] = "JSON/taggings_delete.json"
//
// let deleteExpection = self.expectation(description: "Delete contents")
// account.refreshAll() { _ in
// deleteExpection.fulfill()
// }
// waitForExpectations(timeout: 5, handler: nil)
//
// XCTAssertEqual(153, folder?.topLevelFeeds.count ?? 0)
// XCTAssertEqual(5, account.topLevelFeeds.count)
//
// TestAccountManager.shared.deleteAccount(account)
//
// }
//
//}

View File

@ -9,75 +9,75 @@
import XCTest
@testable import Account
class AccountFeedbinFolderSyncTest: XCTestCase {
override func setUp() {
}
override func tearDown() {
}
func testDownloadSync() {
let testTransport = TestTransport()
testTransport.testFiles["https://api.feedbin.com/v2/tags.json"] = "JSON/tags_initial.json"
let account = TestAccountManager.shared.createAccount(type: .feedbin, transport: testTransport)
// Test initial folders
let initialExpection = self.expectation(description: "Initial tags")
account.refreshAll() { _ in
initialExpection.fulfill()
}
waitForExpectations(timeout: 5, handler: nil)
guard let intialFolders = account.folders else {
XCTFail()
return
}
XCTAssertEqual(9, intialFolders.count)
let initialFolderNames = intialFolders.map { $0.name ?? "" }
XCTAssertTrue(initialFolderNames.contains("Outdoors"))
// Test removing folders
testTransport.testFiles["https://api.feedbin.com/v2/tags.json"] = "JSON/tags_delete.json"
let deleteExpection = self.expectation(description: "Delete tags")
account.refreshAll() { _ in
deleteExpection.fulfill()
}
waitForExpectations(timeout: 5, handler: nil)
guard let deleteFolders = account.folders else {
XCTFail()
return
}
XCTAssertEqual(8, deleteFolders.count)
let deleteFolderNames = deleteFolders.map { $0.name ?? "" }
XCTAssertTrue(deleteFolderNames.contains("Outdoors"))
XCTAssertFalse(deleteFolderNames.contains("Tech Media"))
// Test Adding Folders
testTransport.testFiles["https://api.feedbin.com/v2/tags.json"] = "JSON/tags_add.json"
let addExpection = self.expectation(description: "Add tags")
account.refreshAll() { _ in
addExpection.fulfill()
}
waitForExpectations(timeout: 5, handler: nil)
guard let addFolders = account.folders else {
XCTFail()
return
}
XCTAssertEqual(10, addFolders.count)
let addFolderNames = addFolders.map { $0.name ?? "" }
XCTAssertTrue(addFolderNames.contains("Vanlife"))
TestAccountManager.shared.deleteAccount(account)
}
}
//class AccountFeedbinFolderSyncTest: XCTestCase {
//
// override func setUp() {
// }
//
// override func tearDown() {
// }
//
// func testDownloadSync() {
//
// let testTransport = TestTransport()
// testTransport.testFiles["https://api.feedbin.com/v2/tags.json"] = "JSON/tags_initial.json"
// let account = TestAccountManager.shared.createAccount(type: .feedbin, transport: testTransport)
//
// // Test initial folders
// let initialExpection = self.expectation(description: "Initial tags")
// account.refreshAll() { _ in
// initialExpection.fulfill()
// }
// waitForExpectations(timeout: 5, handler: nil)
//
// guard let intialFolders = account.folders else {
// XCTFail()
// return
// }
//
// XCTAssertEqual(9, intialFolders.count)
// let initialFolderNames = intialFolders.map { $0.name ?? "" }
// XCTAssertTrue(initialFolderNames.contains("Outdoors"))
//
// // Test removing folders
// testTransport.testFiles["https://api.feedbin.com/v2/tags.json"] = "JSON/tags_delete.json"
//
// let deleteExpection = self.expectation(description: "Delete tags")
// account.refreshAll() { _ in
// deleteExpection.fulfill()
// }
// waitForExpectations(timeout: 5, handler: nil)
//
// guard let deleteFolders = account.folders else {
// XCTFail()
// return
// }
//
// XCTAssertEqual(8, deleteFolders.count)
// let deleteFolderNames = deleteFolders.map { $0.name ?? "" }
// XCTAssertTrue(deleteFolderNames.contains("Outdoors"))
// XCTAssertFalse(deleteFolderNames.contains("Tech Media"))
//
// // Test Adding Folders
// testTransport.testFiles["https://api.feedbin.com/v2/tags.json"] = "JSON/tags_add.json"
//
// let addExpection = self.expectation(description: "Add tags")
// account.refreshAll() { _ in
// addExpection.fulfill()
// }
// waitForExpectations(timeout: 5, handler: nil)
//
// guard let addFolders = account.folders else {
// XCTFail()
// return
// }
//
// XCTAssertEqual(10, addFolders.count)
// let addFolderNames = addFolders.map { $0.name ?? "" }
// XCTAssertTrue(addFolderNames.contains("Vanlife"))
//
// TestAccountManager.shared.deleteAccount(account)
//
// }
//
//}

View File

@ -9,63 +9,63 @@
import XCTest
@testable import Account
class AccountFeedbinSyncTest: XCTestCase {
override func setUp() {
}
override func tearDown() {
}
func testDownloadSync() {
let testTransport = TestTransport()
testTransport.testFiles["tags.json"] = "JSON/tags_add.json"
testTransport.testFiles["subscriptions.json"] = "JSON/subscriptions_initial.json"
let account = TestAccountManager.shared.createAccount(type: .feedbin, transport: testTransport)
// Test initial folders
let initialExpection = self.expectation(description: "Initial feeds")
account.refreshAll() { result in
switch result {
case .success:
initialExpection.fulfill()
case .failure(let error):
XCTFail(error.localizedDescription)
}
}
waitForExpectations(timeout: 5, handler: nil)
XCTAssertEqual(224, account.flattenedFeeds().count)
let daringFireball = account.idToFeedDictionary["1296379"]
XCTAssertEqual("Daring Fireball", daringFireball!.name)
XCTAssertEqual("https://daringfireball.net/feeds/json", daringFireball!.url)
XCTAssertEqual("https://daringfireball.net/", daringFireball!.homePageURL)
// Test Adding a Feed
testTransport.testFiles["subscriptions.json"] = "JSON/subscriptions_add.json"
let addExpection = self.expectation(description: "Add feeds")
account.refreshAll() { result in
switch result {
case .success:
addExpection.fulfill()
case .failure(let error):
XCTFail(error.localizedDescription)
}
}
waitForExpectations(timeout: 5, handler: nil)
XCTAssertEqual(225, account.flattenedFeeds().count)
let bPixels = account.idToFeedDictionary["1096623"]
XCTAssertEqual("Beautiful Pixels", bPixels?.name)
XCTAssertEqual("https://feedpress.me/beautifulpixels", bPixels?.url)
XCTAssertEqual("https://beautifulpixels.com/", bPixels?.homePageURL)
TestAccountManager.shared.deleteAccount(account)
}
}
//class AccountFeedbinSyncTest: XCTestCase {
//
// override func setUp() {
// }
//
// override func tearDown() {
// }
//
// func testDownloadSync() {
//
// let testTransport = TestTransport()
// testTransport.testFiles["tags.json"] = "JSON/tags_add.json"
// testTransport.testFiles["subscriptions.json"] = "JSON/subscriptions_initial.json"
// let account = TestAccountManager.shared.createAccount(type: .feedbin, transport: testTransport)
//
// // Test initial folders
// let initialExpection = self.expectation(description: "Initial feeds")
// account.refreshAll() { result in
// switch result {
// case .success:
// initialExpection.fulfill()
// case .failure(let error):
// XCTFail(error.localizedDescription)
// }
// }
// waitForExpectations(timeout: 5, handler: nil)
//
// XCTAssertEqual(224, account.flattenedFeeds().count)
//
// let daringFireball = account.idToFeedDictionary["1296379"]
// XCTAssertEqual("Daring Fireball", daringFireball!.name)
// XCTAssertEqual("https://daringfireball.net/feeds/json", daringFireball!.url)
// XCTAssertEqual("https://daringfireball.net/", daringFireball!.homePageURL)
//
// // Test Adding a Feed
// testTransport.testFiles["subscriptions.json"] = "JSON/subscriptions_add.json"
//
// let addExpection = self.expectation(description: "Add feeds")
// account.refreshAll() { result in
// switch result {
// case .success:
// addExpection.fulfill()
// case .failure(let error):
// XCTFail(error.localizedDescription)
// }
// }
// waitForExpectations(timeout: 5, handler: nil)
//
// XCTAssertEqual(225, account.flattenedFeeds().count)
//
// let bPixels = account.idToFeedDictionary["1096623"]
// XCTAssertEqual("Beautiful Pixels", bPixels?.name)
// XCTAssertEqual("https://feedpress.me/beautifulpixels", bPixels?.url)
// XCTAssertEqual("https://beautifulpixels.com/", bPixels?.homePageURL)
//
// TestAccountManager.shared.deleteAccount(account)
//
// }
//
//}

View File

@ -9,187 +9,187 @@
import XCTest
@testable import Account
class FeedlyCreateFeedsForCollectionFoldersOperationTests: XCTestCase {
private var account: Account!
private let support = FeedlyTestSupport()
override func setUp() {
super.setUp()
account = support.makeTestAccount()
}
override func tearDown() {
if let account = account {
support.destroy(account)
}
super.tearDown()
}
class FeedsAndFoldersProvider: FeedlyFeedsAndFoldersProviding {
var feedsAndFolders = [([FeedlyFeed], Folder)]()
}
func testAddFeeds() {
let feedsForFolderOne = [
FeedlyFeed(id: "feed/1", title: "Feed One", updated: nil, website: nil),
FeedlyFeed(id: "feed/2", title: "Feed Two", updated: nil, website: nil)
]
let feedsForFolderTwo = [
FeedlyFeed(id: "feed/1", title: "Feed One", updated: nil, website: nil),
FeedlyFeed(id: "feed/3", title: "Feed Three", updated: nil, website: nil),
]
let folderOne: (name: String, id: String) = ("FolderOne", "folder/1")
let folderTwo: (name: String, id: String) = ("FolderTwo", "folder/2")
let namesAndFeeds = [(folderOne, feedsForFolderOne), (folderTwo, feedsForFolderTwo)]
let provider = FeedsAndFoldersProvider()
provider.feedsAndFolders = namesAndFeeds.map { (folder, feeds) in
let accountFolder = account.ensureFolder(with: folder.name)!
accountFolder.externalID = folder.id
return (feeds, accountFolder)
}
let createFeeds = FeedlyCreateFeedsForCollectionFoldersOperation(account: account, feedsAndFoldersProvider: provider, log: support.log)
let completionExpectation = expectation(description: "Did Finish")
createFeeds.completionBlock = { _ in
completionExpectation.fulfill()
}
XCTAssertTrue(account.flattenedFeeds().isEmpty, "Expected empty account.")
MainThreadOperationQueue.shared.add(createFeeds)
waitForExpectations(timeout: 2)
let feedIDs = Set([feedsForFolderOne, feedsForFolderTwo]
.flatMap { $0 }
.map { $0.id })
let feedTitles = Set([feedsForFolderOne, feedsForFolderTwo]
.flatMap { $0 }
.map { $0.title })
let accountFeeds = account.flattenedFeeds()
let ingestedIDs = Set(accountFeeds.map { $0.feedID })
let ingestedTitles = Set(accountFeeds.map { $0.nameForDisplay })
let missingIDs = feedIDs.subtracting(ingestedIDs)
let missingTitles = feedTitles.subtracting(ingestedTitles)
XCTAssertTrue(missingIDs.isEmpty, "Failed to ingest feeds with these ids.")
XCTAssertTrue(missingTitles.isEmpty, "Failed to ingest feeds with these titles.")
let expectedFolderAndFeedIDs = namesAndFeeds
.sorted { $0.0.id < $1.0.id }
.map { folder, feeds -> [String: [String]] in
return [folder.id: feeds.map { $0.id }.sorted(by: <)]
}
let ingestedFolderAndFeedIDs = (account.folders ?? Set())
.sorted { $0.externalID! < $1.externalID! }
.compactMap { folder -> [String: [String]]? in
return [folder.externalID!: folder.topLevelFeeds.map { $0.feedID }.sorted(by: <)]
}
XCTAssertEqual(expectedFolderAndFeedIDs, ingestedFolderAndFeedIDs, "Did not ingest feeds in their corresponding folders.")
}
func testRemoveFeeds() {
let folderOne: (name: String, id: String) = ("FolderOne", "folder/1")
let folderTwo: (name: String, id: String) = ("FolderTwo", "folder/2")
let feedToRemove = FeedlyFeed(id: "feed/1", title: "Feed One", updated: nil, website: nil)
var feedsForFolderOne = [
feedToRemove,
FeedlyFeed(id: "feed/2", title: "Feed Two", updated: nil, website: nil)
]
var feedsForFolderTwo = [
feedToRemove,
FeedlyFeed(id: "feed/3", title: "Feed Three", updated: nil, website: nil),
]
// Add initial content.
do {
let namesAndFeeds = [(folderOne, feedsForFolderOne), (folderTwo, feedsForFolderTwo)]
let provider = FeedsAndFoldersProvider()
provider.feedsAndFolders = namesAndFeeds.map { (folder, feeds) in
let accountFolder = account.ensureFolder(with: folder.name)!
accountFolder.externalID = folder.id
return (feeds, accountFolder)
}
let createFeeds = FeedlyCreateFeedsForCollectionFoldersOperation(account: account, feedsAndFoldersProvider: provider, log: support.log)
let completionExpectation = expectation(description: "Did Finish")
createFeeds.completionBlock = { _ in
completionExpectation.fulfill()
}
XCTAssertTrue(account.flattenedFeeds().isEmpty, "Expected empty account.")
MainThreadOperationQueue.shared.add(createFeeds)
waitForExpectations(timeout: 2)
}
feedsForFolderOne.removeAll { $0.id == feedToRemove.id }
feedsForFolderTwo.removeAll { $0.id == feedToRemove.id }
let namesAndFeeds = [(folderOne, feedsForFolderOne), (folderTwo, feedsForFolderTwo)]
let provider = FeedsAndFoldersProvider()
provider.feedsAndFolders = namesAndFeeds.map { (folder, feeds) in
let accountFolder = account.ensureFolder(with: folder.name)!
accountFolder.externalID = folder.id
return (feeds, accountFolder)
}
let removeFeeds = FeedlyCreateFeedsForCollectionFoldersOperation(account: account, feedsAndFoldersProvider: provider, log: support.log)
let completionExpectation = expectation(description: "Did Finish")
removeFeeds.completionBlock = { _ in
completionExpectation.fulfill()
}
MainThreadOperationQueue.shared.add(removeFeeds)
waitForExpectations(timeout: 2)
let feedIDs = Set([feedsForFolderOne, feedsForFolderTwo]
.flatMap { $0 }
.map { $0.id })
let feedTitles = Set([feedsForFolderOne, feedsForFolderTwo]
.flatMap { $0 }
.map { $0.title })
let accountFeeds = account.flattenedFeeds()
let ingestedIDs = Set(accountFeeds.map { $0.feedID })
let ingestedTitles = Set(accountFeeds.map { $0.nameForDisplay })
XCTAssertEqual(ingestedIDs.count, feedIDs.count)
XCTAssertEqual(ingestedTitles.count, feedTitles.count)
let missingIDs = feedIDs.subtracting(ingestedIDs)
let missingTitles = feedTitles.subtracting(ingestedTitles)
XCTAssertTrue(missingIDs.isEmpty, "Failed to ingest feeds with these ids.")
XCTAssertTrue(missingTitles.isEmpty, "Failed to ingest feeds with these titles.")
let expectedFolderAndFeedIDs = namesAndFeeds
.sorted { $0.0.id < $1.0.id }
.map { folder, feeds -> [String: [String]] in
return [folder.id: feeds.map { $0.id }.sorted(by: <)]
}
let ingestedFolderAndFeedIDs = (account.folders ?? Set())
.sorted { $0.externalID! < $1.externalID! }
.compactMap { folder -> [String: [String]]? in
return [folder.externalID!: folder.topLevelFeeds.map { $0.feedID }.sorted(by: <)]
}
XCTAssertEqual(expectedFolderAndFeedIDs, ingestedFolderAndFeedIDs, "Did not ingest feeds to their corresponding folders.")
}
}
//class FeedlyCreateFeedsForCollectionFoldersOperationTests: XCTestCase {
//
// private var account: Account!
// private let support = FeedlyTestSupport()
//
// override func setUp() {
// super.setUp()
// account = support.makeTestAccount()
// }
//
// override func tearDown() {
// if let account = account {
// support.destroy(account)
// }
// super.tearDown()
// }
//
// class FeedsAndFoldersProvider: FeedlyFeedsAndFoldersProviding {
// var feedsAndFolders = [([FeedlyFeed], Folder)]()
// }
//
// func testAddFeeds() {
// let feedsForFolderOne = [
// FeedlyFeed(id: "feed/1", title: "Feed One", updated: nil, website: nil),
// FeedlyFeed(id: "feed/2", title: "Feed Two", updated: nil, website: nil)
// ]
//
// let feedsForFolderTwo = [
// FeedlyFeed(id: "feed/1", title: "Feed One", updated: nil, website: nil),
// FeedlyFeed(id: "feed/3", title: "Feed Three", updated: nil, website: nil),
// ]
//
// let folderOne: (name: String, id: String) = ("FolderOne", "folder/1")
// let folderTwo: (name: String, id: String) = ("FolderTwo", "folder/2")
// let namesAndFeeds = [(folderOne, feedsForFolderOne), (folderTwo, feedsForFolderTwo)]
//
// let provider = FeedsAndFoldersProvider()
// provider.feedsAndFolders = namesAndFeeds.map { (folder, feeds) in
// let accountFolder = account.ensureFolder(with: folder.name)!
// accountFolder.externalID = folder.id
// return (feeds, accountFolder)
// }
//
// let createFeeds = FeedlyCreateFeedsForCollectionFoldersOperation(account: account, feedsAndFoldersProvider: provider, log: support.log)
// let completionExpectation = expectation(description: "Did Finish")
// createFeeds.completionBlock = { _ in
// completionExpectation.fulfill()
// }
//
// XCTAssertTrue(account.flattenedFeeds().isEmpty, "Expected empty account.")
//
// MainThreadOperationQueue.shared.add(createFeeds)
//
// waitForExpectations(timeout: 2)
//
// let feedIDs = Set([feedsForFolderOne, feedsForFolderTwo]
// .flatMap { $0 }
// .map { $0.id })
//
// let feedTitles = Set([feedsForFolderOne, feedsForFolderTwo]
// .flatMap { $0 }
// .map { $0.title })
//
// let accountFeeds = account.flattenedFeeds()
// let ingestedIDs = Set(accountFeeds.map { $0.feedID })
// let ingestedTitles = Set(accountFeeds.map { $0.nameForDisplay })
//
// let missingIDs = feedIDs.subtracting(ingestedIDs)
// let missingTitles = feedTitles.subtracting(ingestedTitles)
//
// XCTAssertTrue(missingIDs.isEmpty, "Failed to ingest feeds with these ids.")
// XCTAssertTrue(missingTitles.isEmpty, "Failed to ingest feeds with these titles.")
//
// let expectedFolderAndFeedIDs = namesAndFeeds
// .sorted { $0.0.id < $1.0.id }
// .map { folder, feeds -> [String: [String]] in
// return [folder.id: feeds.map { $0.id }.sorted(by: <)]
// }
//
// let ingestedFolderAndFeedIDs = (account.folders ?? Set())
// .sorted { $0.externalID! < $1.externalID! }
// .compactMap { folder -> [String: [String]]? in
// return [folder.externalID!: folder.topLevelFeeds.map { $0.feedID }.sorted(by: <)]
// }
//
// XCTAssertEqual(expectedFolderAndFeedIDs, ingestedFolderAndFeedIDs, "Did not ingest feeds in their corresponding folders.")
// }
//
// func testRemoveFeeds() {
// let folderOne: (name: String, id: String) = ("FolderOne", "folder/1")
// let folderTwo: (name: String, id: String) = ("FolderTwo", "folder/2")
// let feedToRemove = FeedlyFeed(id: "feed/1", title: "Feed One", updated: nil, website: nil)
//
// var feedsForFolderOne = [
// feedToRemove,
// FeedlyFeed(id: "feed/2", title: "Feed Two", updated: nil, website: nil)
// ]
//
// var feedsForFolderTwo = [
// feedToRemove,
// FeedlyFeed(id: "feed/3", title: "Feed Three", updated: nil, website: nil),
// ]
//
// // Add initial content.
// do {
// let namesAndFeeds = [(folderOne, feedsForFolderOne), (folderTwo, feedsForFolderTwo)]
//
// let provider = FeedsAndFoldersProvider()
// provider.feedsAndFolders = namesAndFeeds.map { (folder, feeds) in
// let accountFolder = account.ensureFolder(with: folder.name)!
// accountFolder.externalID = folder.id
// return (feeds, accountFolder)
// }
//
// let createFeeds = FeedlyCreateFeedsForCollectionFoldersOperation(account: account, feedsAndFoldersProvider: provider, log: support.log)
// let completionExpectation = expectation(description: "Did Finish")
// createFeeds.completionBlock = { _ in
// completionExpectation.fulfill()
// }
//
// XCTAssertTrue(account.flattenedFeeds().isEmpty, "Expected empty account.")
//
// MainThreadOperationQueue.shared.add(createFeeds)
//
// waitForExpectations(timeout: 2)
// }
//
// feedsForFolderOne.removeAll { $0.id == feedToRemove.id }
// feedsForFolderTwo.removeAll { $0.id == feedToRemove.id }
// let namesAndFeeds = [(folderOne, feedsForFolderOne), (folderTwo, feedsForFolderTwo)]
//
// let provider = FeedsAndFoldersProvider()
// provider.feedsAndFolders = namesAndFeeds.map { (folder, feeds) in
// let accountFolder = account.ensureFolder(with: folder.name)!
// accountFolder.externalID = folder.id
// return (feeds, accountFolder)
// }
//
// let removeFeeds = FeedlyCreateFeedsForCollectionFoldersOperation(account: account, feedsAndFoldersProvider: provider, log: support.log)
// let completionExpectation = expectation(description: "Did Finish")
// removeFeeds.completionBlock = { _ in
// completionExpectation.fulfill()
// }
//
// MainThreadOperationQueue.shared.add(removeFeeds)
//
// waitForExpectations(timeout: 2)
//
// let feedIDs = Set([feedsForFolderOne, feedsForFolderTwo]
// .flatMap { $0 }
// .map { $0.id })
//
// let feedTitles = Set([feedsForFolderOne, feedsForFolderTwo]
// .flatMap { $0 }
// .map { $0.title })
//
// let accountFeeds = account.flattenedFeeds()
// let ingestedIDs = Set(accountFeeds.map { $0.feedID })
// let ingestedTitles = Set(accountFeeds.map { $0.nameForDisplay })
//
// XCTAssertEqual(ingestedIDs.count, feedIDs.count)
// XCTAssertEqual(ingestedTitles.count, feedTitles.count)
//
// let missingIDs = feedIDs.subtracting(ingestedIDs)
// let missingTitles = feedTitles.subtracting(ingestedTitles)
//
// XCTAssertTrue(missingIDs.isEmpty, "Failed to ingest feeds with these ids.")
// XCTAssertTrue(missingTitles.isEmpty, "Failed to ingest feeds with these titles.")
//
// let expectedFolderAndFeedIDs = namesAndFeeds
// .sorted { $0.0.id < $1.0.id }
// .map { folder, feeds -> [String: [String]] in
// return [folder.id: feeds.map { $0.id }.sorted(by: <)]
// }
//
// let ingestedFolderAndFeedIDs = (account.folders ?? Set())
// .sorted { $0.externalID! < $1.externalID! }
// .compactMap { folder -> [String: [String]]? in
// return [folder.externalID!: folder.topLevelFeeds.map { $0.feedID }.sorted(by: <)]
// }
//
// XCTAssertEqual(expectedFolderAndFeedIDs, ingestedFolderAndFeedIDs, "Did not ingest feeds to their corresponding folders.")
// }
//}

View File

@ -10,83 +10,83 @@ import XCTest
@testable import Account
import os.log
class FeedlyGetCollectionsOperationTests: XCTestCase {
func testGetCollections() {
let support = FeedlyTestSupport()
let (transport, caller) = support.makeMockNetworkStack()
let jsonName = "JSON/feedly_collections_initial"
transport.testFiles["/v3/collections"] = "\(jsonName).json"
let getCollections = FeedlyGetCollectionsOperation(service: caller, log: support.log)
let completionExpectation = expectation(description: "Did Finish")
getCollections.completionBlock = { _ in
completionExpectation.fulfill()
}
MainThreadOperationQueue.shared.add(getCollections)
waitForExpectations(timeout: 2)
let collections = support.testJSON(named: jsonName) as! [[String:Any]]
let labelsInJSON = Set(collections.map { $0["label"] as! String })
let idsInJSON = Set(collections.map { $0["id"] as! String })
let labels = Set(getCollections.collections.map { $0.label })
let ids = Set(getCollections.collections.map { $0.id })
let missingLabels = labelsInJSON.subtracting(labels)
let missingIDs = idsInJSON.subtracting(ids)
XCTAssertEqual(getCollections.collections.count, collections.count, "Mismatch between collections provided by operation and test JSON collections.")
XCTAssertTrue(missingLabels.isEmpty, "Collections with these labels did not have a corresponding \(FeedlyCollection.self) value with the same name.")
XCTAssertTrue(missingIDs.isEmpty, "Collections with these ids did not have a corresponding \(FeedlyCollection.self) with the same id.")
for collection in collections {
let collectionID = collection["id"] as! String
let collectionFeeds = collection["feeds"] as! [[String: Any]]
let collectionFeedIDs = Set(collectionFeeds.map { $0["id"] as! String })
for operationCollection in getCollections.collections where operationCollection.id == collectionID {
let feedIDs = Set(operationCollection.feeds.map { $0.id })
let missingIDs = collectionFeedIDs.subtracting(feedIDs)
XCTAssertTrue(missingIDs.isEmpty, "Feeds with these ids were not found in the \"\(operationCollection.label)\" \(FeedlyCollection.self).")
}
}
}
func testGetCollectionsError() {
class TestDelegate: FeedlyOperationDelegate {
var errorExpectation: XCTestExpectation?
var error: Error?
func feedlyOperation(_ operation: FeedlyOperation, didFailWith error: Error) {
self.error = error
errorExpectation?.fulfill()
}
}
let delegate = TestDelegate()
delegate.errorExpectation = expectation(description: "Did Fail With Expected Error")
let support = FeedlyTestSupport()
let service = TestGetCollectionsService()
service.mockResult = .failure(URLError(.timedOut))
let getCollections = FeedlyGetCollectionsOperation(service: service, log: support.log)
getCollections.delegate = delegate
let completionExpectation = expectation(description: "Did Finish")
getCollections.completionBlock = { _ in
completionExpectation.fulfill()
}
MainThreadOperationQueue.shared.add(getCollections)
waitForExpectations(timeout: 2)
XCTAssertNotNil(delegate.error)
XCTAssertTrue(getCollections.collections.isEmpty, "Collections should be empty.")
}
}
//class FeedlyGetCollectionsOperationTests: XCTestCase {
//
// func testGetCollections() {
// let support = FeedlyTestSupport()
// let (transport, caller) = support.makeMockNetworkStack()
// let jsonName = "JSON/feedly_collections_initial"
// transport.testFiles["/v3/collections"] = "\(jsonName).json"
//
// let getCollections = FeedlyGetCollectionsOperation(service: caller, log: support.log)
// let completionExpectation = expectation(description: "Did Finish")
// getCollections.completionBlock = { _ in
// completionExpectation.fulfill()
// }
//
// MainThreadOperationQueue.shared.add(getCollections)
//
// waitForExpectations(timeout: 2)
//
// let collections = support.testJSON(named: jsonName) as! [[String:Any]]
// let labelsInJSON = Set(collections.map { $0["label"] as! String })
// let idsInJSON = Set(collections.map { $0["id"] as! String })
//
// let labels = Set(getCollections.collections.map { $0.label })
// let ids = Set(getCollections.collections.map { $0.id })
//
// let missingLabels = labelsInJSON.subtracting(labels)
// let missingIDs = idsInJSON.subtracting(ids)
//
// XCTAssertEqual(getCollections.collections.count, collections.count, "Mismatch between collections provided by operation and test JSON collections.")
// XCTAssertTrue(missingLabels.isEmpty, "Collections with these labels did not have a corresponding \(FeedlyCollection.self) value with the same name.")
// XCTAssertTrue(missingIDs.isEmpty, "Collections with these ids did not have a corresponding \(FeedlyCollection.self) with the same id.")
//
// for collection in collections {
// let collectionID = collection["id"] as! String
// let collectionFeeds = collection["feeds"] as! [[String: Any]]
// let collectionFeedIDs = Set(collectionFeeds.map { $0["id"] as! String })
//
// for operationCollection in getCollections.collections where operationCollection.id == collectionID {
// let feedIDs = Set(operationCollection.feeds.map { $0.id })
// let missingIDs = collectionFeedIDs.subtracting(feedIDs)
// XCTAssertTrue(missingIDs.isEmpty, "Feeds with these ids were not found in the \"\(operationCollection.label)\" \(FeedlyCollection.self).")
// }
// }
// }
//
// func testGetCollectionsError() {
//
// class TestDelegate: FeedlyOperationDelegate {
// var errorExpectation: XCTestExpectation?
// var error: Error?
//
// func feedlyOperation(_ operation: FeedlyOperation, didFailWith error: Error) {
// self.error = error
// errorExpectation?.fulfill()
// }
// }
//
// let delegate = TestDelegate()
// delegate.errorExpectation = expectation(description: "Did Fail With Expected Error")
//
// let support = FeedlyTestSupport()
// let service = TestGetCollectionsService()
// service.mockResult = .failure(URLError(.timedOut))
//
// let getCollections = FeedlyGetCollectionsOperation(service: service, log: support.log)
// getCollections.delegate = delegate
//
// let completionExpectation = expectation(description: "Did Finish")
// getCollections.completionBlock = { _ in
// completionExpectation.fulfill()
// }
//
// MainThreadOperationQueue.shared.add(getCollections)
//
// waitForExpectations(timeout: 2)
//
// XCTAssertNotNil(delegate.error)
// XCTAssertTrue(getCollections.collections.isEmpty, "Collections should be empty.")
// }
//}

View File

@ -9,123 +9,123 @@
import XCTest
@testable import Account
class FeedlyGetStreamContentsOperationTests: XCTestCase {
private var account: Account!
private let support = FeedlyTestSupport()
override func setUp() {
super.setUp()
account = support.makeTestAccount()
}
override func tearDown() {
if let account = account {
support.destroy(account)
}
super.tearDown()
}
func testGetStreamContentsFailure() {
let service = TestGetStreamContentsService()
let resource = FeedlyCategoryResourceID(id: "user/1234/category/5678")
let getStreamContents = FeedlyGetStreamContentsOperation(account: account, resource: resource, service: service, continuation: nil, newerThan: nil, unreadOnly: nil, log: support.log)
service.mockResult = .failure(URLError(.fileDoesNotExist))
let completionExpectation = expectation(description: "Did Finish")
getStreamContents.completionBlock = { _ in
completionExpectation.fulfill()
}
MainThreadOperationQueue.shared.add(getStreamContents)
waitForExpectations(timeout: 2)
XCTAssertNil(getStreamContents.stream)
}
func testValuesPassingForGetStreamContents() {
let service = TestGetStreamContentsService()
let resource = FeedlyCategoryResourceID(id: "user/1234/category/5678")
let continuation: String? = "abcdefg"
let newerThan: Date? = Date(timeIntervalSinceReferenceDate: 86)
let unreadOnly: Bool? = true
let getStreamContents = FeedlyGetStreamContentsOperation(account: account, resource: resource, service: service, continuation: continuation, newerThan: newerThan, unreadOnly: unreadOnly, log: support.log)
let mockStream = FeedlyStream(id: "stream/1", updated: nil, continuation: nil, items: [])
service.mockResult = .success(mockStream)
service.getStreamContentsExpectation = expectation(description: "Did Call Service")
service.parameterTester = { serviceResource, serviceContinuation, serviceNewerThan, serviceUnreadOnly in
// Verify these values given to the operation are passed to the service.
XCTAssertEqual(serviceResource.id, resource.id)
XCTAssertEqual(serviceContinuation, continuation)
XCTAssertEqual(serviceNewerThan, newerThan)
XCTAssertEqual(serviceUnreadOnly, unreadOnly)
}
let completionExpectation = expectation(description: "Did Finish")
getStreamContents.completionBlock = { _ in
completionExpectation.fulfill()
}
MainThreadOperationQueue.shared.add(getStreamContents)
waitForExpectations(timeout: 2)
guard let stream = getStreamContents.stream else {
XCTFail("\(FeedlyGetStreamContentsOperation.self) did not store the stream.")
return
}
XCTAssertEqual(stream.id, mockStream.id)
XCTAssertEqual(stream.updated, mockStream.updated)
XCTAssertEqual(stream.continuation, mockStream.continuation)
let streamIDs = stream.items.map { $0.id }
let mockStreamIDs = mockStream.items.map { $0.id }
XCTAssertEqual(streamIDs, mockStreamIDs)
}
func testGetStreamContentsFromJSON() {
let support = FeedlyTestSupport()
let (transport, caller) = support.makeMockNetworkStack()
let jsonName = "JSON/feedly_macintosh_initial"
transport.testFiles["/v3/streams/contents"] = "\(jsonName).json"
let resource = FeedlyCategoryResourceID(id: "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/category/5ca4d61d-e55d-4999-a8d1-c3b9d8789815")
let getStreamContents = FeedlyGetStreamContentsOperation(account: account, resource: resource, service: caller, continuation: nil, newerThan: nil, unreadOnly: nil, log: support.log)
let completionExpectation = expectation(description: "Did Finish")
getStreamContents.completionBlock = { _ in
completionExpectation.fulfill()
}
MainThreadOperationQueue.shared.add(getStreamContents)
waitForExpectations(timeout: 2)
// verify entry providing and parsed item providing
guard let stream = getStreamContents.stream else {
return XCTFail("Expected to have stream.")
}
let streamJSON = support.testJSON(named: jsonName) as! [String:Any]
let id = streamJSON["id"] as! String
XCTAssertEqual(stream.id, id)
let milliseconds = streamJSON["updated"] as! Double
let updated = Date(timeIntervalSince1970: TimeInterval(milliseconds / 1000))
XCTAssertEqual(stream.updated, updated)
let continuation = streamJSON["continuation"] as! String
XCTAssertEqual(stream.continuation, continuation)
support.check(getStreamContents.entries, correspondToStreamItemsIn: streamJSON)
support.check(stream.items, correspondToStreamItemsIn: streamJSON)
}
}
//class FeedlyGetStreamContentsOperationTests: XCTestCase {
//
// private var account: Account!
// private let support = FeedlyTestSupport()
//
// override func setUp() {
// super.setUp()
// account = support.makeTestAccount()
// }
//
// override func tearDown() {
// if let account = account {
// support.destroy(account)
// }
// super.tearDown()
// }
//
// func testGetStreamContentsFailure() {
// let service = TestGetStreamContentsService()
// let resource = FeedlyCategoryResourceID(id: "user/1234/category/5678")
//
// let getStreamContents = FeedlyGetStreamContentsOperation(account: account, resource: resource, service: service, continuation: nil, newerThan: nil, unreadOnly: nil, log: support.log)
//
// service.mockResult = .failure(URLError(.fileDoesNotExist))
//
// let completionExpectation = expectation(description: "Did Finish")
// getStreamContents.completionBlock = { _ in
// completionExpectation.fulfill()
// }
//
// MainThreadOperationQueue.shared.add(getStreamContents)
//
// waitForExpectations(timeout: 2)
//
// XCTAssertNil(getStreamContents.stream)
// }
//
// func testValuesPassingForGetStreamContents() {
// let service = TestGetStreamContentsService()
// let resource = FeedlyCategoryResourceID(id: "user/1234/category/5678")
//
// let continuation: String? = "abcdefg"
// let newerThan: Date? = Date(timeIntervalSinceReferenceDate: 86)
// let unreadOnly: Bool? = true
//
// let getStreamContents = FeedlyGetStreamContentsOperation(account: account, resource: resource, service: service, continuation: continuation, newerThan: newerThan, unreadOnly: unreadOnly, log: support.log)
//
// let mockStream = FeedlyStream(id: "stream/1", updated: nil, continuation: nil, items: [])
// service.mockResult = .success(mockStream)
// service.getStreamContentsExpectation = expectation(description: "Did Call Service")
// service.parameterTester = { serviceResource, serviceContinuation, serviceNewerThan, serviceUnreadOnly in
// // Verify these values given to the operation are passed to the service.
// XCTAssertEqual(serviceResource.id, resource.id)
// XCTAssertEqual(serviceContinuation, continuation)
// XCTAssertEqual(serviceNewerThan, newerThan)
// XCTAssertEqual(serviceUnreadOnly, unreadOnly)
// }
//
// let completionExpectation = expectation(description: "Did Finish")
// getStreamContents.completionBlock = { _ in
// completionExpectation.fulfill()
// }
//
// MainThreadOperationQueue.shared.add(getStreamContents)
//
// waitForExpectations(timeout: 2)
//
// guard let stream = getStreamContents.stream else {
// XCTFail("\(FeedlyGetStreamContentsOperation.self) did not store the stream.")
// return
// }
//
// XCTAssertEqual(stream.id, mockStream.id)
// XCTAssertEqual(stream.updated, mockStream.updated)
// XCTAssertEqual(stream.continuation, mockStream.continuation)
//
// let streamIDs = stream.items.map { $0.id }
// let mockStreamIDs = mockStream.items.map { $0.id }
// XCTAssertEqual(streamIDs, mockStreamIDs)
// }
//
// func testGetStreamContentsFromJSON() {
// let support = FeedlyTestSupport()
// let (transport, caller) = support.makeMockNetworkStack()
// let jsonName = "JSON/feedly_macintosh_initial"
// transport.testFiles["/v3/streams/contents"] = "\(jsonName).json"
//
// let resource = FeedlyCategoryResourceID(id: "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/category/5ca4d61d-e55d-4999-a8d1-c3b9d8789815")
// let getStreamContents = FeedlyGetStreamContentsOperation(account: account, resource: resource, service: caller, continuation: nil, newerThan: nil, unreadOnly: nil, log: support.log)
//
// let completionExpectation = expectation(description: "Did Finish")
// getStreamContents.completionBlock = { _ in
// completionExpectation.fulfill()
// }
//
// MainThreadOperationQueue.shared.add(getStreamContents)
//
// waitForExpectations(timeout: 2)
//
// // verify entry providing and parsed item providing
// guard let stream = getStreamContents.stream else {
// return XCTFail("Expected to have stream.")
// }
//
// let streamJSON = support.testJSON(named: jsonName) as! [String:Any]
//
// let id = streamJSON["id"] as! String
// XCTAssertEqual(stream.id, id)
//
// let milliseconds = streamJSON["updated"] as! Double
// let updated = Date(timeIntervalSince1970: TimeInterval(milliseconds / 1000))
// XCTAssertEqual(stream.updated, updated)
//
// let continuation = streamJSON["continuation"] as! String
// XCTAssertEqual(stream.continuation, continuation)
//
// support.check(getStreamContents.entries, correspondToStreamItemsIn: streamJSON)
// support.check(stream.items, correspondToStreamItemsIn: streamJSON)
// }
//}

View File

@ -10,166 +10,166 @@ import XCTest
@testable import Account
import Secrets
class FeedlyLogoutOperationTests: XCTestCase {
private var account: Account!
private let support = FeedlyTestSupport()
override func setUp() {
super.setUp()
account = support.makeTestAccount()
}
override func tearDown() {
if let account = account {
support.destroy(account)
}
super.tearDown()
}
private func getTokens(for account: Account) throws -> (accessToken: Credentials, refreshToken: Credentials) {
guard let accessToken = try account.retrieveCredentials(type: .oauthAccessToken), let refreshToken = try account.retrieveCredentials(type: .oauthRefreshToken) else {
XCTFail("Unable to retrieve access and/or refresh token from account.")
throw CredentialsError.incompleteCredentials
}
return (accessToken, refreshToken)
}
class TestFeedlyLogoutService: FeedlyLogoutService {
var mockResult: Result<Void, Error>?
var logoutExpectation: XCTestExpectation?
func logout(completion: @escaping (Result<Void, Error>) -> ()) {
guard let result = mockResult else {
XCTFail("Missing mock result. Test may time out because the completion will not be called.")
return
}
DispatchQueue.main.async {
completion(result)
self.logoutExpectation?.fulfill()
}
}
}
func testLogoutSuccess() {
let service = TestFeedlyLogoutService()
service.logoutExpectation = expectation(description: "Did Call Logout")
service.mockResult = .success(())
let logout = FeedlyLogoutOperation(account: account, service: service, log: support.log)
// If this expectation is not fulfilled, the operation is not calling `didFinish`.
let completionExpectation = expectation(description: "Did Finish")
logout.completionBlock = { _ in
completionExpectation.fulfill()
}
MainThreadOperationQueue.shared.add(logout)
waitForExpectations(timeout: 1)
XCTAssertFalse(logout.isCanceled)
do {
let accountAccessToken = try account.retrieveCredentials(type: .oauthAccessToken)
let accountRefreshToken = try account.retrieveCredentials(type: .oauthRefreshToken)
XCTAssertNil(accountAccessToken)
XCTAssertNil(accountRefreshToken)
} catch {
XCTFail("Could not verify tokens were deleted.")
}
}
class TestLogoutDelegate: FeedlyOperationDelegate {
var error: Error?
var didFailExpectation: XCTestExpectation?
func feedlyOperation(_ operation: FeedlyOperation, didFailWith error: Error) {
self.error = error
didFailExpectation?.fulfill()
}
}
func testLogoutMissingAccessToken() {
support.removeCredentials(matching: .oauthAccessToken, from: account)
let (_, service) = support.makeMockNetworkStack()
service.credentials = nil
let logout = FeedlyLogoutOperation(account: account, service: service, log: support.log)
let delegate = TestLogoutDelegate()
delegate.didFailExpectation = expectation(description: "Did Fail")
logout.delegate = delegate
// If this expectation is not fulfilled, the operation is not calling `didFinish`.
let completionExpectation = expectation(description: "Did Finish")
logout.completionBlock = { _ in
completionExpectation.fulfill()
}
MainThreadOperationQueue.shared.add(logout)
waitForExpectations(timeout: 1)
XCTAssertFalse(logout.isCanceled)
do {
let accountAccessToken = try account.retrieveCredentials(type: .oauthAccessToken)
XCTAssertNil(accountAccessToken)
} catch {
XCTFail("Could not verify tokens were deleted.")
}
XCTAssertNotNil(delegate.error, "Should have failed with error.")
if let error = delegate.error {
switch error {
case CredentialsError.incompleteCredentials:
break
default:
XCTFail("Expected \(CredentialsError.incompleteCredentials)")
}
}
}
func testLogoutFailure() {
let service = TestFeedlyLogoutService()
service.logoutExpectation = expectation(description: "Did Call Logout")
service.mockResult = .failure(URLError(.timedOut))
let accessToken: Credentials
let refreshToken: Credentials
do {
(accessToken, refreshToken) = try getTokens(for: account)
} catch {
XCTFail("Could not retrieve credentials to verify their integrity later.")
return
}
let logout = FeedlyLogoutOperation(account: account, service: service, log: support.log)
// If this expectation is not fulfilled, the operation is not calling `didFinish`.
let completionExpectation = expectation(description: "Did Finish")
logout.completionBlock = { _ in
completionExpectation.fulfill()
}
MainThreadOperationQueue.shared.add(logout)
waitForExpectations(timeout: 1)
XCTAssertFalse(logout.isCanceled)
do {
let accountAccessToken = try account.retrieveCredentials(type: .oauthAccessToken)
let accountRefreshToken = try account.retrieveCredentials(type: .oauthRefreshToken)
XCTAssertEqual(accountAccessToken, accessToken)
XCTAssertEqual(accountRefreshToken, refreshToken)
} catch {
XCTFail("Could not verify tokens were left intact. Did the operation delete them?")
}
}
}
//class FeedlyLogoutOperationTests: XCTestCase {
//
// private var account: Account!
// private let support = FeedlyTestSupport()
//
// override func setUp() {
// super.setUp()
// account = support.makeTestAccount()
// }
//
// override func tearDown() {
// if let account = account {
// support.destroy(account)
// }
// super.tearDown()
// }
//
// private func getTokens(for account: Account) throws -> (accessToken: Credentials, refreshToken: Credentials) {
// guard let accessToken = try account.retrieveCredentials(type: .oauthAccessToken), let refreshToken = try account.retrieveCredentials(type: .oauthRefreshToken) else {
// XCTFail("Unable to retrieve access and/or refresh token from account.")
// throw CredentialsError.incompleteCredentials
// }
// return (accessToken, refreshToken)
// }
//
// class TestFeedlyLogoutService: FeedlyLogoutService {
// var mockResult: Result<Void, Error>?
// var logoutExpectation: XCTestExpectation?
//
// func logout(completion: @escaping (Result<Void, Error>) -> ()) {
// guard let result = mockResult else {
// XCTFail("Missing mock result. Test may time out because the completion will not be called.")
// return
// }
// DispatchQueue.main.async {
// completion(result)
// self.logoutExpectation?.fulfill()
// }
// }
// }
//
// func testLogoutSuccess() {
// let service = TestFeedlyLogoutService()
// service.logoutExpectation = expectation(description: "Did Call Logout")
// service.mockResult = .success(())
//
// let logout = FeedlyLogoutOperation(account: account, service: service, log: support.log)
//
// // If this expectation is not fulfilled, the operation is not calling `didFinish`.
// let completionExpectation = expectation(description: "Did Finish")
// logout.completionBlock = { _ in
// completionExpectation.fulfill()
// }
//
// MainThreadOperationQueue.shared.add(logout)
//
// waitForExpectations(timeout: 1)
//
// XCTAssertFalse(logout.isCanceled)
//
// do {
// let accountAccessToken = try account.retrieveCredentials(type: .oauthAccessToken)
// let accountRefreshToken = try account.retrieveCredentials(type: .oauthRefreshToken)
//
// XCTAssertNil(accountAccessToken)
// XCTAssertNil(accountRefreshToken)
// } catch {
// XCTFail("Could not verify tokens were deleted.")
// }
// }
//
// class TestLogoutDelegate: FeedlyOperationDelegate {
// var error: Error?
// var didFailExpectation: XCTestExpectation?
//
// func feedlyOperation(_ operation: FeedlyOperation, didFailWith error: Error) {
// self.error = error
// didFailExpectation?.fulfill()
// }
// }
//
// func testLogoutMissingAccessToken() {
// support.removeCredentials(matching: .oauthAccessToken, from: account)
//
// let (_, service) = support.makeMockNetworkStack()
// service.credentials = nil
//
// let logout = FeedlyLogoutOperation(account: account, service: service, log: support.log)
//
// let delegate = TestLogoutDelegate()
// delegate.didFailExpectation = expectation(description: "Did Fail")
//
// logout.delegate = delegate
//
// // If this expectation is not fulfilled, the operation is not calling `didFinish`.
// let completionExpectation = expectation(description: "Did Finish")
// logout.completionBlock = { _ in
// completionExpectation.fulfill()
// }
//
// MainThreadOperationQueue.shared.add(logout)
//
// waitForExpectations(timeout: 1)
//
// XCTAssertFalse(logout.isCanceled)
//
// do {
// let accountAccessToken = try account.retrieveCredentials(type: .oauthAccessToken)
// XCTAssertNil(accountAccessToken)
// } catch {
// XCTFail("Could not verify tokens were deleted.")
// }
//
// XCTAssertNotNil(delegate.error, "Should have failed with error.")
// if let error = delegate.error {
// switch error {
// case CredentialsError.incompleteCredentials:
// break
// default:
// XCTFail("Expected \(CredentialsError.incompleteCredentials)")
// }
// }
// }
//
// func testLogoutFailure() {
// let service = TestFeedlyLogoutService()
// service.logoutExpectation = expectation(description: "Did Call Logout")
// service.mockResult = .failure(URLError(.timedOut))
//
// let accessToken: Credentials
// let refreshToken: Credentials
// do {
// (accessToken, refreshToken) = try getTokens(for: account)
// } catch {
// XCTFail("Could not retrieve credentials to verify their integrity later.")
// return
// }
//
// let logout = FeedlyLogoutOperation(account: account, service: service, log: support.log)
//
// // If this expectation is not fulfilled, the operation is not calling `didFinish`.
// let completionExpectation = expectation(description: "Did Finish")
// logout.completionBlock = { _ in
// completionExpectation.fulfill()
// }
//
// MainThreadOperationQueue.shared.add(logout)
//
// waitForExpectations(timeout: 1)
//
// XCTAssertFalse(logout.isCanceled)
//
// do {
// let accountAccessToken = try account.retrieveCredentials(type: .oauthAccessToken)
// let accountRefreshToken = try account.retrieveCredentials(type: .oauthRefreshToken)
//
// XCTAssertEqual(accountAccessToken, accessToken)
// XCTAssertEqual(accountRefreshToken, refreshToken)
// } catch {
// XCTFail("Could not verify tokens were left intact. Did the operation delete them?")
// }
// }
//}

View File

@ -9,195 +9,195 @@
import XCTest
@testable import Account
class FeedlyMirrorCollectionsAsFoldersOperationTests: XCTestCase {
private var account: Account!
private let support = FeedlyTestSupport()
override func setUp() {
super.setUp()
account = support.makeTestAccount()
}
override func tearDown() {
if let account = account {
support.destroy(account)
}
super.tearDown()
}
class CollectionsProvider: FeedlyCollectionProviding {
var collections = [
FeedlyCollection(feeds: [], label: "One", id: "collections/1"),
FeedlyCollection(feeds: [], label: "Two", id: "collections/2")
]
}
func testAddsFolders() {
let provider = CollectionsProvider()
let mirrorOperation = FeedlyMirrorCollectionsAsFoldersOperation(account: account, collectionsProvider: provider, log: support.log)
let completionExpectation = expectation(description: "Did Finish")
mirrorOperation.completionBlock = { _ in
completionExpectation.fulfill()
}
MainThreadOperationQueue.shared.add(mirrorOperation)
waitForExpectations(timeout: 2)
let folders = account.folders ?? Set()
let folderNames = Set(folders.compactMap { $0.nameForDisplay })
let folderExternalIDs = Set(folders.compactMap { $0.externalID })
let collectionLabels = Set(provider.collections.map { $0.label })
let collectionIDs = Set(provider.collections.map { $0.id })
let missingNames = collectionLabels.subtracting(folderNames)
let missingIDs = collectionIDs.subtracting(folderExternalIDs)
XCTAssertTrue(missingNames.isEmpty, "Collections with these labels have no corresponding folder.")
XCTAssertTrue(missingIDs.isEmpty, "Collections with these ids have no corresponding folder.")
// XCTAssertEqual(mirrorOperation.collectionsAndFolders.count, provider.collections.count, "Mismatch between collections and folders.")
}
func testRemovesFolders() {
let provider = CollectionsProvider()
do {
let addFolders = FeedlyMirrorCollectionsAsFoldersOperation(account: account, collectionsProvider: provider, log: support.log)
let completionExpectation = expectation(description: "Did Finish")
addFolders.completionBlock = { _ in
completionExpectation.fulfill()
}
MainThreadOperationQueue.shared.add(addFolders)
waitForExpectations(timeout: 2)
}
// Now that the folders are added, remove them all.
provider.collections = []
let removeFolders = FeedlyMirrorCollectionsAsFoldersOperation(account: account, collectionsProvider: provider, log: support.log)
let completionExpectation = expectation(description: "Did Finish")
removeFolders.completionBlock = { _ in
completionExpectation.fulfill()
}
MainThreadOperationQueue.shared.add(removeFolders)
waitForExpectations(timeout: 2)
let folders = account.folders ?? Set()
let folderNames = Set(folders.compactMap { $0.nameForDisplay })
let folderExternalIDs = Set(folders.compactMap { $0.externalID })
let collectionLabels = Set(provider.collections.map { $0.label })
let collectionIDs = Set(provider.collections.map { $0.id })
let remainingNames = folderNames.subtracting(collectionLabels)
let remainingIDs = folderExternalIDs.subtracting(collectionIDs)
XCTAssertTrue(remainingNames.isEmpty, "Folders with these names remain with no corresponding collection.")
XCTAssertTrue(remainingIDs.isEmpty, "Folders with these ids remain with no corresponding collection.")
XCTAssertTrue(removeFolders.feedsAndFolders.isEmpty)
}
class CollectionsAndFeedsProvider: FeedlyCollectionProviding {
var feedsForCollectionOne = [
FeedlyFeed(id: "feed/1", title: "Feed One", updated: nil, website: nil),
FeedlyFeed(id: "feed/2", title: "Feed Two", updated: nil, website: nil)
]
var feedsForCollectionTwo = [
FeedlyFeed(id: "feed/1", title: "Feed One", updated: nil, website: nil),
FeedlyFeed(id: "feed/3", title: "Feed Three", updated: nil, website: nil),
]
var collections: [FeedlyCollection] {
return [
FeedlyCollection(feeds: feedsForCollectionOne, label: "One", id: "collections/1"),
FeedlyCollection(feeds: feedsForCollectionTwo, label: "Two", id: "collections/2")
]
}
}
func testFeedMappedToFolders() {
let provider = CollectionsAndFeedsProvider()
let mirrorOperation = FeedlyMirrorCollectionsAsFoldersOperation(account: account, collectionsProvider: provider, log: support.log)
let completionExpectation = expectation(description: "Did Finish")
mirrorOperation.completionBlock = { _ in
completionExpectation.fulfill()
}
MainThreadOperationQueue.shared.add(mirrorOperation)
waitForExpectations(timeout: 2)
let folders = account.folders ?? Set()
let folderNames = Set(folders.compactMap { $0.nameForDisplay })
let folderExternalIDs = Set(folders.compactMap { $0.externalID })
let collectionLabels = Set(provider.collections.map { $0.label })
let collectionIDs = Set(provider.collections.map { $0.id })
let missingNames = collectionLabels.subtracting(folderNames)
let missingIDs = collectionIDs.subtracting(folderExternalIDs)
XCTAssertTrue(missingNames.isEmpty, "Collections with these labels have no corresponding folder.")
XCTAssertTrue(missingIDs.isEmpty, "Collections with these ids have no corresponding folder.")
let collectionIDsAndFeedIDs = provider.collections.map { collection -> [String:[String]] in
return [collection.id: collection.feeds.map { $0.id }.sorted(by: <)]
}
let folderIDsAndFeedIDs = mirrorOperation.feedsAndFolders.compactMap { feeds, folder -> [String:[String]]? in
guard let id = folder.externalID else {
return nil
}
return [id: feeds.map { $0.id }.sorted(by: <)]
}
XCTAssertEqual(collectionIDsAndFeedIDs, folderIDsAndFeedIDs, "Did not map folders to feeds correctly.")
}
func testRemovingFolderRemovesFeeds() {
do {
let provider = CollectionsAndFeedsProvider()
let addFoldersAndFeeds = FeedlyMirrorCollectionsAsFoldersOperation(account: account, collectionsProvider: provider, log: support.log)
let createFeeds = FeedlyCreateFeedsForCollectionFoldersOperation(account: account, feedsAndFoldersProvider: addFoldersAndFeeds, log: support.log)
MainThreadOperationQueue.shared.make(createFeeds, dependOn: addFoldersAndFeeds)
let completionExpectation = expectation(description: "Did Finish")
createFeeds.completionBlock = { _ in
completionExpectation.fulfill()
}
MainThreadOperationQueue.shared.addOperations([addFoldersAndFeeds, createFeeds])
waitForExpectations(timeout: 2)
XCTAssertFalse(account.flattenedFeeds().isEmpty, "Expected account to have feeds.")
}
// Now that the folders are added, remove them all.
let provider = CollectionsProvider()
provider.collections = []
let removeFolders = FeedlyMirrorCollectionsAsFoldersOperation(account: account, collectionsProvider: provider, log: support.log)
let completionExpectation = expectation(description: "Did Finish")
removeFolders.completionBlock = { _ in
completionExpectation.fulfill()
}
MainThreadOperationQueue.shared.add(removeFolders)
waitForExpectations(timeout: 2)
let feeds = account.flattenedFeeds()
XCTAssertTrue(feeds.isEmpty)
}
}
//class FeedlyMirrorCollectionsAsFoldersOperationTests: XCTestCase {
//
// private var account: Account!
// private let support = FeedlyTestSupport()
//
// override func setUp() {
// super.setUp()
// account = support.makeTestAccount()
// }
//
// override func tearDown() {
// if let account = account {
// support.destroy(account)
// }
// super.tearDown()
// }
//
// class CollectionsProvider: FeedlyCollectionProviding {
// var collections = [
// FeedlyCollection(feeds: [], label: "One", id: "collections/1"),
// FeedlyCollection(feeds: [], label: "Two", id: "collections/2")
// ]
// }
//
// func testAddsFolders() {
// let provider = CollectionsProvider()
// let mirrorOperation = FeedlyMirrorCollectionsAsFoldersOperation(account: account, collectionsProvider: provider, log: support.log)
// let completionExpectation = expectation(description: "Did Finish")
// mirrorOperation.completionBlock = { _ in
// completionExpectation.fulfill()
// }
//
// MainThreadOperationQueue.shared.add(mirrorOperation)
//
// waitForExpectations(timeout: 2)
//
// let folders = account.folders ?? Set()
// let folderNames = Set(folders.compactMap { $0.nameForDisplay })
// let folderExternalIDs = Set(folders.compactMap { $0.externalID })
//
// let collectionLabels = Set(provider.collections.map { $0.label })
// let collectionIDs = Set(provider.collections.map { $0.id })
//
// let missingNames = collectionLabels.subtracting(folderNames)
// let missingIDs = collectionIDs.subtracting(folderExternalIDs)
//
// XCTAssertTrue(missingNames.isEmpty, "Collections with these labels have no corresponding folder.")
// XCTAssertTrue(missingIDs.isEmpty, "Collections with these ids have no corresponding folder.")
//// XCTAssertEqual(mirrorOperation.collectionsAndFolders.count, provider.collections.count, "Mismatch between collections and folders.")
// }
//
// func testRemovesFolders() {
// let provider = CollectionsProvider()
//
// do {
// let addFolders = FeedlyMirrorCollectionsAsFoldersOperation(account: account, collectionsProvider: provider, log: support.log)
// let completionExpectation = expectation(description: "Did Finish")
// addFolders.completionBlock = { _ in
// completionExpectation.fulfill()
// }
//
// MainThreadOperationQueue.shared.add(addFolders)
//
// waitForExpectations(timeout: 2)
// }
//
// // Now that the folders are added, remove them all.
// provider.collections = []
//
// let removeFolders = FeedlyMirrorCollectionsAsFoldersOperation(account: account, collectionsProvider: provider, log: support.log)
// let completionExpectation = expectation(description: "Did Finish")
// removeFolders.completionBlock = { _ in
// completionExpectation.fulfill()
// }
//
// MainThreadOperationQueue.shared.add(removeFolders)
//
// waitForExpectations(timeout: 2)
//
// let folders = account.folders ?? Set()
// let folderNames = Set(folders.compactMap { $0.nameForDisplay })
// let folderExternalIDs = Set(folders.compactMap { $0.externalID })
//
// let collectionLabels = Set(provider.collections.map { $0.label })
// let collectionIDs = Set(provider.collections.map { $0.id })
//
// let remainingNames = folderNames.subtracting(collectionLabels)
// let remainingIDs = folderExternalIDs.subtracting(collectionIDs)
//
// XCTAssertTrue(remainingNames.isEmpty, "Folders with these names remain with no corresponding collection.")
// XCTAssertTrue(remainingIDs.isEmpty, "Folders with these ids remain with no corresponding collection.")
//
// XCTAssertTrue(removeFolders.feedsAndFolders.isEmpty)
// }
//
// class CollectionsAndFeedsProvider: FeedlyCollectionProviding {
// var feedsForCollectionOne = [
// FeedlyFeed(id: "feed/1", title: "Feed One", updated: nil, website: nil),
// FeedlyFeed(id: "feed/2", title: "Feed Two", updated: nil, website: nil)
// ]
//
// var feedsForCollectionTwo = [
// FeedlyFeed(id: "feed/1", title: "Feed One", updated: nil, website: nil),
// FeedlyFeed(id: "feed/3", title: "Feed Three", updated: nil, website: nil),
// ]
//
// var collections: [FeedlyCollection] {
// return [
// FeedlyCollection(feeds: feedsForCollectionOne, label: "One", id: "collections/1"),
// FeedlyCollection(feeds: feedsForCollectionTwo, label: "Two", id: "collections/2")
// ]
// }
// }
//
// func testFeedMappedToFolders() {
// let provider = CollectionsAndFeedsProvider()
// let mirrorOperation = FeedlyMirrorCollectionsAsFoldersOperation(account: account, collectionsProvider: provider, log: support.log)
// let completionExpectation = expectation(description: "Did Finish")
// mirrorOperation.completionBlock = { _ in
// completionExpectation.fulfill()
// }
//
// MainThreadOperationQueue.shared.add(mirrorOperation)
//
// waitForExpectations(timeout: 2)
//
// let folders = account.folders ?? Set()
// let folderNames = Set(folders.compactMap { $0.nameForDisplay })
// let folderExternalIDs = Set(folders.compactMap { $0.externalID })
//
// let collectionLabels = Set(provider.collections.map { $0.label })
// let collectionIDs = Set(provider.collections.map { $0.id })
//
// let missingNames = collectionLabels.subtracting(folderNames)
// let missingIDs = collectionIDs.subtracting(folderExternalIDs)
//
// XCTAssertTrue(missingNames.isEmpty, "Collections with these labels have no corresponding folder.")
// XCTAssertTrue(missingIDs.isEmpty, "Collections with these ids have no corresponding folder.")
//
// let collectionIDsAndFeedIDs = provider.collections.map { collection -> [String:[String]] in
// return [collection.id: collection.feeds.map { $0.id }.sorted(by: <)]
// }
//
// let folderIDsAndFeedIDs = mirrorOperation.feedsAndFolders.compactMap { feeds, folder -> [String:[String]]? in
// guard let id = folder.externalID else {
// return nil
// }
// return [id: feeds.map { $0.id }.sorted(by: <)]
// }
//
// XCTAssertEqual(collectionIDsAndFeedIDs, folderIDsAndFeedIDs, "Did not map folders to feeds correctly.")
// }
//
// func testRemovingFolderRemovesFeeds() {
// do {
// let provider = CollectionsAndFeedsProvider()
// let addFoldersAndFeeds = FeedlyMirrorCollectionsAsFoldersOperation(account: account, collectionsProvider: provider, log: support.log)
//
// let createFeeds = FeedlyCreateFeedsForCollectionFoldersOperation(account: account, feedsAndFoldersProvider: addFoldersAndFeeds, log: support.log)
// MainThreadOperationQueue.shared.make(createFeeds, dependOn: addFoldersAndFeeds)
//
// let completionExpectation = expectation(description: "Did Finish")
// createFeeds.completionBlock = { _ in
// completionExpectation.fulfill()
// }
//
// MainThreadOperationQueue.shared.addOperations([addFoldersAndFeeds, createFeeds])
//
// waitForExpectations(timeout: 2)
//
// XCTAssertFalse(account.flattenedFeeds().isEmpty, "Expected account to have feeds.")
// }
//
// // Now that the folders are added, remove them all.
// let provider = CollectionsProvider()
// provider.collections = []
//
// let removeFolders = FeedlyMirrorCollectionsAsFoldersOperation(account: account, collectionsProvider: provider, log: support.log)
// let completionExpectation = expectation(description: "Did Finish")
// removeFolders.completionBlock = { _ in
// completionExpectation.fulfill()
// }
//
// MainThreadOperationQueue.shared.add(removeFolders)
//
// waitForExpectations(timeout: 2)
//
// let feeds = account.flattenedFeeds()
//
// XCTAssertTrue(feeds.isEmpty)
// }
//}

View File

@ -9,130 +9,130 @@
import XCTest
@testable import Account
class FeedlySyncStreamContentsOperationTests: XCTestCase {
private var account: Account!
private let support = FeedlyTestSupport()
override func setUp() {
super.setUp()
account = support.makeTestAccount()
}
override func tearDown() {
if let account = account {
support.destroy(account)
}
super.tearDown()
}
func testIngestsOnePageSuccess() throws {
let service = TestGetStreamContentsService()
let resource = FeedlyCategoryResourceID(id: "user/1234/category/5678")
let newerThan: Date? = Date(timeIntervalSinceReferenceDate: 0)
let items = service.makeMockFeedlyEntryItem()
service.mockResult = .success(FeedlyStream(id: resource.id, updated: nil, continuation: nil, items: items))
let getStreamContentsExpectation = expectation(description: "Did Get Page of Stream Contents")
getStreamContentsExpectation.expectedFulfillmentCount = 1
service.getStreamContentsExpectation = getStreamContentsExpectation
service.parameterTester = { serviceResource, continuation, serviceNewerThan, serviceUnreadOnly in
XCTAssertEqual(serviceResource.id, resource.id)
XCTAssertEqual(serviceNewerThan, newerThan)
XCTAssertNil(continuation)
XCTAssertNil(serviceUnreadOnly)
}
let syncStreamContents = FeedlySyncStreamContentsOperation(account: account, resource: resource, service: service, isPagingEnabled: true, newerThan: newerThan, log: support.log)
let completionExpectation = expectation(description: "Did Finish")
syncStreamContents.completionBlock = { _ in
completionExpectation.fulfill()
}
MainThreadOperationQueue.shared.add(syncStreamContents)
waitForExpectations(timeout: 2)
let expectedArticleIDs = Set(items.map { $0.id })
let expectedArticles = try account.fetchArticles(.articleIDs(expectedArticleIDs))
XCTAssertEqual(expectedArticles.count, expectedArticleIDs.count, "Did not fetch all the articles.")
}
func testIngestsOnePageFailure() {
let service = TestGetStreamContentsService()
let resource = FeedlyCategoryResourceID(id: "user/1234/category/5678")
let newerThan: Date? = Date(timeIntervalSinceReferenceDate: 0)
service.mockResult = .failure(URLError(.timedOut))
let getStreamContentsExpectation = expectation(description: "Did Get Page of Stream Contents")
getStreamContentsExpectation.expectedFulfillmentCount = 1
service.getStreamContentsExpectation = getStreamContentsExpectation
service.parameterTester = { serviceResource, continuation, serviceNewerThan, serviceUnreadOnly in
XCTAssertEqual(serviceResource.id, resource.id)
XCTAssertEqual(serviceNewerThan, newerThan)
XCTAssertNil(continuation)
XCTAssertNil(serviceUnreadOnly)
}
let syncStreamContents = FeedlySyncStreamContentsOperation(account: account, resource: resource, service: service, isPagingEnabled: true, newerThan: newerThan, log: support.log)
let completionExpectation = expectation(description: "Did Finish")
syncStreamContents.completionBlock = { _ in
completionExpectation.fulfill()
}
MainThreadOperationQueue.shared.add(syncStreamContents)
waitForExpectations(timeout: 2)
}
func testIngestsManyPagesSuccess() throws {
let service = TestGetPagedStreamContentsService()
let resource = FeedlyCategoryResourceID(id: "user/1234/category/5678")
let newerThan: Date? = Date(timeIntervalSinceReferenceDate: 0)
let continuations = (1...10).map { "\($0)" }
service.addAtLeastOnePage(for: resource, continuations: continuations, numberOfEntriesPerPage: 1000)
let getStreamContentsExpectation = expectation(description: "Did Get Page of Stream Contents")
getStreamContentsExpectation.expectedFulfillmentCount = 1 + continuations.count
var remainingContinuations = Set(continuations)
let getStreamPageExpectation = expectation(description: "Did Request Page")
getStreamPageExpectation.expectedFulfillmentCount = 1 + continuations.count
service.getStreamContentsExpectation = getStreamContentsExpectation
service.parameterTester = { serviceResource, continuation, serviceNewerThan, serviceUnreadOnly in
XCTAssertEqual(serviceResource.id, resource.id)
XCTAssertEqual(serviceNewerThan, newerThan)
XCTAssertNil(serviceUnreadOnly)
if let continuation = continuation {
XCTAssertTrue(remainingContinuations.contains(continuation))
remainingContinuations.remove(continuation)
}
getStreamPageExpectation.fulfill()
}
let syncStreamContents = FeedlySyncStreamContentsOperation(account: account, resource: resource, service: service, isPagingEnabled: true, newerThan: newerThan, log: support.log)
let completionExpectation = expectation(description: "Did Finish")
syncStreamContents.completionBlock = { _ in
completionExpectation.fulfill()
}
MainThreadOperationQueue.shared.add(syncStreamContents)
waitForExpectations(timeout: 30)
// Find articles inserted.
let articleIDs = Set(service.pages.values.map { $0.items }.flatMap { $0 }.map { $0.id })
let articles = try account.fetchArticles(.articleIDs(articleIDs))
XCTAssertEqual(articleIDs.count, articles.count)
}
}
//class FeedlySyncStreamContentsOperationTests: XCTestCase {
//
// private var account: Account!
// private let support = FeedlyTestSupport()
//
// override func setUp() {
// super.setUp()
// account = support.makeTestAccount()
// }
//
// override func tearDown() {
// if let account = account {
// support.destroy(account)
// }
// super.tearDown()
// }
//
// func testIngestsOnePageSuccess() throws {
// let service = TestGetStreamContentsService()
// let resource = FeedlyCategoryResourceID(id: "user/1234/category/5678")
// let newerThan: Date? = Date(timeIntervalSinceReferenceDate: 0)
// let items = service.makeMockFeedlyEntryItem()
// service.mockResult = .success(FeedlyStream(id: resource.id, updated: nil, continuation: nil, items: items))
//
// let getStreamContentsExpectation = expectation(description: "Did Get Page of Stream Contents")
// getStreamContentsExpectation.expectedFulfillmentCount = 1
//
// service.getStreamContentsExpectation = getStreamContentsExpectation
// service.parameterTester = { serviceResource, continuation, serviceNewerThan, serviceUnreadOnly in
// XCTAssertEqual(serviceResource.id, resource.id)
// XCTAssertEqual(serviceNewerThan, newerThan)
// XCTAssertNil(continuation)
// XCTAssertNil(serviceUnreadOnly)
// }
//
// let syncStreamContents = FeedlySyncStreamContentsOperation(account: account, resource: resource, service: service, isPagingEnabled: true, newerThan: newerThan, log: support.log)
//
// let completionExpectation = expectation(description: "Did Finish")
// syncStreamContents.completionBlock = { _ in
// completionExpectation.fulfill()
// }
//
// MainThreadOperationQueue.shared.add(syncStreamContents)
//
// waitForExpectations(timeout: 2)
//
// let expectedArticleIDs = Set(items.map { $0.id })
// let expectedArticles = try account.fetchArticles(.articleIDs(expectedArticleIDs))
// XCTAssertEqual(expectedArticles.count, expectedArticleIDs.count, "Did not fetch all the articles.")
// }
//
// func testIngestsOnePageFailure() {
// let service = TestGetStreamContentsService()
// let resource = FeedlyCategoryResourceID(id: "user/1234/category/5678")
// let newerThan: Date? = Date(timeIntervalSinceReferenceDate: 0)
//
// service.mockResult = .failure(URLError(.timedOut))
//
// let getStreamContentsExpectation = expectation(description: "Did Get Page of Stream Contents")
// getStreamContentsExpectation.expectedFulfillmentCount = 1
//
// service.getStreamContentsExpectation = getStreamContentsExpectation
// service.parameterTester = { serviceResource, continuation, serviceNewerThan, serviceUnreadOnly in
// XCTAssertEqual(serviceResource.id, resource.id)
// XCTAssertEqual(serviceNewerThan, newerThan)
// XCTAssertNil(continuation)
// XCTAssertNil(serviceUnreadOnly)
// }
//
// let syncStreamContents = FeedlySyncStreamContentsOperation(account: account, resource: resource, service: service, isPagingEnabled: true, newerThan: newerThan, log: support.log)
//
// let completionExpectation = expectation(description: "Did Finish")
// syncStreamContents.completionBlock = { _ in
// completionExpectation.fulfill()
// }
//
// MainThreadOperationQueue.shared.add(syncStreamContents)
//
// waitForExpectations(timeout: 2)
// }
//
// func testIngestsManyPagesSuccess() throws {
// let service = TestGetPagedStreamContentsService()
// let resource = FeedlyCategoryResourceID(id: "user/1234/category/5678")
// let newerThan: Date? = Date(timeIntervalSinceReferenceDate: 0)
//
// let continuations = (1...10).map { "\($0)" }
// service.addAtLeastOnePage(for: resource, continuations: continuations, numberOfEntriesPerPage: 1000)
//
// let getStreamContentsExpectation = expectation(description: "Did Get Page of Stream Contents")
// getStreamContentsExpectation.expectedFulfillmentCount = 1 + continuations.count
//
// var remainingContinuations = Set(continuations)
// let getStreamPageExpectation = expectation(description: "Did Request Page")
// getStreamPageExpectation.expectedFulfillmentCount = 1 + continuations.count
//
// service.getStreamContentsExpectation = getStreamContentsExpectation
// service.parameterTester = { serviceResource, continuation, serviceNewerThan, serviceUnreadOnly in
// XCTAssertEqual(serviceResource.id, resource.id)
// XCTAssertEqual(serviceNewerThan, newerThan)
// XCTAssertNil(serviceUnreadOnly)
//
// if let continuation = continuation {
// XCTAssertTrue(remainingContinuations.contains(continuation))
// remainingContinuations.remove(continuation)
// }
//
// getStreamPageExpectation.fulfill()
// }
//
// let syncStreamContents = FeedlySyncStreamContentsOperation(account: account, resource: resource, service: service, isPagingEnabled: true, newerThan: newerThan, log: support.log)
//
// let completionExpectation = expectation(description: "Did Finish")
// syncStreamContents.completionBlock = { _ in
// completionExpectation.fulfill()
// }
//
// MainThreadOperationQueue.shared.add(syncStreamContents)
//
// waitForExpectations(timeout: 30)
//
// // Find articles inserted.
// let articleIDs = Set(service.pages.values.map { $0.items }.flatMap { $0 }.map { $0.id })
// let articles = try account.fetchArticles(.articleIDs(articleIDs))
// XCTAssertEqual(articleIDs.count, articles.count)
// }
//}

View File

@ -13,262 +13,262 @@ import Secrets
import os.log
import SyncDatabase
class FeedlyTestSupport {
var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "FeedlyTests")
var accessToken = Credentials(type: .oauthAccessToken, username: "Test", secret: "t3st-access-tok3n")
var refreshToken = Credentials(type: .oauthRefreshToken, username: "Test", secret: "t3st-refresh-tok3n")
var transport = TestTransport()
func makeMockNetworkStack() -> (TestTransport, FeedlyAPICaller) {
let caller = FeedlyAPICaller(transport: transport, api: .sandbox)
caller.credentials = accessToken
return (transport, caller)
}
func makeTestAccount() -> Account {
let manager = TestAccountManager()
let account = manager.createAccount(type: .feedly, transport: transport)
do {
try account.storeCredentials(refreshToken)
// This must be done last or the account uses the refresh token for request Authorization!
try account.storeCredentials(accessToken)
} catch {
XCTFail("Unable to register mock credentials because \(error)")
}
return account
}
func makeMockOAuthClient() -> OAuthAuthorizationClient {
return OAuthAuthorizationClient(id: "test", redirectURI: "test://test/auth", state: nil, secret: "password")
}
func removeCredentials(matching type: CredentialsType, from account: Account) {
do {
try account.removeCredentials(type: type)
} catch {
XCTFail("Unable to remove \(type)")
}
}
func makeTestDatabaseContainer() -> TestDatabaseContainer {
return TestDatabaseContainer()
}
class TestDatabaseContainer {
private let path: String
private(set) var database: SyncDatabase!
init() {
let dataFolder = try! FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
path = dataFolder.appendingPathComponent("\(UUID().uuidString)-Sync.sqlite3").path
database = SyncDatabase(databasePath: path)
}
deinit {
// We should close the database before removing the database.
database = nil
do {
try FileManager.default.removeItem(atPath: path)
print("Removed database at \(path)")
} catch {
print("Unable to remove database owned by \(self) because \(error).")
}
}
}
func destroy(_ testAccount: Account) {
do {
// These should not throw when the keychain items are not found.
try testAccount.removeCredentials(type: .oauthAccessToken)
try testAccount.removeCredentials(type: .oauthRefreshToken)
} catch {
XCTFail("Unable to clean up mock credentials because \(error)")
}
let manager = TestAccountManager()
manager.deleteAccount(testAccount)
}
func testJSON(named: String, subdirectory: String? = nil) -> Any {
let url = Bundle.module.url(forResource: named, withExtension: "json", subdirectory: subdirectory)!
let data = try! Data(contentsOf: url)
let json = try! JSONSerialization.jsonObject(with: data)
return json
}
func checkFoldersAndFeeds(in account: Account, againstCollectionsAndFeedsInJSONNamed name: String, subdirectory: String? = nil) {
let collections = testJSON(named: name, subdirectory: subdirectory) as! [[String:Any]]
let collectionNames = Set(collections.map { $0["label"] as! String })
let collectionIDs = Set(collections.map { $0["id"] as! String })
let folders = account.folders ?? Set()
let folderNames = Set(folders.compactMap { $0.name })
let folderIDs = Set(folders.compactMap { $0.externalID })
let missingNames = collectionNames.subtracting(folderNames)
let missingIDs = collectionIDs.subtracting(folderIDs)
XCTAssertEqual(folders.count, collections.count, "Mismatch between collections and folders.")
XCTAssertTrue(missingNames.isEmpty, "Collections with these names did not have a corresponding folder with the same name.")
XCTAssertTrue(missingIDs.isEmpty, "Collections with these ids did not have a corresponding folder with the same id.")
for collection in collections {
checkSingleFolderAndFeeds(in: account, againstOneCollectionAndFeedsInJSONPayload: collection)
}
}
func checkSingleFolderAndFeeds(in account: Account, againstOneCollectionAndFeedsInJSONNamed name: String) {
let collection = testJSON(named: name) as! [String:Any]
checkSingleFolderAndFeeds(in: account, againstOneCollectionAndFeedsInJSONPayload: collection)
}
func checkSingleFolderAndFeeds(in account: Account, againstOneCollectionAndFeedsInJSONPayload collection: [String: Any]) {
let label = collection["label"] as! String
guard let folder = account.existingFolder(with: label) else {
// due to a previous test failure?
XCTFail("Could not find the \"\(label)\" folder.")
return
}
let collectionFeeds = collection["feeds"] as! [[String: Any]]
let folderFeeds = folder.topLevelFeeds
XCTAssertEqual(collectionFeeds.count, folderFeeds.count)
let collectionFeedIDs = Set(collectionFeeds.map { $0["id"] as! String })
let folderFeedIDs = Set(folderFeeds.map { $0.feedID })
let missingFeedIDs = collectionFeedIDs.subtracting(folderFeedIDs)
XCTAssertTrue(missingFeedIDs.isEmpty, "Feeds with these ids were not found in the \"\(label)\" folder.")
}
func checkArticles(in account: Account, againstItemsInStreamInJSONNamed name: String, subdirectory: String? = nil) throws {
let stream = testJSON(named: name, subdirectory: subdirectory) as! [String:Any]
try checkArticles(in: account, againstItemsInStreamInJSONPayload: stream)
}
func checkArticles(in account: Account, againstItemsInStreamInJSONPayload stream: [String: Any]) throws {
try checkArticles(in: account, correspondToStreamItemsIn: stream)
}
private struct ArticleItem {
var id: String
var feedID: String
var content: String
var JSON: [String: Any]
var unread: Bool
/// Convoluted external URL logic "documented" here:
/// https://groups.google.com/forum/#!searchin/feedly-cloud/feed$20url%7Csort:date/feedly-cloud/Rx3dVd4aTFQ/Hf1ZfLJoCQAJ
var externalUrl: String? {
return ((JSON["canonical"] as? [[String: Any]]) ?? (JSON["alternate"] as? [[String: Any]]))?.compactMap { link -> String? in
let href = link["href"] as? String
if let type = link["type"] as? String {
if type == "text/html" {
return href
}
return nil
}
return href
}.first
}
init(item: [String: Any]) {
self.JSON = item
self.id = item["id"] as! String
let origin = item["origin"] as! [String: Any]
self.feedID = origin["streamId"] as! String
let content = item["content"] as? [String: Any]
let summary = item["summary"] as? [String: Any]
self.content = ((content ?? summary)?["content"] as? String) ?? ""
self.unread = item["unread"] as! Bool
}
}
/// Awkwardly titled to make it clear the JSON given is from a stream response.
func checkArticles(in testAccount: Account, correspondToStreamItemsIn stream: [String: Any]) throws {
let items = stream["items"] as! [[String: Any]]
let articleItems = items.map { ArticleItem(item: $0) }
let itemIDs = Set(articleItems.map { $0.id })
let articles = try testAccount.fetchArticles(.articleIDs(itemIDs))
let articleIDs = Set(articles.map { $0.articleID })
let missing = itemIDs.subtracting(articleIDs)
XCTAssertEqual(items.count, articles.count)
XCTAssertTrue(missing.isEmpty, "Items with these ids did not have a corresponding article with the same id.")
for article in articles {
for item in articleItems where item.id == article.articleID {
XCTAssertEqual(article.uniqueID, item.id)
XCTAssertEqual(article.contentHTML, item.content)
XCTAssertEqual(article.feedID, item.feedId)
XCTAssertEqual(article.externalURL, item.externalUrl)
}
}
}
func checkUnreadStatuses(in account: Account, againstIDsInStreamInJSONNamed name: String, subdirectory: String? = nil, testCase: XCTestCase) {
let streadIDs = testJSON(named: name, subdirectory: subdirectory) as! [String:Any]
checkUnreadStatuses(in: account, correspondToIDsInJSONPayload: streadIDs, testCase: testCase)
}
func checkUnreadStatuses(in testAccount: Account, correspondToIDsInJSONPayload streadIDs: [String: Any], testCase: XCTestCase) {
let ids = Set(streadIDs["ids"] as! [String])
let fetchIDsExpectation = testCase.expectation(description: "Fetch Article IDs")
testAccount.fetchUnreadArticleIDs { articleIDsResult in
do {
let articleIDs = try articleIDsResult.get()
// Unread statuses can be paged from Feedly.
// Instead of joining test data, the best we can do is
// make sure that these ids are marked as unread (a subset of the total).
XCTAssertTrue(ids.isSubset(of: articleIDs), "Some articles in `ids` are not marked as unread.")
fetchIDsExpectation.fulfill()
} catch {
XCTFail("Error unwrapping article IDs: \(error)")
}
}
testCase.wait(for: [fetchIDsExpectation], timeout: 2)
}
func checkStarredStatuses(in account: Account, againstItemsInStreamInJSONNamed name: String, subdirectory: String? = nil, testCase: XCTestCase) {
let streadIDs = testJSON(named: name, subdirectory: subdirectory) as! [String:Any]
checkStarredStatuses(in: account, correspondToStreamItemsIn: streadIDs, testCase: testCase)
}
func checkStarredStatuses(in testAccount: Account, correspondToStreamItemsIn stream: [String: Any], testCase: XCTestCase) {
let items = stream["items"] as! [[String: Any]]
let ids = Set(items.map { $0["id"] as! String })
let fetchIDsExpectation = testCase.expectation(description: "Fetch Article Ids")
testAccount.fetchStarredArticleIDs { articleIDsResult in
do {
let articleIDs = try articleIDsResult.get()
// Starred articles can be paged from Feedly.
// Instead of joining test data, the best we can do is
// make sure that these articles are marked as starred (a subset of the total).
XCTAssertTrue(ids.isSubset(of: articleIDs), "Some articles in `ids` are not marked as starred.")
fetchIDsExpectation.fulfill()
} catch {
XCTFail("Error unwrapping article IDs: \(error)")
}
}
testCase.wait(for: [fetchIDsExpectation], timeout: 2)
}
func check(_ entries: [FeedlyEntry], correspondToStreamItemsIn stream: [String: Any]) {
let items = stream["items"] as! [[String: Any]]
let itemIDs = Set(items.map { $0["id"] as! String })
let articleIDs = Set(entries.map { $0.id })
let missing = itemIDs.subtracting(articleIDs)
XCTAssertEqual(items.count, entries.count)
XCTAssertTrue(missing.isEmpty, "Failed to create \(FeedlyEntry.self) values from objects in the JSON with these ids.")
}
}
//class FeedlyTestSupport {
// var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "FeedlyTests")
// var accessToken = Credentials(type: .oauthAccessToken, username: "Test", secret: "t3st-access-tok3n")
// var refreshToken = Credentials(type: .oauthRefreshToken, username: "Test", secret: "t3st-refresh-tok3n")
// var transport = TestTransport()
//
// func makeMockNetworkStack() -> (TestTransport, FeedlyAPICaller) {
// let caller = FeedlyAPICaller(transport: transport, api: .sandbox)
// caller.credentials = accessToken
// return (transport, caller)
// }
//
// func makeTestAccount() -> Account {
// let manager = TestAccountManager()
// let account = manager.createAccount(type: .feedly, transport: transport)
// do {
// try account.storeCredentials(refreshToken)
// // This must be done last or the account uses the refresh token for request Authorization!
// try account.storeCredentials(accessToken)
// } catch {
// XCTFail("Unable to register mock credentials because \(error)")
// }
// return account
// }
//
// func makeMockOAuthClient() -> OAuthAuthorizationClient {
// return OAuthAuthorizationClient(id: "test", redirectURI: "test://test/auth", state: nil, secret: "password")
// }
//
// func removeCredentials(matching type: CredentialsType, from account: Account) {
// do {
// try account.removeCredentials(type: type)
// } catch {
// XCTFail("Unable to remove \(type)")
// }
// }
//
// func makeTestDatabaseContainer() -> TestDatabaseContainer {
// return TestDatabaseContainer()
// }
//
// class TestDatabaseContainer {
// private let path: String
// private(set) var database: SyncDatabase!
//
// init() {
// let dataFolder = try! FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
// path = dataFolder.appendingPathComponent("\(UUID().uuidString)-Sync.sqlite3").path
// database = SyncDatabase(databasePath: path)
// }
//
// deinit {
// // We should close the database before removing the database.
// database = nil
// do {
// try FileManager.default.removeItem(atPath: path)
// print("Removed database at \(path)")
// } catch {
// print("Unable to remove database owned by \(self) because \(error).")
// }
// }
// }
//
// func destroy(_ testAccount: Account) {
// do {
// // These should not throw when the keychain items are not found.
// try testAccount.removeCredentials(type: .oauthAccessToken)
// try testAccount.removeCredentials(type: .oauthRefreshToken)
// } catch {
// XCTFail("Unable to clean up mock credentials because \(error)")
// }
//
// let manager = TestAccountManager()
// manager.deleteAccount(testAccount)
// }
//
// func testJSON(named: String, subdirectory: String? = nil) -> Any {
// let url = Bundle.module.url(forResource: named, withExtension: "json", subdirectory: subdirectory)!
// let data = try! Data(contentsOf: url)
// let json = try! JSONSerialization.jsonObject(with: data)
// return json
// }
//
// func checkFoldersAndFeeds(in account: Account, againstCollectionsAndFeedsInJSONNamed name: String, subdirectory: String? = nil) {
// let collections = testJSON(named: name, subdirectory: subdirectory) as! [[String:Any]]
// let collectionNames = Set(collections.map { $0["label"] as! String })
// let collectionIDs = Set(collections.map { $0["id"] as! String })
//
// let folders = account.folders ?? Set()
// let folderNames = Set(folders.compactMap { $0.name })
// let folderIDs = Set(folders.compactMap { $0.externalID })
//
// let missingNames = collectionNames.subtracting(folderNames)
// let missingIDs = collectionIDs.subtracting(folderIDs)
//
// XCTAssertEqual(folders.count, collections.count, "Mismatch between collections and folders.")
// XCTAssertTrue(missingNames.isEmpty, "Collections with these names did not have a corresponding folder with the same name.")
// XCTAssertTrue(missingIDs.isEmpty, "Collections with these ids did not have a corresponding folder with the same id.")
//
// for collection in collections {
// checkSingleFolderAndFeeds(in: account, againstOneCollectionAndFeedsInJSONPayload: collection)
// }
// }
//
// func checkSingleFolderAndFeeds(in account: Account, againstOneCollectionAndFeedsInJSONNamed name: String) {
// let collection = testJSON(named: name) as! [String:Any]
// checkSingleFolderAndFeeds(in: account, againstOneCollectionAndFeedsInJSONPayload: collection)
// }
//
// func checkSingleFolderAndFeeds(in account: Account, againstOneCollectionAndFeedsInJSONPayload collection: [String: Any]) {
// let label = collection["label"] as! String
// guard let folder = account.existingFolder(with: label) else {
// // due to a previous test failure?
// XCTFail("Could not find the \"\(label)\" folder.")
// return
// }
// let collectionFeeds = collection["feeds"] as! [[String: Any]]
// let folderFeeds = folder.topLevelFeeds
//
// XCTAssertEqual(collectionFeeds.count, folderFeeds.count)
//
// let collectionFeedIDs = Set(collectionFeeds.map { $0["id"] as! String })
// let folderFeedIDs = Set(folderFeeds.map { $0.feedID })
// let missingFeedIDs = collectionFeedIDs.subtracting(folderFeedIDs)
//
// XCTAssertTrue(missingFeedIDs.isEmpty, "Feeds with these ids were not found in the \"\(label)\" folder.")
// }
//
// func checkArticles(in account: Account, againstItemsInStreamInJSONNamed name: String, subdirectory: String? = nil) throws {
// let stream = testJSON(named: name, subdirectory: subdirectory) as! [String:Any]
// try checkArticles(in: account, againstItemsInStreamInJSONPayload: stream)
// }
//
// func checkArticles(in account: Account, againstItemsInStreamInJSONPayload stream: [String: Any]) throws {
// try checkArticles(in: account, correspondToStreamItemsIn: stream)
// }
//
// private struct ArticleItem {
// var id: String
// var feedID: String
// var content: String
// var JSON: [String: Any]
// var unread: Bool
//
// /// Convoluted external URL logic "documented" here:
// /// https://groups.google.com/forum/#!searchin/feedly-cloud/feed$20url%7Csort:date/feedly-cloud/Rx3dVd4aTFQ/Hf1ZfLJoCQAJ
// var externalUrl: String? {
// return ((JSON["canonical"] as? [[String: Any]]) ?? (JSON["alternate"] as? [[String: Any]]))?.compactMap { link -> String? in
// let href = link["href"] as? String
// if let type = link["type"] as? String {
// if type == "text/html" {
// return href
// }
// return nil
// }
// return href
// }.first
// }
//
// init(item: [String: Any]) {
// self.JSON = item
// self.id = item["id"] as! String
//
// let origin = item["origin"] as! [String: Any]
// self.feedID = origin["streamId"] as! String
//
// let content = item["content"] as? [String: Any]
// let summary = item["summary"] as? [String: Any]
// self.content = ((content ?? summary)?["content"] as? String) ?? ""
//
// self.unread = item["unread"] as! Bool
// }
// }
//
// /// Awkwardly titled to make it clear the JSON given is from a stream response.
// func checkArticles(in testAccount: Account, correspondToStreamItemsIn stream: [String: Any]) throws {
//
// let items = stream["items"] as! [[String: Any]]
// let articleItems = items.map { ArticleItem(item: $0) }
// let itemIDs = Set(articleItems.map { $0.id })
//
// let articles = try testAccount.fetchArticles(.articleIDs(itemIDs))
// let articleIDs = Set(articles.map { $0.articleID })
//
// let missing = itemIDs.subtracting(articleIDs)
//
// XCTAssertEqual(items.count, articles.count)
// XCTAssertTrue(missing.isEmpty, "Items with these ids did not have a corresponding article with the same id.")
//
// for article in articles {
// for item in articleItems where item.id == article.articleID {
// XCTAssertEqual(article.uniqueID, item.id)
// XCTAssertEqual(article.contentHTML, item.content)
// XCTAssertEqual(article.feedID, item.feedId)
// XCTAssertEqual(article.externalURL, item.externalUrl)
// }
// }
// }
//
// func checkUnreadStatuses(in account: Account, againstIDsInStreamInJSONNamed name: String, subdirectory: String? = nil, testCase: XCTestCase) {
// let streadIDs = testJSON(named: name, subdirectory: subdirectory) as! [String:Any]
// checkUnreadStatuses(in: account, correspondToIDsInJSONPayload: streadIDs, testCase: testCase)
// }
//
// func checkUnreadStatuses(in testAccount: Account, correspondToIDsInJSONPayload streadIDs: [String: Any], testCase: XCTestCase) {
// let ids = Set(streadIDs["ids"] as! [String])
// let fetchIDsExpectation = testCase.expectation(description: "Fetch Article IDs")
// testAccount.fetchUnreadArticleIDs { articleIDsResult in
// do {
// let articleIDs = try articleIDsResult.get()
// // Unread statuses can be paged from Feedly.
// // Instead of joining test data, the best we can do is
// // make sure that these ids are marked as unread (a subset of the total).
// XCTAssertTrue(ids.isSubset(of: articleIDs), "Some articles in `ids` are not marked as unread.")
// fetchIDsExpectation.fulfill()
// } catch {
// XCTFail("Error unwrapping article IDs: \(error)")
// }
// }
// testCase.wait(for: [fetchIDsExpectation], timeout: 2)
// }
//
// func checkStarredStatuses(in account: Account, againstItemsInStreamInJSONNamed name: String, subdirectory: String? = nil, testCase: XCTestCase) {
// let streadIDs = testJSON(named: name, subdirectory: subdirectory) as! [String:Any]
// checkStarredStatuses(in: account, correspondToStreamItemsIn: streadIDs, testCase: testCase)
// }
//
// func checkStarredStatuses(in testAccount: Account, correspondToStreamItemsIn stream: [String: Any], testCase: XCTestCase) {
// let items = stream["items"] as! [[String: Any]]
// let ids = Set(items.map { $0["id"] as! String })
// let fetchIDsExpectation = testCase.expectation(description: "Fetch Article Ids")
// testAccount.fetchStarredArticleIDs { articleIDsResult in
// do {
// let articleIDs = try articleIDsResult.get()
// // Starred articles can be paged from Feedly.
// // Instead of joining test data, the best we can do is
// // make sure that these articles are marked as starred (a subset of the total).
// XCTAssertTrue(ids.isSubset(of: articleIDs), "Some articles in `ids` are not marked as starred.")
// fetchIDsExpectation.fulfill()
// } catch {
// XCTFail("Error unwrapping article IDs: \(error)")
// }
// }
// testCase.wait(for: [fetchIDsExpectation], timeout: 2)
// }
//
// func check(_ entries: [FeedlyEntry], correspondToStreamItemsIn stream: [String: Any]) {
//
// let items = stream["items"] as! [[String: Any]]
// let itemIDs = Set(items.map { $0["id"] as! String })
//
// let articleIDs = Set(entries.map { $0.id })
//
// let missing = itemIDs.subtracting(articleIDs)
//
// XCTAssertEqual(items.count, entries.count)
// XCTAssertTrue(missing.isEmpty, "Failed to create \(FeedlyEntry.self) values from objects in the JSON with these ids.")
// }
//}

View File

@ -9,18 +9,18 @@
import XCTest
@testable import Account
final class TestGetCollectionsService: FeedlyGetCollectionsService {
var mockResult: Result<[FeedlyCollection], Error>?
var getCollectionsExpectation: XCTestExpectation?
func getCollections(completion: @escaping (Result<[FeedlyCollection], Error>) -> ()) {
guard let result = mockResult else {
XCTFail("Missing mock result. Test may time out because the completion will not be called.")
return
}
DispatchQueue.main.async {
completion(result)
self.getCollectionsExpectation?.fulfill()
}
}
}
//final class TestGetCollectionsService: FeedlyGetCollectionsService {
// var mockResult: Result<[FeedlyCollection], Error>?
// var getCollectionsExpectation: XCTestExpectation?
//
// func getCollections(completion: @escaping (Result<[FeedlyCollection], Error>) -> ()) {
// guard let result = mockResult else {
// XCTFail("Missing mock result. Test may time out because the completion will not be called.")
// return
// }
// DispatchQueue.main.async {
// completion(result)
// self.getCollectionsExpectation?.fulfill()
// }
// }
//}

View File

@ -9,18 +9,18 @@
import XCTest
@testable import Account
final class TestGetEntriesService: FeedlyGetEntriesService {
var mockResult: Result<[FeedlyEntry], Error>?
var getEntriesExpectation: XCTestExpectation?
func getEntries(for ids: Set<String>, completion: @escaping (Result<[FeedlyEntry], Error>) -> ()) {
guard let result = mockResult else {
XCTFail("Missing mock result. Test may time out because the completion will not be called.")
return
}
DispatchQueue.main.async {
completion(result)
self.getEntriesExpectation?.fulfill()
}
}
}
//final class TestGetEntriesService: FeedlyGetEntriesService {
// var mockResult: Result<[FeedlyEntry], Error>?
// var getEntriesExpectation: XCTestExpectation?
//
// func getEntries(for ids: Set<String>, completion: @escaping (Result<[FeedlyEntry], Error>) -> ()) {
// guard let result = mockResult else {
// XCTFail("Missing mock result. Test may time out because the completion will not be called.")
// return
// }
// DispatchQueue.main.async {
// completion(result)
// self.getEntriesExpectation?.fulfill()
// }
// }
//}

View File

@ -9,72 +9,72 @@
import XCTest
@testable import Account
final class TestGetPagedStreamContentsService: FeedlyGetStreamContentsService {
var parameterTester: ((FeedlyResourceID, String?, Date?, Bool?) -> ())?
var getStreamContentsExpectation: XCTestExpectation?
var pages = [String: FeedlyStream]()
func addAtLeastOnePage(for resource: FeedlyResourceID, continuations: [String], numberOfEntriesPerPage count: Int) {
pages = [String: FeedlyStream](minimumCapacity: continuations.count + 1)
// A continuation is an identifier for the next page.
// The first page has a nil identifier.
// The last page has no next page, so the next continuation value for that page is nil.
// Therefore, each page needs to know the identifier of the next page.
for index in -1..<continuations.count {
let nextIndex = index + 1
let continuation: String? = nextIndex < continuations.count ? continuations[nextIndex] : nil
let page = makeStreamContents(for: resource, continuation: continuation, between: 0..<count)
let key = TestGetPagedStreamContentsService.getPagingKey(for: resource, continuation: index < 0 ? nil : continuations[index])
pages[key] = page
}
}
private func makeStreamContents(for resource: FeedlyResourceID, continuation: String?, between range: Range<Int>) -> FeedlyStream {
let entries = range.map { index -> FeedlyEntry in
let content = FeedlyEntry.Content(content: "Content \(index)",
direction: .leftToRight)
let origin = FeedlyOrigin(title: "Origin \(index)",
streamId: resource.id,
htmlUrl: "http://localhost/feedly/origin/\(index)")
return FeedlyEntry(id: "/articles/\(index)",
title: "Article \(index)",
content: content,
summary: content,
author: nil,
crawled: Date(),
recrawled: nil,
origin: origin,
canonical: nil,
alternate: nil,
unread: true,
tags: nil,
categories: nil,
enclosure: nil)
}
let stream = FeedlyStream(id: resource.id, updated: nil, continuation: continuation, items: entries)
return stream
}
static func getPagingKey(for stream: FeedlyResourceID, continuation: String?) -> String {
return "\(stream.id)@\(continuation ?? "")"
}
func getStreamContents(for resource: FeedlyResourceID, continuation: String?, newerThan: Date?, unreadOnly: Bool?, completion: @escaping (Result<FeedlyStream, Error>) -> ()) {
let key = TestGetPagedStreamContentsService.getPagingKey(for: resource, continuation: continuation)
guard let page = pages[key] else {
XCTFail("Missing page for \(resource.id) and continuation \(String(describing: continuation)). Test may time out because the completion will not be called.")
return
}
parameterTester?(resource, continuation, newerThan, unreadOnly)
DispatchQueue.main.async {
completion(.success(page))
self.getStreamContentsExpectation?.fulfill()
}
}
}
//final class TestGetPagedStreamContentsService: FeedlyGetStreamContentsService {
//
// var parameterTester: ((FeedlyResourceID, String?, Date?, Bool?) -> ())?
// var getStreamContentsExpectation: XCTestExpectation?
// var pages = [String: FeedlyStream]()
//
// func addAtLeastOnePage(for resource: FeedlyResourceID, continuations: [String], numberOfEntriesPerPage count: Int) {
// pages = [String: FeedlyStream](minimumCapacity: continuations.count + 1)
//
// // A continuation is an identifier for the next page.
// // The first page has a nil identifier.
// // The last page has no next page, so the next continuation value for that page is nil.
// // Therefore, each page needs to know the identifier of the next page.
// for index in -1..<continuations.count {
// let nextIndex = index + 1
// let continuation: String? = nextIndex < continuations.count ? continuations[nextIndex] : nil
// let page = makeStreamContents(for: resource, continuation: continuation, between: 0..<count)
// let key = TestGetPagedStreamContentsService.getPagingKey(for: resource, continuation: index < 0 ? nil : continuations[index])
// pages[key] = page
// }
// }
//
// private func makeStreamContents(for resource: FeedlyResourceID, continuation: String?, between range: Range<Int>) -> FeedlyStream {
// let entries = range.map { index -> FeedlyEntry in
// let content = FeedlyEntry.Content(content: "Content \(index)",
// direction: .leftToRight)
//
// let origin = FeedlyOrigin(title: "Origin \(index)",
// streamId: resource.id,
// htmlUrl: "http://localhost/feedly/origin/\(index)")
//
// return FeedlyEntry(id: "/articles/\(index)",
// title: "Article \(index)",
// content: content,
// summary: content,
// author: nil,
// crawled: Date(),
// recrawled: nil,
// origin: origin,
// canonical: nil,
// alternate: nil,
// unread: true,
// tags: nil,
// categories: nil,
// enclosure: nil)
// }
//
// let stream = FeedlyStream(id: resource.id, updated: nil, continuation: continuation, items: entries)
//
// return stream
// }
//
// static func getPagingKey(for stream: FeedlyResourceID, continuation: String?) -> String {
// return "\(stream.id)@\(continuation ?? "")"
// }
//
// func getStreamContents(for resource: FeedlyResourceID, continuation: String?, newerThan: Date?, unreadOnly: Bool?, completion: @escaping (Result<FeedlyStream, Error>) -> ()) {
// let key = TestGetPagedStreamContentsService.getPagingKey(for: resource, continuation: continuation)
// guard let page = pages[key] else {
// XCTFail("Missing page for \(resource.id) and continuation \(String(describing: continuation)). Test may time out because the completion will not be called.")
// return
// }
// parameterTester?(resource, continuation, newerThan, unreadOnly)
// DispatchQueue.main.async {
// completion(.success(page))
// self.getStreamContentsExpectation?.fulfill()
// }
// }
//}

View File

@ -9,48 +9,48 @@
import XCTest
@testable import Account
final class TestGetPagedStreadIDsService: FeedlyGetStreamIDsService {
var parameterTester: ((FeedlyResourceID, String?, Date?, Bool?) -> ())?
var getStreadIDsExpectation: XCTestExpectation?
var pages = [String: FeedlyStreamIDs]()
func addAtLeastOnePage(for resource: FeedlyResourceID, continuations: [String], numberOfEntriesPerPage count: Int) {
pages = [String: FeedlyStreamIDs](minimumCapacity: continuations.count + 1)
// A continuation is an identifier for the next page.
// The first page has a nil identifier.
// The last page has no next page, so the next continuation value for that page is nil.
// Therefore, each page needs to know the identifier of the next page.
for index in -1..<continuations.count {
let nextIndex = index + 1
let continuation: String? = nextIndex < continuations.count ? continuations[nextIndex] : nil
let page = makeStreadIDs(for: resource, continuation: continuation, between: 0..<count)
let key = TestGetPagedStreadIDsService.getPagingKey(for: resource, continuation: index < 0 ? nil : continuations[index])
pages[key] = page
}
}
private func makeStreadIDs(for resource: FeedlyResourceID, continuation: String?, between range: Range<Int>) -> FeedlyStreamIDs {
let entryIDs = range.map { _ in UUID().uuidString }
let stream = FeedlyStreamIDs(continuation: continuation, ids: entryIDs)
return stream
}
static func getPagingKey(for stream: FeedlyResourceID, continuation: String?) -> String {
return "\(stream.id)@\(continuation ?? "")"
}
func getStreamIDs(for resource: FeedlyResourceID, continuation: String?, newerThan: Date?, unreadOnly: Bool?, completion: @escaping (Result<FeedlyStreamIDs, Error>) -> ()) {
let key = TestGetPagedStreadIDsService.getPagingKey(for: resource, continuation: continuation)
guard let page = pages[key] else {
XCTFail("Missing page for \(resource.id) and continuation \(String(describing: continuation)). Test may time out because the completion will not be called.")
return
}
parameterTester?(resource, continuation, newerThan, unreadOnly)
DispatchQueue.main.async {
completion(.success(page))
self.getStreadIDsExpectation?.fulfill()
}
}
}
//final class TestGetPagedStreadIDsService: FeedlyGetStreamIDsService {
//
// var parameterTester: ((FeedlyResourceID, String?, Date?, Bool?) -> ())?
// var getStreadIDsExpectation: XCTestExpectation?
// var pages = [String: FeedlyStreamIDs]()
//
// func addAtLeastOnePage(for resource: FeedlyResourceID, continuations: [String], numberOfEntriesPerPage count: Int) {
// pages = [String: FeedlyStreamIDs](minimumCapacity: continuations.count + 1)
//
// // A continuation is an identifier for the next page.
// // The first page has a nil identifier.
// // The last page has no next page, so the next continuation value for that page is nil.
// // Therefore, each page needs to know the identifier of the next page.
// for index in -1..<continuations.count {
// let nextIndex = index + 1
// let continuation: String? = nextIndex < continuations.count ? continuations[nextIndex] : nil
// let page = makeStreadIDs(for: resource, continuation: continuation, between: 0..<count)
// let key = TestGetPagedStreadIDsService.getPagingKey(for: resource, continuation: index < 0 ? nil : continuations[index])
// pages[key] = page
// }
// }
//
// private func makeStreadIDs(for resource: FeedlyResourceID, continuation: String?, between range: Range<Int>) -> FeedlyStreamIDs {
// let entryIDs = range.map { _ in UUID().uuidString }
// let stream = FeedlyStreamIDs(continuation: continuation, ids: entryIDs)
// return stream
// }
//
// static func getPagingKey(for stream: FeedlyResourceID, continuation: String?) -> String {
// return "\(stream.id)@\(continuation ?? "")"
// }
//
// func getStreamIDs(for resource: FeedlyResourceID, continuation: String?, newerThan: Date?, unreadOnly: Bool?, completion: @escaping (Result<FeedlyStreamIDs, Error>) -> ()) {
// let key = TestGetPagedStreadIDsService.getPagingKey(for: resource, continuation: continuation)
// guard let page = pages[key] else {
// XCTFail("Missing page for \(resource.id) and continuation \(String(describing: continuation)). Test may time out because the completion will not be called.")
// return
// }
// parameterTester?(resource, continuation, newerThan, unreadOnly)
// DispatchQueue.main.async {
// completion(.success(page))
// self.getStreadIDsExpectation?.fulfill()
// }
// }
//}

View File

@ -9,41 +9,41 @@
import XCTest
@testable import Account
final class TestGetStreamContentsService: FeedlyGetStreamContentsService {
var mockResult: Result<FeedlyStream, Error>?
var parameterTester: ((FeedlyResourceID, String?, Date?, Bool?) -> ())?
var getStreamContentsExpectation: XCTestExpectation?
func getStreamContents(for resource: FeedlyResourceID, continuation: String?, newerThan: Date?, unreadOnly: Bool?, completion: @escaping (Result<FeedlyStream, Error>) -> ()) {
guard let result = mockResult else {
XCTFail("Missing mock result. Test may time out because the completion will not be called.")
return
}
parameterTester?(resource, continuation, newerThan, unreadOnly)
DispatchQueue.main.async {
completion(result)
self.getStreamContentsExpectation?.fulfill()
}
}
func makeMockFeedlyEntryItem() -> [FeedlyEntry] {
let origin = FeedlyOrigin(title: "XCTest@localhost", streamId: "user/12345/category/67890", htmlUrl: "http://localhost/nnw/xctest")
let content = FeedlyEntry.Content(content: "In the beginning...", direction: .leftToRight)
let items = [FeedlyEntry(id: "feeds/0/article/0",
title: "RSS Reader Ingests Man",
content: content,
summary: content,
author: nil,
crawled: Date(),
recrawled: nil,
origin: origin,
canonical: nil,
alternate: nil,
unread: true,
tags: nil,
categories: nil,
enclosure: nil)]
return items
}
}
//final class TestGetStreamContentsService: FeedlyGetStreamContentsService {
//
// var mockResult: Result<FeedlyStream, Error>?
// var parameterTester: ((FeedlyResourceID, String?, Date?, Bool?) -> ())?
// var getStreamContentsExpectation: XCTestExpectation?
//
// func getStreamContents(for resource: FeedlyResourceID, continuation: String?, newerThan: Date?, unreadOnly: Bool?, completion: @escaping (Result<FeedlyStream, Error>) -> ()) {
// guard let result = mockResult else {
// XCTFail("Missing mock result. Test may time out because the completion will not be called.")
// return
// }
// parameterTester?(resource, continuation, newerThan, unreadOnly)
// DispatchQueue.main.async {
// completion(result)
// self.getStreamContentsExpectation?.fulfill()
// }
// }
//
// func makeMockFeedlyEntryItem() -> [FeedlyEntry] {
// let origin = FeedlyOrigin(title: "XCTest@localhost", streamId: "user/12345/category/67890", htmlUrl: "http://localhost/nnw/xctest")
// let content = FeedlyEntry.Content(content: "In the beginning...", direction: .leftToRight)
// let items = [FeedlyEntry(id: "feeds/0/article/0",
// title: "RSS Reader Ingests Man",
// content: content,
// summary: content,
// author: nil,
// crawled: Date(),
// recrawled: nil,
// origin: origin,
// canonical: nil,
// alternate: nil,
// unread: true,
// tags: nil,
// categories: nil,
// enclosure: nil)]
// return items
// }
//}

View File

@ -9,21 +9,21 @@
import XCTest
@testable import Account
final class TestGetStreadIDsService: FeedlyGetStreamIDsService {
var mockResult: Result<FeedlyStreamIDs, Error>?
var parameterTester: ((FeedlyResourceID, String?, Date?, Bool?) -> ())?
var getStreadIDsExpectation: XCTestExpectation?
func getStreamIDs(for resource: FeedlyResourceID, continuation: String?, newerThan: Date?, unreadOnly: Bool?, completion: @escaping (Result<FeedlyStreamIDs, Error>) -> ()) {
guard let result = mockResult else {
XCTFail("Missing mock result. Test may time out because the completion will not be called.")
return
}
parameterTester?(resource, continuation, newerThan, unreadOnly)
DispatchQueue.main.async {
completion(result)
self.getStreadIDsExpectation?.fulfill()
}
}
}
//final class TestGetStreadIDsService: FeedlyGetStreamIDsService {
//
// var mockResult: Result<FeedlyStreamIDs, Error>?
// var parameterTester: ((FeedlyResourceID, String?, Date?, Bool?) -> ())?
// var getStreadIDsExpectation: XCTestExpectation?
//
// func getStreamIDs(for resource: FeedlyResourceID, continuation: String?, newerThan: Date?, unreadOnly: Bool?, completion: @escaping (Result<FeedlyStreamIDs, Error>) -> ()) {
// guard let result = mockResult else {
// XCTFail("Missing mock result. Test may time out because the completion will not be called.")
// return
// }
// parameterTester?(resource, continuation, newerThan, unreadOnly)
// DispatchQueue.main.async {
// completion(result)
// self.getStreadIDsExpectation?.fulfill()
// }
// }
//}

View File

@ -9,18 +9,18 @@
import XCTest
@testable import Account
class TestMarkArticlesService: FeedlyMarkArticlesService {
var didMarkExpectation: XCTestExpectation?
var parameterTester: ((Set<String>, FeedlyMarkAction) -> ())?
var mockResult: Result<Void, Error> = .success(())
func mark(_ articleIDs: Set<String>, as action: FeedlyMarkAction, completion: @escaping (Result<Void, Error>) -> ()) {
DispatchQueue.main.async {
self.parameterTester?(articleIDs, action)
completion(self.mockResult)
self.didMarkExpectation?.fulfill()
}
}
}
//class TestMarkArticlesService: FeedlyMarkArticlesService {
//
// var didMarkExpectation: XCTestExpectation?
// var parameterTester: ((Set<String>, FeedlyMarkAction) -> ())?
// var mockResult: Result<Void, Error> = .success(())
//
// func mark(_ articleIDs: Set<String>, as action: FeedlyMarkAction, completion: @escaping (Result<Void, Error>) -> ()) {
//
// DispatchQueue.main.async {
// self.parameterTester?(articleIDs, action)
// completion(self.mockResult)
// self.didMarkExpectation?.fulfill()
// }
// }
//}

View File

@ -10,46 +10,46 @@ import Foundation
import Web
@testable import Account
final class TestAccountManager {
static let shared = TestAccountManager()
var accountsFolder: URL {
return try! FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
}
func createAccount(type: AccountType, username: String? = nil, password: String? = nil, transport: Transport) -> Account {
let accountID = UUID().uuidString
let accountFolder = accountsFolder.appendingPathComponent("\(type.rawValue)_\(accountID)")
do {
try FileManager.default.createDirectory(at: accountFolder, withIntermediateDirectories: true, attributes: nil)
} catch {
assertionFailure("Could not create folder for \(accountID) account.")
abort()
}
let account = Account(dataFolder: accountFolder.absoluteString, type: type, accountID: accountID, transport: transport)
return account
}
func deleteAccount(_ account: Account) {
do {
try FileManager.default.removeItem(atPath: account.dataFolder)
}
catch let error as CocoaError where error.code == .fileNoSuchFile {
}
catch {
assertionFailure("Could not delete folder at: \(account.dataFolder) because \(error)")
abort()
}
}
}
//final class TestAccountManager {
//
// static let shared = TestAccountManager()
//
// var accountsFolder: URL {
// return try! FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
//
// }
//
// func createAccount(type: AccountType, username: String? = nil, password: String? = nil, transport: Transport) -> Account {
//
// let accountID = UUID().uuidString
// let accountFolder = accountsFolder.appendingPathComponent("\(type.rawValue)_\(accountID)")
//
// do {
// try FileManager.default.createDirectory(at: accountFolder, withIntermediateDirectories: true, attributes: nil)
// } catch {
// assertionFailure("Could not create folder for \(accountID) account.")
// abort()
// }
//
// let account = Account(dataFolder: accountFolder.absoluteString, type: type, accountID: accountID, transport: transport)
//
// return account
//
// }
//
// func deleteAccount(_ account: Account) {
//
// do {
// try FileManager.default.removeItem(atPath: account.dataFolder)
// }
// catch let error as CocoaError where error.code == .fileNoSuchFile {
//
// }
// catch {
// assertionFailure("Could not delete folder at: \(account.dataFolder) because \(error)")
// abort()
// }
//
// }
//
//}

View File

@ -14,74 +14,74 @@ protocol TestTransportMockResponseProviding: AnyObject {
func mockResponseFileUrl(for components: URLComponents) -> URL?
}
final class TestTransport: Transport {
enum TestTransportError: String, Error {
case invalidState = "The test wasn't set up correctly."
}
var testFiles = [String: String]()
var testStatusCodes = [String: Int]()
weak var mockResponseFileUrlProvider: TestTransportMockResponseProviding?
private func httpResponse(for request: URLRequest, statusCode: Int = 200) -> HTTPURLResponse {
guard let url = request.url else {
fatalError("Attempting to mock a http response for a request without a URL \(request).")
}
return HTTPURLResponse(url: url, statusCode: statusCode, httpVersion: "HTTP/1.1", headerFields: nil)!
}
func cancelAll() { }
func send(request: URLRequest, completion: @escaping (Result<(HTTPURLResponse, Data?), Error>) -> Void) {
guard let url = request.url, let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
completion(.failure(TestTransportError.invalidState))
return
}
let urlString = url.absoluteString
let response = httpResponse(for: request, statusCode: testStatusCodes[urlString] ?? 200)
let testFileURL: URL
if let provider = mockResponseFileUrlProvider {
guard let providerUrl = provider.mockResponseFileUrl(for: components) else {
XCTFail("Test behaviour undefined. Mock provider failed to provide non-nil URL for \(components).")
return
}
testFileURL = providerUrl
} else if let testKeyAndFileName = testFiles.first(where: { urlString.contains($0.key) }) {
testFileURL = Bundle.module.resourceURL!.appendingPathComponent(testKeyAndFileName.value)
} else {
// XCTFail("Missing mock response for: \(urlString)")
print("***\nWARNING: \(self) missing mock response for:\n\(urlString)\n***")
DispatchQueue.global(qos: .background).async {
completion(.success((response, nil)))
}
return
}
do {
let data = try Data(contentsOf: testFileURL)
DispatchQueue.global(qos: .background).async {
completion(.success((response, data)))
}
} catch {
XCTFail("Unable to read file at \(testFileURL) because \(error).")
DispatchQueue.global(qos: .background).async {
completion(.failure(error))
}
}
}
func send(request: URLRequest, method: String, completion: @escaping (Result<Void, Error>) -> Void) {
fatalError("Unimplemented.")
}
func send(request: URLRequest, method: String, payload: Data, completion: @escaping (Result<(HTTPURLResponse, Data?), Error>) -> Void) {
fatalError("Unimplemented.")
}
}
//final class TestTransport: Transport {
//
// enum TestTransportError: String, Error {
// case invalidState = "The test wasn't set up correctly."
// }
//
// var testFiles = [String: String]()
// var testStatusCodes = [String: Int]()
//
// weak var mockResponseFileUrlProvider: TestTransportMockResponseProviding?
//
// private func httpResponse(for request: URLRequest, statusCode: Int = 200) -> HTTPURLResponse {
// guard let url = request.url else {
// fatalError("Attempting to mock a http response for a request without a URL \(request).")
// }
// return HTTPURLResponse(url: url, statusCode: statusCode, httpVersion: "HTTP/1.1", headerFields: nil)!
// }
//
// func cancelAll() { }
//
// func send(request: URLRequest, completion: @escaping (Result<(HTTPURLResponse, Data?), Error>) -> Void) {
//
// guard let url = request.url, let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
// completion(.failure(TestTransportError.invalidState))
// return
// }
//
// let urlString = url.absoluteString
// let response = httpResponse(for: request, statusCode: testStatusCodes[urlString] ?? 200)
// let testFileURL: URL
//
// if let provider = mockResponseFileUrlProvider {
// guard let providerUrl = provider.mockResponseFileUrl(for: components) else {
// XCTFail("Test behaviour undefined. Mock provider failed to provide non-nil URL for \(components).")
// return
// }
// testFileURL = providerUrl
//
// } else if let testKeyAndFileName = testFiles.first(where: { urlString.contains($0.key) }) {
// testFileURL = Bundle.module.resourceURL!.appendingPathComponent(testKeyAndFileName.value)
//
// } else {
// // XCTFail("Missing mock response for: \(urlString)")
// print("***\nWARNING: \(self) missing mock response for:\n\(urlString)\n***")
// DispatchQueue.global(qos: .background).async {
// completion(.success((response, nil)))
// }
// return
// }
//
// do {
// let data = try Data(contentsOf: testFileURL)
// DispatchQueue.global(qos: .background).async {
// completion(.success((response, data)))
// }
// } catch {
// XCTFail("Unable to read file at \(testFileURL) because \(error).")
// DispatchQueue.global(qos: .background).async {
// completion(.failure(error))
// }
// }
// }
//
// func send(request: URLRequest, method: String, completion: @escaping (Result<Void, Error>) -> Void) {
// fatalError("Unimplemented.")
// }
//
// func send(request: URLRequest, method: String, payload: Data, completion: @escaping (Result<(HTTPURLResponse, Data?), Error>) -> Void) {
// fatalError("Unimplemented.")
// }
//}

View File

@ -53,11 +53,20 @@
}
},
{
"parallelizable" : true,
"target" : {
"containerPath" : "container:",
"identifier" : "FeedlyTests",
"name" : "FeedlyTests"
}
},
{
"parallelizable" : true,
"target" : {
"containerPath" : "container:",
"identifier" : "AccountTests",
"name" : "AccountTests"
}
}
],
"version" : 1

View File

@ -37,11 +37,20 @@
}
},
{
"parallelizable" : true,
"target" : {
"containerPath" : "container:",
"identifier" : "FeedlyTests",
"name" : "FeedlyTests"
}
},
{
"parallelizable" : true,
"target" : {
"containerPath" : "container:",
"identifier" : "AccountTests",
"name" : "AccountTests"
}
}
],
"version" : 1