Compare commits

...

13 Commits

Author SHA1 Message Date
Brent Simmons 2ed93e447a Make TestAccountManager final. 2024-05-17 23:07:34 -07:00
Brent Simmons 16ed322209 Delete unused test. 2024-05-17 22:53:11 -07:00
Brent Simmons c491fd2b88 Remove unneeded extension breaks. 2024-05-17 22:48:00 -07:00
Brent Simmons d962fc5e1e Delete unused test. 2024-05-17 22:41:39 -07:00
Brent Simmons 45c08cd155 Delete no-longer-used function. 2024-05-17 22:40:29 -07:00
Brent Simmons a579126e92 Use FeedlyUtilities in FeedlyAccountDelegate. 2024-05-17 22:39:51 -07:00
Brent Simmons d024e6049d Create FeedlyUtilities and FeedlyUtilitiesTests. 2024-05-17 22:34:00 -07:00
Brent Simmons 5760705784 Rename FeedlyOrganiseParsedItemsByFeedOperationTests to ParsedItemsKeyedByFeedURLTests, since the operation it was testing is now a function named parsedItemsKeyedByFeedURL. 2024-05-17 22:02:46 -07:00
Brent Simmons 544dcf651d Move FeedlyEntryParserTests to Feedly module. 2024-05-16 08:38:28 -07:00
Brent Simmons efe413bef7 Move FeedlyFeedParserTests to Feedly module. 2024-05-16 08:36:23 -07:00
Brent Simmons c75f3a8e88 Move FeedlyResourceIDTests to Feedly module. 2024-05-16 08:32:15 -07:00
Brent Simmons 6adbc8245d Delete unused FeedlyTestSecrets. 2024-05-16 08:29:51 -07:00
Brent Simmons 66e169d552 Move FeedlyTextSanitizationTests to Feedly module. 2024-05-16 08:28:36 -07:00
14 changed files with 132 additions and 380 deletions

View File

