summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJon Petersson <jon.petersson@kvadrat.se>2023-11-16 11:55:14 +0100
committerJon Petersson <jon.petersson@kvadrat.se>2023-11-20 09:58:30 +0100
commitdc746f1352d62c8be188d0c82ebcf2740add663c (patch)
tree86470b537fb032376e95dbf374a5638373a52856
parent20a9bfb30fd7e8afc881405caab3e1ee737cafd1 (diff)
downloadmullvadvpn-dc746f1352d62c8be188d0c82ebcf2740add663c.tar.xz
mullvadvpn-dc746f1352d62c8be188d0c82ebcf2740add663c.zip
Refactor URLSession out of outgoing connection proxy tests
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj16
-rw-r--r--ios/MullvadVPN/GeneralAPIs/OutgoingConnectionProxy.swift14
-rw-r--r--ios/MullvadVPN/Protocols/URLSessionProtocol.swift15
-rw-r--r--ios/MullvadVPNTests/MockURLProtocol.swift55
-rw-r--r--ios/MullvadVPNTests/OutgoingConnectionProxyTests.swift80
-rw-r--r--ios/MullvadVPNTests/URLSessionStub.swift21
6 files changed, 80 insertions, 121 deletions
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index ffb67a06f1..15eaaed353 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -419,6 +419,8 @@
7A09C98129D99215000C2CAC /* String+FuzzyMatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A09C98029D99215000C2CAC /* String+FuzzyMatch.swift */; };
7A0C0F632A979C4A0058EFCE /* Coordinator+Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0C0F622A979C4A0058EFCE /* Coordinator+Router.swift */; };
7A11DD0B2A9495D400098CD8 /* AppRoutes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5802EBC42A8E44AC00E5CE4C /* AppRoutes.swift */; };
+ 7A12D0762B062D5C00E9602D /* URLSessionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A12D0752B062D5C00E9602D /* URLSessionProtocol.swift */; };
+ 7A12D0772B062D6500E9602D /* URLSessionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A12D0752B062D5C00E9602D /* URLSessionProtocol.swift */; };
7A1A26432A2612AE00B978AA /* PaymentAlertPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1A26422A2612AE00B978AA /* PaymentAlertPresenter.swift */; };
7A1A26452A29CEF700B978AA /* RelayFilterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1A26442A29CEF700B978AA /* RelayFilterViewController.swift */; };
7A1A26472A29CF0800B978AA /* RelayFilterDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1A26462A29CF0800B978AA /* RelayFilterDataSource.swift */; };
@@ -654,7 +656,7 @@
F09D04B52AE93CB6003D4F89 /* OutgoingConnectionProxy+Stub.swift in Sources */ = {isa = PBXBuildFile; fileRef = F09D04B42AE93CB6003D4F89 /* OutgoingConnectionProxy+Stub.swift */; };
F09D04B72AE941DA003D4F89 /* OutgoingConnectionProxyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F09D04B62AE941DA003D4F89 /* OutgoingConnectionProxyTests.swift */; };
F09D04B92AE95111003D4F89 /* OutgoingConnectionProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = F09D04AF2AE7F83D003D4F89 /* OutgoingConnectionProxy.swift */; };
- F09D04BB2AE95396003D4F89 /* MockURLProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F09D04BA2AE95396003D4F89 /* MockURLProtocol.swift */; };
+ F09D04BB2AE95396003D4F89 /* URLSessionStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = F09D04BA2AE95396003D4F89 /* URLSessionStub.swift */; };
F09D04BD2AEBB7C5003D4F89 /* OutgoingConnectionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F09D04BC2AEBB7C5003D4F89 /* OutgoingConnectionService.swift */; };
F09D04C02AF39D63003D4F89 /* OutgoingConnectionServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F09D04BF2AF39D63003D4F89 /* OutgoingConnectionServiceTests.swift */; };
F09D04C12AF39EA2003D4F89 /* OutgoingConnectionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F09D04BC2AEBB7C5003D4F89 /* OutgoingConnectionService.swift */; };
@@ -1527,6 +1529,7 @@
7A02D4EA2A9CEC7A00C19E31 /* MullvadVPNScreenshots.xctestplan */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = MullvadVPNScreenshots.xctestplan; sourceTree = "<group>"; };
7A09C98029D99215000C2CAC /* String+FuzzyMatch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+FuzzyMatch.swift"; sourceTree = "<group>"; };
7A0C0F622A979C4A0058EFCE /* Coordinator+Router.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Coordinator+Router.swift"; sourceTree = "<group>"; };
+ 7A12D0752B062D5C00E9602D /* URLSessionProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLSessionProtocol.swift; sourceTree = "<group>"; };
7A1A26422A2612AE00B978AA /* PaymentAlertPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentAlertPresenter.swift; sourceTree = "<group>"; };
7A1A26442A29CEF700B978AA /* RelayFilterViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayFilterViewController.swift; sourceTree = "<group>"; };
7A1A26462A29CF0800B978AA /* RelayFilterDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayFilterDataSource.swift; sourceTree = "<group>"; };
@@ -1650,7 +1653,7 @@
F09D04AF2AE7F83D003D4F89 /* OutgoingConnectionProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutgoingConnectionProxy.swift; sourceTree = "<group>"; };
F09D04B42AE93CB6003D4F89 /* OutgoingConnectionProxy+Stub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OutgoingConnectionProxy+Stub.swift"; sourceTree = "<group>"; };
F09D04B62AE941DA003D4F89 /* OutgoingConnectionProxyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutgoingConnectionProxyTests.swift; sourceTree = "<group>"; };
- F09D04BA2AE95396003D4F89 /* MockURLProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockURLProtocol.swift; sourceTree = "<group>"; };
+ F09D04BA2AE95396003D4F89 /* URLSessionStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLSessionStub.swift; sourceTree = "<group>"; };
F09D04BC2AEBB7C5003D4F89 /* OutgoingConnectionService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutgoingConnectionService.swift; sourceTree = "<group>"; };
F09D04BF2AF39D63003D4F89 /* OutgoingConnectionServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutgoingConnectionServiceTests.swift; sourceTree = "<group>"; };
F0B0E6962AFE6E7E001DC66B /* XCTest+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCTest+Async.swift"; sourceTree = "<group>"; };
@@ -2361,8 +2364,9 @@
5864AF0629C78816005B0CD9 /* Protocols */ = {
isa = PBXGroup;
children = (
- 58E11187292FA11F009FCA84 /* SettingsMigrationUIHandler.swift */,
5864AF0129C7879B005B0CD9 /* CellFactoryProtocol.swift */,
+ 58E11187292FA11F009FCA84 /* SettingsMigrationUIHandler.swift */,
+ 7A12D0752B062D5C00E9602D /* URLSessionProtocol.swift */,
);
path = Protocols;
sourceTree = "<group>";
@@ -2513,7 +2517,7 @@
F07BF2572A26112D00042943 /* InputTextFormatterTests.swift */,
A9B6AC172ADE8F4300F7802A /* MigrationManagerTests.swift */,
58C3FA652A38549D006A450A /* MockFileCache.swift */,
- F09D04BA2AE95396003D4F89 /* MockURLProtocol.swift */,
+ F09D04BA2AE95396003D4F89 /* URLSessionStub.swift */,
F09D04B42AE93CB6003D4F89 /* OutgoingConnectionProxy+Stub.swift */,
F09D04B62AE941DA003D4F89 /* OutgoingConnectionProxyTests.swift */,
F09D04BF2AF39D63003D4F89 /* OutgoingConnectionServiceTests.swift */,
@@ -4134,6 +4138,7 @@
A9A5F9EC2ACB05160083449F /* CodingErrors+CustomErrorDescription.swift in Sources */,
A9A5F9ED2ACB05160083449F /* NSRegularExpression+IPAddress.swift in Sources */,
A9A5F9EE2ACB05160083449F /* RESTCreateApplePaymentResponse+Localization.swift in Sources */,
+ 7A12D0772B062D6500E9602D /* URLSessionProtocol.swift in Sources */,
A9A5F9EF2ACB05160083449F /* String+AccountFormatting.swift in Sources */,
A9A5F9F02ACB05160083449F /* String+FuzzyMatch.swift in Sources */,
F09D04C12AF39EA2003D4F89 /* OutgoingConnectionService.swift in Sources */,
@@ -4190,7 +4195,7 @@
A9A5FA1A2ACB05160083449F /* StopTunnelOperation.swift in Sources */,
A9A5FA1B2ACB05160083449F /* Tunnel.swift in Sources */,
A9A5FA1C2ACB05160083449F /* Tunnel+Messaging.swift in Sources */,
- F09D04BB2AE95396003D4F89 /* MockURLProtocol.swift in Sources */,
+ F09D04BB2AE95396003D4F89 /* URLSessionStub.swift in Sources */,
A9A5FA1D2ACB05160083449F /* TunnelBlockObserver.swift in Sources */,
A9A5FA1E2ACB05160083449F /* TunnelConfiguration.swift in Sources */,
A9A5FA1F2ACB05160083449F /* TunnelInteractor.swift in Sources */,
@@ -4510,6 +4515,7 @@
5878A27329091D6D0096FC88 /* TunnelBlockObserver.swift in Sources */,
A9E034642ABB302000E59A5A /* UIEdgeInsets+Extensions.swift in Sources */,
58E0A98827C8F46300FE6BDD /* Tunnel.swift in Sources */,
+ 7A12D0762B062D5C00E9602D /* URLSessionProtocol.swift in Sources */,
58ACF64F26567A7100ACE4B7 /* CustomSwitchContainer.swift in Sources */,
58EE2E3A272FF814003BFF93 /* SettingsDataSource.swift in Sources */,
F0E8E4C12A602CCB00ED26A3 /* AccountDeletionContentView.swift in Sources */,
diff --git a/ios/MullvadVPN/GeneralAPIs/OutgoingConnectionProxy.swift b/ios/MullvadVPN/GeneralAPIs/OutgoingConnectionProxy.swift
index a078efce1a..233bd4d763 100644
--- a/ios/MullvadVPN/GeneralAPIs/OutgoingConnectionProxy.swift
+++ b/ios/MullvadVPN/GeneralAPIs/OutgoingConnectionProxy.swift
@@ -17,7 +17,7 @@ protocol OutgoingConnectionHandling {
}
final class OutgoingConnectionProxy: OutgoingConnectionHandling {
- private enum ExitIPVersion: String {
+ enum ExitIPVersion: String {
case v4 = "ipv4", v6 = "ipv6"
var host: String {
@@ -25,9 +25,9 @@ final class OutgoingConnectionProxy: OutgoingConnectionHandling {
}
}
- let urlSession: URLSession
+ let urlSession: URLSessionProtocol
- init(urlSession: URLSession) {
+ init(urlSession: URLSessionProtocol) {
self.urlSession = urlSession
}
@@ -74,7 +74,7 @@ final class OutgoingConnectionProxy: OutgoingConnectionHandling {
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: REST.defaultAPINetworkTimeout.timeInterval
)
- let (data, response) = try await urlSession.data(for: request)
+ let (data, response) = try await data(for: request)
guard let httpResponse = response as? HTTPURLResponse else {
throw REST.Error.network(URLError(.badServerResponse))
}
@@ -92,3 +92,9 @@ final class OutgoingConnectionProxy: OutgoingConnectionHandling {
return connectionData
}
}
+
+extension OutgoingConnectionProxy: URLSessionProtocol {
+ func data(for request: URLRequest) async throws -> (Data, URLResponse) {
+ return try await urlSession.data(for: request)
+ }
+}
diff --git a/ios/MullvadVPN/Protocols/URLSessionProtocol.swift b/ios/MullvadVPN/Protocols/URLSessionProtocol.swift
new file mode 100644
index 0000000000..ec2f2982e4
--- /dev/null
+++ b/ios/MullvadVPN/Protocols/URLSessionProtocol.swift
@@ -0,0 +1,15 @@
+//
+// URLSessionProtocol.swift
+// MullvadVPN
+//
+// Created by Jon Petersson on 2023-11-16.
+// Copyright © 2023 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+
+protocol URLSessionProtocol {
+ func data(for request: URLRequest) async throws -> (Data, URLResponse)
+}
+
+extension URLSession: URLSessionProtocol {}
diff --git a/ios/MullvadVPNTests/MockURLProtocol.swift b/ios/MullvadVPNTests/MockURLProtocol.swift
deleted file mode 100644
index 061a132528..0000000000
--- a/ios/MullvadVPNTests/MockURLProtocol.swift
+++ /dev/null
@@ -1,55 +0,0 @@
-//
-// MockURLProtocol.swift
-// MullvadVPNTests
-//
-// Created by Mojgan on 2023-10-25.
-// Copyright © 2023 Mullvad VPN AB. All rights reserved.
-//
-
-import Foundation
-
-class MockURLProtocol: URLProtocol {
- static var error: Error?
- static var requestHandler: ((URLRequest) throws -> (HTTPURLResponse, Data))?
-
- override class func canInit(with request: URLRequest) -> Bool {
- return true
- }
-
- override class func canonicalRequest(for request: URLRequest) -> URLRequest {
- return request
- }
-
- override func startLoading() {
- if let error = MockURLProtocol.error {
- client?.urlProtocol(self, didFailWithError: error)
- return
- }
-
- guard let handler = MockURLProtocol.requestHandler else {
- assertionFailure("Received unexpected request with no handler set")
- return
- }
-
- do {
- let (response, data) = try handler(request)
- client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
- client?.urlProtocol(self, didLoad: data)
- client?.urlProtocolDidFinishLoading(self)
- } catch {
- client?.urlProtocol(self, didFailWithError: error)
- }
- }
-
- override func stopLoading() {
- // stop loading here
- }
-}
-
-extension URLSession {
- static let mock = {
- let configuration = URLSessionConfiguration.ephemeral
- configuration.protocolClasses = [MockURLProtocol.self]
- return URLSession(configuration: configuration)
- }()
-}
diff --git a/ios/MullvadVPNTests/OutgoingConnectionProxyTests.swift b/ios/MullvadVPNTests/OutgoingConnectionProxyTests.swift
index afa8ada0fa..2ffdbdf92d 100644
--- a/ios/MullvadVPNTests/OutgoingConnectionProxyTests.swift
+++ b/ios/MullvadVPNTests/OutgoingConnectionProxyTests.swift
@@ -9,51 +9,27 @@ import MullvadREST
import XCTest
final class OutgoingConnectionProxyTests: XCTestCase {
- private var outgoingConnectionProxy: OutgoingConnectionProxy!
private var mockIPV6ConnectionData: Data!
private var mockIPV4ConnectionData: Data!
private let encoder = JSONEncoder()
override func setUpWithError() throws {
- outgoingConnectionProxy = OutgoingConnectionProxy(urlSession: .mock)
mockIPV4ConnectionData = try encoder.encode(IPV4ConnectionData.mock)
mockIPV6ConnectionData = try encoder.encode(IPV6ConnectionData.mock)
}
override func tearDownWithError() throws {
- outgoingConnectionProxy = nil
mockIPV4ConnectionData.removeAll()
mockIPV6ConnectionData.removeAll()
}
- func testNoInternetConnection() async throws {
- let noIPv4Expectation = expectation(description: "Did not receive IPv4")
- let error = URLError(URLError.notConnectedToInternet)
-
- MockURLProtocol.error = error
- MockURLProtocol.requestHandler = nil
-
- await XCTAssertThrowsErrorAsync(try await outgoingConnectionProxy.getIPV4(retryStrategy: .noRetry)) { error in
- noIPv4Expectation.fulfill()
- XCTAssertEqual((error as? URLError)?.code, .notConnectedToInternet)
- }
- await fulfillment(of: [noIPv4Expectation], timeout: 1)
- }
-
func testSuccessGettingIPV4() async throws {
let iPv4Expectation = expectation(description: "Did receive IPv4")
- MockURLProtocol.error = nil
- MockURLProtocol.requestHandler = { _ in
- let response = HTTPURLResponse(
- url: URL(string: "https://ipv4.am.i.mullvad.net/json")!,
- statusCode: 200,
- httpVersion: nil,
- headerFields: ["Content-Type": "application/json"]
- )!
- return (response, self.mockIPV4ConnectionData)
- }
+ let outgoingConnectionProxy = OutgoingConnectionProxy(urlSession: URLSessionStub(
+ response: (mockIPV4ConnectionData, createHTTPURLResponse(ip: .v4, statusCode: 200))
+ ))
let result = try await outgoingConnectionProxy.getIPV4(retryStrategy: .noRetry)
@@ -66,16 +42,9 @@ final class OutgoingConnectionProxyTests: XCTestCase {
func testFailureGettingIPV4() async throws {
let noIPv4Expectation = expectation(description: "Did not receive IPv4")
- MockURLProtocol.error = nil
- MockURLProtocol.requestHandler = { _ in
- let response = HTTPURLResponse(
- url: URL(string: "https://ipv4.am.i.mullvad.net/json")!,
- statusCode: 503,
- httpVersion: nil,
- headerFields: ["Content-Type": "application/json"]
- )!
- return (response, Data())
- }
+ let outgoingConnectionProxy = OutgoingConnectionProxy(urlSession: URLSessionStub(
+ response: (Data(), createHTTPURLResponse(ip: .v4, statusCode: 503))
+ ))
await XCTAssertThrowsErrorAsync(try await outgoingConnectionProxy.getIPV4(retryStrategy: .noRetry)) { _ in
noIPv4Expectation.fulfill()
@@ -86,16 +55,9 @@ final class OutgoingConnectionProxyTests: XCTestCase {
func testSuccessGettingIPV6() async throws {
let ipv6Expectation = expectation(description: "Did receive IPv6")
- MockURLProtocol.error = nil
- MockURLProtocol.requestHandler = { _ in
- let response = HTTPURLResponse(
- url: URL(string: "https://ipv6.am.i.mullvad.net/json")!,
- statusCode: 200,
- httpVersion: nil,
- headerFields: ["Content-Type": "application/json"]
- )!
- return (response, self.mockIPV6ConnectionData)
- }
+ let outgoingConnectionProxy = OutgoingConnectionProxy(urlSession: URLSessionStub(
+ response: (mockIPV6ConnectionData, createHTTPURLResponse(ip: .v6, statusCode: 200))
+ ))
let result = try await outgoingConnectionProxy.getIPV6(retryStrategy: .noRetry)
@@ -108,16 +70,9 @@ final class OutgoingConnectionProxyTests: XCTestCase {
func testFailureGettingIPV6() async throws {
let noIPv6Expectation = expectation(description: "Did not receive IPv6")
- MockURLProtocol.error = nil
- MockURLProtocol.requestHandler = { _ in
- let response = HTTPURLResponse(
- url: URL(string: "https://ipv6.am.i.mullvad.net/json")!,
- statusCode: 404,
- httpVersion: nil,
- headerFields: ["Content-Type": "application/json"]
- )!
- return (response, Data())
- }
+ let outgoingConnectionProxy = OutgoingConnectionProxy(urlSession: URLSessionStub(
+ response: (mockIPV6ConnectionData, createHTTPURLResponse(ip: .v6, statusCode: 404))
+ ))
await XCTAssertThrowsErrorAsync(try await outgoingConnectionProxy.getIPV6(retryStrategy: .noRetry)) { _ in
noIPv6Expectation.fulfill()
@@ -125,3 +80,14 @@ final class OutgoingConnectionProxyTests: XCTestCase {
await fulfillment(of: [noIPv6Expectation], timeout: 1)
}
}
+
+extension OutgoingConnectionProxyTests {
+ private func createHTTPURLResponse(ip: OutgoingConnectionProxy.ExitIPVersion, statusCode: Int) -> HTTPURLResponse {
+ return HTTPURLResponse(
+ url: URL(string: "https://\(ip.host)/json")!,
+ statusCode: statusCode,
+ httpVersion: nil,
+ headerFields: ["Content-Type": "application/json"]
+ )!
+ }
+}
diff --git a/ios/MullvadVPNTests/URLSessionStub.swift b/ios/MullvadVPNTests/URLSessionStub.swift
new file mode 100644
index 0000000000..ecde3456fd
--- /dev/null
+++ b/ios/MullvadVPNTests/URLSessionStub.swift
@@ -0,0 +1,21 @@
+//
+// URLSessionStub.swift
+// MullvadVPNTests
+//
+// Created by Mojgan on 2023-10-25.
+// Copyright © 2023 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+
+class URLSessionStub: URLSessionProtocol {
+ var response: (Data, URLResponse)
+
+ init(response: (Data, URLResponse)) {
+ self.response = response
+ }
+
+ func data(for request: URLRequest) async throws -> (Data, URLResponse) {
+ return response
+ }
+}