444 lines
18 KiB
Swift
444 lines
18 KiB
Swift
|
import XCTest
|
||
|
@testable import Wikipedia
|
||
|
@testable import WMF
|
||
|
|
||
|
fileprivate class MockTalkPageFetcher: OldTalkPageFetcher {
|
||
|
|
||
|
static var name = "Username1"
|
||
|
static var domain = "en.wikipedia.org"
|
||
|
var fetchCalled = false
|
||
|
private let data: Data
|
||
|
|
||
|
required init(session: Session, configuration: Configuration, data: Data) {
|
||
|
self.data = data
|
||
|
super.init(session: session, configuration: configuration)
|
||
|
}
|
||
|
|
||
|
required init(session: Session, configuration: Configuration) {
|
||
|
fatalError("init(session:configuration:) has not been implemented")
|
||
|
}
|
||
|
|
||
|
override func fetchTalkPage(urlTitle: String, displayTitle: String, siteURL: URL, revisionID: Int?, completion: @escaping (Result<NetworkTalkPage, Error>) -> Void) {
|
||
|
|
||
|
fetchCalled = true
|
||
|
|
||
|
if let networkTalkPage = TalkPageTestHelpers.networkTalkPage(for: "https://\(MockTalkPageFetcher.domain)/api/rest_v1/page/talk/\(urlTitle)", data: data, revisionId: MockArticleRevisionFetcher.revisionId) {
|
||
|
completion(.success(networkTalkPage))
|
||
|
} else {
|
||
|
XCTFail("Expected network talk page from helper")
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fileprivate class MockArticleRevisionFetcher: WMFArticleRevisionFetcher {
|
||
|
|
||
|
static var revisionId: Int = 894272715
|
||
|
|
||
|
var resultsDictionary: [AnyHashable : Any] {
|
||
|
return ["batchcomplete": 1,
|
||
|
"query" : ["pages": [
|
||
|
["ns": 0,
|
||
|
"pageid": 2360669,
|
||
|
"revisions": [
|
||
|
["minor": 1,
|
||
|
"parentid": 894272641,
|
||
|
"revid": MockArticleRevisionFetcher.revisionId,
|
||
|
"size": 61252]
|
||
|
],
|
||
|
"title": "Benty Grange helmet"
|
||
|
]
|
||
|
]
|
||
|
]
|
||
|
]
|
||
|
}
|
||
|
|
||
|
override func fetchLatestRevisions(forArticleURL articleURL: URL!, resultLimit numberOfResults: UInt, startingWithRevision startRevisionId: NSNumber, endingWithRevision endRevisionId: NSNumber, failure: WMFErrorHandler!, success: WMFSuccessIdHandler!) -> URLSessionTask? {
|
||
|
do {
|
||
|
let revisionQueryResults = try WMFLegacySerializer.models(of: WMFRevisionQueryResults.self, fromArrayForKeyPath: "query.pages", inJSONDictionary: resultsDictionary, languageVariantCode: articleURL.wmf_languageVariantCode)
|
||
|
success(revisionQueryResults)
|
||
|
return nil
|
||
|
} catch {
|
||
|
XCTFail("Failure to create WMFRevisionQueryResults")
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class TalkPageControllerTests: XCTestCase {
|
||
|
|
||
|
var tempDataStore: MWKDataStore!
|
||
|
var talkPageController: OldTalkPageController!
|
||
|
fileprivate var talkPageFetcher: MockTalkPageFetcher!
|
||
|
fileprivate var articleRevisionFetcher: MockArticleRevisionFetcher!
|
||
|
|
||
|
override func setUp() {
|
||
|
super.setUp()
|
||
|
tempDataStore = MWKDataStore.temporary()
|
||
|
|
||
|
if let data = wmf_bundle().wmf_data(fromContentsOfFile: TalkPageTestHelpers.TalkPageJSONType.original.fileName, ofType: "json") {
|
||
|
talkPageFetcher = MockTalkPageFetcher(session: tempDataStore.session, configuration: tempDataStore.configuration, data: data)
|
||
|
} else {
|
||
|
XCTFail("Failure setting up MockTalkPageFetcher")
|
||
|
}
|
||
|
|
||
|
articleRevisionFetcher = MockArticleRevisionFetcher()
|
||
|
talkPageController = OldTalkPageController(fetcher: talkPageFetcher, articleRevisionFetcher: articleRevisionFetcher, moc: tempDataStore.viewContext, title: "User talk:Username1", siteURL: URL(string: "https://en.wikipedia.org")!, type: .user)
|
||
|
MockArticleRevisionFetcher.revisionId = 894272715
|
||
|
|
||
|
}
|
||
|
|
||
|
override func tearDown() {
|
||
|
tempDataStore.removeFolderAtBasePath()
|
||
|
|
||
|
let fetchRequest: NSFetchRequest<TalkPage> = TalkPage.fetchRequest()
|
||
|
|
||
|
guard let firstResults = try? tempDataStore.viewContext.fetch(fetchRequest) else {
|
||
|
XCTFail("Unable to fetch results from fetch request")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
for talkPage in firstResults {
|
||
|
tempDataStore.viewContext.delete(talkPage)
|
||
|
}
|
||
|
|
||
|
do {
|
||
|
try tempDataStore.save()
|
||
|
} catch {
|
||
|
XCTFail("Failure saving temporary data store")
|
||
|
}
|
||
|
|
||
|
|
||
|
tempDataStore = nil
|
||
|
super.tearDown()
|
||
|
}
|
||
|
|
||
|
func testInitialFetchSavesRecordInDB() {
|
||
|
|
||
|
// confirm no talk pages in DB at first
|
||
|
let fetchRequest: NSFetchRequest<TalkPage> = TalkPage.fetchRequest()
|
||
|
|
||
|
guard let firstResults = try? tempDataStore.viewContext.fetch(fetchRequest) else {
|
||
|
XCTFail("Failure fetching initial talk pages")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
XCTAssertEqual(firstResults.count, 0, "Expected zero existing talk pages at first")
|
||
|
|
||
|
let initialFetchCallback = expectation(description: "Waiting for initial fetch callback")
|
||
|
talkPageController.fetchTalkPage { (result) in
|
||
|
initialFetchCallback.fulfill()
|
||
|
|
||
|
switch result {
|
||
|
case .success(let dbTalkPageID):
|
||
|
|
||
|
// fetch from db again, confirm count is 1 and matches returned talk page
|
||
|
let fetchRequest: NSFetchRequest<TalkPage> = TalkPage.fetchRequest()
|
||
|
|
||
|
guard let results = try? self.tempDataStore.viewContext.fetch(fetchRequest) else {
|
||
|
XCTFail("Failure fetching initial talk pages")
|
||
|
return
|
||
|
}
|
||
|
let dbTalkPage = try? self.tempDataStore.viewContext.existingObject(with: dbTalkPageID.objectID) as? TalkPage
|
||
|
XCTAssertEqual(results.count, 1, "Expected one talk page in DB")
|
||
|
XCTAssertEqual(results.first, dbTalkPage)
|
||
|
XCTAssertEqual(dbTalkPage?.revisionId?.intValue, MockArticleRevisionFetcher.revisionId)
|
||
|
|
||
|
case .failure:
|
||
|
XCTFail("TalkPageController fetchTalkPage failure")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
wait(for: [initialFetchCallback], timeout: 5)
|
||
|
}
|
||
|
|
||
|
/* //todo: this fails when run consecutively
|
||
|
func testFetchSameUserTwiceDoesNotAddMultipleTalkPageRecords() {
|
||
|
|
||
|
//confirm no talk pages in DB at first
|
||
|
let fetchRequest: NSFetchRequest<TalkPage> = TalkPage.fetchRequest()
|
||
|
|
||
|
guard let firstResults = try? tempDataStore.viewContext.fetch(fetchRequest) else {
|
||
|
XCTFail("Failure fetching initial talk pages")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
XCTAssertEqual(firstResults.count, 0, "Expected zero existing talk pages at first")
|
||
|
|
||
|
let initialFetchCallback = expectation(description: "Waiting for initial fetch callback")
|
||
|
talkPageController.fetchTalkPage { (result) in
|
||
|
initialFetchCallback.fulfill()
|
||
|
|
||
|
switch result {
|
||
|
case .success(let dbTalkPage):
|
||
|
|
||
|
//confirm count is 1 and matches returned talk page
|
||
|
let fetchRequest: NSFetchRequest<TalkPage> = TalkPage.fetchRequest()
|
||
|
|
||
|
guard let results = try? self.tempDataStore.viewContext.fetch(fetchRequest) else {
|
||
|
XCTFail("Failure fetching initial talk pages")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
XCTAssertEqual(results.count, 1, "Expected one talk page in DB")
|
||
|
XCTAssertEqual(results.first, dbTalkPage)
|
||
|
|
||
|
case .failure:
|
||
|
XCTFail("TalkPageController fetchTalkPage failure")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
wait(for: [initialFetchCallback], timeout: 5)
|
||
|
|
||
|
//same fetch again
|
||
|
let nextFetchCallback = expectation(description: "Waiting for next fetch callback")
|
||
|
talkPageController.fetchTalkPage { (result) in
|
||
|
nextFetchCallback.fulfill()
|
||
|
|
||
|
switch result {
|
||
|
case .success(let dbTalkPage):
|
||
|
|
||
|
//fetch from db again, confirm count is still 1 and matches returned talk page
|
||
|
let fetchRequest: NSFetchRequest<TalkPage> = TalkPage.fetchRequest()
|
||
|
|
||
|
guard let results = try? self.tempDataStore.viewContext.fetch(fetchRequest) else {
|
||
|
XCTFail("Failure fetching initial talk pages")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
XCTAssertEqual(results.count, 1, "Expected one talk page in DB")
|
||
|
XCTAssertEqual(results.first, dbTalkPage)
|
||
|
|
||
|
case .failure:
|
||
|
XCTFail("TalkPageController fetchTalkPage failure")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
wait(for: [nextFetchCallback], timeout: 5)
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
func testFetchSameUserDifferentLanguageAddsMultipleTalkPageRecords() {
|
||
|
let initialFetchCallback = expectation(description: "Waiting for initial fetch callback")
|
||
|
talkPageController.fetchTalkPage { (result) in
|
||
|
initialFetchCallback.fulfill()
|
||
|
|
||
|
switch result {
|
||
|
case .success(let dbTalkPageID):
|
||
|
|
||
|
// fetch from db again, confirm count is 1 and matches returned talk page
|
||
|
let fetchRequest: NSFetchRequest<TalkPage> = TalkPage.fetchRequest()
|
||
|
|
||
|
guard let results = try? self.tempDataStore.viewContext.fetch(fetchRequest) else {
|
||
|
XCTFail("Failure fetching initial talk pages")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
XCTAssertEqual(results.count, 1, "Expected one talk page in DB")
|
||
|
XCTAssertEqual(results.first?.objectID, dbTalkPageID.objectID)
|
||
|
|
||
|
case .failure:
|
||
|
XCTFail("TalkPageController fetchTalkPage failure")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
wait(for: [initialFetchCallback], timeout: 5)
|
||
|
|
||
|
// fetch again for ES language
|
||
|
MockTalkPageFetcher.domain = "es.wikipedia.org"
|
||
|
talkPageController = OldTalkPageController(fetcher: talkPageFetcher, articleRevisionFetcher: articleRevisionFetcher, moc: tempDataStore.viewContext, title: "User talk:Username1", siteURL: URL(string: "https://es.wikipedia.org")!, type: .user)
|
||
|
|
||
|
let nextFetchCallback = expectation(description: "Waiting for next fetch callback")
|
||
|
talkPageController.fetchTalkPage { (result) in
|
||
|
nextFetchCallback.fulfill()
|
||
|
|
||
|
switch result {
|
||
|
case .success:
|
||
|
|
||
|
let fetchRequest: NSFetchRequest<TalkPage> = TalkPage.fetchRequest()
|
||
|
|
||
|
guard let results = try? self.tempDataStore.viewContext.fetch(fetchRequest) else {
|
||
|
XCTFail("Failure fetching initial talk pages")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
XCTAssertEqual(results.count, 2, "Expected two talk pages in DB")
|
||
|
|
||
|
case .failure:
|
||
|
XCTFail("TalkPageController fetchTalkPage failure")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
wait(for: [nextFetchCallback], timeout: 5)
|
||
|
}
|
||
|
|
||
|
func testFetchDifferentUserSameLanguageAddsMultipleTalkPageRecords() {
|
||
|
let initialFetchCallback = expectation(description: "Waiting for initial fetch callback")
|
||
|
talkPageController.fetchTalkPage { (result) in
|
||
|
initialFetchCallback.fulfill()
|
||
|
|
||
|
switch result {
|
||
|
case .success:
|
||
|
|
||
|
// fetch from db again, confirm count is 1 and matches returned talk page
|
||
|
let fetchRequest: NSFetchRequest<TalkPage> = TalkPage.fetchRequest()
|
||
|
|
||
|
guard let results = try? self.tempDataStore.viewContext.fetch(fetchRequest) else {
|
||
|
XCTFail("Failure fetching initial talk pages")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
XCTAssertEqual(results.count, 1, "Expected one talk page in DB")
|
||
|
|
||
|
case .failure:
|
||
|
XCTFail("TalkPageController fetchTalkPage failure")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
wait(for: [initialFetchCallback], timeout: 5)
|
||
|
|
||
|
MockTalkPageFetcher.name = "Username2"
|
||
|
talkPageController = OldTalkPageController(fetcher: talkPageFetcher, articleRevisionFetcher: articleRevisionFetcher, moc: tempDataStore.viewContext, title: "User talk:Username2", siteURL: URL(string: "https://en.wikipedia.org")!, type: .user)
|
||
|
|
||
|
let nextFetchCallback = expectation(description: "Waiting for next fetch callback")
|
||
|
talkPageController.fetchTalkPage { (result) in
|
||
|
nextFetchCallback.fulfill()
|
||
|
|
||
|
switch result {
|
||
|
case .success:
|
||
|
|
||
|
let fetchRequest: NSFetchRequest<TalkPage> = TalkPage.fetchRequest()
|
||
|
|
||
|
guard let results = try? self.tempDataStore.viewContext.fetch(fetchRequest) else {
|
||
|
XCTFail("Failure fetching initial talk pages")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
XCTAssertEqual(results.count, 2, "Expected two talk pages in DB")
|
||
|
|
||
|
case .failure:
|
||
|
XCTFail("TalkPageController fetchTalkPage failure")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
wait(for: [nextFetchCallback], timeout: 5)
|
||
|
}
|
||
|
|
||
|
func testFetchSameRevisionIdDoesNotCallFetcher() {
|
||
|
// confirm no talk pages in DB at first
|
||
|
let fetchRequest: NSFetchRequest<TalkPage> = TalkPage.fetchRequest()
|
||
|
|
||
|
guard let firstResults = try? tempDataStore.viewContext.fetch(fetchRequest) else {
|
||
|
XCTFail("Failure fetching initial talk pages")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
XCTAssertEqual(firstResults.count, 0, "Expected zero existing talk pages at first")
|
||
|
|
||
|
// initial fetch to populate DB
|
||
|
let initialFetchCallback = expectation(description: "Waiting for initial fetch callback")
|
||
|
|
||
|
var firstDBTalkPage: TalkPage?
|
||
|
talkPageController.fetchTalkPage { (result) in
|
||
|
initialFetchCallback.fulfill()
|
||
|
|
||
|
switch result {
|
||
|
case .success(let dbTalkPageID):
|
||
|
let dbTalkPage = try? self.tempDataStore.viewContext.existingObject(with: dbTalkPageID.objectID) as? TalkPage
|
||
|
firstDBTalkPage = dbTalkPage
|
||
|
XCTAssertEqual(dbTalkPage?.revisionId?.intValue, MockArticleRevisionFetcher.revisionId)
|
||
|
XCTAssertTrue(self.talkPageFetcher.fetchCalled, "Expected fetcher to be called for initial fetch")
|
||
|
|
||
|
case .failure:
|
||
|
XCTFail("TalkPageController fetchTalkPage failure")
|
||
|
}
|
||
|
}
|
||
|
wait(for: [initialFetchCallback], timeout: 5)
|
||
|
|
||
|
// reset fetchCalled
|
||
|
talkPageFetcher.fetchCalled = false
|
||
|
|
||
|
// make same fetch again, same revision ID. Confirm fetcher was never called and same talk page is returned
|
||
|
let secondFetchCallback = expectation(description: "Waiting for initial fetch callback")
|
||
|
|
||
|
talkPageController.fetchTalkPage { (result) in
|
||
|
|
||
|
switch result {
|
||
|
case .success(let dbTalkPageID):
|
||
|
guard !dbTalkPageID.isInitialLocalResult else {
|
||
|
return
|
||
|
}
|
||
|
let dbTalkPage = try? self.tempDataStore.viewContext.existingObject(with: dbTalkPageID.objectID) as? TalkPage
|
||
|
XCTAssertEqual(firstDBTalkPage, dbTalkPage)
|
||
|
XCTAssertEqual(dbTalkPage?.revisionId?.intValue, MockArticleRevisionFetcher.revisionId)
|
||
|
XCTAssertFalse(self.talkPageFetcher.fetchCalled, "Expected fetcher to not be called for second fetch")
|
||
|
|
||
|
case .failure:
|
||
|
XCTFail("TalkPageController fetchTalkPage failure")
|
||
|
}
|
||
|
|
||
|
secondFetchCallback.fulfill()
|
||
|
}
|
||
|
wait(for: [secondFetchCallback], timeout: 5)
|
||
|
}
|
||
|
|
||
|
/* //todo: this fails when run consecutively
|
||
|
func testIncrementedRevisionDoesCallFetcher() {
|
||
|
|
||
|
//confirm no talk pages in DB at first
|
||
|
let fetchRequest: NSFetchRequest<TalkPage> = TalkPage.fetchRequest()
|
||
|
|
||
|
guard let firstResults = try? tempDataStore.viewContext.fetch(fetchRequest) else {
|
||
|
XCTFail("Failure fetching initial talk pages")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
XCTAssertEqual(firstResults.count, 0, "Expected zero existing talk pages at first")
|
||
|
|
||
|
//initial fetch to populate DB
|
||
|
let initialFetchCallback = expectation(description: "Waiting for initial fetch callback")
|
||
|
|
||
|
var firstDBTalkPage: TalkPage?
|
||
|
talkPageController.fetchTalkPage { (result) in
|
||
|
initialFetchCallback.fulfill()
|
||
|
|
||
|
switch result {
|
||
|
case .success(let dbTalkPage):
|
||
|
firstDBTalkPage = dbTalkPage
|
||
|
XCTAssertEqual(dbTalkPage.revisionId, MockArticleRevisionFetcher.revisionId)
|
||
|
XCTAssertTrue(self.talkPageFetcher.fetchCalled, "Expected fetcher to be called for initial fetch")
|
||
|
|
||
|
case .failure:
|
||
|
XCTFail("TalkPageController fetchTalkPage failure")
|
||
|
}
|
||
|
}
|
||
|
wait(for: [initialFetchCallback], timeout: 5)
|
||
|
|
||
|
//reset fetchCalled
|
||
|
talkPageFetcher.fetchCalled = false
|
||
|
|
||
|
MockArticleRevisionFetcher.revisionId += 1
|
||
|
|
||
|
//make same fetch again, same revision ID. Confirm fetcher was never called and same talk page is returned
|
||
|
let secondFetchCallback = expectation(description: "Waiting for initial fetch callback")
|
||
|
|
||
|
talkPageController.fetchTalkPage { (result) in
|
||
|
secondFetchCallback.fulfill()
|
||
|
|
||
|
switch result {
|
||
|
case .success(let dbTalkPage):
|
||
|
|
||
|
XCTAssertEqual(firstDBTalkPage, dbTalkPage)
|
||
|
XCTAssertEqual(dbTalkPage.revisionId, MockArticleRevisionFetcher.revisionId)
|
||
|
XCTAssertTrue(self.talkPageFetcher.fetchCalled, "Expected fetcher to be called for second fetch")
|
||
|
|
||
|
case .failure:
|
||
|
XCTFail("TalkPageController fetchTalkPage failure")
|
||
|
}
|
||
|
}
|
||
|
wait(for: [secondFetchCallback], timeout: 5)
|
||
|
}
|
||
|
*/
|
||
|
}
|