@ -409,7 +409,7 @@ final class FeedlyAccountDelegate: AccountDelegate {
func updateAccountFeeds(parsedItems: Set<ParsedItem>) async throws {
let feedIDsAndItems = parsedItemsKeyedByFeedURL(parsedItems)
let feedIDsAndItems = FeedlyUtilities.parsedItemsKeyedByFeedURL(parsedItems)
try await updateAccountFeedsWithItems(feedIDsAndItems: feedIDsAndItems)
}
@ -451,26 +451,6 @@ final class FeedlyAccountDelegate: AccountDelegate {
return feedsAndFolders
}
func parsedItemsKeyedByFeedURL(_ parsedItems: Set<ParsedItem>) -> [String: Set<ParsedItem>] {
var d = [String: Set<ParsedItem>]()
for parsedItem in parsedItems {
let key = parsedItem.feedURL
let value: Set<ParsedItem> = {
if var items = d[key] {
items.insert(parsedItem)
return items
} else {
return [parsedItem]
}
}()
d[key] = value
}
return d
}
func downloadArticles(missingArticleIDs: Set<String>?, updatedArticleIDs: Set<String>?) async throws {
let allArticleIDs: Set<String> = {

View File

@ -1,116 +0,0 @@
//
// FeedlyGetStreamIDsOperationTests.swift
// AccountTests
//
// Created by Kiel Gillard on 23/10/19.
// Copyright © 2019 Ranchero Software, LLC. All rights reserved.
//
import XCTest
@testable import Account
class FeedlyGetStreamIDsOperationTests: 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 testGetStreamIDsFailure() {
let service = TestGetStreamIDsService()
let resource = FeedlyCategoryResourceID(id: "user/1234/category/5678")
let getStreamIDs = FeedlyGetStreamIDsOperation(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")
getStreamIDs.completionBlock = { _ in
completionExpectation.fulfill()
}
MainThreadOperationQueue.shared.add(getStreamIDs)
waitForExpectations(timeout: 2)
XCTAssertNil(getStreamIDs.streamIDs)
}
func testValuesPassingForGetStreamIDs() {
let service = TestGetStreamIDsService()
let resource = FeedlyCategoryResourceID(id: "user/1234/category/5678")
let continuation: String? = "gfdsa"
let newerThan: Date? = Date(timeIntervalSinceReferenceDate: 1000)
let unreadOnly: Bool? = false
let getStreamIDs = FeedlyGetStreamIDsOperation(account: account, resource: resource, service: service, continuation: continuation, newerThan: newerThan, unreadOnly: unreadOnly, log: support.log)
let mockStreamIDs = FeedlyStreamIDs(continuation: "1234", ids: ["item/1", "item/2", "item/3"])
service.mockResult = .success(mockStreamIDs)
service.getStreamIDsExpectation = 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")
getStreamIDs.completionBlock = { _ in
completionExpectation.fulfill()
}
MainThreadOperationQueue.shared.add(getStreamIDs)
waitForExpectations(timeout: 2)
guard let streamIDs = getStreamIDs.streamIDs else {
XCTFail("\(FeedlyGetStreamIDsOperation.self) did not store the stream.")
return
}
XCTAssertEqual(streamIDs.continuation, mockStreamIDs.continuation)
XCTAssertEqual(streamIDs.ids, mockStreamIDs.ids)
}
func testGetStreamIDsFromJSON() {
let support = FeedlyTestSupport()
let (transport, caller) = support.makeMockNetworkStack()
let jsonName = "JSON/feedly_unreads_1000"
transport.testFiles["/v3/streams/ids"] = "\(jsonName).json"
let resource = FeedlyCategoryResourceID(id: "user/1234/category/5678")
let getStreamIDs = FeedlyGetStreamIDsOperation(account: account, resource: resource, service: caller, continuation: nil, newerThan: nil, unreadOnly: nil, log: support.log)
let completionExpectation = expectation(description: "Did Finish")
getStreamIDs.completionBlock = { _ in
completionExpectation.fulfill()
}
MainThreadOperationQueue.shared.add(getStreamIDs)
waitForExpectations(timeout: 2)
guard let streamIDs = getStreamIDs.streamIDs else {
return XCTFail("Expected to have a stream of identifiers.")
}
let streamIDsJSON = support.testJSON(named: jsonName) as! [String:Any]
let continuation = streamIDsJSON["continuation"] as! String
XCTAssertEqual(streamIDs.continuation, continuation)
XCTAssertEqual(streamIDs.ids, streamIDsJSON["ids"] as! [String])
}
}

View File

@ -51,47 +51,6 @@ class FeedlyLogoutOperationTests: XCTestCase {
}
}
func testCancel() {
let service = TestFeedlyLogoutService()
service.logoutExpectation = expectation(description: "Did Call Logout")
service.logoutExpectation?.isInverted = true
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)
MainThreadOperationQueue.shared.cancelOperations([logout])
waitForExpectations(timeout: 1)
XCTAssertTrue(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?")
}
}
func testLogoutSuccess() {
let service = TestFeedlyLogoutService()
service.logoutExpectation = expectation(description: "Did Call Logout")

View File

@ -1,98 +0,0 @@
//
// FeedlyOrganiseParsedItemsByFeedOperationTests.swift
// AccountTests
//
// Created by Kiel Gillard on 24/10/19.
// Copyright © 2019 Ranchero Software, LLC. All rights reserved.
//
import XCTest
@testable import Account
import Parser
class FeedlyOrganiseParsedItemsByFeedOperationTests: 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()
}
struct TestParsedItemsProvider: FeedlyParsedItemProviding {
let parsedItemProviderName = "TestParsedItemsProvider"
var resource: FeedlyResourceID
var parsedEntries: Set<ParsedItem>
}
func testNoEntries() {
let entries = support.makeParsedItemTestDataFor(numberOfFeeds: 0, numberOfItemsInFeeds: 0)
let resource = FeedlyCategoryResourceID(id: "user/12345/category/6789")
let parsedEntries = Set(entries.values.flatMap { $0 })
let provider = TestParsedItemsProvider(resource: resource, parsedEntries: parsedEntries)
let organise = FeedlyOrganiseParsedItemsByFeedOperation(account: account, parsedItemProvider: provider, log: support.log)
let completionExpectation = expectation(description: "Did Finish")
organise.completionBlock = { _ in
completionExpectation.fulfill()
}
MainThreadOperationQueue.shared.add(organise)
waitForExpectations(timeout: 2)
let itemsAndFeedIDs = organise.parsedItemsKeyedByFeedID
XCTAssertEqual(itemsAndFeedIDs, entries)
}
func testGroupsOneEntryByFeedId() {
let entries = support.makeParsedItemTestDataFor(numberOfFeeds: 1, numberOfItemsInFeeds: 1)
let resource = FeedlyCategoryResourceID(id: "user/12345/category/6789")
let parsedEntries = Set(entries.values.flatMap { $0 })
let provider = TestParsedItemsProvider(resource: resource, parsedEntries: parsedEntries)
let organise = FeedlyOrganiseParsedItemsByFeedOperation(account: account, parsedItemProvider: provider, log: support.log)
let completionExpectation = expectation(description: "Did Finish")
organise.completionBlock = { _ in
completionExpectation.fulfill()
}
MainThreadOperationQueue.shared.add(organise)
waitForExpectations(timeout: 2)
let itemsAndFeedIDs = organise.parsedItemsKeyedByFeedID
XCTAssertEqual(itemsAndFeedIDs, entries)
}
func testGroupsManyEntriesByFeedId() {
let entries = support.makeParsedItemTestDataFor(numberOfFeeds: 100, numberOfItemsInFeeds: 100)
let resource = FeedlyCategoryResourceID(id: "user/12345/category/6789")
let parsedEntries = Set(entries.values.flatMap { $0 })
let provider = TestParsedItemsProvider(resource: resource, parsedEntries: parsedEntries)
let organise = FeedlyOrganiseParsedItemsByFeedOperation(account: account, parsedItemProvider: provider, log: support.log)
let completionExpectation = expectation(description: "Did Finish")
organise.completionBlock = { _ in
completionExpectation.fulfill()
}
MainThreadOperationQueue.shared.add(organise)
waitForExpectations(timeout: 2)
let itemsAndFeedIDs = organise.parsedItemsKeyedByFeedID
XCTAssertEqual(itemsAndFeedIDs, entries)
}
}

