Skip to content

Commit

Permalink
Merge pull request #36 from niscy-eudiw/main
Browse files Browse the repository at this point in the history
Completed All Unit testing for Controllers and Interactors. Separated RQESKit Logic to a standalone controller to improve Interactor testing
  • Loading branch information
stzouvaras authored Dec 2, 2024
2 parents 733e46f + e575223 commit 1b7a81a
Show file tree
Hide file tree
Showing 11 changed files with 1,004 additions and 37 deletions.
9 changes: 7 additions & 2 deletions Sources/Domain/Controller/LocalizationController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,20 @@ protocol LocalizationController: Sendable {
final class LocalizationControllerImpl: LocalizationController {

private let config: any EudiRQESUiConfig
private let locale: Locale

init(config: any EudiRQESUiConfig) {
init(
config: any EudiRQESUiConfig,
locale: Locale
) {
self.config = config
self.locale = locale
}

func get(with key: LocalizableKey, args: [String]) -> String {
guard
!config.translations.isEmpty,
let translations = config.translations[Locale.current.identifier],
let translations = config.translations[locale.identifier],
let translation = translations[key]
else {
return key.defaultTranslation(args: args)
Expand Down
83 changes: 83 additions & 0 deletions Sources/Domain/Controller/RQESController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* 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
import Foundation

protocol RQESController: Sendable {
func getRSSPMetadata() async throws -> RSSPMetadata
func getServiceAuthorizationUrl() async throws -> URL
func authorizeService(_ authorizationCode: String) async throws -> RQESServiceAuthorized
func authorizeCredential(_ authorizationCode: String) async throws -> RQESServiceCredentialAuthorized
func signDocuments(_ authorizationCode: String) async throws -> [Document]
func getCredentialsList() async throws -> [CredentialInfo]
func getCredentialAuthorizationUrl(credentialInfo: CredentialInfo, documents: [Document]) async throws -> URL
}

final class RQESControllerImpl: RQESController {

private let rqesUi: EudiRQESUi

init(rqesUi: EudiRQESUi) {
self.rqesUi = rqesUi
}

func getRSSPMetadata() async throws -> RSSPMetadata {
guard let rqesService = await self.rqesUi.getRQESService() else {
throw EudiRQESUiError.noRQESServiceProvided
}
return try await rqesService.getRSSPMetadata()
}

func getServiceAuthorizationUrl() async throws -> URL {
guard let rqesService = await self.rqesUi.getRQESService() else {
throw EudiRQESUiError.noRQESServiceProvided
}
return try await rqesService.getServiceAuthorizationUrl()
}

func authorizeService(_ authorizationCode: String) async throws -> RQESServiceAuthorized {
guard let rqesService = await self.rqesUi.getRQESService() else {
throw EudiRQESUiError.noRQESServiceProvided
}
return try await rqesService.authorizeService(authorizationCode: authorizationCode)
}

func authorizeCredential(_ authorizationCode: String) async throws -> RQESServiceCredentialAuthorized {
guard let rqesService = await self.rqesUi.getRQESServiceAuthorized() else {
throw EudiRQESUiError.noRQESServiceProvided
}
return try await rqesService.authorizeCredential(authorizationCode: authorizationCode)
}

func signDocuments(_ authorizationCode: String) async throws -> [Document] {
let authorized = try await authorizeCredential(authorizationCode)
return try await authorized.signDocuments()
}

func getCredentialsList() async throws -> [CredentialInfo] {
guard let rqesService = await self.rqesUi.getRQESServiceAuthorized() else {
throw EudiRQESUiError.noRQESServiceProvided
}
return try await rqesService.getCredentialsList()
}

func getCredentialAuthorizationUrl(credentialInfo: CredentialInfo, documents: [Document]) async throws -> URL {
guard let rqesService = await self.rqesUi.getRQESServiceAuthorized() else {
throw EudiRQESUiError.noRQESServiceProvided
}
return try await rqesService.getCredentialAuthorizationUrl(credentialInfo: credentialInfo, documents: documents)
}
}
10 changes: 9 additions & 1 deletion Sources/Domain/DI/Assembly/ControllerAssembly.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ final class ControllerAssembly: Assembly {
func assemble(container: Container) {

container.register(LocalizationController.self) { r in
LocalizationControllerImpl(config: EudiRQESUi.forceConfig())
LocalizationControllerImpl(
config: EudiRQESUi.forceConfig(),
locale: Locale.current
)
}
.inObjectScope(ObjectScope.container)

Expand All @@ -35,5 +38,10 @@ final class ControllerAssembly: Assembly {
PreferencesControllerImpl()
}
.inObjectScope(ObjectScope.transient)

container.register(RQESController.self) { r in
RQESControllerImpl(rqesUi: EudiRQESUi.forceInstance())
}
.inObjectScope(ObjectScope.transient)
}
}
5 changes: 4 additions & 1 deletion Sources/Domain/DI/Assembly/InteractorAssembly.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ final class InteractorAssembly: Assembly {

func assemble(container: Container) {
container.register(RQESInteractor.self) { r in
RQESInteractorImpl(rqesUi: EudiRQESUi.forceInstance())
RQESInteractorImpl(
rqesUi: EudiRQESUi.forceInstance(),
rqesController: r.force(RQESController.self)
)
}
.inObjectScope(ObjectScope.transient)
}
Expand Down
42 changes: 16 additions & 26 deletions Sources/Domain/Interactor/RQESInteractor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,18 @@ protocol RQESInteractor: Sendable {
final class RQESInteractorImpl: RQESInteractor {

private let rqesUi: EudiRQESUi
private let rqesController: RQESController

init(rqesUi: EudiRQESUi) {
init(rqesUi: EudiRQESUi, rqesController: RQESController) {
self.rqesUi = rqesUi
self.rqesController = rqesController
}

func createRQESService(_ qtsp: QTSPData) async throws {
let rQESConfig = await rqesUi.getRQESConfig()
guard
let fileExtension = await getSession()?.document?.uri.pathExtension
let fileExtension = await getSession()?.document?.uri.pathExtension,
fileExtension.isEmpty == false
else {
throw EudiRQESUiError.noDocumentProvided
}
Expand All @@ -62,10 +65,8 @@ final class RQESInteractorImpl: RQESInteractor {

func signDocument() async throws -> Document? {
let authorizationCode = await self.getSession()?.code
let rQESServiceAuthorized = await self.rqesUi.getRQESServiceAuthorized()
if let authorizationCode, let rQESServiceAuthorized {
let authorizedCredential = try await rQESServiceAuthorized.authorizeCredential(authorizationCode: authorizationCode)
let signedDocuments = try await authorizedCredential.signDocuments()
if let authorizationCode {
let signedDocuments = try await rqesController.signDocuments(authorizationCode)
return signedDocuments.first
} else {
throw EudiRQESUiError.unableToSignHashDocument
Expand Down Expand Up @@ -93,51 +94,40 @@ final class RQESInteractorImpl: RQESInteractor {
}

func openAuthrorizationURL() async throws -> URL {
guard let rqesService = await self.rqesUi.getRQESService() else {
throw EudiRQESUiError.noRQESServiceProvided
}
let _ = try await rqesService.getRSSPMetadata()
let authorizationUrl = try await rqesService.getServiceAuthorizationUrl()
let _ = try await rqesController.getRSSPMetadata()
let authorizationUrl = try await rqesController.getServiceAuthorizationUrl()
return authorizationUrl
}

func openCredentialAuthrorizationURL() async throws -> URL {
if let uri = await self.getSession()?.document?.uri,
let certificate = await self.getSession()?.certificate {

let unsignedDocuments = [
Document(
id: UUID().uuidString,
fileURL: uri
)
]

let credentialAuthorizationUrl = try await self.rqesUi.getRQESServiceAuthorized()?.getCredentialAuthorizationUrl(
let credentialAuthorizationUrl = try await rqesController.getCredentialAuthorizationUrl(
credentialInfo: certificate,
documents: unsignedDocuments
)
return credentialAuthorizationUrl

if let credentialAuthorizationUrl {
return credentialAuthorizationUrl
} else {
throw EudiRQESUiError.unableToOpenURL
}
} else {
throw EudiRQESUiError.noDocumentProvided
}
}

func fetchCredentials() async throws -> Result<[CredentialInfo], any Error> {
if let rqesService = await self.rqesUi.getRQESService(),
let authorizationCode = await self.getSession()?.code {
if let authorizationCode = await self.getSession()?.code {
do {
let rQESServiceAuthorized = try await rqesService.authorizeService(authorizationCode: authorizationCode)
let rQESServiceAuthorized = try await rqesController.authorizeService(authorizationCode)
await self.rqesUi.setRQESServiceAuthorized(rQESServiceAuthorized)
let credentials = try? await self.rqesUi.getRQESServiceAuthorized()?.getCredentialsList()
if let credentials {
return .success(credentials)
} else {
return .failure(EudiRQESUiError.unableToFetchCredentials)
}
let credentials = try await rqesController.getCredentialsList()
return .success(credentials)
} catch {
return .failure(error)
}
Expand Down
14 changes: 7 additions & 7 deletions Sources/Infrastructure/EudiRQESUi.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public final actor EudiRQESUi {
private var session = SessionData()

private static var _rqesService: RQESService?
private static var _rQESServiceAuthorized: RQESServiceAuthorized?
private static var _rqesServiceAuthorized: RQESServiceAuthorized?

@discardableResult
public init(config: any EudiRQESUiConfig) {
Expand All @@ -41,18 +41,18 @@ public final actor EudiRQESUi {
config: any EudiRQESUiConfig,
router: any RouterGraph,
state: State = .none,
selection: SessionData = .init(),
session: SessionData = .init(),
rqesService: RQESService? = nil,
rQESServiceAuthorized: RQESServiceAuthorized? = nil
rqesServiceAuthorized: RQESServiceAuthorized? = nil
) {
DIGraph.shared.load()
self.router = router
self.session = selection
self.session = session
Self._config = config
Self._state = state
Self._shared = self
Self._rqesService = rqesService
Self._rQESServiceAuthorized = rQESServiceAuthorized
Self._rqesServiceAuthorized = rqesServiceAuthorized
}

@MainActor
Expand Down Expand Up @@ -191,11 +191,11 @@ extension EudiRQESUi {
}

func getRQESServiceAuthorized() -> RQESServiceAuthorized? {
Self._rQESServiceAuthorized
Self._rqesServiceAuthorized
}

func setRQESServiceAuthorized(_ service: RQESServiceAuthorized?) {
Self._rQESServiceAuthorized = service
Self._rqesServiceAuthorized = service
}

func getRQESConfig() -> RqesServiceConfig {
Expand Down
61 changes: 61 additions & 0 deletions Tests/Domain/Controller/TestLocalizationController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,75 @@
* governing permissions and limitations under the Licence.
*/
import XCTest
import Cuckoo
@testable import EudiRQESUi

final class TestLocalizationController: XCTestCase {

var config: MockEudiRQESUiConfig!
var controller: LocalizationController!

override func setUp() {
self.config = MockEudiRQESUiConfig()
self.controller = LocalizationControllerImpl(
config: config,
locale: .init(identifier: "en_US")
)
}

override func tearDown() {
self.config = nil
self.controller = nil
}

func testGet_WhenTranslationIsAvailableWithoutArgs_ReturnsStringTranslation() {
// Given
let customGenericErrorTranslation = "CustomGenericError"
stub(config) { mock in
when(mock.translations.get).thenReturn(
["en_US": [.genericErrorMessage: customGenericErrorTranslation]]
)
}

let result = self.controller.get(with: .genericErrorMessage)

XCTAssertEqual(result, customGenericErrorTranslation)
}

func testGet_WhenTranslationIsAvailableWithoutArgs_ReturnsLocalizedStringKeyTranslation() {
// Given
let customGenericErrorTranslation = "CustomGenericError"
stub(config) { mock in
when(mock.translations.get).thenReturn(
["en_US": [.genericErrorMessage: customGenericErrorTranslation]]
)
}

let result: LocalizedStringKey = self.controller.get(with: .genericErrorMessage, args: [])

XCTAssertEqual(result, customGenericErrorTranslation.toLocalizedStringKey)
}

func testGet_WhenTranslationIsNotAvailableWithoutArgs_ReturnsDefaultStringTranslation() {
// Given
stub(config) { mock in
when(mock.translations.get).thenReturn([:])
}

let result = self.controller.get(with: .genericErrorMessage)

XCTAssertEqual(result, LocalizableKey.genericErrorMessage.defaultTranslation(args: []))
}

func testGet_WhenTranslationIsNotAvailableWithArgs_ReturnsDefaultStringTranslation() {
// Given
stub(config) { mock in
when(mock.translations.get).thenReturn([:])
}

let result: String = self.controller.get(with: .signedBy, args: ["NISCY"])

XCTAssertEqual(result, LocalizableKey.signedBy.defaultTranslation(args: ["NISCY"]))
}

}
Loading

0 comments on commit 1b7a81a

Please sign in to comment.