Signal-iOS/SignalServiceKit/tests/Messages/LinkPreview/OWSLinkPreviewTest.swift

412 lines
17 KiB
Swift

//
// Copyright 2019 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import Foundation
@testable import SignalServiceKit
import XCTest
func XCTAssertMatch(expectedPattern: String, actualText: String, file: StaticString = #file, line: UInt = #line) {
let regex = try! NSRegularExpression(pattern: expectedPattern, options: [])
XCTAssert(regex.hasMatch(input: actualText), "\(actualText) did not match pattern \(expectedPattern)", file: file, line: line)
}
class MockSSKPreferences: LinkPreviewManagerImpl.Shims.SSKPreferences {
init() {}
func areLinkPreviewsEnabled(tx: DBReadTransaction) -> Bool {
return true
}
}
class OWSLinkPreviewTest: SSKBaseTest {
let shouldRunNetworkTests = false
var mockDB: MockDB!
var linkPreviewManager: LinkPreviewManagerImpl!
override func setUp() {
super.setUp()
mockDB = MockDB()
linkPreviewManager = LinkPreviewManagerImpl(
attachmentManager: TSResourceManagerMock(),
attachmentStore: TSResourceStoreMock(),
attachmentValidator: AttachmentContentValidatorMock(),
db: mockDB,
groupsV2: LinkPreviewManagerImpl.Wrappers.GroupsV2(MockGroupsV2()),
sskPreferences: MockSSKPreferences()
)
}
func testBuildValidatedLinkPreview_TitleAndImage() {
let url = "https://www.youtube.com/watch?v=tP-Ipsat90c"
let body = "\(url)"
let previewBuilder = SSKProtoPreview.builder(url: url)
previewBuilder.setTitle("Some Youtube Video")
let imageAttachmentBuilder = SSKProtoAttachmentPointer.builder()
imageAttachmentBuilder.setCdnID(1)
imageAttachmentBuilder.setKey(Randomness.generateRandomBytes(32))
imageAttachmentBuilder.setContentType(MimeType.imageJpeg.rawValue)
previewBuilder.setImage(imageAttachmentBuilder.buildInfallibly())
let dataBuilder = SSKProtoDataMessage.builder()
dataBuilder.addPreview(try! previewBuilder.build())
dataBuilder.setBody(body)
let dataMessage = try! dataBuilder.build()
mockDB.write { tx in
let linkPreviewBuilder = try! linkPreviewManager.validateAndBuildLinkPreview(
from: dataMessage.preview.first!,
dataMessage: dataMessage,
tx: tx
)
XCTAssertNotNil(linkPreviewBuilder)
try! linkPreviewBuilder.finalize(
owner: .messageLinkPreview(.init(
messageRowId: 0,
receivedAtTimestamp: 1000,
threadRowId: 0
)),
tx: tx
)
}
}
func testBuildValidatedLinkPreview_Title() {
let url = "https://www.youtube.com/watch?v=tP-Ipsat90c"
let body = "\(url)"
let previewBuilder = SSKProtoPreview.builder(url: url)
previewBuilder.setTitle("Some Youtube Video")
let dataBuilder = SSKProtoDataMessage.builder()
dataBuilder.addPreview(try! previewBuilder.build())
dataBuilder.setBody(body)
let dataMessage = try! dataBuilder.build()
mockDB.write { tx in
let linkPreviewBuilder = try! linkPreviewManager.validateAndBuildLinkPreview(
from: dataMessage.preview.first!,
dataMessage: dataMessage,
tx: tx
)
XCTAssertNotNil(linkPreviewBuilder)
try! linkPreviewBuilder.finalize(
owner: .messageLinkPreview(.init(
messageRowId: 0,
receivedAtTimestamp: 100,
threadRowId: 0
)),
tx: tx
)
}
}
func testBuildValidatedLinkPreview_Image() {
let url = "https://www.youtube.com/watch?v=tP-Ipsat90c"
let body = "\(url)"
let previewBuilder = SSKProtoPreview.builder(url: url)
let imageAttachmentBuilder = SSKProtoAttachmentPointer.builder()
imageAttachmentBuilder.setCdnID(1)
imageAttachmentBuilder.setKey(Randomness.generateRandomBytes(32))
imageAttachmentBuilder.setContentType(MimeType.imageJpeg.rawValue)
previewBuilder.setImage(imageAttachmentBuilder.buildInfallibly())
let dataBuilder = SSKProtoDataMessage.builder()
dataBuilder.addPreview(try! previewBuilder.build())
dataBuilder.setBody(body)
let dataMessage = try! dataBuilder.build()
mockDB.write { tx in
let linkPreviewBuilder = try! linkPreviewManager.validateAndBuildLinkPreview(
from: dataMessage.preview.first!,
dataMessage: dataMessage,
tx: tx
)
XCTAssertNotNil(linkPreviewBuilder)
try! linkPreviewBuilder.finalize(
owner: .messageLinkPreview(.init(
messageRowId: 0,
receivedAtTimestamp: 300,
threadRowId: 1
)),
tx: tx
)
}
}
}
// MARK: - Network dependent tests, disabled by default
extension OWSLinkPreviewTest {
func testLinkDownloadAndParsing() {
guard shouldRunNetworkTests else { return }
let expectation = self.expectation(description: "link download and parsing")
linkPreviewManager.fetchLinkPreview(for: URL(string: "https://www.youtube.com/watch?v=tP-Ipsat90c")!)
.done { (draft: OWSLinkPreviewDraft) in
XCTAssertNotNil(draft)
XCTAssertEqual(draft.title, "Randomness is Random - Numberphile")
XCTAssertNotNil(draft.imageData)
expectation.fulfill()
}.catch { (error) in
Logger.error("error: \(error)")
XCTFail("Unexpected error: \(error)")
expectation.fulfill()
}
self.waitForExpectations(timeout: 15.0, handler: nil)
}
func testLinkParsingWithRealData1() {
guard shouldRunNetworkTests else { return }
let expectation = self.expectation(description: "link download and parsing")
linkPreviewManager.fetchStringResource(from: URL(string: "https://www.youtube.com/watch?v=tP-Ipsat90c")!)
.done { _, linkText in
let content = HTMLMetadata.construct(parsing: linkText)
XCTAssertNotNil(content)
XCTAssertEqual(content.ogTitle, "Randomness is Random - Numberphile")
XCTAssertEqual(content.ogImageUrlString, "https://i.ytimg.com/vi/tP-Ipsat90c/maxresdefault.jpg")
expectation.fulfill()
}.catch { (error) in
Logger.error("error: \(error)")
XCTFail("Unexpected error: \(error)")
expectation.fulfill()
}
self.waitForExpectations(timeout: 15.0, handler: nil)
}
func testLinkParsingWithRealData2() {
guard shouldRunNetworkTests else { return }
let expectation = self.expectation(description: "link download and parsing")
linkPreviewManager.fetchStringResource(from: URL(string: "https://youtu.be/tP-Ipsat90c")!)
.done { _, linkText in
let content = HTMLMetadata.construct(parsing: linkText)
XCTAssertNotNil(content)
XCTAssertEqual(content.ogTitle, "Randomness is Random - Numberphile")
XCTAssertEqual(content.ogImageUrlString, "https://i.ytimg.com/vi/tP-Ipsat90c/maxresdefault.jpg")
expectation.fulfill()
}.catch { (error) in
Logger.error("error: \(error)")
XCTFail("Unexpected error: \(error)")
expectation.fulfill()
}
self.waitForExpectations(timeout: 15.0, handler: nil)
}
func testLinkParsingWithRealData3() {
guard shouldRunNetworkTests else { return }
let expectation = self.expectation(description: "link download and parsing")
linkPreviewManager.fetchStringResource(from: URL(string:
"https://www.reddit.com/r/memes/comments/c3p3dy/i_drew_all_the_boys_together_and_i_did_it_for_the/")!)
.done { _, linkText in
let content = HTMLMetadata.construct(parsing: linkText)
XCTAssertNotNil(content)
XCTAssertEqual(content.ogTitle, "r/memes - I drew all the boys together and i did it for the internet")
XCTAssertEqual(content.ogImageUrlString, "https://preview.redd.it/yb3996njhw531.jpg?auto=webp&s=f8977087ab74e57063fff19c5df9534f22c0f521")
expectation.fulfill()
}.catch { (error) in
Logger.error("error: \(error)")
XCTFail("Unexpected error: \(error)")
expectation.fulfill()
}
self.waitForExpectations(timeout: 15.0, handler: nil)
}
func testLinkParsingWithRealData4() {
guard shouldRunNetworkTests else { return }
let expectation = self.expectation(description: "link download and parsing")
linkPreviewManager.fetchStringResource(from: URL(string: "https://www.reddit.com/r/WhitePeopleTwitter/comments/a7j3mm/why/")!)
.done { _, linkText in
let content = HTMLMetadata.construct(parsing: linkText)
XCTAssertNotNil(content)
XCTAssertEqual(content.ogTitle, "r/WhitePeopleTwitter - Why")
XCTAssertEqual(content.ogImageUrlString, "https://preview.redd.it/ehakvm9vx5521.jpg?auto=webp&s=925fb2d8776ca7102b944ab00e0615ae20c1bd5a")
expectation.fulfill()
}.catch { (error) in
Logger.error("error: \(error)")
XCTFail("Unexpected error: \(error)")
expectation.fulfill()
}
self.waitForExpectations(timeout: 15.0, handler: nil)
}
func testLinkParsingWithRealData5() {
guard shouldRunNetworkTests else { return }
let expectation = self.expectation(description: "link download and parsing")
linkPreviewManager.fetchStringResource(from: URL(string: "https://imgur.com/gallery/KFCL8fm")!)
.done { _, linkText in
let content = HTMLMetadata.construct(parsing: linkText)
XCTAssertNotNil(content)
XCTAssertEqual(content.ogTitle, "imgur.com")
// Actual URL can change based on network response
//
// It seems like some parts of the URL are stable, but if this continues to be brittle we may choose
// to remove it or stub the network response
XCTAssertTrue(content.ogImageUrlString!.hasPrefix("https://i.imgur.com/Y3wjlwY."))
expectation.fulfill()
}.catch { (error) in
Logger.error("error: \(error)")
XCTFail("Unexpected error: \(error)")
expectation.fulfill()
}
self.waitForExpectations(timeout: 15.0, handler: nil)
}
func testLinkParsingWithRealData6() {
guard shouldRunNetworkTests else { return }
let expectation = self.expectation(description: "link download and parsing")
linkPreviewManager.fetchStringResource(from: URL(string: "https://imgur.com/gallery/FMdwTiV")!)
.done { _, linkText in
let content = HTMLMetadata.construct(parsing: linkText)
XCTAssertNotNil(content)
XCTAssertEqual(content.ogTitle, "Freddy would be proud!")
XCTAssertEqual(content.ogImageUrlString, "https://i.imgur.com/Vot3iHh.jpg?fbplay")
expectation.fulfill()
}.catch { (error) in
Logger.error("error: \(error)")
XCTFail("Unexpected error: \(error)")
expectation.fulfill()
}
self.waitForExpectations(timeout: 15.0, handler: nil)
}
func testLinkParsingWithRealData_instagram_web_link() {
guard shouldRunNetworkTests else { return }
let expectation = self.expectation(description: "link download and parsing")
linkPreviewManager.fetchStringResource(from: URL(string: "https://www.instagram.com/p/BtjTTyHnDKJ/?utm_source=ig_web_button_share_sheet")!)
.done { _, linkText in
let content = HTMLMetadata.construct(parsing: linkText)
XCTAssertNotNil(content)
XCTAssertEqual(content.ogTitle, "Signal on Instagram: “I link therefore I am: https://signal.org/blog/i-link-therefore-i-am/”")
// Actual URL can change based on network response
//
// It seems like some parts of the URL are stable, so we can pattern match, but if this continues to be brittle we may choose
// to remove it or stub the network response
XCTAssertMatch(expectedPattern: "^https://.*.cdninstagram.com/.*/50654775_634096837020403_4737154112061769375_n.jpg\\?.*$",
actualText: content.ogImageUrlString!)
// XCTAssertEqual(content.imageUrl, "https://scontent-iad3-1.cdninstagram.com/vp/88656d9c10074b97b503d3b7b86eba84/5D774562/t51.2885-15/e35/50654775_634096837020403_4737154112061769375_n.jpg?_nc_ht=scontent-iad3-1.cdninstagram.com")
expectation.fulfill()
}.catch { (error) in
Logger.error("error: \(error)")
XCTFail("Unexpected error: \(error)")
expectation.fulfill()
}
self.waitForExpectations(timeout: 15.0, handler: nil)
}
func testLinkParsingWithRealData_instagram_app_sharesheet() {
guard shouldRunNetworkTests else { return }
let expectation = self.expectation(description: "link download and parsing")
linkPreviewManager.fetchStringResource(from: URL(string: "https://www.instagram.com/p/BtjTTyHnDKJ/?utm_source=ig_share_sheet&igshid=1bgo1ur9m9hi5")!)
.done { _, linkText in
let content = HTMLMetadata.construct(parsing: linkText)
XCTAssertNotNil(content)
XCTAssertEqual(content.ogTitle, "Signal on Instagram: “I link therefore I am: https://signal.org/blog/i-link-therefore-i-am/”")
// Actual URL can change based on network response
//
// It seems like some parts of the URL are stable, so we can pattern match, but if this continues to be brittle we may choose
// to remove it or stub the network response
XCTAssertMatch(expectedPattern: "^https://.*.cdninstagram.com/.*/50654775_634096837020403_4737154112061769375_n.jpg\\?.*$",
actualText: content.ogImageUrlString!)
// XCTAssertEqual(content.imageUrl, "https://scontent-iad3-1.cdninstagram.com/vp/88656d9c10074b97b503d3b7b86eba84/5D774562/t51.2885-15/e35/50654775_634096837020403_4737154112061769375_n.jpg?_nc_ht=scontent-iad3-1.cdninstagram.com")
expectation.fulfill()
}.catch { (error) in
Logger.error("error: \(error)")
XCTFail("Unexpected error: \(error)")
expectation.fulfill()
}
self.waitForExpectations(timeout: 15.0, handler: nil)
}
func testLinkParsingWithRealData9() {
guard shouldRunNetworkTests else { return }
let expectation = self.expectation(description: "link download and parsing")
linkPreviewManager.fetchStringResource(from: URL(string: "https://imgur.com/gallery/igHOwDM")!)
.done { _, linkText in
let content = HTMLMetadata.construct(parsing: linkText)
XCTAssertNotNil(content)
XCTAssertEqual(content.ogTitle, "Sheet dance")
XCTAssertEqual(content.ogImageUrlString, "https://i.imgur.com/PYiyLv1.jpg?fbplay")
expectation.fulfill()
}.catch { (error) in
Logger.error("error: \(error)")
XCTFail("Unexpected error: \(error)")
expectation.fulfill()
}
self.waitForExpectations(timeout: 15.0, handler: nil)
}
func testLinkParsingWithRealData10() {
guard shouldRunNetworkTests else { return }
let expectation = self.expectation(description: "link download and parsing")
linkPreviewManager.fetchStringResource(from: URL(string: "https://www.pinterest.com/norat0464/test-board/")!)
.done { _, linkText in
let content = HTMLMetadata.construct(parsing: linkText)
XCTAssertNotNil(content)
XCTAssertEqual(content.ogTitle, "Test board")
XCTAssertEqual(content.ogImageUrlString, "https://i.pinimg.com/200x150/3e/85/f8/3e85f88e7be0dd1418a5b430d2ee8a55.jpg")
expectation.fulfill()
}.catch { (error) in
Logger.error("error: \(error)")
XCTFail("Unexpected error: \(error)")
expectation.fulfill()
}
self.waitForExpectations(timeout: 15.0, handler: nil)
}
}