View File

@ -1,18 +0,0 @@
//
// FeedlyTestSecrets.swift
//
//
// Created by Maurice Parker on 8/4/20.
//
import Foundation
import Secrets
struct FeedlyTestSecrets: SecretsProvider {
var mercuryClientId = ""
var mercuryClientSecret = ""
var feedlyClientId = ""
var feedlyClientSecret = ""
var inoreaderAppId = ""
var inoreaderAppKey = ""
}

View File

@ -271,41 +271,4 @@ class FeedlyTestSupport {
XCTAssertEqual(items.count, entries.count)
XCTAssertTrue(missing.isEmpty, "Failed to create \(FeedlyEntry.self) values from objects in the JSON with these ids.")
}
func makeParsedItemTestDataFor(numberOfFeeds: Int, numberOfItemsInFeeds: Int) -> [String: Set<ParsedItem>] {
let ids = (0..<numberOfFeeds).map { "feed/\($0)" }
let feedIDsAndItemCounts = ids.map { ($0, numberOfItemsInFeeds) }
let entries = feedIDsAndItemCounts.map { (feedId, count) -> (String, [Int]) in
return (feedId, (0..<count).map { $0 })
}.map { pair -> (String, Set<ParsedItem>) in
let items = pair.1.map { index -> ParsedItem in
ParsedItem(syncServiceID: "\(pair.0)/articles/\(index)",
uniqueID: UUID().uuidString,
feedURL: pair.0,
url: "http://localhost/",
externalURL: "http://localhost/\(pair.0)/articles/\(index).html",
title: "Title\(index)",
language: nil,
contentHTML: "Content \(index) HTML.",
contentText: "Content \(index) Text",
summary: nil,
imageURL: nil,
bannerImageURL: nil,
datePublished: nil,
dateModified: nil,
authors: nil,
tags: nil,
attachments: nil)
}
return (pair.0, Set(items))
}.reduce([String: Set<ParsedItem>](minimumCapacity: feedIDsAndItemCounts.count)) { (dict, pair) in
var mutant = dict
mutant[pair.0] = pair.1
return mutant
}
return entries
}
}

View File

