diff --git a/Frameworks/Account/AccountTests/Feedly/FeedlyEntryParserTests.swift b/Frameworks/Account/AccountTests/Feedly/FeedlyEntryParserTests.swift index e62a18e6b..1329b4339 100644 --- a/Frameworks/Account/AccountTests/Feedly/FeedlyEntryParserTests.swift +++ b/Frameworks/Account/AccountTests/Feedly/FeedlyEntryParserTests.swift @@ -15,18 +15,23 @@ class FeedlyEntryParserTests: XCTestCase { 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 canonicalLink = FeedlyLink(href: "tests://feeds/1/entries/1", type: "text/html") + let tags = [ + FeedlyTag(id: "tests/tags/1", label: "Tag 1"), + FeedlyTag(id: "tests/tags/2", label: "Tag 2") + ] let entry = FeedlyEntry(id: "tests/feeds/1/entries/1", title: "Test Entry 1", content: content, summary: summary, - author: nil, + author: "Bob Alice", crawled: .distantPast, recrawled: Date(timeIntervalSinceReferenceDate: 0), origin: origin, - canonical: nil, + canonical: [canonicalLink], alternate: nil, unread: false, - tags: nil, + tags: tags, categories: nil, enclosure: nil) @@ -34,12 +39,37 @@ class FeedlyEntryParserTests: XCTestCase { XCTAssertEqual(parser.id, entry.id) XCTAssertEqual(parser.feedUrl, origin.streamId) - XCTAssertEqual(parser.externalUrl, nil) + XCTAssertEqual(parser.externalUrl, canonicalLink.href) XCTAssertEqual(parser.title, entry.title) XCTAssertEqual(parser.contentHMTL, content.content) XCTAssertEqual(parser.summary, summary.content) XCTAssertEqual(parser.datePublished, .distantPast) XCTAssertEqual(parser.dateModified, Date(timeIntervalSinceReferenceDate: 0)) + + guard let item = parser.parsedItemRepresentation else { + XCTFail("Expected a parsed item representation.") + return + } + + XCTAssertEqual(item.syncServiceID, entry.id) + XCTAssertEqual(item.uniqueID, entry.id) + + // The following is not an error. + // The feedURL must match the webFeedID for the article to be connected to its matching feed. + XCTAssertEqual(item.feedURL, origin.streamId) + XCTAssertEqual(item.title, entry.title) + XCTAssertEqual(item.contentHTML, content.content) + XCTAssertEqual(item.contentText, nil, "Is it now free of HTML characters?") + XCTAssertEqual(item.summary, summary.content) + XCTAssertEqual(item.datePublished, entry.crawled) + XCTAssertEqual(item.dateModified, entry.recrawled) + + let expectedTags = Set(tags.compactMap { $0.label }) + XCTAssertEqual(item.tags, expectedTags) + + let expectedAuthors = Set([entry.author]) + let calculatedAuthors = Set(item.authors?.compactMap { $0.name } ?? []) + XCTAssertEqual(calculatedAuthors, expectedAuthors) } func testSanitization() { @@ -118,4 +148,49 @@ class FeedlyEntryParserTests: XCTestCase { XCTAssertEqual(parser.externalUrl, alternateLink.href) } + + func testContentPreferredToSummary() { + let content = FeedlyEntry.Content(content: "Test Content", direction: .leftToRight) + let summary = FeedlyEntry.Content(content: "Test Summary", direction: .leftToRight) + let entry = FeedlyEntry(id: "tests/feeds/1/entries/1", + title: "Test Entry 1", + content: content, + summary: summary, + author: nil, + crawled: .distantPast, + recrawled: Date(timeIntervalSinceReferenceDate: 0), + origin: nil, + canonical: nil, + alternate: nil, + unread: false, + tags: nil, + categories: nil, + enclosure: nil) + + let parser = FeedlyEntryParser(entry: entry) + + XCTAssertEqual(parser.contentHMTL, content.content) + } + + func testSummaryUsedAsContentWhenContentMissing() { + let summary = FeedlyEntry.Content(content: "Test Summary", direction: .leftToRight) + let entry = FeedlyEntry(id: "tests/feeds/1/entries/1", + title: "Test Entry 1", + content: nil, + summary: summary, + author: nil, + crawled: .distantPast, + recrawled: Date(timeIntervalSinceReferenceDate: 0), + origin: nil, + canonical: nil, + alternate: nil, + unread: false, + tags: nil, + categories: nil, + enclosure: nil) + + let parser = FeedlyEntryParser(entry: entry) + + XCTAssertEqual(parser.contentHMTL, summary.content) + } } diff --git a/Frameworks/Account/Feedly/Models/FeedlyEntryParser.swift b/Frameworks/Account/Feedly/Models/FeedlyEntryParser.swift index 2e71b3ab8..8af6714c7 100644 --- a/Frameworks/Account/Feedly/Models/FeedlyEntryParser.swift +++ b/Frameworks/Account/Feedly/Models/FeedlyEntryParser.swift @@ -19,9 +19,11 @@ struct FeedlyEntryParser { return entry.id } + /// When ingesting articles, the feedURL must match a feed's `webFeedID` for the article to be reachable between it and its matching feed. It reminds me of a foreign key. var feedUrl: String? { guard let id = entry.origin?.streamId else { - assertionFailure() + // At this point, check Feedly's API isn't glitching or the response has not changed structure. + assertionFailure("Entries need to be traceable to a feed or this entry will be dropped.") return nil } return id @@ -69,6 +71,7 @@ struct FeedlyEntryParser { return Set([ParsedAuthor(name: name, url: nil, avatarURL: nil, emailAddress: nil)]) } + /// While there is not yet a tagging interface, articles can still be searched for by tags. var tags: Set? { guard let labels = entry.tags?.compactMap({ $0.label }), !labels.isEmpty else { return nil