Skip to content

Commit

Permalink
feat: have AuthenticationAPI depend on NetworkService directly - …
Browse files Browse the repository at this point in the history
…WPB-16179 (#2556)
  • Loading branch information
samwyndham authored Feb 18, 2025
1 parent 6b15af1 commit b016ab9
Show file tree
Hide file tree
Showing 7 changed files with 270 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@

public struct AuthenticationAPIBuilder {

let apiService: any APIServiceProtocol
let networkService: any NetworkServiceProtocol

/// Create a new builder.
///
/// - Parameter apiService: An api service.

public init(apiService: any APIServiceProtocol) {
self.apiService = apiService
public init(networkService: any NetworkServiceProtocol) {
self.networkService = networkService
}

/// Make a versioned `AuthenticationAPI`.
Expand All @@ -38,23 +38,23 @@ public struct AuthenticationAPIBuilder {
public func makeAPI(for version: APIVersion) -> any AuthenticationAPI {
switch version {
case .v0:
AuthenticationAPIV0(apiService: apiService)
AuthenticationAPIV0(networkService: networkService)
case .v1:
AuthenticationAPIV1(apiService: apiService)
AuthenticationAPIV1(networkService: networkService)
case .v2:
AuthenticationAPIV2(apiService: apiService)
AuthenticationAPIV2(networkService: networkService)
case .v3:
AuthenticationAPIV3(apiService: apiService)
AuthenticationAPIV3(networkService: networkService)
case .v4:
AuthenticationAPIV4(apiService: apiService)
AuthenticationAPIV4(networkService: networkService)
case .v5:
AuthenticationAPIV5(apiService: apiService)
AuthenticationAPIV5(networkService: networkService)
case .v6:
AuthenticationAPIV6(apiService: apiService)
AuthenticationAPIV6(networkService: networkService)
case .v7:
AuthenticationAPIV7(apiService: apiService)
AuthenticationAPIV7(networkService: networkService)
case .v8:
AuthenticationAPIV8(apiService: apiService)
AuthenticationAPIV8(networkService: networkService)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@
import Foundation

class AuthenticationAPIV0: AuthenticationAPI, VersionedAPI {
let apiService: any APIServiceProtocol
let networkService: any NetworkServiceProtocol

init(apiService: any APIServiceProtocol) {
self.apiService = apiService
init(networkService: any NetworkServiceProtocol) {
self.networkService = networkService
}

var apiVersion: APIVersion {
Expand Down Expand Up @@ -56,10 +56,7 @@ class AuthenticationAPIV0: AuthenticationAPI, VersionedAPI {
.withMethod(.post)
.build()

let (data, response) = try await apiService.executeRequest(
request,
requiringAccessToken: false
)
let (data, response) = try await networkService.executeRequest(request)

guard
let responseURL = response.url,
Expand Down Expand Up @@ -124,10 +121,7 @@ class AuthenticationAPIV0: AuthenticationAPI, VersionedAPI {
.withMethod(.get)
.build()

let (data, response) = try await apiService.executeRequest(
request,
requiringAccessToken: false
)
let (data, response) = try await networkService.executeRequest(request)

return try ResponseParser()
.success(code: .ok, type: DomainInfoV0.self)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,7 @@ final class AuthenticationAPIV8: AuthenticationAPIV7 {
.withMethod(.post)
.build()

let (data, response) = try await apiService.executeRequest(
request,
requiringAccessToken: false
)
let (data, response) = try await networkService.executeRequest(request)

return try ResponseParser()
.success(code: .ok, type: DomainRegistrationConfigurationV8.self)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,14 @@

import Foundation

public final class NetworkService: NSObject {
// sourcery: AutoMockable
public protocol NetworkServiceProtocol {

func executeRequest(_ request: URLRequest) async throws -> (Data, HTTPURLResponse)

}

public final class NetworkService: NSObject, NetworkServiceProtocol {

private let baseURL: URL
private let serverTrustValidator: ServerTrustValidator
Expand All @@ -41,7 +48,7 @@ public final class NetworkService: NSObject {
self.urlSession = urlSession
}

func executeRequest(_ request: URLRequest) async throws -> (Data, HTTPURLResponse) {
public func executeRequest(_ request: URLRequest) async throws -> (Data, HTTPURLResponse) {
guard let urlSession else {
throw NetworkServiceError.serviceNotConfigured
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ import XCTest

final class AuthenticationAPITests: XCTestCase {

private var apiSnapshotHelper: APIServiceSnapshotHelper<any AuthenticationAPI>!
private var apiSnapshotHelper: NetworkServiceSnapshotHelper<any AuthenticationAPI>!

// MARK: - Setup

override func setUp() {
apiSnapshotHelper = APIServiceSnapshotHelper<any AuthenticationAPI> { apiService, apiVersion in
AuthenticationAPIBuilder(apiService: apiService)
apiSnapshotHelper = NetworkServiceSnapshotHelper<any AuthenticationAPI> { networkService, apiVersion in
AuthenticationAPIBuilder(networkService: networkService)
.makeAPI(for: apiVersion)
}
}
Expand All @@ -42,8 +42,8 @@ final class AuthenticationAPITests: XCTestCase {

func testGetDomainRegistration_V0_To_V7() async throws {
// Given
let apiService = MockAPIServiceProtocol()
let builder = AuthenticationAPIBuilder(apiService: apiService)
let networkService = MockNetworkServiceProtocol()
let builder = AuthenticationAPIBuilder(networkService: networkService)

for apiVersion in [APIVersion.v0, .v1, .v2, .v3, .v4, .v5, .v6, .v7] {
let sut = builder.makeAPI(for: apiVersion)
Expand Down Expand Up @@ -93,11 +93,11 @@ final class AuthenticationAPITests: XCTestCase {

func testGetDomainRegistration_Response_Handling_V8_Success() async throws {
// Given
let apiService = MockAPIServiceProtocol.withResponses([
let networkService = MockNetworkServiceProtocol.withResponses([
(.ok, "GetDomainRegistrationSuccessResponseV8")
])

let sut = AuthenticationAPIV8(apiService: apiService)
let sut = AuthenticationAPIV8(networkService: networkService)

// When
let response = try await sut.getDomainRegistration(forEmail: "[email protected]")
Expand All @@ -116,11 +116,11 @@ final class AuthenticationAPITests: XCTestCase {

func testGetDomainRegistration_ResponseWithNullValues_Handling_V8_Success() async throws {
// Given
let apiService = MockAPIServiceProtocol.withResponses([
let networkService = MockNetworkServiceProtocol.withResponses([
(.ok, "GetDomainRegistrationSuccessResponse_WithNullValuesV8")
])

let sut = AuthenticationAPIV8(apiService: apiService)
let sut = AuthenticationAPIV8(networkService: networkService)

// When
let response = try await sut.getDomainRegistration(forEmail: "[email protected]")
Expand All @@ -139,11 +139,11 @@ final class AuthenticationAPITests: XCTestCase {

func testGetDomainRegistration_Response_Handling_V8_Invalid_Domain() async throws {
// Given
let apiService = MockAPIServiceProtocol.withResponses([
let networkService = MockNetworkServiceProtocol.withResponses([
(.badRequest, "GetDomainRegistrationErrorResponse_InvalidDomainV8")
])

let sut = AuthenticationAPIV8(apiService: apiService)
let sut = AuthenticationAPIV8(networkService: networkService)

// Then
await XCTAssertThrowsErrorAsync(AuthenticationAPIError.invalidDomain) {
Expand All @@ -154,11 +154,11 @@ final class AuthenticationAPITests: XCTestCase {

func testGetOnPremConfigURL_Response_Handling_Success() async throws {
// Given
let apiService = MockAPIServiceProtocol.withResponses([
let networkService = MockNetworkServiceProtocol.withResponses([
(.ok, "GetOnPremConfigURLSuccessResponseV0")
])

let sut = AuthenticationAPIV8(apiService: apiService)
let sut = AuthenticationAPIV8(networkService: networkService)

// When
let response = try await sut.getOnPremConfigURL(forDomain: "example.com")
Expand All @@ -172,11 +172,11 @@ final class AuthenticationAPITests: XCTestCase {

func testGetOnPremConfigURL_Response_Handling_Custom_Backend_Not_Found() async throws {
// Given
let apiService = MockAPIServiceProtocol.withResponses([
let networkService = MockNetworkServiceProtocol.withResponses([
(.notFound, "GetOnPremConfigURLErrorResponse_CustomBackendNotFound_V0")
])

let sut = AuthenticationAPIV8(apiService: apiService)
let sut = AuthenticationAPIV8(networkService: networkService)

// Then
await XCTAssertThrowsErrorAsync(AuthenticationAPIError.configNotFound) {
Expand All @@ -187,11 +187,11 @@ final class AuthenticationAPITests: XCTestCase {

func testLoginViaEmail_Response_Handling_Success() async throws {
// Given
let apiService = MockAPIServiceProtocol.withResponses([
let networkService = MockNetworkServiceProtocol.withResponses([
(.ok, "LoginViaEmailSuccessResponseV0")
])

let sut = AuthenticationAPIV8(apiService: apiService)
let sut = AuthenticationAPIV8(networkService: networkService)

// When
let response = try await sut.login(
Expand Down Expand Up @@ -219,11 +219,11 @@ final class AuthenticationAPITests: XCTestCase {

func testLoginViaEmail_Response_Handling_Custom_Backend_Not_Found() async throws {
// Given
let apiService = MockAPIServiceProtocol.withResponses([
let networkService = MockNetworkServiceProtocol.withResponses([
(.notFound, "LoginViaEmailErrorResponse_CodeAuthenticationRequired_V0")
])

let sut = AuthenticationAPIV8(apiService: apiService)
let sut = AuthenticationAPIV8(networkService: networkService)

// Then
await XCTAssertThrowsErrorAsync(AuthenticationAPIError.twoFactorAuthenticationRequired) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//
// Wire
// Copyright (C) 2025 Wire Swiss GmbH
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses/.
//

import Foundation

@testable import WireAPI
@testable import WireAPISupport

extension MockNetworkServiceProtocol {

typealias Response = (statusCode: HTTPStatusCode, resourceName: String?)

/// Create a mock network service that returns zero or more responses.
///
/// Some ways you can use this:
/// - Mock a series of paged responses
/// - Mock a series or responses for repeated requests
/// - Mock a single response
///
/// - Parameter responses: The responses to return, one per request received.
/// - Returns: A mock network service.

static func withResponses(_ responses: [Response]) -> MockNetworkServiceProtocol {
let networkService = MockNetworkServiceProtocol()
var responses = responses

networkService.executeRequest_MockMethod = { request in
guard !responses.isEmpty else {
throw "no response"
}

let response = responses.removeFirst()

return try request.mockResponse(
statusCode: response.statusCode,
jsonResourceName: response.resourceName
)
}

return networkService
}

static func withError(statusCode: HTTPStatusCode, label: String = "") -> MockNetworkServiceProtocol {
let networkService = MockNetworkServiceProtocol()
networkService.executeRequest_MockMethod = { request in
try request.mockErrorResponse(
statusCode: statusCode,
label: label
)
}

return networkService
}

}
Loading

0 comments on commit b016ab9

Please sign in to comment.