@ -8,11 +8,10 @@
import Foundation
import Web
@testable import Account
class TestAccountManager {
final class TestAccountManager {
static let shared = TestAccountManager()
var accountsFolder: URL {

View File

@ -258,10 +258,7 @@ public protocol FeedlyAPICallerDelegate: AnyObject {
throw URLError(.cannotDecodeContentData)
}
}
}
extension FeedlyAPICaller {
@discardableResult
@MainActor public func addFeed(with feedID: FeedlyFeedResourceID, title: String? = nil, toCollectionWith collectionID: String) async throws -> [FeedlyFeed] {
@ -294,10 +291,7 @@ extension FeedlyAPICaller {
return collectionFeeds
}
}
extension FeedlyAPICaller {
/// https://tools.ietf.org/html/rfc6749#section-4.1
/// Provides the URL request that allows users to consent to the client having access to their information. Typically loaded by a web view.
@ -338,10 +332,7 @@ extension FeedlyAPICaller {
return tokenResponse
}
}
extension FeedlyAPICaller {
/// Access tokens expire. Perform a request for a fresh access token given the long life refresh token received when authorization was granted.
///
/// [Documentation](https://tools.ietf.org/html/rfc6749#section-6)
@ -362,10 +353,7 @@ extension FeedlyAPICaller {
return tokenResponse
}
}
extension FeedlyAPICaller {
public func getCollections() async throws -> Set<FeedlyCollection> {
guard !isSuspended else { throw TransportError.suspended }
@ -379,10 +367,7 @@ extension FeedlyAPICaller {
return Set(collections)
}
}
extension FeedlyAPICaller {
@MainActor public func getStreamContents(for resource: FeedlyResourceID, continuation: String?, newerThan: Date?, unreadOnly: Bool?) async throws -> FeedlyStream {
guard !isSuspended else { throw TransportError.suspended }
@ -432,10 +417,7 @@ extension FeedlyAPICaller {
return collections
}
}
extension FeedlyAPICaller {
@MainActor public func getStreamIDs(for resource: FeedlyResourceID, continuation: String? = nil, newerThan: Date?, unreadOnly: Bool?) async throws -> FeedlyStreamIDs {
guard !isSuspended else { throw TransportError.suspended }
@ -485,10 +467,7 @@ extension FeedlyAPICaller {
return collections
}
}
extension FeedlyAPICaller {
@MainActor public func getEntries(for ids: Set<String>) async throws -> [FeedlyEntry] {
guard !isSuspended else { throw TransportError.suspended }
@ -505,10 +484,7 @@ extension FeedlyAPICaller {
return entries
}
}
extension FeedlyAPICaller {
private struct MarkerEntriesBody: Encodable {
let type = "entries"
var action: String
@ -533,10 +509,7 @@ extension FeedlyAPICaller {
}
}
}
}
extension FeedlyAPICaller {
public func getFeeds(for query: String, count: Int, localeIdentifier: String) async throws -> FeedlyFeedsSearchResponse {
guard !isSuspended else { throw TransportError.suspended }
@ -563,9 +536,6 @@ extension FeedlyAPICaller {
return searchResponse
}
}
extension FeedlyAPICaller {
public func logout() async throws {

View File

@ -0,0 +1,26 @@
//
// FeedlyUtilities.swift
//
//
// Created by Brent Simmons on 5/17/24.
//
import Foundation
import Parser
public final class FeedlyUtilities {
public static func parsedItemsKeyedByFeedURL(_ parsedItems: Set<ParsedItem>) -> [String: Set<ParsedItem>] {
var d = [String: Set<ParsedItem>]()
for parsedItem in parsedItems {
let key = parsedItem.feedURL
var parsedItems = d[key] ?? Set<ParsedItem>()
parsedItems.insert(parsedItem)
d[key] = parsedItems
}
return d
}
}

View File

@ -7,14 +7,14 @@
//
import XCTest
@testable import Account
@testable import Feedly
final class FeedlyEntryParserTests: XCTestCase {
class FeedlyEntryParserTests: XCTestCase {
func testParsing() {
let content = FeedlyEntry.Content(content: "Test Content", direction: .leftToRight)
let summary = FeedlyEntry.Content(content: "Test Summary", direction: .leftToRight)
let origin = FeedlyOrigin(title: "Test Feed", streamId: "tests://feeds/1", htmlUrl: nil)
let origin = FeedlyOrigin(title: "Test Feed", streamID: "tests://feeds/1", htmlURL: nil)
let canonicalLink = FeedlyLink(href: "tests://feeds/1/entries/1", type: "text/html")
let tags = [
FeedlyTag(id: "tests/tags/1", label: "Tag 1"),
@ -43,7 +43,7 @@ class FeedlyEntryParserTests: XCTestCase {
XCTAssertEqual(parser.title, entry.title)
XCTAssertEqual(parser.contentHMTL, content.content)
XCTAssertEqual(parser.summary, summary.content)
XCTAssertEqual(parser.datePublished, .distantPast)
XCTAssertEqual(parser.datePublished, Date.distantPast)
XCTAssertEqual(parser.dateModified, Date(timeIntervalSinceReferenceDate: 0))
guard let item = parser.parsedItemRepresentation else {
@ -76,7 +76,7 @@ class FeedlyEntryParserTests: XCTestCase {
let content = FeedlyEntry.Content(content: "<div style=\"direction:rtl;text-align:right\">Test Content</div>", direction: .rightToLeft)
let summaryContent = "Test Summary"
let summary = FeedlyEntry.Content(content: "<div style=\"direction:rtl;text-align:right\">\(summaryContent)</div>", direction: .rightToLeft)
let origin = FeedlyOrigin(title: "Test Feed", streamId: "tests://feeds/1", htmlUrl: nil)
let origin = FeedlyOrigin(title: "Test Feed", streamID: "tests://feeds/1", htmlURL: nil)
let title = "Test Entry 1"
let entry = FeedlyEntry(id: "tests/feeds/1/entries/1",
title: "<div style=\"direction:rtl;text-align:right\">\(title)</div>",

View File

@ -7,10 +7,10 @@
//
import XCTest
@testable import Account
@testable import Feedly
final class FeedlyFeedParserTests: XCTestCase {
class FeedlyFeedParserTests: XCTestCase {
func testParsing() {
let name = "Test Feed"
let website = "tests://nnw/feed/1"

View File

@ -7,11 +7,11 @@
//
import XCTest
@testable import Account
@testable import Feedly
class FeedlyResourceIDTests: XCTestCase {
func testFeedResourceID() {
final class FeedlyResourceIDTests: XCTestCase {
@MainActor func testFeedResourceID() {
let expectedUrl = "http://ranchero.com/blog/atom.xml"
let feedResource = FeedlyFeedResourceID(id: "feed/\(expectedUrl)")

View File

@ -7,10 +7,10 @@
//
import XCTest
@testable import Account
@testable import Feedly
final class FeedlyTextSanitizationTests: XCTestCase {
class FeedlyTextSanitizationTests: XCTestCase {
func testRTLSanitization() {
let targetsAndExpectations: [(target: String?, expectation: String?)] = [

View File

@ -0,0 +1,87 @@
//
// FeedlyUtilitiesTests.swift
// AccountTests
//
// Created by Kiel Gillard on 24/10/19.
// Copyright © 2019 Ranchero Software, LLC. All rights reserved.
//
import XCTest
import Parser
@testable import Feedly
final class FeedlyUtilitiesTests: XCTestCase {
// MARK: - Test parsedItemsKeyedByFeedURL
func testParsedItemsKeyedByFeedURL_Empty() {
let testDictionary = makeParsedItemTestDataFor(numberOfFeeds: 0, numberOfItemsInFeeds: 0)
let parsedItems = parsedItemsFromDictionary(testDictionary)
let resultDictionary = FeedlyUtilities.parsedItemsKeyedByFeedURL(parsedItems)
let expectedDictionary = testDictionary
XCTAssertEqual(resultDictionary, expectedDictionary)
}
func testParsedItemsKeyedByFeedURL_OneFeedOneItem() {
let testDictionary = makeParsedItemTestDataFor(numberOfFeeds: 1, numberOfItemsInFeeds: 1)
let parsedItems = parsedItemsFromDictionary(testDictionary)
let resultDictionary = FeedlyUtilities.parsedItemsKeyedByFeedURL(parsedItems)
let expectedDictionary = testDictionary
XCTAssertEqual(resultDictionary, expectedDictionary)
}
func testParsedItemsKeyedByFeedURL_ManyFeedsManyItems() {
let testDictionary = makeParsedItemTestDataFor(numberOfFeeds: 100, numberOfItemsInFeeds: 100)
let parsedItems = parsedItemsFromDictionary(testDictionary)
let resultDictionary = FeedlyUtilities.parsedItemsKeyedByFeedURL(parsedItems)
let expectedDictionary = testDictionary
XCTAssertEqual(resultDictionary, expectedDictionary)
}
}
// MARK: - Private
private extension FeedlyUtilitiesTests {
func makeParsedItemTestDataFor(numberOfFeeds: Int, numberOfItemsInFeeds: Int) -> [String: Set<ParsedItem>] {
var d = [String: Set<ParsedItem>]()
for feedIndex in 0..<numberOfFeeds {
let feedID = "feed/\(feedIndex)"
var items = Set<ParsedItem>()
for parsedItemIndex in 0..<numberOfItemsInFeeds {
let parsedItem = makeTestParsedItem(feedID, parsedItemIndex)
items.insert(parsedItem)
}
d[feedID] = items
}
return d
}
func makeTestParsedItem(_ feedID: String, _ index: Int) -> ParsedItem {
ParsedItem(syncServiceID: "\(feedID)/articles/\(index)", uniqueID: UUID().uuidString, feedURL: feedID, url: "http://localhost/", externalURL: "http://localhost/\(feedID)/articles/\(index).html", title: "Title\(index)", language: nil, contentHTML: "Content \(index) HTML.", contentText: "Content \(index) Text", summary: nil, imageURL: nil, bannerImageURL: nil, datePublished: nil, dateModified: nil, authors: nil, tags: nil, attachments: nil)
}
func parsedItemsFromDictionary(_ d: [String: Set<ParsedItem>]) -> Set<ParsedItem> {
var parsedItems = Set<ParsedItem>()
for (key, value) in d {
parsedItems.formUnion(value)
}
return parsedItems
}
}