From c16d135730572dcc4cdee0f7ba5098282cabe00f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Omar=20Zu=CC=81n=CC=83iga=20Lagunas?= Date: Wed, 29 May 2024 16:23:41 -0600 Subject: [PATCH 01/15] added call --- .../CreateProvisioningProfileCommand.swift | 58 ++++++++++++++++++- .../Models/CreateProfileResponse.swift | 33 ++++++----- .../Models/GetProfilesResponse.swift | 12 ++++ .../Services/iTunesConnectService.swift | 37 ++++++++++++ 4 files changed, 122 insertions(+), 18 deletions(-) create mode 100644 Sources/SignHereLibrary/Models/GetProfilesResponse.swift diff --git a/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift b/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift index ed373fa..35ee9ee 100644 --- a/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift +++ b/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift @@ -38,6 +38,7 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand { ) case unableToCreateCSR(output: ShellOutput) case unableToImportIntermediaryAppleCertificate(certificate: String, output: ShellOutput) + case profileNameMissing var description: String { switch self { @@ -101,6 +102,8 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand { - Output: \(output.outputString) - Error: \(output.errorString) """ + case .profileNameMissing: + return "--auto-regenerate flag requires that you include a profile name using the argument --profile-name" } } } @@ -122,6 +125,7 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand { case intermediaryAppleCertificates = "intermediaryAppleCertificates" case certificateSigningRequestSubject = "certificateSigningRequestSubject" case profileName = "profileName" + case autoRegenerate = "autoRegenerate" } @Option(help: "The key identifier of the private key (https://developer.apple.com/documentation/appstoreconnectapi/generating_tokens_for_api_requests)") @@ -182,6 +186,9 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand { """) internal var certificateSigningRequestSubject: String + @Flag(help: "Determines if the profile should be regenated in case it already exists (optional)") + internal var autoRegenerate = false + private let files: Files private let log: Log private let shell: Shell @@ -228,7 +235,8 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand { certificateSigningRequestSubject: String, bundleIdentifierName: String?, platform: String, - profileName: String? + profileName: String?, + autoRegenerate: Bool ) { self.files = files self.log = log @@ -252,6 +260,7 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand { self.bundleIdentifierName = bundleIdentifierName self.platform = platform self.profileName = profileName + self.autoRegenerate = autoRegenerate } internal init(from decoder: Decoder) throws { @@ -286,11 +295,27 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand { certificateSigningRequestSubject: try container.decode(String.self, forKey: .certificateSigningRequestSubject), bundleIdentifierName: try container.decodeIfPresent(String.self, forKey: .bundleIdentifierName), platform: try container.decode(String.self, forKey: .platform), - profileName: try container.decodeIfPresent(String.self, forKey: .profileName) + profileName: try container.decodeIfPresent(String.self, forKey: .profileName), + autoRegenerate: try container.decode(Bool.self, forKey: .autoRegenerate) ) } internal func run() throws { + let jsonWebToken: String = try jsonWebTokenService.createToken( + keyIdentifier: keyIdentifier, + issuerID: issuerID, + secretKey: try files.read(Path(itunesConnectKeyPath)) + ) + if let profileName { + try fetchProvisioningProfile(jsonWebToken: jsonWebToken, name: profileName) + } else if autoRegenerate { + throw Error.profileNameMissing + } else { + try createProvisioningProfile(jsonWebToken: jsonWebToken) + } + } + + private func createProvisioningProfile(jsonWebToken: String) throws { let privateKey: Path = .init(privateKeyPath) let csr: Path = try createCSR(privateKey: privateKey) let jsonWebToken: String = try jsonWebTokenService.createToken( @@ -496,4 +521,33 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand { ) } } + + private func fetchProvisioningProfile(jsonWebToken: String, name: String) throws { + if let provisioningProfile = try iTunesConnectService.fetchProvisioningProfile( + jsonWebToken: jsonWebToken, + name: name + ).first { + // guard let data: Data = .init(base64Encoded: fetchedActiveCertificate.attributes.certificateContent) + // else { + // throw Error.unableToBase64DecodeCertificate(displayName: fetchedActiveCertificate.attributes.displayName) + // } + // cer = try files.uniqueTemporaryPath() + "\(fetchedActiveCertificate.id).cer" + // try files.write(data, to: cer) + // certificateId = fetchedActiveCertificate.id + print(provisioningProfile) + } else { + // let createCertificateResponse: CreateCertificateResponse = try iTunesConnectService.createCertificate( + // jsonWebToken: jsonWebToken, + // csr: csr, + // certificateType: certificateType + // ) + // guard let cerData: Data = .init(base64Encoded: createCertificateResponse.data.attributes.certificateContent) + // else { + // throw Error.unableToBase64DecodeCertificate(displayName: createCertificateResponse.data.attributes.displayName) + // } + // cer = try files.uniqueTemporaryPath() + "\(createCertificateResponse.data.id).cer" + // try files.write(cerData, to: cer) + // certificateId = createCertificateResponse.data.id + } + } } diff --git a/Sources/SignHereLibrary/Models/CreateProfileResponse.swift b/Sources/SignHereLibrary/Models/CreateProfileResponse.swift index b00ce82..628f4cd 100644 --- a/Sources/SignHereLibrary/Models/CreateProfileResponse.swift +++ b/Sources/SignHereLibrary/Models/CreateProfileResponse.swift @@ -8,20 +8,21 @@ import Foundation internal struct CreateProfileResponse: Codable { - struct CreateProfileResponseData: Codable { - struct Attributes: Codable { - var profileContent: String - var uuid: String - var name: String - var platform: String - var createdDate: Date - var profileState: String - var profileType: String - var expirationDate: Date - } - var id: String - var type: String - var attributes: Attributes - } - var data: CreateProfileResponseData + var data: ProfileResponseData } + +struct ProfileResponseData: Codable { + struct Attributes: Codable { + var profileContent: String + var uuid: String + var name: String + var platform: String + var createdDate: Date + var profileState: String + var profileType: String + var expirationDate: Date + } + var id: String + var type: String + var attributes: Attributes +} \ No newline at end of file diff --git a/Sources/SignHereLibrary/Models/GetProfilesResponse.swift b/Sources/SignHereLibrary/Models/GetProfilesResponse.swift new file mode 100644 index 0000000..397e146 --- /dev/null +++ b/Sources/SignHereLibrary/Models/GetProfilesResponse.swift @@ -0,0 +1,12 @@ +// +// GetProfilesResponse.swift +// Models +// +// Created by Omar Zuniga on 29/05/24. +// + +import Foundation + +internal struct GetProfilesResponse: Codable { + var data: [ProfileResponseData] +} diff --git a/Sources/SignHereLibrary/Services/iTunesConnectService.swift b/Sources/SignHereLibrary/Services/iTunesConnectService.swift index 781d27b..a7562a6 100644 --- a/Sources/SignHereLibrary/Services/iTunesConnectService.swift +++ b/Sources/SignHereLibrary/Services/iTunesConnectService.swift @@ -42,6 +42,10 @@ internal protocol iTunesConnectService { jsonWebToken: String, id: String ) throws + func fetchProvisioningProfile( + jsonWebToken: String, + name: String + ) throws -> [ProfileResponseData] } internal class iTunesConnectServiceImp: iTunesConnectService { @@ -440,6 +444,39 @@ internal class iTunesConnectServiceImp: iTunesConnectService { } } + func fetchProvisioningProfile( + jsonWebToken: String, + name: String + ) throws -> [ProfileResponseData] { + var urlComponents: URLComponents = .init() + urlComponents.scheme = Constants.httpsScheme + urlComponents.host = Constants.itcHost + urlComponents.path = "/v1/profiles" + urlComponents.queryItems = [ + .init(name: "filter[name]", value: name), + .init(name: "include", value: "devices") + ] + guard let url: URL = urlComponents.url + else { + throw Error.unableToCreateURL(urlComponents: urlComponents) + } + var request: URLRequest = .init(url: url) + request.setValue("Bearer \(jsonWebToken)", forHTTPHeaderField: "Authorization") + request.setValue(Constants.applicationJSONHeaderValue, forHTTPHeaderField: "Accept") + request.setValue(Constants.applicationJSONHeaderValue, forHTTPHeaderField: Constants.contentTypeHeaderName) + request.httpMethod = "GET" + let jsonDecoder: JSONDecoder = createITCApiJSONDecoder() + let data: Data = try network.execute(request: request) + do { + return try jsonDecoder.decode( + GetProfilesResponse.self, + from: data + ).data + } catch let decodingError as DecodingError { + throw Error.unableToDecodeResponse(responseData: data, decodingError: decodingError) + } + } + private func createITCApiJSONDecoder() -> JSONDecoder { let jsonDecoder: JSONDecoder = .init() let dateFormatter: DateFormatter = .init() From ca53ff0dfa993fe2e2f85713f075c995b93120a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Omar=20Zu=CC=81n=CC=83iga=20Lagunas?= Date: Wed, 29 May 2024 17:28:51 -0600 Subject: [PATCH 02/15] changed logic to handle existing profile --- .../CreateProvisioningProfileCommand.swift | 57 ++++++++----------- 1 file changed, 24 insertions(+), 33 deletions(-) diff --git a/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift b/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift index 35ee9ee..0aade05 100644 --- a/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift +++ b/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift @@ -306,13 +306,22 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand { issuerID: issuerID, secretKey: try files.read(Path(itunesConnectKeyPath)) ) - if let profileName { - try fetchProvisioningProfile(jsonWebToken: jsonWebToken, name: profileName) - } else if autoRegenerate { + if autoRegenerate, profileName == nil { throw Error.profileNameMissing - } else { + } + guard let profileName, let profile = try? fetchProvisioningProfile(jsonWebToken: jsonWebToken, name: profileName) + else { try createProvisioningProfile(jsonWebToken: jsonWebToken) + return } + guard autoRegenerate + else { + try save(profile: profile) + log.append("The profile already exists") + return + } + + try createProvisioningProfile(jsonWebToken: jsonWebToken) } private func createProvisioningProfile(jsonWebToken: String) throws { @@ -350,11 +359,7 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand { profileType: profileType, profileName: profileName ) - guard let profileData: Data = .init(base64Encoded: profileResponse.data.attributes.profileContent) - else { - throw Error.unableToBase64DecodeProfile(name: profileResponse.data.attributes.name) - } - try files.write(profileData, to: .init(outputPath)) + try save(profile: profileResponse.data) log.append(profileResponse.data.id) } @@ -522,32 +527,18 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand { } } - private func fetchProvisioningProfile(jsonWebToken: String, name: String) throws { - if let provisioningProfile = try iTunesConnectService.fetchProvisioningProfile( + private func fetchProvisioningProfile(jsonWebToken: String, name: String) throws -> ProfileResponseData? { + try iTunesConnectService.fetchProvisioningProfile( jsonWebToken: jsonWebToken, name: name - ).first { - // guard let data: Data = .init(base64Encoded: fetchedActiveCertificate.attributes.certificateContent) - // else { - // throw Error.unableToBase64DecodeCertificate(displayName: fetchedActiveCertificate.attributes.displayName) - // } - // cer = try files.uniqueTemporaryPath() + "\(fetchedActiveCertificate.id).cer" - // try files.write(data, to: cer) - // certificateId = fetchedActiveCertificate.id - print(provisioningProfile) - } else { - // let createCertificateResponse: CreateCertificateResponse = try iTunesConnectService.createCertificate( - // jsonWebToken: jsonWebToken, - // csr: csr, - // certificateType: certificateType - // ) - // guard let cerData: Data = .init(base64Encoded: createCertificateResponse.data.attributes.certificateContent) - // else { - // throw Error.unableToBase64DecodeCertificate(displayName: createCertificateResponse.data.attributes.displayName) - // } - // cer = try files.uniqueTemporaryPath() + "\(createCertificateResponse.data.id).cer" - // try files.write(cerData, to: cer) - // certificateId = createCertificateResponse.data.id + ).first + } + + private func save(profile: ProfileResponseData) throws { + guard let profileData: Data = .init(base64Encoded: profile.attributes.profileContent) + else { + throw Error.unableToBase64DecodeProfile(name: profile.attributes.name) } + try files.write(profileData, to: .init(outputPath)) } } From 340c49f5b0562804219baa5bd1722413c1629f96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Omar=20Zu=CC=81n=CC=83iga=20Lagunas?= Date: Wed, 29 May 2024 17:52:29 -0600 Subject: [PATCH 03/15] added regenerating feature --- .../CreateProvisioningProfileCommand.swift | 23 ++++++++---- .../Models/CreateProfileResponse.swift | 16 --------- .../Models/ProfileResponseData.swift | 36 +++++++++++++++++++ 3 files changed, 53 insertions(+), 22 deletions(-) create mode 100644 Sources/SignHereLibrary/Models/ProfileResponseData.swift diff --git a/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift b/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift index 0aade05..b74aae9 100644 --- a/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift +++ b/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift @@ -306,25 +306,29 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand { issuerID: issuerID, secretKey: try files.read(Path(itunesConnectKeyPath)) ) + let deviceIDs: Set = try iTunesConnectService.fetchITCDeviceIDs(jsonWebToken: jsonWebToken) if autoRegenerate, profileName == nil { throw Error.profileNameMissing } guard let profileName, let profile = try? fetchProvisioningProfile(jsonWebToken: jsonWebToken, name: profileName) else { - try createProvisioningProfile(jsonWebToken: jsonWebToken) + try createProvisioningProfile(jsonWebToken: jsonWebToken, deviceIDs: deviceIDs) return } - guard autoRegenerate + let profileDevices = Set(profile.relationships.devices.data.map { $0.id }) + guard autoRegenerate, deviceIDs != profileDevices else { try save(profile: profile) log.append("The profile already exists") return } - - try createProvisioningProfile(jsonWebToken: jsonWebToken) + let missingDevices = deviceIDs.subtracting(profileDevices) + log.append("The profile will be regenerated because it is missing the device(s): \(missingDevices.joined(separator: ", "))") + try deleteProvisioningProfile(jsonWebToken: jsonWebToken, id: profile.id) + try createProvisioningProfile(jsonWebToken: jsonWebToken, deviceIDs: deviceIDs) } - private func createProvisioningProfile(jsonWebToken: String) throws { + private func createProvisioningProfile(jsonWebToken: String, deviceIDs: Set) throws { let privateKey: Path = .init(privateKeyPath) let csr: Path = try createCSR(privateKey: privateKey) let jsonWebToken: String = try jsonWebTokenService.createToken( @@ -345,7 +349,6 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand { try importP12IdentityIntoKeychain(p12Identity: p12Identity, identityPassword: identityPassword) try importIntermediaryAppleCertificates() try updateKeychainPartitionList() - let deviceIDs: Set = try iTunesConnectService.fetchITCDeviceIDs(jsonWebToken: jsonWebToken) let profileResponse: CreateProfileResponse = try iTunesConnectService.createProfile( jsonWebToken: jsonWebToken, bundleId: try iTunesConnectService.determineBundleIdITCId( @@ -534,6 +537,14 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand { ).first } + private func deleteProvisioningProfile(jsonWebToken: String, id: String) throws { + try iTunesConnectService.deleteProvisioningProfile( + jsonWebToken: jsonWebToken, + id: id + ) + log.append("Deleted profile with id: \(id)") + } + private func save(profile: ProfileResponseData) throws { guard let profileData: Data = .init(base64Encoded: profile.attributes.profileContent) else { diff --git a/Sources/SignHereLibrary/Models/CreateProfileResponse.swift b/Sources/SignHereLibrary/Models/CreateProfileResponse.swift index 628f4cd..b49095d 100644 --- a/Sources/SignHereLibrary/Models/CreateProfileResponse.swift +++ b/Sources/SignHereLibrary/Models/CreateProfileResponse.swift @@ -9,20 +9,4 @@ import Foundation internal struct CreateProfileResponse: Codable { var data: ProfileResponseData -} - -struct ProfileResponseData: Codable { - struct Attributes: Codable { - var profileContent: String - var uuid: String - var name: String - var platform: String - var createdDate: Date - var profileState: String - var profileType: String - var expirationDate: Date - } - var id: String - var type: String - var attributes: Attributes } \ No newline at end of file diff --git a/Sources/SignHereLibrary/Models/ProfileResponseData.swift b/Sources/SignHereLibrary/Models/ProfileResponseData.swift new file mode 100644 index 0000000..8460f7e --- /dev/null +++ b/Sources/SignHereLibrary/Models/ProfileResponseData.swift @@ -0,0 +1,36 @@ +// +// CreateProfileResponse.swift +// Models +// +// Created by Omar Zuniga on 29/05/24. +// + +import Foundation + +struct ProfileResponseData: Codable { + struct Attributes: Codable { + var profileContent: String + var uuid: String + var name: String + var platform: String + var createdDate: Date + var profileState: String + var profileType: String + var expirationDate: Date + } + struct Relationships: Codable { + struct Devices: Codable { + struct DevicesData: Codable { + var id: String + var type: String + } + + var data: [DevicesData] + } + var devices: Devices + } + var id: String + var type: String + var attributes: Attributes + var relationships: Relationships +} \ No newline at end of file From 9405eae362f987929cc5e9873d2bede6e77764ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Omar=20Zu=CC=81n=CC=83iga=20Lagunas?= Date: Thu, 30 May 2024 11:25:39 -0600 Subject: [PATCH 04/15] handled logic to only regenerate some type of profiles --- .../CreateProvisioningProfileCommand.swift | 16 ++++++--- .../SignHereLibrary/Models/ProfileType.swift | 35 +++++++++++++++++++ .../Services/iTunesConnectService.swift | 2 +- 3 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 Sources/SignHereLibrary/Models/ProfileType.swift diff --git a/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift b/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift index b74aae9..ea94614 100644 --- a/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift +++ b/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift @@ -315,15 +315,12 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand { try createProvisioningProfile(jsonWebToken: jsonWebToken, deviceIDs: deviceIDs) return } - let profileDevices = Set(profile.relationships.devices.data.map { $0.id }) - guard autoRegenerate, deviceIDs != profileDevices + guard autoRegenerate, shouldRegenerate(profile: profile, with: deviceIDs) else { try save(profile: profile) log.append("The profile already exists") return } - let missingDevices = deviceIDs.subtracting(profileDevices) - log.append("The profile will be regenerated because it is missing the device(s): \(missingDevices.joined(separator: ", "))") try deleteProvisioningProfile(jsonWebToken: jsonWebToken, id: profile.id) try createProvisioningProfile(jsonWebToken: jsonWebToken, deviceIDs: deviceIDs) } @@ -552,4 +549,15 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand { } try files.write(profileData, to: .init(outputPath)) } + + private func shouldRegenerate(profile: ProfileResponseData, with deviceIDs: Set) -> Bool { + guard ProfileType(rawValue: profileType).usesDevices else { return false } + let profileDevices = Set(profile.relationships.devices.data.map { $0.id }) + let shouldRegenerate = deviceIDs != profileDevices + if shouldRegenerate { + let missingDevices = deviceIDs.subtracting(profileDevices) + log.append("The profile will be regenerated because it is missing the device(s): \(missingDevices.joined(separator: ", "))") + } + return shouldRegenerate + } } diff --git a/Sources/SignHereLibrary/Models/ProfileType.swift b/Sources/SignHereLibrary/Models/ProfileType.swift new file mode 100644 index 0000000..219fc02 --- /dev/null +++ b/Sources/SignHereLibrary/Models/ProfileType.swift @@ -0,0 +1,35 @@ +// +// ProfileType.swift +// Models +// +// Created by Omar Zuniga on 29/05/24. +// + +import Foundation + +enum ProfileType { + case development + case adHoc + case appStore + case inHouse + case direct + case unknown + + init(rawValue: String) { + switch rawValue { + case let str where str.hasSuffix("_APP_DEVELOPMENT"): self = .development + case let str where str.hasSuffix("_APP_ADHOC"): self = .adHoc + case let str where str.hasSuffix("_APP_STORE"): self = .appStore + case let str where str.hasSuffix("_APP_INHOUSE"): self = .inHouse + case let str where str.hasSuffix("_APP_DIRECT"): self = .direct + default: self = .unknown + } + } + + var usesDevices: Bool { + switch self { + case .appStore: return false + default: return true + } + } +} \ No newline at end of file diff --git a/Sources/SignHereLibrary/Services/iTunesConnectService.swift b/Sources/SignHereLibrary/Services/iTunesConnectService.swift index a7562a6..d2eed94 100644 --- a/Sources/SignHereLibrary/Services/iTunesConnectService.swift +++ b/Sources/SignHereLibrary/Services/iTunesConnectService.swift @@ -372,7 +372,7 @@ internal class iTunesConnectServiceImp: iTunesConnectService { let profileName = profileName ?? "\(certificateId)_\(profileType)_\(clock.now().timeIntervalSince1970)" var devices: CreateProfileRequest.CreateProfileRequestData.Relationships.Devices? = nil // ME: App Store profiles cannot use UDIDs - if !["IOS_APP_STORE", "MAC_APP_STORE", "TVOS_APP_STORE", "MAC_CATALYST_APP_STORE"].contains(profileType) { + if ProfileType(rawValue: profileType).usesDevices { devices = .init( data: deviceIDs.sorted().map { CreateProfileRequest.CreateProfileRequestData.Relationships.Devices.DevicesData( From 74621c3e9823243354d920c7f32d72c6e0dccf40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Omar=20Zu=CC=81n=CC=83iga=20Lagunas?= Date: Thu, 30 May 2024 12:03:03 -0600 Subject: [PATCH 05/15] fixed existing tests --- .../CreateProvisioningProfileCommand.swift | 5 ----- ...reateProvisioningProfileCommandTests.swift | 22 ++++++++++++++----- ...nnectServiceTests_test_createProfile.2.txt | 5 ++++- ...est_createProfile_iosAppStoreProfile.2.txt | 5 ++++- ...s_test_createProfile_withProfileName.2.txt | 5 ++++- .../iTunesConnectServiceTests.swift | 9 ++++++-- 6 files changed, 36 insertions(+), 15 deletions(-) diff --git a/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift b/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift index ea94614..9a3fea0 100644 --- a/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift +++ b/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift @@ -328,11 +328,6 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand { private func createProvisioningProfile(jsonWebToken: String, deviceIDs: Set) throws { let privateKey: Path = .init(privateKeyPath) let csr: Path = try createCSR(privateKey: privateKey) - let jsonWebToken: String = try jsonWebTokenService.createToken( - keyIdentifier: keyIdentifier, - issuerID: issuerID, - secretKey: try files.read(Path(itunesConnectKeyPath)) - ) let tuple: (cer: Path, certificateId: String) = try fetchOrCreateCertificate(jsonWebToken: jsonWebToken, csr: csr) let cer: Path = tuple.cer let certificateId: String = tuple.certificateId diff --git a/Tests/SignHereLibraryTests/CreateProvisioningProfileCommandTests.swift b/Tests/SignHereLibraryTests/CreateProvisioningProfileCommandTests.swift index de10048..b41df06 100644 --- a/Tests/SignHereLibraryTests/CreateProvisioningProfileCommandTests.swift +++ b/Tests/SignHereLibraryTests/CreateProvisioningProfileCommandTests.swift @@ -57,7 +57,8 @@ final class CreateProvisioningProfileCommandTests: XCTestCase { certificateSigningRequestSubject: "certificateSigningRequestSubject", bundleIdentifierName: "bundleIdentifierName", platform: "platform", - profileName: "profileName" + profileName: "profileName", + autoRegenerate: false ) isRecording = false } @@ -162,7 +163,8 @@ final class CreateProvisioningProfileCommandTests: XCTestCase { "certificateSigningRequestSubject": "certificateSigningRequestSubject", "bundleIdentifierName": "bundleIdentifierName", "platform": "platform", - "profileName": "profileName" + "profileName": "profileName", + "autoRegenerate": false } """.utf8) @@ -216,7 +218,9 @@ final class CreateProvisioningProfileCommandTests: XCTestCase { iTunesConnectService.createProfileHandler = { _, _, _, _, _, _ in self.createCreateProfileResponse() } - + iTunesConnectService.fetchProvisioningProfileHandler = { _, _ in + return [] + } // WHEN try subject.run() @@ -259,6 +263,9 @@ final class CreateProvisioningProfileCommandTests: XCTestCase { iTunesConnectService.createProfileHandler = { _, _, _, _, _, _ in self.createCreateProfileResponse() } + iTunesConnectService.fetchProvisioningProfileHandler = { _, _ in + return [] + } // WHEN try subject.run() @@ -321,10 +328,10 @@ final class CreateProvisioningProfileCommandTests: XCTestCase { private func createCreateProfileResponse() -> CreateProfileResponse { .init( - data: CreateProfileResponse.CreateProfileResponseData( + data: ProfileResponseData( id: "createdProfileITCID", type: "type", - attributes: CreateProfileResponse.CreateProfileResponseData.Attributes( + attributes: ProfileResponseData.Attributes( profileContent: "dGVzdAo=", uuid: "uuid", name: "createdProfileName", @@ -333,6 +340,11 @@ final class CreateProvisioningProfileCommandTests: XCTestCase { profileState: "profileState", profileType: "profileType", expirationDate: .init(timeIntervalSince1970: 100) + ), + relationships: ProfileResponseData.Relationships( + devices: ProfileResponseData.Relationships.Devices( + data: [] + ) ) ) ) diff --git a/Tests/SignHereLibraryTests/__Snapshots__/iTunesConnectServiceTests/iTunesConnectServiceTests_test_createProfile.2.txt b/Tests/SignHereLibraryTests/__Snapshots__/iTunesConnectServiceTests/iTunesConnectServiceTests_test_createProfile.2.txt index f9a01d1..69c2fb1 100644 --- a/Tests/SignHereLibraryTests/__Snapshots__/iTunesConnectServiceTests/iTunesConnectServiceTests_test_createProfile.2.txt +++ b/Tests/SignHereLibraryTests/__Snapshots__/iTunesConnectServiceTests/iTunesConnectServiceTests_test_createProfile.2.txt @@ -1,5 +1,5 @@ ▿ CreateProfileResponse - ▿ data: CreateProfileResponseData + ▿ data: ProfileResponseData ▿ attributes: Attributes - createdDate: 1970-01-01T00:00:00Z - expirationDate: 1970-01-01T00:01:40Z @@ -10,4 +10,7 @@ - profileType: "profileType" - uuid: "uuid" - id: "createdProfileITCID" + ▿ relationships: Relationships + ▿ devices: Devices + - data: 0 elements - type: "type" diff --git a/Tests/SignHereLibraryTests/__Snapshots__/iTunesConnectServiceTests/iTunesConnectServiceTests_test_createProfile_iosAppStoreProfile.2.txt b/Tests/SignHereLibraryTests/__Snapshots__/iTunesConnectServiceTests/iTunesConnectServiceTests_test_createProfile_iosAppStoreProfile.2.txt index f9a01d1..69c2fb1 100644 --- a/Tests/SignHereLibraryTests/__Snapshots__/iTunesConnectServiceTests/iTunesConnectServiceTests_test_createProfile_iosAppStoreProfile.2.txt +++ b/Tests/SignHereLibraryTests/__Snapshots__/iTunesConnectServiceTests/iTunesConnectServiceTests_test_createProfile_iosAppStoreProfile.2.txt @@ -1,5 +1,5 @@ ▿ CreateProfileResponse - ▿ data: CreateProfileResponseData + ▿ data: ProfileResponseData ▿ attributes: Attributes - createdDate: 1970-01-01T00:00:00Z - expirationDate: 1970-01-01T00:01:40Z @@ -10,4 +10,7 @@ - profileType: "profileType" - uuid: "uuid" - id: "createdProfileITCID" + ▿ relationships: Relationships + ▿ devices: Devices + - data: 0 elements - type: "type" diff --git a/Tests/SignHereLibraryTests/__Snapshots__/iTunesConnectServiceTests/iTunesConnectServiceTests_test_createProfile_withProfileName.2.txt b/Tests/SignHereLibraryTests/__Snapshots__/iTunesConnectServiceTests/iTunesConnectServiceTests_test_createProfile_withProfileName.2.txt index f9a01d1..69c2fb1 100644 --- a/Tests/SignHereLibraryTests/__Snapshots__/iTunesConnectServiceTests/iTunesConnectServiceTests_test_createProfile_withProfileName.2.txt +++ b/Tests/SignHereLibraryTests/__Snapshots__/iTunesConnectServiceTests/iTunesConnectServiceTests_test_createProfile_withProfileName.2.txt @@ -1,5 +1,5 @@ ▿ CreateProfileResponse - ▿ data: CreateProfileResponseData + ▿ data: ProfileResponseData ▿ attributes: Attributes - createdDate: 1970-01-01T00:00:00Z - expirationDate: 1970-01-01T00:01:40Z @@ -10,4 +10,7 @@ - profileType: "profileType" - uuid: "uuid" - id: "createdProfileITCID" + ▿ relationships: Relationships + ▿ devices: Devices + - data: 0 elements - type: "type" diff --git a/Tests/SignHereLibraryTests/iTunesConnectServiceTests.swift b/Tests/SignHereLibraryTests/iTunesConnectServiceTests.swift index 47fc880..22fdb5e 100644 --- a/Tests/SignHereLibraryTests/iTunesConnectServiceTests.swift +++ b/Tests/SignHereLibraryTests/iTunesConnectServiceTests.swift @@ -808,10 +808,10 @@ final class iTunesConnectServiceTests: XCTestCase { private func createCreateProfileResponse() -> CreateProfileResponse { .init( - data: CreateProfileResponse.CreateProfileResponseData( + data: ProfileResponseData( id: "createdProfileITCID", type: "type", - attributes: CreateProfileResponse.CreateProfileResponseData.Attributes( + attributes: ProfileResponseData.Attributes( profileContent: "dGVzdAo=", uuid: "uuid", name: "createdProfileName", @@ -820,6 +820,11 @@ final class iTunesConnectServiceTests: XCTestCase { profileState: "profileState", profileType: "profileType", expirationDate: .init(timeIntervalSince1970: 100) + ), + relationships: ProfileResponseData.Relationships( + devices: ProfileResponseData.Relationships.Devices( + data: [] + ) ) ) ) From bf7013b3aa89f1cbaa7cabb554172a89aae2f666 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Omar=20Zu=CC=81n=CC=83iga=20Lagunas?= Date: Thu, 30 May 2024 14:57:27 -0600 Subject: [PATCH 06/15] added request tests --- .../Models/ProfileResponseData.swift | 4 +- ...onnectServiceTests_test_fetchProfile.1.txt | 5 ++ ...onnectServiceTests_test_fetchProfile.2.txt | 19 +++++ ...eTests_test_fetchProfile_decodeError.1.txt | 5 ++ .../iTunesConnectServiceTests.swift | 82 +++++++++++++++++++ 5 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 Tests/SignHereLibraryTests/__Snapshots__/iTunesConnectServiceTests/iTunesConnectServiceTests_test_fetchProfile.1.txt create mode 100644 Tests/SignHereLibraryTests/__Snapshots__/iTunesConnectServiceTests/iTunesConnectServiceTests_test_fetchProfile.2.txt create mode 100644 Tests/SignHereLibraryTests/__Snapshots__/iTunesConnectServiceTests/iTunesConnectServiceTests_test_fetchProfile_decodeError.1.txt diff --git a/Sources/SignHereLibrary/Models/ProfileResponseData.swift b/Sources/SignHereLibrary/Models/ProfileResponseData.swift index 8460f7e..114a9fe 100644 --- a/Sources/SignHereLibrary/Models/ProfileResponseData.swift +++ b/Sources/SignHereLibrary/Models/ProfileResponseData.swift @@ -20,12 +20,12 @@ struct ProfileResponseData: Codable { } struct Relationships: Codable { struct Devices: Codable { - struct DevicesData: Codable { + struct Data: Codable { var id: String var type: String } - var data: [DevicesData] + var data: [Data] } var devices: Devices } diff --git a/Tests/SignHereLibraryTests/__Snapshots__/iTunesConnectServiceTests/iTunesConnectServiceTests_test_fetchProfile.1.txt b/Tests/SignHereLibraryTests/__Snapshots__/iTunesConnectServiceTests/iTunesConnectServiceTests_test_fetchProfile.1.txt new file mode 100644 index 0000000..12b94a5 --- /dev/null +++ b/Tests/SignHereLibraryTests/__Snapshots__/iTunesConnectServiceTests/iTunesConnectServiceTests_test_fetchProfile.1.txt @@ -0,0 +1,5 @@ +curl \ + --header "Accept: application/json" \ + --header "Authorization: Bearer jsonWebToken" \ + --header "Content-Type: application/json" \ + "https://api.appstoreconnect.apple.com/v1/profiles?filter%5Bname%5D=Test&include=devices" \ No newline at end of file diff --git a/Tests/SignHereLibraryTests/__Snapshots__/iTunesConnectServiceTests/iTunesConnectServiceTests_test_fetchProfile.2.txt b/Tests/SignHereLibraryTests/__Snapshots__/iTunesConnectServiceTests/iTunesConnectServiceTests_test_fetchProfile.2.txt new file mode 100644 index 0000000..1b169aa --- /dev/null +++ b/Tests/SignHereLibraryTests/__Snapshots__/iTunesConnectServiceTests/iTunesConnectServiceTests_test_fetchProfile.2.txt @@ -0,0 +1,19 @@ +▿ Optional + ▿ some: ProfileResponseData + ▿ attributes: Attributes + - createdDate: 1970-01-01T00:00:00Z + - expirationDate: 1970-01-01T00:01:40Z + - name: "getProfileName" + - platform: "platform" + - profileContent: "dGVzdAo=" + - profileState: "profileState" + - profileType: "profileType" + - uuid: "uuid" + - id: "getProfileITCID" + ▿ relationships: Relationships + ▿ devices: Devices + ▿ data: 1 element + ▿ Data + - id: "1234" + - type: "device" + - type: "type" diff --git a/Tests/SignHereLibraryTests/__Snapshots__/iTunesConnectServiceTests/iTunesConnectServiceTests_test_fetchProfile_decodeError.1.txt b/Tests/SignHereLibraryTests/__Snapshots__/iTunesConnectServiceTests/iTunesConnectServiceTests_test_fetchProfile_decodeError.1.txt new file mode 100644 index 0000000..12b94a5 --- /dev/null +++ b/Tests/SignHereLibraryTests/__Snapshots__/iTunesConnectServiceTests/iTunesConnectServiceTests_test_fetchProfile_decodeError.1.txt @@ -0,0 +1,5 @@ +curl \ + --header "Accept: application/json" \ + --header "Authorization: Bearer jsonWebToken" \ + --header "Content-Type: application/json" \ + "https://api.appstoreconnect.apple.com/v1/profiles?filter%5Bname%5D=Test&include=devices" \ No newline at end of file diff --git a/Tests/SignHereLibraryTests/iTunesConnectServiceTests.swift b/Tests/SignHereLibraryTests/iTunesConnectServiceTests.swift index 22fdb5e..7a8e362 100644 --- a/Tests/SignHereLibraryTests/iTunesConnectServiceTests.swift +++ b/Tests/SignHereLibraryTests/iTunesConnectServiceTests.swift @@ -713,6 +713,59 @@ final class iTunesConnectServiceTests: XCTestCase { } } + func test_fetchProfile() throws { + // GIVEN + let jsonEncoder: JSONEncoder = createJSONEncoder() + var networkExecutes: [Data] = [ + try jsonEncoder.encode(createFetchProfileResponse()), + ] + network.executeHandler = { _ in + networkExecutes.removeFirst() + } + + // WHEN + let value: ProfileResponseData? = try subject.fetchProvisioningProfile( + jsonWebToken: "jsonWebToken", + name: "Test" + ).first + + // THEN + for argValue in network.executeArgValues { + assertSnapshot(matching: argValue, as: .curl) + } + + assertSnapshot( + matching: value, + as: .dump + ) + } + + func test_fetchProfile_decodeError() throws { + // GIVEN + var networkExecutes: [Data] = [ + .init() + ] + network.executeHandler = { _ in + networkExecutes.removeFirst() + } + + // WHEN + XCTAssertThrowsError(try subject.fetchProvisioningProfile( + jsonWebToken: "jsonWebToken", + name: "Test" + ).first) { + if case iTunesConnectServiceImp.Error.unableToDecodeResponse = $0 { + return + } + XCTFail($0.localizedDescription) + } + + // THEN + for argValue in network.executeArgValues { + assertSnapshot(matching: argValue, as: .curl) + } + } + private func createJSONEncoder() -> JSONEncoder { let jsonEncoder: JSONEncoder = .init() let dateFormatter: DateFormatter = .init() @@ -829,4 +882,33 @@ final class iTunesConnectServiceTests: XCTestCase { ) ) } + + private func createFetchProfileResponse() -> GetProfilesResponse { + .init( + data: [ProfileResponseData( + id: "getProfileITCID", + type: "type", + attributes: ProfileResponseData.Attributes( + profileContent: "dGVzdAo=", + uuid: "uuid", + name: "getProfileName", + platform: "platform", + createdDate: .init(timeIntervalSince1970: 0), + profileState: "profileState", + profileType: "profileType", + expirationDate: .init(timeIntervalSince1970: 100) + ), + relationships: ProfileResponseData.Relationships( + devices: ProfileResponseData.Relationships.Devices( + data: [ + ProfileResponseData.Relationships.Devices.Data( + id: "1234", + type: "device" + ) + ] + ) + ) + )] + ) + } } From 536a134c890a1dc7254e89e25448c9f9cd773bda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Omar=20Zu=CC=81n=CC=83iga=20Lagunas?= Date: Thu, 30 May 2024 15:10:20 -0600 Subject: [PATCH 07/15] added model tests --- .../ProfileTypeModelTests.swift | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 Tests/SignHereLibraryTests/ProfileTypeModelTests.swift diff --git a/Tests/SignHereLibraryTests/ProfileTypeModelTests.swift b/Tests/SignHereLibraryTests/ProfileTypeModelTests.swift new file mode 100644 index 0000000..d66a30c --- /dev/null +++ b/Tests/SignHereLibraryTests/ProfileTypeModelTests.swift @@ -0,0 +1,114 @@ +// +// CreateProvisioningProfileCommandTests.swift +// SignHereLibraryTests +// +// Created by Omar Zuniga on 29/05/24. +// + +import ArgumentParser +import CoreLibrary +import CoreLibrary_GeneratedMocks +import CoreLibraryTestKit +import PathKit +import XCTest + +@testable import SignHereLibrary +@testable import SignHereLibrary_GeneratedMocks + +final class ProfileTypeModelTests: XCTestCase { + + func testDevelopmentType() { + // GIVEN + let prefixes = [ + "IOS", + "MAC", + "TVOS", + "MAC_CATALYST" + ] + let suffix = "_APP_DEVELOPMENT" + // WHEN + let profileTypes = prefixes.map { + ProfileType(rawValue: "\($0)\(suffix)") + } + // THEN + profileTypes.forEach { profileType in + XCTAssertEqual(profileType, .development) + XCTAssertEqual(profileType.usesDevices, true) + } + } + + func testAdHocType() { + // GIVEN + let prefixes = [ + "IOS", + "TVOS" + ] + let suffix = "_APP_ADHOC" + // WHEN + let profileTypes = prefixes.map { + ProfileType(rawValue: "\($0)\(suffix)") + } + // THEN + profileTypes.forEach { profileType in + XCTAssertEqual(profileType, .adHoc) + XCTAssertEqual(profileType.usesDevices, true) + } + } + + func testAppStoreType() { + // GIVEN + let prefixes = [ + "IOS", + "MAC", + "TVOS", + "MAC_CATALYST" + ] + let suffix = "_APP_STORE" + // WHEN + let profileTypes = prefixes.map { + ProfileType(rawValue: "\($0)\(suffix)") + } + // THEN + profileTypes.forEach { profileType in + XCTAssertEqual(profileType, .appStore) + XCTAssertEqual(profileType.usesDevices, false) + } + } + + + func testInHouseType() { + // GIVEN + let prefixes = [ + "IOS", + "TVOS" + ] + let suffix = "_APP_INHOUSE" + // WHEN + let profileTypes = prefixes.map { + ProfileType(rawValue: "\($0)\(suffix)") + } + // THEN + profileTypes.forEach { profileType in + XCTAssertEqual(profileType, .inHouse) + XCTAssertEqual(profileType.usesDevices, true) + } + } + + func testDirectType() { + // GIVEN + let prefixes = [ + "MAC", + "MAC_CATALYST" + ] + let suffix = "_APP_DIRECT" + // WHEN + let profileTypes = prefixes.map { + ProfileType(rawValue: "\($0)\(suffix)") + } + // THEN + profileTypes.forEach { profileType in + XCTAssertEqual(profileType, .direct) + XCTAssertEqual(profileType.usesDevices, true) + } + } +} From 2510be13f6473a4a8e23982e58beb2c5927c507e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Omar=20Zu=CC=81n=CC=83iga=20Lagunas?= Date: Thu, 30 May 2024 15:12:20 -0600 Subject: [PATCH 08/15] added error test --- .../CreateProvisioningProfileCommandTests.swift | 4 ++++ .../CreateProvisioningProfileCommandTests_testErrors.10.txt | 1 + 2 files changed, 5 insertions(+) create mode 100644 Tests/SignHereLibraryTests/__Snapshots__/CreateProvisioningProfileCommandTests/CreateProvisioningProfileCommandTests_testErrors.10.txt diff --git a/Tests/SignHereLibraryTests/CreateProvisioningProfileCommandTests.swift b/Tests/SignHereLibraryTests/CreateProvisioningProfileCommandTests.swift index b41df06..ef60d72 100644 --- a/Tests/SignHereLibraryTests/CreateProvisioningProfileCommandTests.swift +++ b/Tests/SignHereLibraryTests/CreateProvisioningProfileCommandTests.swift @@ -143,6 +143,10 @@ final class CreateProvisioningProfileCommandTests: XCTestCase { ).description, as: .lines ) + assertSnapshot( + matching: CreateProvisioningProfileCommand.Error.profileNameMissing.description, + as: .lines + ) } func test_initDecoder() throws { diff --git a/Tests/SignHereLibraryTests/__Snapshots__/CreateProvisioningProfileCommandTests/CreateProvisioningProfileCommandTests_testErrors.10.txt b/Tests/SignHereLibraryTests/__Snapshots__/CreateProvisioningProfileCommandTests/CreateProvisioningProfileCommandTests_testErrors.10.txt new file mode 100644 index 0000000..40e4768 --- /dev/null +++ b/Tests/SignHereLibraryTests/__Snapshots__/CreateProvisioningProfileCommandTests/CreateProvisioningProfileCommandTests_testErrors.10.txt @@ -0,0 +1 @@ +--auto-regenerate flag requires that you include a profile name using the argument --profile-name \ No newline at end of file From 9dad99a95b9d69753529febbaa23f30d5047da82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Omar=20Zu=CC=81n=CC=83iga=20Lagunas?= Date: Thu, 30 May 2024 16:25:00 -0600 Subject: [PATCH 09/15] added test when the profile already exists --- ...reateProvisioningProfileCommandTests.swift | 52 +++++++++++++++++++ ...ts_test_execute_profileAlreadyExists.1.txt | 1 + ...ts_test_execute_profileAlreadyExists.2.txt | 4 ++ 3 files changed, 57 insertions(+) create mode 100644 Tests/SignHereLibraryTests/__Snapshots__/CreateProvisioningProfileCommandTests/CreateProvisioningProfileCommandTests_test_execute_profileAlreadyExists.1.txt create mode 100644 Tests/SignHereLibraryTests/__Snapshots__/CreateProvisioningProfileCommandTests/CreateProvisioningProfileCommandTests_test_execute_profileAlreadyExists.2.txt diff --git a/Tests/SignHereLibraryTests/CreateProvisioningProfileCommandTests.swift b/Tests/SignHereLibraryTests/CreateProvisioningProfileCommandTests.swift index ef60d72..117d070 100644 --- a/Tests/SignHereLibraryTests/CreateProvisioningProfileCommandTests.swift +++ b/Tests/SignHereLibraryTests/CreateProvisioningProfileCommandTests.swift @@ -189,6 +189,7 @@ final class CreateProvisioningProfileCommandTests: XCTestCase { XCTAssertEqual(subject.bundleIdentifierName, "bundleIdentifierName") XCTAssertEqual(subject.platform, "platform") XCTAssertEqual(subject.profileName, "profileName") + XCTAssertEqual(subject.autoRegenerate, false) } func test_execute_alreadyActiveCertificate() throws { @@ -237,6 +238,9 @@ final class CreateProvisioningProfileCommandTests: XCTestCase { matching: log.appendArgValues, as: .dump ) + + XCTAssertEqual(executeLaunchPaths.count, 0) + XCTAssertEqual(fileDataReads.count, 0) } func test_execute_noActiveCertificates() throws { @@ -283,6 +287,54 @@ final class CreateProvisioningProfileCommandTests: XCTestCase { matching: log.appendArgValues, as: .dump ) + + XCTAssertEqual(executeLaunchPaths.count, 0) + XCTAssertEqual(fileDataReads.count, 0) + } + + func test_execute_profileAlreadyExists() throws { + // GIVEN + var executeLaunchPaths: [ShellOutput] = [] + + shell.executeLaunchPathHandler = { _, _, _, _ in + XCTAssert(false, "Shouldn't be executed") + return executeLaunchPaths.removeFirst() + } + var fileDataReads: [Data] = [ + Data("iTunesConnectAPIKey".utf8), + ] + files.readPathHandler = { _ in + fileDataReads.removeFirst() + } + iTunesConnectService.fetchActiveCertificatesHandler = { _, _, _, _ in + XCTAssert(false, "Shouldn't be executed") + return [] + } + iTunesConnectService.createCertificateHandler = { _, _, _ in + XCTAssert(false, "Shouldn't be executed") + return self.createCreateCertificateResponse() + } + iTunesConnectService.createProfileHandler = { _, _, _, _, _, _ in + XCTAssert(false, "Shouldn't be executed") + return self.createCreateProfileResponse() + } + iTunesConnectService.fetchProvisioningProfileHandler = { _, _ in + return [self.createCreateProfileResponse().data] + } + // WHEN + try subject.run() + + // THEN + assertSnapshot( + matching: shell.executeLaunchPathArgValues, + as: .dump + ) + assertSnapshot( + matching: log.appendArgValues, + as: .dump + ) + + XCTAssertEqual(fileDataReads.count, 0) } private func createDownloadCertificateResponse() -> DownloadCertificateResponse { diff --git a/Tests/SignHereLibraryTests/__Snapshots__/CreateProvisioningProfileCommandTests/CreateProvisioningProfileCommandTests_test_execute_profileAlreadyExists.1.txt b/Tests/SignHereLibraryTests/__Snapshots__/CreateProvisioningProfileCommandTests/CreateProvisioningProfileCommandTests_test_execute_profileAlreadyExists.1.txt new file mode 100644 index 0000000..a6eff20 --- /dev/null +++ b/Tests/SignHereLibraryTests/__Snapshots__/CreateProvisioningProfileCommandTests/CreateProvisioningProfileCommandTests_test_execute_profileAlreadyExists.1.txt @@ -0,0 +1 @@ +- 0 elements diff --git a/Tests/SignHereLibraryTests/__Snapshots__/CreateProvisioningProfileCommandTests/CreateProvisioningProfileCommandTests_test_execute_profileAlreadyExists.2.txt b/Tests/SignHereLibraryTests/__Snapshots__/CreateProvisioningProfileCommandTests/CreateProvisioningProfileCommandTests_test_execute_profileAlreadyExists.2.txt new file mode 100644 index 0000000..3fd8a7e --- /dev/null +++ b/Tests/SignHereLibraryTests/__Snapshots__/CreateProvisioningProfileCommandTests/CreateProvisioningProfileCommandTests_test_execute_profileAlreadyExists.2.txt @@ -0,0 +1,4 @@ +▿ 1 element + ▿ (2 elements) + - .0: "The profile already exists" + - .1: "\n" From b9fec86511985b78db75e9226999ec118e7a0ff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Omar=20Zu=CC=81n=CC=83iga=20Lagunas?= Date: Thu, 30 May 2024 18:05:20 -0600 Subject: [PATCH 10/15] added regeneration tests --- ...reateProvisioningProfileCommandTests.swift | 67 ++++++++++++-- ...rofileShouldRegenerateWithNewDevices.1.txt | 91 +++++++++++++++++++ ...rofileShouldRegenerateWithNewDevices.2.txt | 10 ++ 3 files changed, 162 insertions(+), 6 deletions(-) create mode 100644 Tests/SignHereLibraryTests/__Snapshots__/CreateProvisioningProfileCommandTests/CreateProvisioningProfileCommandTests_test_execute_profileShouldRegenerateWithNewDevices.1.txt create mode 100644 Tests/SignHereLibraryTests/__Snapshots__/CreateProvisioningProfileCommandTests/CreateProvisioningProfileCommandTests_test_execute_profileShouldRegenerateWithNewDevices.2.txt diff --git a/Tests/SignHereLibraryTests/CreateProvisioningProfileCommandTests.swift b/Tests/SignHereLibraryTests/CreateProvisioningProfileCommandTests.swift index 117d070..84985d8 100644 --- a/Tests/SignHereLibraryTests/CreateProvisioningProfileCommandTests.swift +++ b/Tests/SignHereLibraryTests/CreateProvisioningProfileCommandTests.swift @@ -223,9 +223,6 @@ final class CreateProvisioningProfileCommandTests: XCTestCase { iTunesConnectService.createProfileHandler = { _, _, _, _, _, _ in self.createCreateProfileResponse() } - iTunesConnectService.fetchProvisioningProfileHandler = { _, _ in - return [] - } // WHEN try subject.run() @@ -271,9 +268,6 @@ final class CreateProvisioningProfileCommandTests: XCTestCase { iTunesConnectService.createProfileHandler = { _, _, _, _, _, _ in self.createCreateProfileResponse() } - iTunesConnectService.fetchProvisioningProfileHandler = { _, _ in - return [] - } // WHEN try subject.run() @@ -337,6 +331,67 @@ final class CreateProvisioningProfileCommandTests: XCTestCase { XCTAssertEqual(fileDataReads.count, 0) } + func test_execute_profileShouldRegenerateWithNewDevices() throws { + // GIVEN + var previousProfileWasDeleted = false + + files.uniqueTemporaryPathHandler = { + Path("/unique_temporary_path_\(self.files.uniqueTemporaryPathCallCount)") + } + var executeLaunchPaths: [ShellOutput] = [ + .init(status: 0, data: .init("createCSR".utf8), errorData: .init()), + .init(status: 0, data: .init("createPEM".utf8), errorData: .init()), + .init(status: 0, data: .init("createP12Identity".utf8), errorData: .init()), + .init(status: 0, data: .init("importP12IdentityIntoKeychain".utf8), errorData: .init()), + .init(status: 0, data: .init("importIntermediateAppleCertificate".utf8), errorData: .init()), + .init(status: 0, data: .init("updateKeychainPartitionList".utf8), errorData: .init()) + ] + shell.executeLaunchPathHandler = { _, _, _, _ in + executeLaunchPaths.removeFirst() + } + var fileDataReads: [Data] = [ + Data("iTunesConnectAPIKey".utf8) + ] + files.readPathHandler = { _ in + fileDataReads.removeFirst() + } + iTunesConnectService.fetchActiveCertificatesHandler = { _, _, _, _ in + self.createDownloadCertificateResponse().data + } + iTunesConnectService.createCertificateHandler = { _, _, _ in + self.createCreateCertificateResponse() + } + iTunesConnectService.createProfileHandler = { _, _, _, _, _, _ in + self.createCreateProfileResponse() + } + iTunesConnectService.fetchITCDeviceIDsHandler = { _ in + Set(["deviceID"]) + } + iTunesConnectService.deleteProvisioningProfileHandler = { _, _ in + previousProfileWasDeleted = true + } + iTunesConnectService.fetchProvisioningProfileHandler = { _, _ in + return [self.createCreateProfileResponse().data] + } + // WHEN + subject.autoRegenerate = true + try subject.run() + + // THEN + assertSnapshot( + matching: shell.executeLaunchPathArgValues, + as: .dump + ) + assertSnapshot( + matching: log.appendArgValues, + as: .dump + ) + + XCTAssertEqual(executeLaunchPaths.count, 0) + XCTAssertEqual(fileDataReads.count, 0) + XCTAssertTrue(previousProfileWasDeleted) + } + private func createDownloadCertificateResponse() -> DownloadCertificateResponse { DownloadCertificateResponse( data: [ diff --git a/Tests/SignHereLibraryTests/__Snapshots__/CreateProvisioningProfileCommandTests/CreateProvisioningProfileCommandTests_test_execute_profileShouldRegenerateWithNewDevices.1.txt b/Tests/SignHereLibraryTests/__Snapshots__/CreateProvisioningProfileCommandTests/CreateProvisioningProfileCommandTests_test_execute_profileShouldRegenerateWithNewDevices.1.txt new file mode 100644 index 0000000..79fb71a --- /dev/null +++ b/Tests/SignHereLibraryTests/__Snapshots__/CreateProvisioningProfileCommandTests/CreateProvisioningProfileCommandTests_test_execute_profileShouldRegenerateWithNewDevices.1.txt @@ -0,0 +1,91 @@ +▿ 6 elements + ▿ (4 elements) + - .0: "/usr/bin/env" + ▿ .1: 9 elements + - "/opensslPath" + - "req" + - "-new" + - "-key" + - "privateKeyPath" + - "-out" + - "/unique_temporary_path_1/certificate_request.csr" + - "-subj" + - "certificateSigningRequestSubject" + - .2: Optional>.none + - .3: Optional.none + ▿ (4 elements) + - .0: "/usr/bin/env" + ▿ .1: 10 elements + - "/opensslPath" + - "x509" + - "-inform" + - "DER" + - "-outform" + - "PEM" + - "-in" + - "/unique_temporary_path_2/activeCertID.cer" + - "-out" + - "/unique_temporary_path_3/certificate.pem" + - .2: Optional>.none + - .3: Optional.none + ▿ (4 elements) + - .0: "/usr/bin/env" + ▿ .1: 17 elements + - "/opensslPath" + - "pkcs12" + - "-export" + - "-macalg" + - "sha1" + - "-keypbe" + - "PBE-SHA1-3DES" + - "-certpbe" + - "PBE-SHA1-3DES" + - "-inkey" + - "privateKeyPath" + - "-in" + - "/unique_temporary_path_3/certificate.pem" + - "-passout" + - "pass:uuid_1" + - "-out" + - "/unique_temporary_path_4/identity.p12" + - .2: Optional>.none + - .3: Optional.none + ▿ (4 elements) + - .0: "/usr/bin/env" + ▿ .1: 9 elements + - "security" + - "import" + - "/unique_temporary_path_4/identity.p12" + - "-k" + - "keychainName" + - "-P" + - "uuid_1" + - "-T" + - "/usr/bin/codesign" + - .2: Optional>.none + - .3: Optional.none + ▿ (4 elements) + - .0: "/usr/bin/env" + ▿ .1: 7 elements + - "security" + - "import" + - "/intermediaryAppleCertificate" + - "-k" + - "keychainName" + - "-T" + - "/usr/bin/codesign" + - .2: Optional>.none + - .3: Optional.none + ▿ (4 elements) + - .0: "/usr/bin/env" + ▿ .1: 8 elements + - "security" + - "set-key-partition-list" + - "-S" + - "apple-tool:,apple:,codesign:" + - "-s" + - "-k" + - "keychainPassword" + - "keychainName" + - .2: Optional>.none + - .3: Optional.none diff --git a/Tests/SignHereLibraryTests/__Snapshots__/CreateProvisioningProfileCommandTests/CreateProvisioningProfileCommandTests_test_execute_profileShouldRegenerateWithNewDevices.2.txt b/Tests/SignHereLibraryTests/__Snapshots__/CreateProvisioningProfileCommandTests/CreateProvisioningProfileCommandTests_test_execute_profileShouldRegenerateWithNewDevices.2.txt new file mode 100644 index 0000000..ef0a376 --- /dev/null +++ b/Tests/SignHereLibraryTests/__Snapshots__/CreateProvisioningProfileCommandTests/CreateProvisioningProfileCommandTests_test_execute_profileShouldRegenerateWithNewDevices.2.txt @@ -0,0 +1,10 @@ +▿ 3 elements + ▿ (2 elements) + - .0: "The profile will be regenerated because it is missing the device(s): deviceID" + - .1: "\n" + ▿ (2 elements) + - .0: "Deleted profile with id: createdProfileITCID" + - .1: "\n" + ▿ (2 elements) + - .0: "createdProfileITCID" + - .1: "\n" From bc8e312fce50e5c1752894ee9b53b14c6e6d609a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Omar=20Zu=CC=81n=CC=83iga=20Lagunas?= Date: Thu, 30 May 2024 18:11:40 -0600 Subject: [PATCH 11/15] handled case when the device hasnt changed --- ...reateProvisioningProfileCommandTests.swift | 46 +++++++++++++++++++ ...leShouldNotRegenerateWithSameDevices.1.txt | 1 + ...leShouldNotRegenerateWithSameDevices.2.txt | 4 ++ 3 files changed, 51 insertions(+) create mode 100644 Tests/SignHereLibraryTests/__Snapshots__/CreateProvisioningProfileCommandTests/CreateProvisioningProfileCommandTests_test_execute_profileShouldNotRegenerateWithSameDevices.1.txt create mode 100644 Tests/SignHereLibraryTests/__Snapshots__/CreateProvisioningProfileCommandTests/CreateProvisioningProfileCommandTests_test_execute_profileShouldNotRegenerateWithSameDevices.2.txt diff --git a/Tests/SignHereLibraryTests/CreateProvisioningProfileCommandTests.swift b/Tests/SignHereLibraryTests/CreateProvisioningProfileCommandTests.swift index 84985d8..1f29849 100644 --- a/Tests/SignHereLibraryTests/CreateProvisioningProfileCommandTests.swift +++ b/Tests/SignHereLibraryTests/CreateProvisioningProfileCommandTests.swift @@ -392,6 +392,52 @@ final class CreateProvisioningProfileCommandTests: XCTestCase { XCTAssertTrue(previousProfileWasDeleted) } + func test_execute_profileShouldNotRegenerateWithSameDevices() throws { + // GIVEN + var executeLaunchPaths: [ShellOutput] = [] + + shell.executeLaunchPathHandler = { _, _, _, _ in + XCTAssert(false, "Shouldn't be executed") + return executeLaunchPaths.removeFirst() + } + var fileDataReads: [Data] = [ + Data("iTunesConnectAPIKey".utf8), + ] + files.readPathHandler = { _ in + fileDataReads.removeFirst() + } + iTunesConnectService.fetchActiveCertificatesHandler = { _, _, _, _ in + XCTAssert(false, "Shouldn't be executed") + return [] + } + iTunesConnectService.createCertificateHandler = { _, _, _ in + XCTAssert(false, "Shouldn't be executed") + return self.createCreateCertificateResponse() + } + iTunesConnectService.createProfileHandler = { _, _, _, _, _, _ in + XCTAssert(false, "Shouldn't be executed") + return self.createCreateProfileResponse() + } + iTunesConnectService.fetchProvisioningProfileHandler = { _, _ in + return [self.createCreateProfileResponse().data] + } + // WHEN + subject.autoRegenerate = true + try subject.run() + + // THEN + assertSnapshot( + matching: shell.executeLaunchPathArgValues, + as: .dump + ) + assertSnapshot( + matching: log.appendArgValues, + as: .dump + ) + + XCTAssertEqual(fileDataReads.count, 0) + } + private func createDownloadCertificateResponse() -> DownloadCertificateResponse { DownloadCertificateResponse( data: [ diff --git a/Tests/SignHereLibraryTests/__Snapshots__/CreateProvisioningProfileCommandTests/CreateProvisioningProfileCommandTests_test_execute_profileShouldNotRegenerateWithSameDevices.1.txt b/Tests/SignHereLibraryTests/__Snapshots__/CreateProvisioningProfileCommandTests/CreateProvisioningProfileCommandTests_test_execute_profileShouldNotRegenerateWithSameDevices.1.txt new file mode 100644 index 0000000..a6eff20 --- /dev/null +++ b/Tests/SignHereLibraryTests/__Snapshots__/CreateProvisioningProfileCommandTests/CreateProvisioningProfileCommandTests_test_execute_profileShouldNotRegenerateWithSameDevices.1.txt @@ -0,0 +1 @@ +- 0 elements diff --git a/Tests/SignHereLibraryTests/__Snapshots__/CreateProvisioningProfileCommandTests/CreateProvisioningProfileCommandTests_test_execute_profileShouldNotRegenerateWithSameDevices.2.txt b/Tests/SignHereLibraryTests/__Snapshots__/CreateProvisioningProfileCommandTests/CreateProvisioningProfileCommandTests_test_execute_profileShouldNotRegenerateWithSameDevices.2.txt new file mode 100644 index 0000000..3fd8a7e --- /dev/null +++ b/Tests/SignHereLibraryTests/__Snapshots__/CreateProvisioningProfileCommandTests/CreateProvisioningProfileCommandTests_test_execute_profileShouldNotRegenerateWithSameDevices.2.txt @@ -0,0 +1,4 @@ +▿ 1 element + ▿ (2 elements) + - .0: "The profile already exists" + - .1: "\n" From c067f1ca50186f79bb5f03a84c462b693dd4e260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Omar=20Zu=CC=81n=CC=83iga=20Lagunas?= Date: Thu, 30 May 2024 18:28:28 -0600 Subject: [PATCH 12/15] added filter to guarantee it has the same name --- .../Commands/CreateProvisioningProfileCommand.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift b/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift index 9a3fea0..813cca4 100644 --- a/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift +++ b/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift @@ -526,7 +526,7 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand { try iTunesConnectService.fetchProvisioningProfile( jsonWebToken: jsonWebToken, name: name - ).first + ).first(where: { $0.attributes.name == name }) } private func deleteProvisioningProfile(jsonWebToken: String, id: String) throws { From b6fb2153b2c213f723b9f83cb7da9751ad4691ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Omar=20Zu=CC=81n=CC=83iga=20Lagunas?= Date: Fri, 31 May 2024 11:13:03 -0600 Subject: [PATCH 13/15] added docs to readme --- README.md | 6 ++++-- .../Commands/CreateProvisioningProfileCommand.swift | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c8366e5..876e690 100644 --- a/README.md +++ b/README.md @@ -141,8 +141,7 @@ OPTIONS: OpenSSL documentation for this flag (https://www.openssl.org/docs/manmaster/man1/openssl-req.html): -Sets - subject name for new request or supersedes the + Sets subject name for new request or supersedes the subject name when processing a certificate request. The arg must be formatted as @@ -157,6 +156,9 @@ Sets specify the members of the set. Example: /DC=org/DC=OpenSSL/DC=users/UID=123456+CN=JohnDoe + --auto-regenerate + Defines if the profile should be regenated in case it + already exists (optional) -h, --help Show help information. diff --git a/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift b/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift index 813cca4..a3e20b7 100644 --- a/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift +++ b/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift @@ -186,7 +186,7 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand { """) internal var certificateSigningRequestSubject: String - @Flag(help: "Determines if the profile should be regenated in case it already exists (optional)") + @Flag(help: "Defines if the profile should be regenated in case it already exists (optional)") internal var autoRegenerate = false private let files: Files From d375a4429fd16783d23ab2ff22f8603cbc49700c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Omar=20Zu=CC=81n=CC=83iga=20Lagunas?= Date: Fri, 31 May 2024 11:22:45 -0600 Subject: [PATCH 14/15] typo fix --- README.md | 4 ++-- .../Commands/CreateProvisioningProfileCommand.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 876e690..66e08fb 100644 --- a/README.md +++ b/README.md @@ -157,8 +157,8 @@ OPTIONS: /DC=org/DC=OpenSSL/DC=users/UID=123456+CN=JohnDoe --auto-regenerate - Defines if the profile should be regenated in case it - already exists (optional) + Defines if the profile should be regenerated in case + it already exists (optional) -h, --help Show help information. diff --git a/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift b/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift index a3e20b7..24e22ce 100644 --- a/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift +++ b/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift @@ -186,7 +186,7 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand { """) internal var certificateSigningRequestSubject: String - @Flag(help: "Defines if the profile should be regenated in case it already exists (optional)") + @Flag(help: "Defines if the profile should be regenerated in case it already exists (optional)") internal var autoRegenerate = false private let files: Files From 94521f6c4eda2103b9f90762672231d62443aaef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Omar=20Zu=CC=81n=CC=83iga=20Lagunas?= Date: Fri, 31 May 2024 13:16:39 -0600 Subject: [PATCH 15/15] fixed comments and tests --- .../Commands/CreateProvisioningProfileCommand.swift | 9 ++++++--- .../Models/CreateProfileResponse.swift | 2 +- .../SignHereLibrary/Models/ProfileResponseData.swift | 2 +- Sources/SignHereLibrary/Models/ProfileType.swift | 2 +- .../Services/iTunesConnectService.swift | 2 +- .../CreateProvisioningProfileCommandTests.swift | 12 +++++++++--- 6 files changed, 19 insertions(+), 10 deletions(-) diff --git a/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift b/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift index 24e22ce..0eb986f 100644 --- a/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift +++ b/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift @@ -307,9 +307,6 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand { secretKey: try files.read(Path(itunesConnectKeyPath)) ) let deviceIDs: Set = try iTunesConnectService.fetchITCDeviceIDs(jsonWebToken: jsonWebToken) - if autoRegenerate, profileName == nil { - throw Error.profileNameMissing - } guard let profileName, let profile = try? fetchProvisioningProfile(jsonWebToken: jsonWebToken, name: profileName) else { try createProvisioningProfile(jsonWebToken: jsonWebToken, deviceIDs: deviceIDs) @@ -555,4 +552,10 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand { } return shouldRegenerate } + + mutating internal func validate() throws { + if autoRegenerate, profileName == nil { + throw Error.profileNameMissing + } + } } diff --git a/Sources/SignHereLibrary/Models/CreateProfileResponse.swift b/Sources/SignHereLibrary/Models/CreateProfileResponse.swift index b49095d..279c92e 100644 --- a/Sources/SignHereLibrary/Models/CreateProfileResponse.swift +++ b/Sources/SignHereLibrary/Models/CreateProfileResponse.swift @@ -9,4 +9,4 @@ import Foundation internal struct CreateProfileResponse: Codable { var data: ProfileResponseData -} \ No newline at end of file +} diff --git a/Sources/SignHereLibrary/Models/ProfileResponseData.swift b/Sources/SignHereLibrary/Models/ProfileResponseData.swift index 114a9fe..48e2ce2 100644 --- a/Sources/SignHereLibrary/Models/ProfileResponseData.swift +++ b/Sources/SignHereLibrary/Models/ProfileResponseData.swift @@ -33,4 +33,4 @@ struct ProfileResponseData: Codable { var type: String var attributes: Attributes var relationships: Relationships -} \ No newline at end of file +} diff --git a/Sources/SignHereLibrary/Models/ProfileType.swift b/Sources/SignHereLibrary/Models/ProfileType.swift index 219fc02..abdd8fd 100644 --- a/Sources/SignHereLibrary/Models/ProfileType.swift +++ b/Sources/SignHereLibrary/Models/ProfileType.swift @@ -32,4 +32,4 @@ enum ProfileType { default: return true } } -} \ No newline at end of file +} diff --git a/Sources/SignHereLibrary/Services/iTunesConnectService.swift b/Sources/SignHereLibrary/Services/iTunesConnectService.swift index d2eed94..97def27 100644 --- a/Sources/SignHereLibrary/Services/iTunesConnectService.swift +++ b/Sources/SignHereLibrary/Services/iTunesConnectService.swift @@ -448,7 +448,7 @@ internal class iTunesConnectServiceImp: iTunesConnectService { jsonWebToken: String, name: String ) throws -> [ProfileResponseData] { - var urlComponents: URLComponents = .init() + var urlComponents: URLComponents = .init() urlComponents.scheme = Constants.httpsScheme urlComponents.host = Constants.itcHost urlComponents.path = "/v1/profiles" diff --git a/Tests/SignHereLibraryTests/CreateProvisioningProfileCommandTests.swift b/Tests/SignHereLibraryTests/CreateProvisioningProfileCommandTests.swift index 1f29849..b8c2cfb 100644 --- a/Tests/SignHereLibraryTests/CreateProvisioningProfileCommandTests.swift +++ b/Tests/SignHereLibraryTests/CreateProvisioningProfileCommandTests.swift @@ -289,6 +289,7 @@ final class CreateProvisioningProfileCommandTests: XCTestCase { func test_execute_profileAlreadyExists() throws { // GIVEN var executeLaunchPaths: [ShellOutput] = [] + let responseObject = createCreateProfileResponse().data shell.executeLaunchPathHandler = { _, _, _, _ in XCTAssert(false, "Shouldn't be executed") @@ -313,9 +314,10 @@ final class CreateProvisioningProfileCommandTests: XCTestCase { return self.createCreateProfileResponse() } iTunesConnectService.fetchProvisioningProfileHandler = { _, _ in - return [self.createCreateProfileResponse().data] + return [responseObject] } // WHEN + subject.profileName = responseObject.attributes.name try subject.run() // THEN @@ -334,6 +336,7 @@ final class CreateProvisioningProfileCommandTests: XCTestCase { func test_execute_profileShouldRegenerateWithNewDevices() throws { // GIVEN var previousProfileWasDeleted = false + let responseObject = createCreateProfileResponse().data files.uniqueTemporaryPathHandler = { Path("/unique_temporary_path_\(self.files.uniqueTemporaryPathCallCount)") @@ -371,9 +374,10 @@ final class CreateProvisioningProfileCommandTests: XCTestCase { previousProfileWasDeleted = true } iTunesConnectService.fetchProvisioningProfileHandler = { _, _ in - return [self.createCreateProfileResponse().data] + return [responseObject] } // WHEN + subject.profileName = responseObject.attributes.name subject.autoRegenerate = true try subject.run() @@ -395,6 +399,7 @@ final class CreateProvisioningProfileCommandTests: XCTestCase { func test_execute_profileShouldNotRegenerateWithSameDevices() throws { // GIVEN var executeLaunchPaths: [ShellOutput] = [] + let responseObject = createCreateProfileResponse().data shell.executeLaunchPathHandler = { _, _, _, _ in XCTAssert(false, "Shouldn't be executed") @@ -419,9 +424,10 @@ final class CreateProvisioningProfileCommandTests: XCTestCase { return self.createCreateProfileResponse() } iTunesConnectService.fetchProvisioningProfileHandler = { _, _ in - return [self.createCreateProfileResponse().data] + return [responseObject] } // WHEN + subject.profileName = responseObject.attributes.name subject.autoRegenerate = true try subject.run()