diff --git a/src/xcode/ENA/ENA.xcodeproj/project.pbxproj b/src/xcode/ENA/ENA.xcodeproj/project.pbxproj index c452a326b24..1981e03220a 100644 --- a/src/xcode/ENA/ENA.xcodeproj/project.pbxproj +++ b/src/xcode/ENA/ENA.xcodeproj/project.pbxproj @@ -1028,6 +1028,8 @@ 8F8B4A332934CD18007ED94E /* DMSRSOptionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F8B4A322934CD18007ED94E /* DMSRSOptionsViewController.swift */; }; 8F8B4A352934CD30007ED94E /* DMSRSOptionsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F8B4A342934CD30007ED94E /* DMSRSOptionsViewModel.swift */; }; 8F95A1B225EFC5BE00506CF2 /* SendErrorLogsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F95A1B125EFC5BE00506CF2 /* SendErrorLogsViewController.swift */; }; + 8FA1E1032948463D00C08D82 /* SRSKeySubmissionResourceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FA1E1022948463D00C08D82 /* SRSKeySubmissionResourceTests.swift */; }; + 8FA1E10729484EC600C08D82 /* OTPAuthorizationForSRSResourceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FA1E10429484DE800C08D82 /* OTPAuthorizationForSRSResourceTests.swift */; }; 8FA3E9C62913F334000512FC /* SRSConsentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FA3E9C52913F334000512FC /* SRSConsentViewController.swift */; }; 8FA3E9C82913F34A000512FC /* SRSConsentViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FA3E9C72913F34A000512FC /* SRSConsentViewModel.swift */; }; 8FA88F672613325700FD36DA /* QRCodePayload+extention.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FA88F662613325700FD36DA /* QRCodePayload+extention.swift */; }; @@ -2943,6 +2945,8 @@ 8F8B4A342934CD30007ED94E /* DMSRSOptionsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DMSRSOptionsViewModel.swift; sourceTree = ""; }; 8F8B4A362934DA10007ED94E /* ENATest.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ENATest.entitlements; sourceTree = ""; }; 8F95A1B125EFC5BE00506CF2 /* SendErrorLogsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendErrorLogsViewController.swift; sourceTree = ""; }; + 8FA1E1022948463D00C08D82 /* SRSKeySubmissionResourceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRSKeySubmissionResourceTests.swift; sourceTree = ""; }; + 8FA1E10429484DE800C08D82 /* OTPAuthorizationForSRSResourceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OTPAuthorizationForSRSResourceTests.swift; sourceTree = ""; }; 8FA3E9C52913F334000512FC /* SRSConsentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRSConsentViewController.swift; sourceTree = ""; }; 8FA3E9C72913F34A000512FC /* SRSConsentViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRSConsentViewModel.swift; sourceTree = ""; }; 8FA88F662613325700FD36DA /* QRCodePayload+extention.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "QRCodePayload+extention.swift"; sourceTree = ""; }; @@ -7390,6 +7394,7 @@ 8F2FA2E3292B2D5C00C6286A /* OTPAuthorizationForSRS */ = { isa = PBXGroup; children = ( + 8FA1E10629484E1500C08D82 /* __tests__ */, 8F2FA2E4292B2D8400C6286A /* OTPAuthorizationForSRSResource.swift */, DC29CBA929376D6800BEAA79 /* OTPForSRSResponsePropertiesReceiveModel.swift */, ); @@ -7576,6 +7581,14 @@ path = DMSRSPrechecks; sourceTree = ""; }; + 8FA1E10629484E1500C08D82 /* __tests__ */ = { + isa = PBXGroup; + children = ( + 8FA1E10429484DE800C08D82 /* OTPAuthorizationForSRSResourceTests.swift */, + ); + path = __tests__; + sourceTree = ""; + }; 8FA3E9C42913F2E8000512FC /* SRSConsent */ = { isa = PBXGroup; children = ( @@ -8705,6 +8718,7 @@ isa = PBXGroup; children = ( ABFDBEFC281AD7C000A11FA8 /* KeySubmissionResourceTests.swift */, + 8FA1E1022948463D00C08D82 /* SRSKeySubmissionResourceTests.swift */, ); path = __tests__; sourceTree = ""; @@ -12699,6 +12713,7 @@ AB34787C269C93C20016A8AB /* CertLogicEngineTestData.swift in Sources */, 010A57A427185E4D001F5A09 /* RecycleBinItemCellModelTests.swift in Sources */, 50681F8B27E3C83C008056E3 /* RetryingTests.swift in Sources */, + 8FA1E10729484EC600C08D82 /* OTPAuthorizationForSRSResourceTests.swift in Sources */, BA015159279B11A900C93B02 /* RestServiceProviderTests.swift in Sources */, 506D881427A2D40D006297FE /* RegistrationTokenResourceTests.swift in Sources */, 35BDAB2E25F1148E004DFE32 /* ELSServiceTests.swift in Sources */, @@ -12918,6 +12933,7 @@ BADDD83F2603956600337223 /* TraceLocationCellModelTests.swift in Sources */, 01D6816327C3D18200FF6D18 /* MockCoronaTestService.swift in Sources */, 71176E2F248922B0004B0C9F /* ENAColorTests.swift in Sources */, + 8FA1E1032948463D00C08D82 /* SRSKeySubmissionResourceTests.swift in Sources */, 35C0C88B2614946F004BF509 /* CheckinSubmissionPreparationTests.swift in Sources */, 014086B82588F9FD00E9E5B2 /* DiaryEditEntriesViewModelTest.swift in Sources */, 01BA06D3261F41E100237DD8 /* ExposureSubmissionServiceTests.swift in Sources */, diff --git a/src/xcode/ENA/ENA/Source/HTTPClient/Resources/KeySubmission/__tests__/SRSKeySubmissionResourceTests.swift b/src/xcode/ENA/ENA/Source/HTTPClient/Resources/KeySubmission/__tests__/SRSKeySubmissionResourceTests.swift new file mode 100644 index 00000000000..dfb9e713f3f --- /dev/null +++ b/src/xcode/ENA/ENA/Source/HTTPClient/Resources/KeySubmission/__tests__/SRSKeySubmissionResourceTests.swift @@ -0,0 +1,218 @@ +// +// 🦠 Corona-Warn-App +// + +import Foundation +import XCTest +@testable import ENA + +class SRSKeySubmissionResourceTests: XCTestCase { + + let mockUrl = URL(staticString: "http://example.com") + let tan = "1234" + + private var keys: [SAP_External_Exposurenotification_TemporaryExposureKey] { + var key = SAP_External_Exposurenotification_TemporaryExposureKey() + key.keyData = Data(bytes: [1, 2, 3], count: 3) + key.rollingPeriod = 1337 + key.rollingStartIntervalNumber = 42 + key.transmissionRiskLevel = 8 + + return [key] + } + + func testSubmit_Success() { + let stack = MockNetworkStack( + httpStatus: 200, + // cannot be nil since this is not a a completion handler can be in (response + nil body) + responseData: Data() + ) + + let expectation = self.expectation(description: "completion handler is called without an error") + + // Act + let payload = SubmissionPayload( + exposureKeys: keys, + visitedCountries: [], + checkins: [], + checkinProtectedReports: [], + tan: nil, + submissionType: .srsSelfTest + ) + + let restServiceProvider = RestServiceProvider(session: stack.urlSession, cache: KeyValueCacheFake()) + + let resource = SRSKeySubmissionResource(payload: payload, srsOtp: "Test") + + restServiceProvider.load(resource) { result in + switch result { + case .success: + break + case let .failure(error): + XCTFail("Test should not fail with error: \(error)") + } + expectation.fulfill() + } + + // THEN + waitForExpectations(timeout: .short) + } + + func testSubmit_Request_SubmissionType() throws { + let payload = SubmissionPayload(exposureKeys: keys, visitedCountries: [], checkins: [], checkinProtectedReports: [], tan: nil, submissionType: .srsSelfTest) + + let expectation = self.expectation(description: "completion handler is called without an error") + + let stack = MockNetworkStack( + httpStatus: 200, + // cannot be nil since this is not a a completion handler can be in (response + nil body) + responseData: Data(), + requestObserver: { request in + print(request) + + guard let protoPayload = try? SAP_Internal_SubmissionPayload(serializedData: request.httpBody ?? Data()) else { + XCTFail("Request data expected to be serializable to protobuf.") + return + } + + XCTAssertEqual(protoPayload.submissionType, payload.submissionType) + + expectation.fulfill() + } + ) + + let restServiceProvider = RestServiceProvider(session: stack.urlSession, cache: KeyValueCacheFake()) + + let resource = SRSKeySubmissionResource(payload: payload, srsOtp: "Test") + + restServiceProvider.load(resource) { _ in } + + // THEN + waitForExpectations(timeout: .short) + } + + func testSubmit_SpecificError() { + let stack = MockNetworkStack( + mockSession: MockUrlSession( + data: nil, + nextResponse: nil, + error: TestError.error + ) + ) + let expectation = self.expectation(description: "SpecificError") + + let payload = SubmissionPayload(exposureKeys: keys, visitedCountries: [], checkins: [], checkinProtectedReports: [], tan: nil, submissionType: .srsSelfTest) + + let restServiceProvider = RestServiceProvider(session: stack.urlSession, cache: KeyValueCacheFake()) + + let resource = SRSKeySubmissionResource(payload: payload, srsOtp: "Test") + + restServiceProvider.load(resource) { result in + switch result { + case .success: + XCTFail("expected an error") + case let .failure(error): + switch error { + case ServiceError.transportationError(let underLyingError): + XCTAssertNotNil(underLyingError) + default: + XCTFail("We expect error to be of type other") + } + } + expectation.fulfill() + } + + waitForExpectations(timeout: .short) + } + + func testSubmit_ResponseNil() { + let mockURLSession = MockUrlSession(data: nil, nextResponse: nil, error: nil) + let stack = MockNetworkStack( + mockSession: mockURLSession + ) + let expectation = self.expectation(description: "ResponseNil") + + let payload = SubmissionPayload(exposureKeys: keys, visitedCountries: [], checkins: [], checkinProtectedReports: [], tan: nil, submissionType: .srsSelfTest) + + let restServiceProvider = RestServiceProvider(session: stack.urlSession, cache: KeyValueCacheFake()) + + let resource = SRSKeySubmissionResource(payload: payload, srsOtp: "Test") + + restServiceProvider.load(resource) { result in + switch result { + case .success: + XCTFail("expected an error") + case let .failure(error): + switch error { + case .invalidResponseType: + break + default: + XCTFail("We expect error to be of type other") + } + } + expectation.fulfill() + } + + waitForExpectations(timeout: .short) + } + + func testSubmit_Response400() { + let stack = MockNetworkStack( + httpStatus: 400, + responseData: Data() + ) + + let expectation = self.expectation(description: "Response400") + + let payload = SubmissionPayload(exposureKeys: keys, visitedCountries: [], checkins: [], checkinProtectedReports: [], tan: nil, submissionType: .srsSelfTest) + + let restServiceProvider = RestServiceProvider(session: stack.urlSession, cache: KeyValueCacheFake()) + + let resource = SRSKeySubmissionResource(payload: payload, srsOtp: "Test") + + restServiceProvider.load(resource) { result in + switch result { + case .success: + XCTFail("error expected") + case let .failure(error): + guard case ServiceError.receivedResourceError(.invalidPayloadOrHeader) = error else { + XCTFail("We expect error to be of type invalidPayloadOrHeaders") + return + } + } + expectation.fulfill() + } + + waitForExpectations(timeout: .short) + } + + func testSubmit_Response403() { + let stack = MockNetworkStack( + httpStatus: 403, + responseData: Data() + ) + + let expectation = self.expectation(description: "Response403") + + let payload = SubmissionPayload(exposureKeys: keys, visitedCountries: [], checkins: [], checkinProtectedReports: [], tan: nil, submissionType: .srsSelfTest) + + let restServiceProvider = RestServiceProvider(session: stack.urlSession, cache: KeyValueCacheFake()) + + let resource = SRSKeySubmissionResource(payload: payload, srsOtp: "Test") + + restServiceProvider.load(resource) { result in + switch result { + case .success: + XCTFail("error expected") + case let .failure(error): + guard case ServiceError.receivedResourceError(.invalidOtp) = error else { + XCTFail("We expect error to be of type invalidPayloadOrHeaders") + return + } + } + expectation.fulfill() + } + + waitForExpectations(timeout: .short) + } +} diff --git a/src/xcode/ENA/ENA/Source/HTTPClient/Resources/OTPAuthorizationForSRS/OTPAuthorizationForSRSResource.swift b/src/xcode/ENA/ENA/Source/HTTPClient/Resources/OTPAuthorizationForSRS/OTPAuthorizationForSRSResource.swift index 0ecc7ac2b4b..a238b6364fd 100644 --- a/src/xcode/ENA/ENA/Source/HTTPClient/Resources/OTPAuthorizationForSRS/OTPAuthorizationForSRSResource.swift +++ b/src/xcode/ENA/ENA/Source/HTTPClient/Resources/OTPAuthorizationForSRS/OTPAuthorizationForSRSResource.swift @@ -76,7 +76,7 @@ struct OTPAuthorizationForSRSResource: Resource { return .otherServerError } default: - return .otherServerError + return .invalidResponseError } } @@ -91,9 +91,8 @@ struct OTPAuthorizationForSRSResource: Resource { do { let decoder = JSONDecoder() - decoder.dateDecodingStrategy = .iso8601 let decodedResponse = try decoder.decode( - OTPResponseProperties.self, + OTPForSRSResponsePropertiesReceiveModel.self, from: responseBody ) guard let errorCode = decodedResponse.errorCode else { @@ -121,7 +120,7 @@ struct OTPAuthorizationForSRSResource: Resource { } } catch { Log.error("Failed to get errorCode because json could not be decoded", log: .api, error: error) - return .otherServerError + return .invalidResponseError } } } diff --git a/src/xcode/ENA/ENA/Source/HTTPClient/Resources/OTPAuthorizationForSRS/__tests__/OTPAuthorizationForSRSResourceTests.swift b/src/xcode/ENA/ENA/Source/HTTPClient/Resources/OTPAuthorizationForSRS/__tests__/OTPAuthorizationForSRSResourceTests.swift new file mode 100644 index 00000000000..cf8f1452427 --- /dev/null +++ b/src/xcode/ENA/ENA/Source/HTTPClient/Resources/OTPAuthorizationForSRS/__tests__/OTPAuthorizationForSRSResourceTests.swift @@ -0,0 +1,433 @@ +// +// 🦠 Corona-Warn-App +// + +import Foundation +import XCTest +@testable import ENA + +final class OTPAuthorizationForSRSResourceTests: XCTestCase { + let expectationsTimeout: TimeInterval = 2 + + func testGIVEN_AuthorizeOTP_WHEN_SuccesWithAuthorization_THEN_ExpirationDateIsReturned() throws { + // GIVEN + let dateString = "2021-02-16T08:34:00+00:00" + let dateFormatter = ISO8601DateFormatter() + + let response: [String: String] = ["expirationDate": dateString] + + let jsonEncoder = JSONEncoder() + jsonEncoder.dateEncodingStrategy = .iso8601 + let encoded = try jsonEncoder.encode(response) + let stack = MockNetworkStack( + httpStatus: 200, + responseData: encoded + ) + + let expectation = self.expectation(description: "completion handler is called without an error") + let otp = "OTPFake" + let ppacToken = PPACToken(apiToken: "APITokenFake", deviceToken: "DeviceTokenFake") + + // WHEN + var expirationDate: String? + let serviceProvider = RestServiceProvider(session: stack.urlSession, cache: KeyValueCacheFake()) + let resource = OTPAuthorizationForSRSResource(otpSRS: otp, ppacToken: ppacToken) + serviceProvider.load(resource) { result in + switch result { + case .success(let result): + expirationDate = result.expirationDate + case .failure(let error): + XCTFail(error.localizedDescription) + } + expectation.fulfill() + } + // THEN + waitForExpectations(timeout: expectationsTimeout) + XCTAssertNotNil(expirationDate) + XCTAssertEqual(expirationDate, dateString) + } + + func testGIVEN_AuthorizeOTP_WHEN_Success_JsonParsing_THEN_invalidResponseErrorIsReturned() { + // GIVEN + let stack = MockNetworkStack( + httpStatus: 200, + responseData: Data() + ) + + let otp = "OTPFake" + let ppacToken = PPACToken(apiToken: "APITokenFake", deviceToken: "DeviceTokenFake") + + // WHEN + let serviceProvider = RestServiceProvider(session: stack.urlSession, cache: KeyValueCacheFake()) + let resource = OTPAuthorizationForSRSResource(otpSRS: otp, ppacToken: ppacToken) + serviceProvider.load(resource) { result in + switch result { + // THEN + case .success: + XCTFail("success should not be called") + case .failure(let otpError): + guard case let .receivedResourceError(customError) = otpError else { + XCTFail("unexpected error case") + return + } + XCTAssertEqual(customError, .invalidResponseError) + } + } + } + + func testGIVEN_AuthorizeOTP_WHEN_Failure_API_TOKEN_ALREADY_ISSUEDIsCalled_THEN_apiTokenAlreadyIssuedIsReturned() throws { + // GIVEN + let response: [String: String] = ["errorCode": "API_TOKEN_ALREADY_ISSUED"] + let stack = MockNetworkStack( + httpStatus: 400, + responseData: try JSONEncoder().encode(response) + ) + + let otp = "OTPFake" + let ppacToken = PPACToken(apiToken: "APITokenFake", deviceToken: "DeviceTokenFake") + + // WHEN + let serviceProvider = RestServiceProvider(session: stack.urlSession, cache: KeyValueCacheFake()) + let resource = OTPAuthorizationForSRSResource(otpSRS: otp, ppacToken: ppacToken) + serviceProvider.load(resource) { result in + switch result { + // THEN + case .success: + XCTFail("success should not be called") + case .failure(let otpError): + guard case let .receivedResourceError(customError) = otpError else { + XCTFail("unexpected error case") + return + } + XCTAssertEqual(customError, .apiTokenAlreadyIssued) + } + } + } + + func testGIVEN_AuthorizeOTP_WHEN_Failure_API_TOKEN_EXPIREDIsCalled_THEN_apiTokenExpiredIsReturned() throws { + // GIVEN + let response: [String: String] = ["errorCode": "API_TOKEN_EXPIRED"] + let stack = MockNetworkStack( + httpStatus: 401, + responseData: try JSONEncoder().encode(response) + ) + + let otp = "OTPFake" + let ppacToken = PPACToken(apiToken: "APITokenFake", deviceToken: "DeviceTokenFake") + + // WHEN + let serviceProvider = RestServiceProvider(session: stack.urlSession, cache: KeyValueCacheFake()) + let resource = OTPAuthorizationForSRSResource(otpSRS: otp, ppacToken: ppacToken) + serviceProvider.load(resource) { result in + switch result { + // THEN + case .success: + XCTFail("success should not be called") + case .failure(let otpError): + guard case let .receivedResourceError(customError) = otpError else { + XCTFail("unexpected error case") + return + } + XCTAssertEqual(customError, .apiTokenExpired) + } + } + } + + func testGIVEN_AuthorizeOTP_WHEN_Failure_API_TOKEN_QUOTA_EXCEEDEDIsCalled_THEN_apiTokenQuotaExceededIsReturned() throws { + // GIVEN + let response: [String: String] = ["errorCode": "API_TOKEN_QUOTA_EXCEEDED"] + let stack = MockNetworkStack( + httpStatus: 403, + responseData: try JSONEncoder().encode(response) + ) + + let otp = "OTPFake" + let ppacToken = PPACToken(apiToken: "APITokenFake", deviceToken: "DeviceTokenFake") + + // WHEN + let serviceProvider = RestServiceProvider(session: stack.urlSession, cache: KeyValueCacheFake()) + let resource = OTPAuthorizationForSRSResource(otpSRS: otp, ppacToken: ppacToken) + serviceProvider.load(resource) { result in + switch result { + // THEN + case .success: + XCTFail("success should not be called") + case .failure(let otpError): + guard case let .receivedResourceError(customError) = otpError else { + XCTFail("unexpected error case") + return + } + XCTAssertEqual(customError, .apiTokenQuotaExceeded) + } + } + } + + func testGIVEN_AuthorizeOTP_WHEN_Failure_DEVICE_TOKEN_INVALIDIsCalled_THEN_deviceTokenInvalidIsReturned() throws { + // GIVEN + let response: [String: String] = ["errorCode": "DEVICE_TOKEN_INVALID"] + let stack = MockNetworkStack( + httpStatus: 400, + responseData: try JSONEncoder().encode(response) + ) + + let otp = "OTPFake" + let ppacToken = PPACToken(apiToken: "APITokenFake", deviceToken: "DeviceTokenFake") + + // WHEN + let serviceProvider = RestServiceProvider(session: stack.urlSession, cache: KeyValueCacheFake()) + let resource = OTPAuthorizationForSRSResource(otpSRS: otp, ppacToken: ppacToken) + serviceProvider.load(resource) { result in + switch result { + // THEN + case .success: + XCTFail("success should not be called") + case .failure(let otpError): + guard case let .receivedResourceError(customError) = otpError else { + XCTFail("unexpected error case") + return + } + XCTAssertEqual(customError, .deviceTokenInvalid) + } + } + } + func testGIVEN_AuthorizeOTP_WHEN_Failure_DEVICE_BLOCKEDIsCalled_THEN_deviceIsBlockedReturned() throws { + // GIVEN + let response: [String: String] = ["errorCode": "DEVICE_BLOCKED"] + let stack = MockNetworkStack( + httpStatus: 400, + responseData: try JSONEncoder().encode(response) + ) + + let otp = "OTPFake" + let ppacToken = PPACToken(apiToken: "APITokenFake", deviceToken: "DeviceTokenFake") + + // WHEN + let serviceProvider = RestServiceProvider(session: stack.urlSession, cache: KeyValueCacheFake()) + let resource = OTPAuthorizationForSRSResource(otpSRS: otp, ppacToken: ppacToken) + serviceProvider.load(resource) { result in + switch result { + // THEN + case .success: + XCTFail("success should not be called") + case .failure(let otpError): + guard case let .receivedResourceError(customError) = otpError else { + XCTFail("unexpected error case") + return + } + XCTAssertEqual(customError, .deviceBlocked) + } + } + } + + func testGIVEN_AuthorizeOTP_WHEN_Failure_DEVICE_TOKEN_REDEEMEDIsCalled_THEN_deviceTokenRedeemedIsReturned() throws { + // GIVEN + let response: [String: String] = ["errorCode": "DEVICE_TOKEN_REDEEMED"] + let stack = MockNetworkStack( + httpStatus: 401, + responseData: try JSONEncoder().encode(response) + ) + + let otp = "OTPFake" + let ppacToken = PPACToken(apiToken: "APITokenFake", deviceToken: "DeviceTokenFake") + + // WHEN + let serviceProvider = RestServiceProvider(session: stack.urlSession, cache: KeyValueCacheFake()) + let resource = OTPAuthorizationForSRSResource(otpSRS: otp, ppacToken: ppacToken) + serviceProvider.load(resource) { result in + switch result { + // THEN + case .success: + XCTFail("success should not be called") + case .failure(let otpError): + guard case let .receivedResourceError(customError) = otpError else { + XCTFail("unexpected error case") + return + } + XCTAssertEqual(customError, .deviceTokenRedeemed) + } + } + } + + func testGIVEN_AuthorizeOTP_WHEN_Failure_DEVICE_TOKEN_SYNTAX_ERRORIsCalled_THEN_deviceTokenSyntaxErrorIsReturned() throws { + // GIVEN + let response: [String: String] = ["errorCode": "DEVICE_TOKEN_SYNTAX_ERROR"] + let stack = MockNetworkStack( + httpStatus: 403, + responseData: try JSONEncoder().encode(response) + ) + + let otp = "OTPFake" + let ppacToken = PPACToken(apiToken: "APITokenFake", deviceToken: "DeviceTokenFake") + + // WHEN + let serviceProvider = RestServiceProvider(session: stack.urlSession, cache: KeyValueCacheFake()) + let resource = OTPAuthorizationForSRSResource(otpSRS: otp, ppacToken: ppacToken) + serviceProvider.load(resource) { result in + switch result { + // THEN + case .success: + XCTFail("success should not be called") + case .failure(let otpError): + guard case let .receivedResourceError(customError) = otpError else { + XCTFail("unexpected error case") + return + } + XCTAssertEqual(customError, .deviceTokenSyntaxError) + } + } + } + + func testGIVEN_AuthorizeOTP_WHEN_Failure_OtherServerErrorIsCalled_THEN_otherServerErrorIsReturned() throws { + // GIVEN + let response: [String: String] = ["errorCode": "JWS_SIGNATURE_VERIFICATION_FAILED"] + let stack = MockNetworkStack( + httpStatus: 403, + responseData: try JSONEncoder().encode(response) + ) + + let otp = "OTPFake" + let ppacToken = PPACToken(apiToken: "APITokenFake", deviceToken: "DeviceTokenFake") + + // WHEN + let serviceProvider = RestServiceProvider(session: stack.urlSession, cache: KeyValueCacheFake()) + let resource = OTPAuthorizationForSRSResource(otpSRS: otp, ppacToken: ppacToken) + serviceProvider.load(resource) { result in + switch result { + // THEN + case .success: + XCTFail("success should not be called") + case .failure(let otpError): + guard case let .receivedResourceError(customError) = otpError else { + XCTFail("unexpected error case") + return + } + XCTAssertEqual(customError, .otherServerError) + } + } + } + + func testGIVEN_AuthorizeOTP_WHEN_Failure_500StatusCode_THEN_otherServerErrorErrorIsReturned() throws { + // GIVEN + let response: [String: String] = ["errorCode": "JWS_SIGNATURE_VERIFICATION_FAILED"] + let stack = MockNetworkStack( + httpStatus: 500, + responseData: try JSONEncoder().encode(response) + ) + + let otp = "OTPFake" + let ppacToken = PPACToken(apiToken: "APITokenFake", deviceToken: "DeviceTokenFake") + + // WHEN + let serviceProvider = RestServiceProvider(session: stack.urlSession, cache: KeyValueCacheFake()) + let resource = OTPAuthorizationForSRSResource(otpSRS: otp, ppacToken: ppacToken) + serviceProvider.load(resource) { result in + switch result { + // THEN + case .success: + XCTFail("success should not be called") + case .failure(let otpError): + guard case let .receivedResourceError(customError) = otpError else { + XCTFail("unexpected error case") + return + } + XCTAssertEqual(customError, .otherServerError) + } + } + } + + func testGIVEN_AuthorizeOTP_WHEN_Failure_UnkownStatusCode_THEN_otherServerErrorIsReturned() throws { + // GIVEN + let response: [String: String] = ["errorCode": "JWS_SIGNATURE_VERIFICATION_FAILED"] + let stack = MockNetworkStack( + httpStatus: 91, + responseData: try JSONEncoder().encode(response) + ) + + let otp = "OTPFake" + let ppacToken = PPACToken(apiToken: "APITokenFake", deviceToken: "DeviceTokenFake") + + // WHEN + let serviceProvider = RestServiceProvider(session: stack.urlSession, cache: KeyValueCacheFake()) + let resource = OTPAuthorizationForSRSResource(otpSRS: otp, ppacToken: ppacToken) + serviceProvider.load(resource) { result in + switch result { + // THEN + case .success: + XCTFail("success should not be called") + case .failure(let otpError): + guard case let .receivedResourceError(customError) = otpError else { + XCTFail("unexpected error case") + return + } + XCTAssertEqual(customError, .otherServerError) + } + } + } + + func testGIVEN_AuthorizeOTP_WHEN_Failure_JsonParsing_THEN_invalidResponseErrorIsReturned() { + // GIVEN + let stack = MockNetworkStack( + httpStatus: 400, + responseData: Data() + ) + + let otp = "OTPFake" + let ppacToken = PPACToken(apiToken: "APITokenFake", deviceToken: "DeviceTokenFake") + + // WHEN + let serviceProvider = RestServiceProvider(session: stack.urlSession, cache: KeyValueCacheFake()) + let resource = OTPAuthorizationForSRSResource(otpSRS: otp, ppacToken: ppacToken) + serviceProvider.load(resource) { result in + switch result { + // THEN + case .success: + XCTFail("success should not be called") + case .failure(let otpError): + guard case let .receivedResourceError(customError) = otpError else { + XCTFail("unexpected error case") + return + } + XCTAssertEqual(customError, .invalidResponseError) + } + } + } + + func testGIVEN_AuthorizeOTP_WHEN_NoNetworkConnection_THEN_NetworkErrorReturned() { + // GIVEN + let notConnectedError = NSError( + domain: NSURLErrorDomain, + code: NSURLErrorNotConnectedToInternet, + userInfo: nil + ) + + let session = MockUrlSession( + data: nil, + nextResponse: nil, + error: notConnectedError + ) + + let stack = MockNetworkStack( + mockSession: session + ) + + let otp = "OTPFake" + let ppacToken = PPACToken(apiToken: "APITokenFake", deviceToken: "DeviceTokenFake") + + // WHEN + let serviceProvider = RestServiceProvider(session: stack.urlSession, cache: KeyValueCacheFake()) + let resource = OTPAuthorizationForSRSResource(otpSRS: otp, ppacToken: ppacToken) + serviceProvider.load(resource) { result in + switch result { + case .success: + XCTFail("success should not be called") + case .failure(let otpError): + guard case let .receivedResourceError(customError) = otpError else { + XCTFail("unexpected error case") + return + } + XCTAssertEqual(customError, .noNetworkConnection) + } + } + } + +}