diff --git a/Sources/MarvelService/Internal/Use Cases/GenerateHashUseCase.swift b/Sources/MarvelService/Internal/Use Cases/GenerateHashUseCase.swift new file mode 100644 index 00000000..f0803e08 --- /dev/null +++ b/Sources/MarvelService/Internal/Use Cases/GenerateHashUseCase.swift @@ -0,0 +1,60 @@ +//===----------------------------------------------------------------------=== +// +// This source file is part of the MarvelService open source project +// +// Copyright (c) -2025 Röck+Cöde VoF. and the MarvelService project authors +// Licensed under the EUPL 1.2 or later. +// +// See LICENSE for license information +// See CONTRIBUTORS for the list of MarvelService project authors +// +//===----------------------------------------------------------------------=== + +import CryptoKit + +import struct Foundation.Data +import struct Foundation.TimeInterval + +/// A use case that generates a MD5 hash value, which is required to authenticate any request. +struct GenerateHashUseCase { + + // MARK: Properties + + /// A private key. + private let privateKey: String + + /// A public key. + private let publicKey: String + + // MARK: Initializers + + /// Initializes this use case. + /// - Parameters: + /// - privateKey: A private key. + /// - publicKey: A public key. + init( + privateKey: String, + publicKey: String, + ) { + self.privateKey = privateKey + self.publicKey = publicKey + } + + // MARK: Functions + + /// Generates a MD5 hash value out of a given public key, private key and a timestamp. + /// - Parameter timestamp: A timestamp that changes on a request-by-request basis. + /// - Returns: A MD5 hash generated out of a private key, an public key, and a timestamp. + func callAsFunction( + timestamp: TimeInterval + ) -> String { + let stringToHash = timestamp.asString + self.privateKey + self.publicKey + let dataToHash = Data(stringToHash.utf8) + + return Insecure.MD5 + .hash(data: dataToHash) + .map { String(format: "%02x", $0) } + .joined() + } + +} diff --git a/Tests/MarvelService/Cases/Internal/Use Cases/GenerateHashUseCaseTests.swift b/Tests/MarvelService/Cases/Internal/Use Cases/GenerateHashUseCaseTests.swift new file mode 100644 index 00000000..921d409c --- /dev/null +++ b/Tests/MarvelService/Cases/Internal/Use Cases/GenerateHashUseCaseTests.swift @@ -0,0 +1,105 @@ +//===----------------------------------------------------------------------=== +// +// This source file is part of the MarvelService open source project +// +// Copyright (c) 2025 Röck+Cöde VoF. and the MarvelService project authors +// Licensed under the EUPL 1.2 or later. +// +// See LICENSE for license information +// See CONTRIBUTORS for the list of MarvelService project authors +// +//===----------------------------------------------------------------------=== + +import Testing + +import struct Foundation.TimeInterval + +@testable import struct MarvelService.GenerateHashUseCase + +@Suite("Generate Hash Use Case", .tags(.useCase)) +struct GenerateHashUseCaseTests { + + // MARK: Functions + +#if swift(>=6.2) + @Test(arguments: zip( + Input.timestamps, + Output.generatedHashes + )) + func `hash`( + timestamp: TimeInterval, + expects hash: String + ) async throws { + assertHash( + timestamp: timestamp, + expects: hash + ) + } +#else + @Test("hash", arguments: zip( + Input.timestamps, + Output.generatedHashes + )) + func hash( + timestamp: TimeInterval, + expects hash: String + ) async throws { + assertHash( + timestamp: timestamp, + expects: hash + ) + } +#endif + +} + +// MARK: - Assertions + +private extension GenerateHashUseCaseTests { + + // MARK: Functions + + /// Asserts the MD5 hash generated from the use case. + /// - Parameters: + /// - timestamp: A timestamp to use in the hash generation. + /// - hash: An expected MD5 hash string as a result of the use case. + func assertHash( + timestamp: TimeInterval, + expects hash: String + ) { + // GIVEN + let useCase: GenerateHashUseCase = .init( + privateKey: .Key.private, + publicKey: .Key.public + ) + + // WHEN + let result = useCase(timestamp: timestamp) + + // THEN + #expect(result == hash) + } + +} + +// MARK: - Constants + +private extension Output { + /// A list of outcomes that are expected from the hash generation. + static let generatedHashes: [String] = [ + "ef9ca6f930e56fb4f8a109a9003580fe", + "b500748e9f0aabc67ffc640ae9b87695", + "b537f18579112902b7ce046dddad558a", + "00fec88a254d42e3a439d49e14cd60d1" + ] +} + +private extension String { + /// A namespace assigned for Marvel API key samples. + enum Key { + /// A Marvel API private key sample. + static let `private` = "SomePrivateKey" + /// A Marvel API public key sample. + static let `public` = "SomePublicKey" + } +} diff --git a/Tests/MarvelService/Types/Extensions/Tag+Customs.swift b/Tests/MarvelService/Types/Extensions/Tag+Customs.swift index 1cbacb4f..dee53ebf 100644 --- a/Tests/MarvelService/Types/Extensions/Tag+Customs.swift +++ b/Tests/MarvelService/Types/Extensions/Tag+Customs.swift @@ -18,4 +18,7 @@ extension Tag { /// A flag that indicates tests for a type extension. @Tag static var `extension`: Self + + /// A flag that indicates tests for a use case type. + @Tag static var useCase: Self }