From 9a59c67c0438704eb7de865a7a385cc45e4a2365 Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Tue, 19 Mar 2024 15:49:01 +1100 Subject: [PATCH 1/2] Use `XCTUnwrap` instead of force-unwrapping for fixture paths in tests This will make it easier to run the tests once the fixture files move around. If a file is misplaced, the test will fail instead of crashing. Operationally, failing is better than crashing because it generates a list of all misplaced tests, so that they can be addressed in one go. The alternative would be a run tests, tests crash, fix individual file path loop for as many misplaced fixture files there are. --- .../ReaderSiteServiceRemoteTests.swift | 16 ++- WordPressKitTests/RemoteTestCase.swift | 8 +- .../WordPressComOAuthClientTests.swift | 124 +++++++++++------- .../WordPressComRestApiTests.swift | 88 ++++++++----- .../WordPressComServiceRemoteRestTests.swift | 12 +- .../WordPressOrgRestApiTests.swift | 12 +- .../WordPressOrgXMLRPCApiTests.swift | 30 ++--- 7 files changed, 177 insertions(+), 113 deletions(-) diff --git a/WordPressKitTests/ReaderSiteServiceRemoteTests.swift b/WordPressKitTests/ReaderSiteServiceRemoteTests.swift index a2b3d70f..ca9e8027 100644 --- a/WordPressKitTests/ReaderSiteServiceRemoteTests.swift +++ b/WordPressKitTests/ReaderSiteServiceRemoteTests.swift @@ -289,12 +289,12 @@ class ReaderSiteServiceRemoteTests: XCTestCase { XCTAssertTrue(failure) } - func testCheckSiteExistsAtURLSuccess() { + func testCheckSiteExistsAtURLSuccess() throws { let testURLString = "http://www.wordpress.com" let testURL = URL(string: testURLString)! + let stubPath = try XCTUnwrap(OHPathForFile("empty.json", type(of: self))) stub(condition: {request in request.url?.absoluteString == testURLString}) { _ in - let stubPath = OHPathForFile("empty.json", type(of: self)) - return fixture(filePath: stubPath!, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + return fixture(filePath: stubPath, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) } let expect = self.expectation(description: "One callback should be invoked") @@ -308,12 +308,16 @@ class ReaderSiteServiceRemoteTests: XCTestCase { self.waitForExpectations(timeout: 2, handler: nil) } - func testCheckSiteExistsAtURLFailure() { + func testCheckSiteExistsAtURLFailure() throws { let testURLString = "http://www.wordpress.com" let testURL = URL(string: testURLString)! + let stubPath = try XCTUnwrap(OHPathForFile("empty.json", type(of: self))) stub(condition: {request in request.url?.absoluteString == testURLString}) { _ in - let stubPath = OHPathForFile("empty.json", type(of: self)) - return fixture(filePath: stubPath!, status: 400, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + return fixture( + filePath: stubPath, + status: 400, + headers: ["Content-Type" as NSObject: "application/json" as AnyObject] + ) } let expect = self.expectation(description: "One callback should be invoked") diff --git a/WordPressKitTests/RemoteTestCase.swift b/WordPressKitTests/RemoteTestCase.swift index f5b74ee4..b6db19ed 100644 --- a/WordPressKitTests/RemoteTestCase.swift +++ b/WordPressKitTests/RemoteTestCase.swift @@ -54,13 +54,15 @@ extension RemoteTestCase { contentType: ResponseContentType, status: Int32 = 200 ) { + // This doesn't follow the XCTUnwrap pattern (yet?) because the method is used many times and it was too time consuming to update every call site at the time + let stubPath = OHPathForFile(filename, type(of: self)) stub(condition: condition) { _ in - let stubPath = OHPathForFile(filename, type(of: self)) var headers: [NSObject: AnyObject]? if contentType != .NoContentType { headers = ["Content-Type" as NSObject: contentType.rawValue as AnyObject] } + // This is force-unwrapped at call site, despite it making more sense at declaration site, so it can be found when grepping for force unwraps. return OHHTTPStubs.fixture(filePath: stubPath!, status: status, headers: headers) } } @@ -74,15 +76,17 @@ extension RemoteTestCase { /// - status: The status code to use for the response. Defaults to 200. /// func stubRemoteResponse(_ endpoint: String, filename: String, contentType: ResponseContentType, status: Int32 = 200) { + // This doesn't follow the XCTUnwrap pattern (yet?) because the method is used many times and it was too time consuming to update every call site at the time + let stubPath = OHPathForFile(filename, type(of: self)) stub(condition: { request in return request.url?.absoluteString.range(of: endpoint) != nil }) { _ in - let stubPath = OHPathForFile(filename, type(of: self)) var headers: [NSObject: AnyObject]? if contentType != .NoContentType { headers = ["Content-Type" as NSObject: contentType.rawValue as AnyObject] } + // This is force-unwrapped at call site, despite it making more sense at declaration site, so it can be found when grepping for force unwraps. return fixture(filePath: stubPath!, status: status, headers: headers) } } diff --git a/WordPressKitTests/WordPressComOAuthClientTests.swift b/WordPressKitTests/WordPressComOAuthClientTests.swift index 1559e5c8..49bfff09 100644 --- a/WordPressKitTests/WordPressComOAuthClientTests.swift +++ b/WordPressKitTests/WordPressComOAuthClientTests.swift @@ -26,10 +26,10 @@ class WordPressComOAuthClientTests: XCTestCase { } } - func testAuthenticateUsernameNo2FASuccessCase() { + func testAuthenticateUsernameNo2FASuccessCase() throws { + let stubPath = try XCTUnwrap(OHPathForFile("WordPressComOAuthSuccess.json", type(of: self))) stub(condition: isOauthTokenRequest(url: .oAuthTokenUrl)) { _ in - let stubPath = OHPathForFile("WordPressComOAuthSuccess.json", type(of: self)) - return fixture(filePath: stubPath!, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + return fixture(filePath: stubPath, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) } let expect = expectation(description: "One callback should be invoked") @@ -52,10 +52,10 @@ class WordPressComOAuthClientTests: XCTestCase { waitForExpectations(timeout: 2, handler: nil) } - func testAuthenticateUsernameNo2FASuccessCase_withMFAClosure() { + func testAuthenticateUsernameNo2FASuccessCase_withMFAClosure() throws { + let stubPath = try XCTUnwrap(OHPathForFile("WordPressComOAuthSuccess.json", type(of: self))) stub(condition: isOauthTokenRequest(url: .oAuthTokenUrl)) { _ in - let stubPath = OHPathForFile("WordPressComOAuthSuccess.json", type(of: self)) - return fixture(filePath: stubPath!, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + return fixture(filePath: stubPath, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) } let expect = expectation(description: "One callback should be invoked") @@ -81,10 +81,12 @@ class WordPressComOAuthClientTests: XCTestCase { waitForExpectations(timeout: 2, handler: nil) } - func testAuthenticateUsernameNo2FAFailureWrongPasswordCase() { + func testAuthenticateUsernameNo2FAFailureWrongPasswordCase() throws { + let stubPath = try XCTUnwrap( + OHPathForFile("WordPressComOAuthWrongPasswordFail.json", type(of: self)) + ) stub(condition: isOauthTokenRequest(url: .oAuthTokenUrl)) { _ in - let stubPath = OHPathForFile("WordPressComOAuthWrongPasswordFail.json", type(of: self)) - return fixture(filePath: stubPath!, status: 400, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + return fixture(filePath: stubPath, status: 400, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) } let expect = expectation(description: "One callback should be invoked") @@ -106,10 +108,12 @@ class WordPressComOAuthClientTests: XCTestCase { waitForExpectations(timeout: 2, handler: nil) } - func testAuthenticateUsernameNo2FAFailureWrongPasswordCase_withMFAClosure() { + func testAuthenticateUsernameNo2FAFailureWrongPasswordCase_withMFAClosure() throws { + let stubPath = try XCTUnwrap( + OHPathForFile("WordPressComOAuthWrongPasswordFail.json", type(of: self)) + ) stub(condition: isOauthTokenRequest(url: .oAuthTokenUrl)) { _ in - let stubPath = OHPathForFile("WordPressComOAuthWrongPasswordFail.json", type(of: self)) - return fixture(filePath: stubPath!, status: 400, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + return fixture(filePath: stubPath, status: 400, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) } let expect = expectation(description: "One callback should be invoked") @@ -134,10 +138,16 @@ class WordPressComOAuthClientTests: XCTestCase { waitForExpectations(timeout: 2, handler: nil) } - func testAuthenticateUsername2FAWrong2FACase() { + func testAuthenticateUsername2FAWrong2FACase() throws { + let stubPath = try XCTUnwrap( + OHPathForFile("WordPressComOAuthNeeds2FAFail.json", type(of: self)) + ) stub(condition: isOauthTokenRequest(url: .oAuthTokenUrl)) { _ in - let stubPath = OHPathForFile("WordPressComOAuthNeeds2FAFail.json", type(of: self)) - return fixture(filePath: stubPath!, status: 400, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + return fixture( + filePath: stubPath, + status: 400, + headers: ["Content-Type" as NSObject: "application/json" as AnyObject] + ) } let expect = expectation(description: "Call should complete") @@ -179,10 +189,16 @@ class WordPressComOAuthClientTests: XCTestCase { waitForExpectations(timeout: 2, handler: nil) } - func testAuthenticateUsername2FAWrong2FACase_withMFAClosure() { + func testAuthenticateUsername2FAWrong2FACase_withMFAClosure() throws { + let stubPath = try XCTUnwrap( + OHPathForFile("WordPressComOAuthNeeds2FAFail.json", type(of: self)) + ) stub(condition: isOauthTokenRequest(url: .oAuthTokenUrl)) { _ in - let stubPath = OHPathForFile("WordPressComOAuthNeeds2FAFail.json", type(of: self)) - return fixture(filePath: stubPath!, status: 400, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + return fixture( + filePath: stubPath, + status: 400, + headers: ["Content-Type" as NSObject: "application/json" as AnyObject] + ) } let expect = expectation(description: "Call should complete") @@ -227,10 +243,12 @@ class WordPressComOAuthClientTests: XCTestCase { waitForExpectations(timeout: 2, handler: nil) } - func testAuthenticateUsernameRequiresWebauthnMultifactorAuthentication() { + func testAuthenticateUsernameRequiresWebauthnMultifactorAuthentication() throws { + let stubPath = try XCTUnwrap( + OHPathForFile("WordPressComOAuthNeedsWebauthnMFA.json", type(of: self)) + ) stub(condition: isOauthTokenRequest(url: .oAuthTokenUrl)) { _ in - let stubPath = OHPathForFile("WordPressComOAuthNeedsWebauthnMFA.json", type(of: self)) - return fixture(filePath: stubPath!, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + return fixture(filePath: stubPath, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) } let expect = expectation(description: "Call should complete") @@ -256,10 +274,12 @@ class WordPressComOAuthClientTests: XCTestCase { waitForExpectations(timeout: 2, handler: nil) } - func testRequestOneTimeCodeWithUsername() { + func testRequestOneTimeCodeWithUsername() throws { + let stubPath = try XCTUnwrap( + OHPathForFile("WordPressComOAuthNeeds2FAFail.json", type(of: self)) + ) stub(condition: isOauthTokenRequest(url: .oAuthTokenUrl)) { _ in - let stubPath = OHPathForFile("WordPressComOAuthNeeds2FAFail.json", type(of: self)) - return fixture(filePath: stubPath!, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + return fixture(filePath: stubPath, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) } let expect = expectation(description: "One callback should be invoked") @@ -275,10 +295,12 @@ class WordPressComOAuthClientTests: XCTestCase { waitForExpectations(timeout: 2, handler: nil) } - func testRequestSocial2FACodeWithUserID() { + func testRequestSocial2FACodeWithUserID() throws { + let stubPath = try XCTUnwrap( + OHPathForFile("WordPressComSocial2FACodeSuccess.json", type(of: self)) + ) stub(condition: isOauthTokenRequest(url: .socialLoginNewSMS2FA)) { _ in - let stubPath = OHPathForFile("WordPressComSocial2FACodeSuccess.json", type(of: self)) - return fixture(filePath: stubPath!, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + return fixture(filePath: stubPath, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) } let expect = expectation(description: "One callback should be invoked") @@ -296,10 +318,12 @@ class WordPressComOAuthClientTests: XCTestCase { waitForExpectations(timeout: 2, handler: nil) } - func testAuthenticateWithIDToken() { + func testAuthenticateWithIDToken() throws { + let stubPath = try XCTUnwrap( + OHPathForFile("WordPressComAuthenticateWithIDTokenBearerTokenSuccess.json", type(of: self)) + ) stub(condition: isOauthTokenRequest(url: .socialLogin)) { _ in - let stubPath = OHPathForFile("WordPressComAuthenticateWithIDTokenBearerTokenSuccess.json", type(of: self)) - return fixture(filePath: stubPath!, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + return fixture(filePath: stubPath, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) } let expect = expectation(description: "One callback should be invoked") @@ -329,10 +353,12 @@ class WordPressComOAuthClientTests: XCTestCase { waitForExpectations(timeout: 2, handler: nil) } - func testAuthenticateWithIDToken2FANeeded() { + func testAuthenticateWithIDToken2FANeeded() throws { + let stubPath = try XCTUnwrap( + OHPathForFile("WordPressComAuthenticateWithIDToken2FANeededSuccess.json", type(of: self)) + ) stub(condition: isOauthTokenRequest(url: .socialLogin)) { _ in - let stubPath = OHPathForFile("WordPressComAuthenticateWithIDToken2FANeededSuccess.json", type(of: self)) - return fixture(filePath: stubPath!, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + return fixture(filePath: stubPath, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) } let expect = expectation(description: "One callback should be invoked") @@ -363,10 +389,12 @@ class WordPressComOAuthClientTests: XCTestCase { waitForExpectations(timeout: 2, handler: nil) } - func testAuthenticateWithIDTokenUserNeedsConnection() { + func testAuthenticateWithIDTokenUserNeedsConnection() throws { + let stubPath = try XCTUnwrap( + OHPathForFile("WordPressComAuthenticateWithIDTokenExistingUserNeedsConnection.json", type(of: self)) + ) stub(condition: isOauthTokenRequest(url: .socialLogin)) { _ in - let stubPath = OHPathForFile("WordPressComAuthenticateWithIDTokenExistingUserNeedsConnection.json", type(of: self)) - return fixture(filePath: stubPath!, status: 400, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + return fixture(filePath: stubPath, status: 400, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) } let expect = expectation(description: "One callback should be invoked") @@ -395,10 +423,12 @@ class WordPressComOAuthClientTests: XCTestCase { waitForExpectations(timeout: 2, handler: nil) } - func testAuthenticateSocialLoginUser() { + func testAuthenticateSocialLoginUser() throws { + let stubPath = try XCTUnwrap( + OHPathForFile("WordPressComAuthenticateWithIDTokenBearerTokenSuccess.json", type(of: self)) + ) stub(condition: isOauthTokenRequest(url: .socialLogin2FA)) { _ in - let stubPath = OHPathForFile("WordPressComAuthenticateWithIDTokenBearerTokenSuccess.json", type(of: self)) - return fixture(filePath: stubPath!, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + return fixture(filePath: stubPath, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) } let expect = expectation(description: "One callback should be invoked") @@ -416,10 +446,12 @@ class WordPressComOAuthClientTests: XCTestCase { waitForExpectations(timeout: 2, handler: nil) } - func testRequestWebauthnChallengeReturnsCompleteChallengeInfo() { + func testRequestWebauthnChallengeReturnsCompleteChallengeInfo() throws { + let stubPath = try XCTUnwrap( + OHPathForFile("WordPressComOAuthRequestChallenge.json", type(of: self)) + ) stub(condition: isOauthTokenRequest(url: .requestWebauthnChallenge)) { _ in - let stubPath = OHPathForFile("WordPressComOAuthRequestChallenge.json", type(of: self)) - return fixture(filePath: stubPath!, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + return fixture(filePath: stubPath, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) } let expect = expectation(description: "One callback should be invoked") @@ -437,10 +469,12 @@ class WordPressComOAuthClientTests: XCTestCase { waitForExpectations(timeout: 2, handler: nil) } - func testAuthenticateWebauthSignatureReturnsOauthToken() { + func testAuthenticateWebauthSignatureReturnsOauthToken() throws { + let stubPath = try XCTUnwrap( + OHPathForFile("WordPressComOAuthAuthenticateSignature.json", type(of: self)) + ) stub(condition: isOauthTokenRequest(url: .verifySignature)) { _ in - let stubPath = OHPathForFile("WordPressComOAuthAuthenticateSignature.json", type(of: self)) - return fixture(filePath: stubPath!, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + return fixture(filePath: stubPath, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) } let expect = expectation(description: "One callback should be invoked") diff --git a/WordPressKitTests/WordPressComRestApiTests.swift b/WordPressKitTests/WordPressComRestApiTests.swift index b0c98ae0..877096a5 100644 --- a/WordPressKitTests/WordPressComRestApiTests.swift +++ b/WordPressKitTests/WordPressComRestApiTests.swift @@ -111,10 +111,12 @@ class WordPressComRestApiTests: XCTestCase { } } - func testSuccessfullCall() { + func testSuccessfullCall() throws { + let stubPath = try XCTUnwrap( + OHPathForFile("WordPressComRestApiMedia.json", type(of: self)) + ) stub(condition: isRestAPIRequest()) { _ in - let stubPath = OHPathForFile("WordPressComRestApiMedia.json", type(of: self)) - return fixture(filePath: stubPath!, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + return fixture(filePath: stubPath, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) } let expect = self.expectation(description: "One callback should be invoked") @@ -166,10 +168,12 @@ class WordPressComRestApiTests: XCTestCase { XCTAssertTrue(request?.url?.query?.contains("arg=value") == true) } - func testInvalidTokenFailedCall() { + func testInvalidTokenFailedCall() throws { + let stubPath = try XCTUnwrap( + OHPathForFile("WordPressComRestApiFailRequestInvalidToken.json", type(of: self)) + ) stub(condition: isRestAPIRequest()) { _ in - let stubPath = OHPathForFile("WordPressComRestApiFailRequestInvalidToken.json", type(of: self)) - return fixture(filePath: stubPath!, status: 400, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + return fixture(filePath: stubPath, status: 400, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) } let expect = self.expectation(description: "One callback should be invoked") @@ -185,10 +189,12 @@ class WordPressComRestApiTests: XCTestCase { self.waitForExpectations(timeout: 2, handler: nil) } - func testInvalidJSONReceivedFailedCall() { + func testInvalidJSONReceivedFailedCall() throws { + let stubPath = try XCTUnwrap( + OHPathForFile("WordPressComRestApiFailInvalidJSON.json", type(of: self)) + ) stub(condition: isRestAPIRequest()) { _ in - let stubPath = OHPathForFile("WordPressComRestApiFailInvalidJSON.json", type(of: self)) - return fixture(filePath: stubPath!, status: 200, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + return fixture(filePath: stubPath, status: 200, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) } let expect = self.expectation(description: "One callback should be invoked") let api = WordPressComRestApi(oAuthToken: "fakeToken") @@ -203,10 +209,12 @@ class WordPressComRestApiTests: XCTestCase { self.waitForExpectations(timeout: 2, handler: nil) } - func testInvalidJSONSentFailedCall() { + func testInvalidJSONSentFailedCall() throws { + let stubPath = try XCTUnwrap( + OHPathForFile("WordPressComRestApiFailInvalidInput.json", type(of: self)) + ) stub(condition: isRestAPIMediaNewRequest()) { _ in - let stubPath = OHPathForFile("WordPressComRestApiFailInvalidInput.json", type(of: self)) - return fixture(filePath: stubPath!, status: 400, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + return fixture(filePath: stubPath, status: 400, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) } let expect = self.expectation(description: "One callback should be invoked") let api = WordPressComRestApi(oAuthToken: "fakeToken") @@ -221,10 +229,12 @@ class WordPressComRestApiTests: XCTestCase { self.waitForExpectations(timeout: 2, handler: nil) } - func testUnauthorizedFailedCall() { + func testUnauthorizedFailedCall() throws { + let stubPath = try XCTUnwrap( + OHPathForFile("WordPressComRestApiFailUnauthorized.json", type(of: self)) + ) stub(condition: isRestAPIMediaNewRequest()) { _ in - let stubPath = OHPathForFile("WordPressComRestApiFailUnauthorized.json", type(of: self)) - return fixture(filePath: stubPath!, status: 403, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + return fixture(filePath: stubPath, status: 403, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) } let expect = self.expectation(description: "One callback should be invoked") let api = WordPressComRestApi(oAuthToken: "fakeToken") @@ -239,10 +249,10 @@ class WordPressComRestApiTests: XCTestCase { self.waitForExpectations(timeout: 2, handler: nil) } - func testMultipleErrorsFailedCall() { + func testMultipleErrorsFailedCall() throws { + let stubPath = try XCTUnwrap(OHPathForFile("WordPressComRestApiMultipleErrors.json", type(of: self))) stub(condition: isRestAPIMediaNewRequest()) { _ in - let stubPath = OHPathForFile("WordPressComRestApiMultipleErrors.json", type(of: self)) - return fixture(filePath: stubPath!, status: 403, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + return fixture(filePath: stubPath, status: 403, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) } let expect = self.expectation(description: "One callback should be invoked") let api = WordPressComRestApi(oAuthToken: "fakeToken") @@ -257,10 +267,12 @@ class WordPressComRestApiTests: XCTestCase { self.waitForExpectations(timeout: 2, handler: nil) } - func testMultipleErrorsFailedMultiPartPostCall() { + func testMultipleErrorsFailedMultiPartPostCall() throws { + let stubPath = try XCTUnwrap( + OHPathForFile("WordPressComRestApiMultipleErrors.json", type(of: self)) + ) stub(condition: isRestAPIMediaNewRequest()) { _ in - let stubPath = OHPathForFile("WordPressComRestApiMultipleErrors.json", type(of: self)) - return fixture(filePath: stubPath!, status: 403, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + return fixture(filePath: stubPath, status: 403, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) } let expect = self.expectation(description: "One callback should be invoked") let api = WordPressComRestApi(oAuthToken: "fakeToken") @@ -275,10 +287,10 @@ class WordPressComRestApiTests: XCTestCase { self.waitForExpectations(timeout: 2, handler: nil) } - func testStreamMethodCallWithInvalidFile() { + func testStreamMethodCallWithInvalidFile() throws { + let stubPath = try XCTUnwrap(OHPathForFile("WordPressComRestApiMedia.json", type(of: self))) stub(condition: isRestAPIMediaNewRequest()) { _ in - let stubPath = OHPathForFile("WordPressComRestApiMedia.json", type(of: self)) - return fixture(filePath: stubPath!, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + return fixture(filePath: stubPath, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) } let expect = self.expectation(description: "One callback should be invoked") @@ -294,10 +306,10 @@ class WordPressComRestApiTests: XCTestCase { self.waitForExpectations(timeout: 2, handler: nil) } - func testStreamMethodParallelCalls() { + func testStreamMethodParallelCalls() throws { + let stubPath = try XCTUnwrap(OHPathForFile("WordPressComRestApiMedia.json", type(of: self))) stub(condition: isRestAPIMediaNewRequest()) { _ in - let stubPath = OHPathForFile("WordPressComRestApiMedia.json", type(of: self)) - return fixture(filePath: stubPath!, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + return fixture(filePath: stubPath, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) } guard let mediaPath = OHPathForFile("test-image.jpg", type(of: self)) @@ -344,10 +356,10 @@ class WordPressComRestApiTests: XCTestCase { self.waitForExpectations(timeout: 2, handler: nil) } - func testSuccessfullCallCommonGETStructure() { + func testSuccessfullCallCommonGETStructure() throws { + let stubPath = try XCTUnwrap(OHPathForFile("WordPressComRestApiMedia.json", type(of: self))) stub(condition: isRestAPIRequest()) { _ in - let stubPath = OHPathForFile("WordPressComRestApiMedia.json", type(of: self)) - return fixture(filePath: stubPath!, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + return fixture(filePath: stubPath, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) } let expect = self.expectation(description: "One callback should be invoked") @@ -363,10 +375,12 @@ class WordPressComRestApiTests: XCTestCase { self.waitForExpectations(timeout: 2, handler: nil) } - func testFailureCallCommonGETStructure() { + func testFailureCallCommonGETStructure() throws { + let stubPath = try XCTUnwrap( + OHPathForFile("WordPressComRestApiFailInvalidJSON.json", type(of: self)) + ) stub(condition: isRestAPIRequest()) { _ in - let stubPath = OHPathForFile("WordPressComRestApiFailInvalidJSON.json", type(of: self)) - return fixture(filePath: stubPath!, status: 200, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + return fixture(filePath: stubPath, status: 200, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) } let expect = self.expectation(description: "One callback should be invoked") @@ -431,10 +445,12 @@ class WordPressComRestApiTests: XCTestCase { wait(for: [complete], timeout: 0.3) } - func testTooManyRequestError() { + func testTooManyRequestError() throws { + let stubPath = try XCTUnwrap( + OHPathForFile("WordPressComRestApiFailThrottled.json", type(of: self)) + ) stub(condition: isAbsoluteURLString("https://public-api.wordpress.com/rest/v1/foo?locale=en")) { _ in - let stubPath = OHPathForFile("WordPressComRestApiFailThrottled.json", type(of: self)) - return fixture(filePath: stubPath!, status: 500, headers: ["Content-Type" as NSObject: "application/html" as AnyObject]) + return fixture(filePath: stubPath, status: 500, headers: ["Content-Type" as NSObject: "application/html" as AnyObject]) } let api = WordPressComRestApi() diff --git a/WordPressKitTests/WordPressComServiceRemoteRestTests.swift b/WordPressKitTests/WordPressComServiceRemoteRestTests.swift index 6c01f502..37d1f508 100644 --- a/WordPressKitTests/WordPressComServiceRemoteRestTests.swift +++ b/WordPressKitTests/WordPressComServiceRemoteRestTests.swift @@ -43,10 +43,16 @@ class WordPressComServiceRemoteRestTests: XCTestCase { } } - func testThrottledFailureCall() { + func testThrottledFailureCall() throws { + let stubPath = try XCTUnwrap( + OHPathForFile("WordPressComRestApiFailThrottled.json", type(of: self)) + ) stub(condition: isRestAPIUsersNewRequest()) { _ in - let stubPath = OHPathForFile("WordPressComRestApiFailThrottled.json", type(of: self)) - return fixture(filePath: stubPath!, status: 500, headers: ["Content-Type" as NSObject: "application/html" as AnyObject]) + return fixture( + filePath: stubPath, + status: 500, + headers: ["Content-Type" as NSObject: "application/html" as AnyObject] + ) } let expect = self.expectation(description: "One callback should be invoked") diff --git a/WordPressKitTests/WordPressOrgRestApiTests.swift b/WordPressKitTests/WordPressOrgRestApiTests.swift index a71aaa85..ec2ac0b9 100644 --- a/WordPressKitTests/WordPressOrgRestApiTests.swift +++ b/WordPressKitTests/WordPressOrgRestApiTests.swift @@ -23,9 +23,9 @@ class WordPressOrgRestApiTests: XCTestCase { } func testUnauthorizedCall() async throws { + let stubPath = try XCTUnwrap(OHPathForFile("wp-forbidden.json", type(of: self))) stub(condition: isAPIRequest()) { _ in - let stubPath = OHPathForFile("wp-forbidden.json", type(of: self)) - return fixture(filePath: stubPath!, status: 401, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + return fixture(filePath: stubPath, status: 401, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) } let api = WordPressOrgRestApi(apiBase: apiBase) let result = await api.get(path: "wp/v2/settings", type: AnyResponse.self) @@ -38,9 +38,9 @@ class WordPressOrgRestApiTests: XCTestCase { } func testSuccessfulGetCall() async throws { + let stubPath = try XCTUnwrap(OHPathForFile("wp-pages.json", type(of: self))) stub(condition: isAPIRequest()) { _ in - let stubPath = OHPathForFile("wp-pages.json", type(of: self)) - return fixture(filePath: stubPath!, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + return fixture(filePath: stubPath, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) } let api = WordPressOrgRestApi(apiBase: apiBase) let pages = try await api.get(path: "wp/v2/pages", type: [AnyResponse].self).get() @@ -48,9 +48,9 @@ class WordPressOrgRestApiTests: XCTestCase { } func testSuccessfulPostCall() async throws { + let stubPath = try XCTUnwrap(OHPathForFile("wp-reusable-blocks.json", type(of: self))) stub(condition: isAPIRequest()) { _ in - let stubPath = OHPathForFile("wp-reusable-blocks.json", type(of: self)) - return fixture(filePath: stubPath!, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + return fixture(filePath: stubPath, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) } struct Response: Decodable { diff --git a/WordPressKitTests/WordPressOrgXMLRPCApiTests.swift b/WordPressKitTests/WordPressOrgXMLRPCApiTests.swift index 8bab45d4..f536e893 100644 --- a/WordPressKitTests/WordPressOrgXMLRPCApiTests.swift +++ b/WordPressKitTests/WordPressOrgXMLRPCApiTests.swift @@ -24,10 +24,10 @@ class WordPressOrgXMLRPCApiTests: XCTestCase { } } - func testSuccessfullCall() { + func testSuccessfullCall() throws { + let stubPath = try XCTUnwrap(OHPathForFile("xmlrpc-response-getpost.xml", type(of: self))) stub(condition: isXmlRpcAPIRequest()) { _ in - let stubPath = OHPathForFile("xmlrpc-response-getpost.xml", type(of: self)) - return fixture(filePath: stubPath!, headers: self.xmlContentTypeHeaders) + return fixture(filePath: stubPath, headers: self.xmlContentTypeHeaders) } let expect = self.expectation(description: "One callback should be invoked") @@ -263,10 +263,10 @@ class WordPressOrgXMLRPCApiTests: XCTestCase { wait(for: [expect], timeout: 0.3) } - func testProgressUpdate() { + func testProgressUpdate() throws { + let stubPath = try XCTUnwrap(OHPathForFile("xmlrpc-response-getpost.xml", type(of: self))) stub(condition: isXmlRpcAPIRequest()) { _ in - let stubPath = OHPathForFile("xmlrpc-response-getpost.xml", type(of: self)) - return fixture(filePath: stubPath!, headers: self.xmlContentTypeHeaders) + return fixture(filePath: stubPath, headers: self.xmlContentTypeHeaders) } let success = self.expectation(description: "The success callback should be invoked") @@ -291,10 +291,10 @@ class WordPressOrgXMLRPCApiTests: XCTestCase { XCTAssertEqual(progress?.fractionCompleted, 1) } - func testProgressUpdateFailure() { + func testProgressUpdateFailure() throws { + let stubPath = try XCTUnwrap(OHPathForFile("xmlrpc-bad-username-password-error.xml", type(of: self))) stub(condition: isXmlRpcAPIRequest()) { _ in - let stubPath = OHPathForFile("xmlrpc-bad-username-password-error.xml", type(of: self)) - return fixture(filePath: stubPath!, headers: self.xmlContentTypeHeaders) + return fixture(filePath: stubPath, headers: self.xmlContentTypeHeaders) } let failure = self.expectation(description: "The failure callback should be invoked") @@ -319,10 +319,10 @@ class WordPressOrgXMLRPCApiTests: XCTestCase { XCTAssertEqual(progress?.fractionCompleted, 1) } - func testProgressUpdateStreamAPI() { + func testProgressUpdateStreamAPI() throws { + let stubPath = try XCTUnwrap(OHPathForFile("xmlrpc-response-getpost.xml", type(of: self))) stub(condition: isXmlRpcAPIRequest()) { _ in - let stubPath = OHPathForFile("xmlrpc-response-getpost.xml", type(of: self)) - return fixture(filePath: stubPath!, headers: self.xmlContentTypeHeaders) + return fixture(filePath: stubPath, headers: self.xmlContentTypeHeaders) } let success = self.expectation(description: "The success callback should be invoked") @@ -347,10 +347,10 @@ class WordPressOrgXMLRPCApiTests: XCTestCase { XCTAssertEqual(progress?.fractionCompleted, 1) } - func testProgressUpdateStreamAPIFailure() { + func testProgressUpdateStreamAPIFailure() throws { + let stubPath = try XCTUnwrap(OHPathForFile("xmlrpc-bad-username-password-error.xml", type(of: self))) stub(condition: isXmlRpcAPIRequest()) { _ in - let stubPath = OHPathForFile("xmlrpc-bad-username-password-error.xml", type(of: self)) - return fixture(filePath: stubPath!, headers: self.xmlContentTypeHeaders) + return fixture(filePath: stubPath, headers: self.xmlContentTypeHeaders) } let failure = self.expectation(description: "The failure callback should be invoked") From 007e11a0602d240213591e31342ff10b501bd215 Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Tue, 19 Mar 2024 16:47:18 +1100 Subject: [PATCH 2/2] Use alternative approach to `XCTUnwrap` for `stubRemoteResponse` --- WordPressKitTests/RemoteTestCase.swift | 33 ++++++++++++++++++-------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/WordPressKitTests/RemoteTestCase.swift b/WordPressKitTests/RemoteTestCase.swift index b6db19ed..a55165b7 100644 --- a/WordPressKitTests/RemoteTestCase.swift +++ b/WordPressKitTests/RemoteTestCase.swift @@ -54,16 +54,20 @@ extension RemoteTestCase { contentType: ResponseContentType, status: Int32 = 200 ) { - // This doesn't follow the XCTUnwrap pattern (yet?) because the method is used many times and it was too time consuming to update every call site at the time - let stubPath = OHPathForFile(filename, type(of: self)) + // There are hundreds of usages of the various `stubRemoteResponse` overloads. + // The pattern here should be to XCTUnwrap and throw. + // In the interest of moving along with the work, let's fail the tests at this level if the file is not found. + guard let stubPath = OHPathForFile(filename, type(of: self)) else { + return XCTFail("Could not find file at path '\(filename)'.") + } + stub(condition: condition) { _ in var headers: [NSObject: AnyObject]? if contentType != .NoContentType { headers = ["Content-Type" as NSObject: contentType.rawValue as AnyObject] } - // This is force-unwrapped at call site, despite it making more sense at declaration site, so it can be found when grepping for force unwraps. - return OHHTTPStubs.fixture(filePath: stubPath!, status: status, headers: headers) + return OHHTTPStubs.fixture(filePath: stubPath, status: status, headers: headers) } } @@ -76,8 +80,13 @@ extension RemoteTestCase { /// - status: The status code to use for the response. Defaults to 200. /// func stubRemoteResponse(_ endpoint: String, filename: String, contentType: ResponseContentType, status: Int32 = 200) { - // This doesn't follow the XCTUnwrap pattern (yet?) because the method is used many times and it was too time consuming to update every call site at the time - let stubPath = OHPathForFile(filename, type(of: self)) + // There are hundreds of usages of the various `stubRemoteResponse` overloads. + // The pattern here should be to XCTUnwrap and throw. + // In the interest of moving along with the work, let's fail the tests at this level if the file is not found. + guard let stubPath = OHPathForFile(filename, type(of: self)) else { + return XCTFail("Could not find file at path '\(filename)'.") + } + stub(condition: { request in return request.url?.absoluteString.range(of: endpoint) != nil }) { _ in @@ -86,8 +95,7 @@ extension RemoteTestCase { if contentType != .NoContentType { headers = ["Content-Type" as NSObject: contentType.rawValue as AnyObject] } - // This is force-unwrapped at call site, despite it making more sense at declaration site, so it can be found when grepping for force unwraps. - return fixture(filePath: stubPath!, status: status, headers: headers) + return fixture(filePath: stubPath, status: status, headers: headers) } } @@ -138,7 +146,12 @@ extension RemoteTestCase { return HTTPStubsResponse(error: notConnectedError) } - let stubPath = OHPathForFile(files[callCounter], type(of: self)) + guard let stubPath = OHPathForFile(files[callCounter], type(of: self)) else { + XCTFail("Could not find file at path '\(files[callCounter])'.") + let error = NSError(domain: "RemoteTestCase", code: 0, userInfo: nil) + return HTTPStubsResponse(error: error) + } + callCounter += 1 var headers: [NSObject: AnyObject]? @@ -146,7 +159,7 @@ extension RemoteTestCase { headers = ["Content-Type" as NSObject: contentType.rawValue as AnyObject] } - return fixture(filePath: stubPath!, status: status, headers: headers) + return fixture(filePath: stubPath, status: status, headers: headers) } }