From b5da391efddbf73594a85336d42f32cb2f9f057a Mon Sep 17 00:00:00 2001 From: Stylianos Tzouvaras Date: Mon, 2 Dec 2024 19:11:22 +0200 Subject: [PATCH] Working on unit tests and overall improvements --- Sources/Domain/Entities/SessionData.swift | 36 ++++++ .../Domain/Interactor/RQESInteractor.swift | 23 ++-- Sources/Infrastructure/EudiRQESUi.swift | 87 ++++++------- .../CredentialSelectionViewModel.swift | 2 +- .../DocumentSelectionViewModel.swift | 2 +- .../UI/DocumentView/DocumentViewModel.swift | 2 +- .../SignedDocumentViewModel.swift | 2 +- .../TestLocalizationController.swift} | 16 +-- .../Interactor/TestRQESInteractor.swift | 27 ++++ .../EudiRQESUi.State+Extensions.swift | 19 +++ Tests/Infrastructure/TestEudiRQESUi.swift | 118 ++++++++++++++++++ Tests/Mock/GeneratedMocks.swift | 26 ++-- Tests/Util/TestConstants.swift | 31 +++++ fastlane/.xcovignore | 7 +- 14 files changed, 315 insertions(+), 83 deletions(-) create mode 100644 Sources/Domain/Entities/SessionData.swift rename Tests/{ExampleTest.swift => Domain/Controller/TestLocalizationController.swift} (69%) create mode 100644 Tests/Domain/Interactor/TestRQESInteractor.swift create mode 100644 Tests/Extension/EudiRQESUi.State+Extensions.swift create mode 100644 Tests/Infrastructure/TestEudiRQESUi.swift create mode 100644 Tests/Util/TestConstants.swift diff --git a/Sources/Domain/Entities/SessionData.swift b/Sources/Domain/Entities/SessionData.swift new file mode 100644 index 0000000..ed8f537 --- /dev/null +++ b/Sources/Domain/Entities/SessionData.swift @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2023 European Commission + * + * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European + * Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work + * except in compliance with the Licence. + * + * You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/software/page/eupl + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the Licence for the specific language + * governing permissions and limitations under the Licence. + */ +import RqesKit + +@Copyable +struct SessionData { + let document: DocumentData? + let qtsp: QTSPData? + let certificate: CredentialInfo? + let code: String? + + init( + document: DocumentData? = nil, + qtsp: QTSPData? = nil, + certificate: CredentialInfo? = nil, + code: String? = nil + ) { + self.document = document + self.qtsp = qtsp + self.certificate = certificate + self.code = code + } +} diff --git a/Sources/Domain/Interactor/RQESInteractor.swift b/Sources/Domain/Interactor/RQESInteractor.swift index b1565b3..d1aeae7 100644 --- a/Sources/Domain/Interactor/RQESInteractor.swift +++ b/Sources/Domain/Interactor/RQESInteractor.swift @@ -18,7 +18,7 @@ import RqesKit protocol RQESInteractor: Sendable { func signDocument() async throws -> Document? - func getCurrentSelection() async -> CurrentSelection? + func getSession() async -> SessionData? func getQTSps() async -> [QTSPData] func fetchCredentials() async throws -> Result<[CredentialInfo], any Error> func updateQTSP(_ qtsp: QTSPData) async @@ -40,7 +40,7 @@ final class RQESInteractorImpl: RQESInteractor { func createRQESService(_ qtsp: QTSPData) async throws { let rQESConfig = await rqesUi.getRQESConfig() guard - let fileExtension = await getCurrentSelection()?.document?.uri.pathExtension + let fileExtension = await getSession()?.document?.uri.pathExtension else { throw EudiRQESUiError.noDocumentProvided } @@ -61,22 +61,19 @@ final class RQESInteractorImpl: RQESInteractor { } func signDocument() async throws -> Document? { - let authorizationCode = await self.rqesUi.selection.code + let authorizationCode = await self.getSession()?.code let rQESServiceAuthorized = await self.rqesUi.getRQESServiceAuthorized() - if let authorizationCode, - let rQESServiceAuthorized { - + if let authorizationCode, let rQESServiceAuthorized { let authorizedCredential = try await rQESServiceAuthorized.authorizeCredential(authorizationCode: authorizationCode) let signedDocuments = try await authorizedCredential.signDocuments() - return signedDocuments.first } else { throw EudiRQESUiError.unableToSignHashDocument } } - func getCurrentSelection() async -> CurrentSelection? { - await self.rqesUi.selection + func getSession() async -> SessionData? { + await self.rqesUi.getSessionData() } func updateQTSP(_ qtsp: QTSPData) async { @@ -84,7 +81,7 @@ final class RQESInteractorImpl: RQESInteractor { } func updateDocument(_ url: URL) async { - let name = await self.rqesUi.selection.document?.documentName + let name = await self.getSession()?.document?.documentName if let name { let document = DocumentData(documentName: name, uri: url) await self.rqesUi.updateSelectionDocument(with: document) @@ -105,8 +102,8 @@ final class RQESInteractorImpl: RQESInteractor { } func openCredentialAuthrorizationURL() async throws -> URL { - if let uri = await self.rqesUi.selection.document?.uri, - let certificate = await self.rqesUi.selection.certificate { + if let uri = await self.getSession()?.document?.uri, + let certificate = await self.getSession()?.certificate { let unsignedDocuments = [ Document( id: UUID().uuidString, @@ -131,7 +128,7 @@ final class RQESInteractorImpl: RQESInteractor { func fetchCredentials() async throws -> Result<[CredentialInfo], any Error> { if let rqesService = await self.rqesUi.getRQESService(), - let authorizationCode = await self.rqesUi.selection.code { + let authorizationCode = await self.getSession()?.code { do { let rQESServiceAuthorized = try await rqesService.authorizeService(authorizationCode: authorizationCode) await self.rqesUi.setRQESServiceAuthorized(rQESServiceAuthorized) diff --git a/Sources/Infrastructure/EudiRQESUi.swift b/Sources/Infrastructure/EudiRQESUi.swift index 8887239..6c11318 100644 --- a/Sources/Infrastructure/EudiRQESUi.swift +++ b/Sources/Infrastructure/EudiRQESUi.swift @@ -16,26 +16,6 @@ import UIKit import RqesKit -@Copyable -struct CurrentSelection { - let document: DocumentData? - let qtsp: QTSPData? - let certificate: CredentialInfo? - let code: String? - - init( - document: DocumentData? = nil, - qtsp: QTSPData? = nil, - certificate: CredentialInfo? = nil, - code: String? = nil - ) { - self.document = document - self.qtsp = qtsp - self.certificate = certificate - self.code = code - } -} - public final actor EudiRQESUi { private static var _shared: EudiRQESUi? @@ -44,7 +24,7 @@ public final actor EudiRQESUi { private static var _viewController: UIViewController? private let router: any RouterGraph - var selection = CurrentSelection() + private var session = SessionData() private static var _rqesService: RQESService? private static var _rQESServiceAuthorized: RQESServiceAuthorized? @@ -56,6 +36,24 @@ public final actor EudiRQESUi { Self._config = config Self._shared = self } + + init( + config: any EudiRQESUiConfig, + router: any RouterGraph, + state: State = .none, + selection: SessionData = .init(), + rqesService: RQESService? = nil, + rQESServiceAuthorized: RQESServiceAuthorized? = nil + ) { + DIGraph.shared.load() + self.router = router + self.session = selection + Self._config = config + Self._state = state + Self._shared = self + Self._rqesService = rqesService + Self._rQESServiceAuthorized = rQESServiceAuthorized + } @MainActor public func initiate( @@ -92,22 +90,6 @@ public final actor EudiRQESUi { try await launcSDK(on: container, animated: animated) } - - func updateSelectionDocument(with document: DocumentData? = nil) async { - selection = selection.copy(document: document) - } - - func updateQTSP(with qtsp: QTSPData? = nil) async { - selection = selection.copy(qtsp: qtsp) - } - - func updateCertificate(with certificate: CredentialInfo) async { - selection = selection.copy(certificate: certificate) - } - - public func updateAuthorizationCode(with code: String) async { - selection = selection.copy(code: code) - } } public extension EudiRQESUi { @@ -122,10 +104,6 @@ public extension EudiRQESUi { private extension EudiRQESUi { - func getState() -> State { - return Self._state - } - func getViewController() -> UIViewController? { return Self._viewController } @@ -164,15 +142,18 @@ private extension EudiRQESUi { } func resetCache() async { - selection = CurrentSelection() + session = SessionData() setRQESService(nil) setRQESServiceAuthorized(nil) } + func updateAuthorizationCode(with code: String) async { + session = session.copy(code: code) + } } extension EudiRQESUi { - + static func forceConfig() -> any EudiRQESUiConfig { return Self._config! } @@ -181,6 +162,26 @@ extension EudiRQESUi { return Self._shared! } + func updateSelectionDocument(with document: DocumentData? = nil) async { + session = session.copy(document: document) + } + + func updateQTSP(with qtsp: QTSPData? = nil) async { + session = session.copy(qtsp: qtsp) + } + + func updateCertificate(with certificate: CredentialInfo) async { + session = session.copy(certificate: certificate) + } + + func getSessionData() -> SessionData { + return self.session + } + + func getState() -> State { + return Self._state + } + func getRQESService() -> RQESService? { Self._rqesService } diff --git a/Sources/Presentation/UI/CredentialSelection/CredentialSelectionViewModel.swift b/Sources/Presentation/UI/CredentialSelection/CredentialSelectionViewModel.swift index d72b92a..a4afb2d 100644 --- a/Sources/Presentation/UI/CredentialSelection/CredentialSelectionViewModel.swift +++ b/Sources/Presentation/UI/CredentialSelection/CredentialSelectionViewModel.swift @@ -114,7 +114,7 @@ final class CredentialSelectionViewModel: ViewModel: ViewModel: ViewModel { } func initiate() async { - let uri = await interactor.getCurrentSelection()?.document?.uri + let uri = await interactor.getSession()?.document?.uri if let uri { let source = DocumentSource.pdfUrl(uri) loadDocument(from: source) diff --git a/Sources/Presentation/UI/SignedDocument/SignedDocumentViewModel.swift b/Sources/Presentation/UI/SignedDocument/SignedDocumentViewModel.swift index ac0be3b..c5e7875 100644 --- a/Sources/Presentation/UI/SignedDocument/SignedDocumentViewModel.swift +++ b/Sources/Presentation/UI/SignedDocument/SignedDocumentViewModel.swift @@ -56,7 +56,7 @@ class SignedDocumentViewModel: ViewModel CurrentSelection? { + func getSession() async -> SessionData? { return await cuckoo_manager.call( - "getCurrentSelection() async -> CurrentSelection?", + "getSession() async -> SessionData?", parameters: (), escapingParameters: (), superclassCall: Cuckoo.MockManager.crashOnProtocolSuperclassCall(), - defaultCall: await __defaultImplStub!.getCurrentSelection() + defaultCall: await __defaultImplStub!.getSession() ) } @@ -897,10 +905,10 @@ class MockRQESInteractor: RQESInteractor, Cuckoo.ProtocolMock, @unchecked Sendab )) } - func getCurrentSelection() -> Cuckoo.ProtocolStubFunction<(), CurrentSelection?> { + func getSession() -> Cuckoo.ProtocolStubFunction<(), SessionData?> { let matchers: [Cuckoo.ParameterMatcher] = [] return .init(stub: cuckoo_manager.createStub(for: MockRQESInteractor.self, - method: "getCurrentSelection() async -> CurrentSelection?", + method: "getSession() async -> SessionData?", parameterMatchers: matchers )) } @@ -995,10 +1003,10 @@ class MockRQESInteractor: RQESInteractor, Cuckoo.ProtocolMock, @unchecked Sendab @discardableResult - func getCurrentSelection() -> Cuckoo.__DoNotUse<(), CurrentSelection?> { + func getSession() -> Cuckoo.__DoNotUse<(), SessionData?> { let matchers: [Cuckoo.ParameterMatcher] = [] return cuckoo_manager.verify( - "getCurrentSelection() async -> CurrentSelection?", + "getSession() async -> SessionData?", callMatcher: callMatcher, parameterMatchers: matchers, sourceLocation: sourceLocation @@ -1111,8 +1119,8 @@ class RQESInteractorStub:RQESInteractor, @unchecked Sendable { return DefaultValueRegistry.defaultValue(for: (Document?).self) } - func getCurrentSelection() async -> CurrentSelection? { - return DefaultValueRegistry.defaultValue(for: (CurrentSelection?).self) + func getSession() async -> SessionData? { + return DefaultValueRegistry.defaultValue(for: (SessionData?).self) } func getQTSps() async -> [QTSPData] { diff --git a/Tests/Util/TestConstants.swift b/Tests/Util/TestConstants.swift new file mode 100644 index 0000000..88e1726 --- /dev/null +++ b/Tests/Util/TestConstants.swift @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 European Commission + * + * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European + * Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work + * except in compliance with the Licence. + * + * You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/software/page/eupl + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the Licence for the specific language + * governing permissions and limitations under the Licence. + */ +@testable import EudiRQESUi + +struct TestConstants { + + static let mockDocumentData = DocumentData( + documentName: "test.pdf", + uri: URL(string: "file://internal/test.pdf")! + ) + + static let mockRqesService: RqesServiceConfig = .init( + clientId: "wallet-client", + clientSecret: "somesecret2", + authFlowRedirectionURI: "rqes://oauth/callback", + hashAlgorithm: .SHA256 + ) +} diff --git a/fastlane/.xcovignore b/fastlane/.xcovignore index 547f004..739ce7a 100644 --- a/fastlane/.xcovignore +++ b/fastlane/.xcovignore @@ -11,9 +11,6 @@ - .*View.swift - .*ViewController.swift - .*ViewModel.swift -- .*Shape.swift -- .*Style.swift -- .*Styles.swift - .*Modifier.swift - .*Content.swift - .*UIModel.swift @@ -25,9 +22,7 @@ # Controller - LogController.swift - -# Entry -- EudiRQESUi.swift +- PreferencesController.swift # Extensions - .*Extensions.swift \ No newline at end of file