diff --git a/Sources/AppStoreConnectCLI/Commands/TestFlight/Sync/TestFlightProgramDifference.swift b/Sources/AppStoreConnectCLI/Commands/TestFlight/Sync/TestFlightProgramDifference.swift index e63e2682..3a96883f 100644 --- a/Sources/AppStoreConnectCLI/Commands/TestFlight/Sync/TestFlightProgramDifference.swift +++ b/Sources/AppStoreConnectCLI/Commands/TestFlight/Sync/TestFlightProgramDifference.swift @@ -52,9 +52,20 @@ struct TestFlightProgramDifference { } } + enum Error: LocalizedError, Equatable { + case duplicateTesters(email: String) + + var errorDescription: String? { + switch self { + case .duplicateTesters(let email): + return "There are two beta testers with the same email '\(email)' exists in your account, please clean that up before continue." + } + } + } + let changes: [Change] - init(local: TestFlightProgram, remote: TestFlightProgram) { + init(local: TestFlightProgram, remote: TestFlightProgram) throws { var changes: [Change] = [] // Groups @@ -80,6 +91,10 @@ struct TestFlightProgramDifference { let remoteApps = remoteTester.apps let remoteBetaGroups = remoteTester.betaGroups + if remote.testers.filter({ $0.email == remoteTester.email}).count > 1 { + throw Error.duplicateTesters(email: remoteTester.email ?? "") + } + if let localTester = local.testers.first(where: { $0.email == remoteTester.email }) { let appsToAdd = localTester.apps.filter { app in let appIds = remoteApps.map(\.id) + remoteBetaGroups.compactMap(\.app?.id) diff --git a/Sources/AppStoreConnectCLI/Commands/TestFlight/Sync/TestFlightPushCommand.swift b/Sources/AppStoreConnectCLI/Commands/TestFlight/Sync/TestFlightPushCommand.swift index d336c110..50ecea9e 100644 --- a/Sources/AppStoreConnectCLI/Commands/TestFlight/Sync/TestFlightPushCommand.swift +++ b/Sources/AppStoreConnectCLI/Commands/TestFlight/Sync/TestFlightPushCommand.swift @@ -26,7 +26,7 @@ struct TestFlightPushCommand: CommonParsableCommand { let local = try FileSystem.readTestFlightConfiguration(from: inputPath) let remote = try service.getTestFlightProgram() - let difference = TestFlightProgramDifference(local: local, remote: remote) + let difference = try TestFlightProgramDifference(local: local, remote: remote) difference.changes.forEach { print($0.description) } diff --git a/Sources/FileSystem/Configuration/TestFlightConfigurationProcessor.swift b/Sources/FileSystem/Configuration/TestFlightConfigurationProcessor.swift index d52d39f4..fbc7f1c3 100644 --- a/Sources/FileSystem/Configuration/TestFlightConfigurationProcessor.swift +++ b/Sources/FileSystem/Configuration/TestFlightConfigurationProcessor.swift @@ -60,18 +60,25 @@ struct TestFlightConfigurationProcessor { enum Error: LocalizedError { case testerNotInTestersList(email: String, betaGroup: BetaGroup, app: App) + case noValidApp var errorDescription: String? { switch self { case .testerNotInTestersList(let email, let betaGroup, let app): return "Tester with email: \(email) in beta group named: \(betaGroup.groupName) " + "for app: \(app.bundleId) is not included in the \(betaTestersCSVName) file" + + case .noValidApp: + return "There's no valid app folder found in your local configuration file path, please run 'sync pull' first" } } } func readConfiguration() throws -> TestFlightConfiguration { let folder = try Folder(path: path) + + guard folder.subfolders.count() > 0 else { throw Error.noValidApp } + var configuration = TestFlightConfiguration() let decodeBetaTesters: (Data) throws -> [BetaTester] = { data in diff --git a/Tests/appstoreconnect-cliTests/Sync/TestFlightProgramDifferenceTests.swift b/Tests/appstoreconnect-cliTests/Sync/TestFlightProgramDifferenceTests.swift new file mode 100644 index 00000000..bd0c70aa --- /dev/null +++ b/Tests/appstoreconnect-cliTests/Sync/TestFlightProgramDifferenceTests.swift @@ -0,0 +1,54 @@ +// Copyright 2020 Itty Bitty Apps Pty Ltd + +@testable import AppStoreConnectCLI + +import FileSystem +import Model +import Foundation +import XCTest + +final class TestFlightProgramDifferenceTests: XCTestCase { + + func testErrorWillBeThrow_whenDuplicateTesters() throws { + let local = TestFlightProgram( + apps: [], + testers: [BetaTester(email: "foo@gmail.com", firstName: "Foo", lastName: "Bar", inviteType: "EMAIL", betaGroups: [], apps: [])], + groups: [] + ) + + let remote = TestFlightProgram( + apps: [], + testers: [ + BetaTester(email: "foo@gmail.com", firstName: "Foo", lastName: "Bar", inviteType: "EMAIL", betaGroups: [], apps: []), + BetaTester(email: "foo@gmail.com", firstName: "Foo", lastName: "Bar", inviteType: "EMAIL", betaGroups: [], apps: []), + ], + groups: [] + ) + + XCTAssertThrowsError(try TestFlightProgramDifference(local: local, remote: remote)) { error in + XCTAssertEqual( + error as? TestFlightProgramDifference.Error, + TestFlightProgramDifference.Error.duplicateTesters(email: "foo@gmail.com") + ) + } + } + + func testErrorNotThrow_withoutDuplicateTesters() throws { + let local = TestFlightProgram( + apps: [], + testers: [BetaTester(email: "foo@gmail.com", firstName: "Foo", lastName: "Bar", inviteType: "EMAIL", betaGroups: [], apps: [])], + groups: [] + ) + + let remote = TestFlightProgram( + apps: [], + testers: [BetaTester(email: "foo@gmail.com", firstName: "Foo", lastName: "Bar", inviteType: "EMAIL", betaGroups: [], apps: [])], + groups: [] + ) + + let result = try TestFlightProgramDifference(local: local, remote: remote) + + XCTAssertTrue(result.changes.isEmpty) + } + +}