Do not return Result (or anything else) from command run(…) functions.

Throw when failure. Normal Void return when success.

Signed-off-by: Ross Goldberg <484615+rgoldberg@users.noreply.github.com>
This commit is contained in:
Ross Goldberg 2024-10-01 21:54:15 -04:00
parent 3b86deb63e
commit 388d963cd1
No known key found for this signature in database
34 changed files with 111 additions and 331 deletions

View file

@ -17,24 +17,16 @@ extension Mas {
/// Runs the command. /// Runs the command.
func run() throws { func run() throws {
let result = runInternal()
if case .failure = result {
try result.get()
}
}
func runInternal() -> Result<Void, MASError> {
if #available(macOS 12, *) { if #available(macOS 12, *) {
// Account information is no longer available as of Monterey. // Account information is no longer available as of Monterey.
// https://github.com/mas-cli/mas/issues/417 // https://github.com/mas-cli/mas/issues/417
return .failure(.notSupported) throw MASError.notSupported
} }
do { do {
print(try ISStoreAccount.primaryAccount.wait().identifier) print(try ISStoreAccount.primaryAccount.wait().identifier)
return .success(())
} catch { } catch {
return .failure(error as? MASError ?? .failed(error: error as NSError)) throw error as? MASError ?? MASError.failed(error: error as NSError)
} }
} }
} }

View file

@ -21,38 +21,29 @@ extension Mas {
/// Runs the command. /// Runs the command.
func run() throws { func run() throws {
let result = run(storeSearch: MasStoreSearch(), openCommand: OpenSystemCommand()) try run(storeSearch: MasStoreSearch(), openCommand: OpenSystemCommand())
if case .failure = result {
try result.get()
}
} }
func run(storeSearch: StoreSearch, openCommand: ExternalCommand) -> Result<Void, MASError> { func run(storeSearch: StoreSearch, openCommand: ExternalCommand) throws {
do { do {
guard let result = try storeSearch.lookup(app: appId).wait() else { guard let result = try storeSearch.lookup(app: appId).wait() else {
return .failure(.noSearchResultsFound) throw MASError.noSearchResultsFound
} }
do { do {
try openCommand.run(arguments: result.trackViewUrl) try openCommand.run(arguments: result.trackViewUrl)
} catch { } catch {
printError("Unable to launch open command") printError("Unable to launch open command")
return .failure(.searchFailed) throw MASError.searchFailed
} }
if openCommand.failed { if openCommand.failed {
let reason = openCommand.process.terminationReason let reason = openCommand.process.terminationReason
printError("Open failed: (\(reason)) \(openCommand.stderr)") printError("Open failed: (\(reason)) \(openCommand.stderr)")
return .failure(.searchFailed) throw MASError.searchFailed
} }
} catch { } catch {
// Bubble up MASErrors throw error as? MASError ?? .searchFailed
if let error = error as? MASError {
return .failure(error)
}
return .failure(.searchFailed)
} }
return .success(())
} }
} }
} }

View file

@ -22,28 +22,19 @@ extension Mas {
/// Runs the command. /// Runs the command.
func run() throws { func run() throws {
let result = run(storeSearch: MasStoreSearch()) try run(storeSearch: MasStoreSearch())
if case .failure = result {
try result.get()
}
} }
func run(storeSearch: StoreSearch) -> Result<Void, MASError> { func run(storeSearch: StoreSearch) throws {
do { do {
guard let result = try storeSearch.lookup(app: appId).wait() else { guard let result = try storeSearch.lookup(app: appId).wait() else {
return .failure(.noSearchResultsFound) throw MASError.noSearchResultsFound
} }
print(AppInfoFormatter.format(app: result)) print(AppInfoFormatter.format(app: result))
} catch { } catch {
// Bubble up MASErrors throw error as? MASError ?? .searchFailed
if let error = error as? MASError {
return .failure(error)
}
return .failure(.searchFailed)
} }
return .success(())
} }
} }
} }

View file

@ -23,13 +23,10 @@ extension Mas {
/// Runs the command. /// Runs the command.
func run() throws { func run() throws {
let result = run(appLibrary: MasAppLibrary()) try run(appLibrary: MasAppLibrary())
if case .failure = result {
try result.get()
}
} }
func run(appLibrary: AppLibrary) -> Result<Void, MASError> { func run(appLibrary: AppLibrary) throws {
// Try to download applications with given identifiers and collect results // Try to download applications with given identifiers and collect results
let appIds = appIds.filter { appId in let appIds = appIds.filter { appId in
if let product = appLibrary.installedApp(forId: appId), !force { if let product = appLibrary.installedApp(forId: appId), !force {
@ -43,10 +40,8 @@ extension Mas {
do { do {
try downloadAll(appIds).wait() try downloadAll(appIds).wait()
} catch { } catch {
return .failure(error as? MASError ?? .downloadFailed(error: error as NSError)) throw error as? MASError ?? .downloadFailed(error: error as NSError)
} }
return .success(())
} }
} }
} }

View file

@ -17,23 +17,16 @@ extension Mas {
/// Runs the command. /// Runs the command.
func run() throws { func run() throws {
let result = run(appLibrary: MasAppLibrary()) try run(appLibrary: MasAppLibrary())
if case .failure = result {
try result.get()
}
} }
func run(appLibrary: AppLibrary) -> Result<Void, MASError> { func run(appLibrary: AppLibrary) throws {
let products = appLibrary.installedApps let products = appLibrary.installedApps
if products.isEmpty { if products.isEmpty {
printError("No installed apps found") printError("No installed apps found")
return .success(()) } else {
print(AppListFormatter.format(products: products))
} }
let output = AppListFormatter.format(products: products)
print(output)
return .success(())
} }
} }
} }

View file

@ -24,34 +24,27 @@ extension Mas {
/// Runs the command. /// Runs the command.
func run() throws { func run() throws {
let result = run(appLibrary: MasAppLibrary(), storeSearch: MasStoreSearch()) try run(appLibrary: MasAppLibrary(), storeSearch: MasStoreSearch())
if case .failure = result {
try result.get()
}
} }
func run(appLibrary: AppLibrary, storeSearch: StoreSearch) -> Result<Void, MASError> { func run(appLibrary: AppLibrary, storeSearch: StoreSearch) throws {
var appId: Int? var appId: Int?
do { do {
let results = try storeSearch.search(for: appName).wait() let results = try storeSearch.search(for: appName).wait()
guard let result = results.first else { guard let result = results.first else {
printError("No results found") printError("No results found")
return .failure(.noSearchResultsFound) throw MASError.noSearchResultsFound
} }
appId = result.trackId appId = result.trackId
} catch { } catch {
// Bubble up MASErrors throw error as? MASError ?? .searchFailed
if let error = error as? MASError {
return .failure(error)
}
return .failure(.searchFailed)
} }
guard let identifier = appId else { fatalError() } guard let identifier = appId else { fatalError() }
return install(UInt64(identifier), appLibrary: appLibrary) try install(UInt64(identifier), appLibrary: appLibrary)
} }
/// Installs an app. /// Installs an app.
@ -59,21 +52,17 @@ extension Mas {
/// - Parameters: /// - Parameters:
/// - appId: App identifier /// - appId: App identifier
/// - appLibrary: Library of installed apps /// - appLibrary: Library of installed apps
/// - Returns: Result of the operation. fileprivate func install(_ appId: UInt64, appLibrary: AppLibrary) throws {
fileprivate func install(_ appId: UInt64, appLibrary: AppLibrary) -> Result<Void, MASError> {
// Try to download applications with given identifiers and collect results // Try to download applications with given identifiers and collect results
if let product = appLibrary.installedApp(forId: appId), !force { if let product = appLibrary.installedApp(forId: appId), !force {
printWarning("\(product.appName) is already installed") printWarning("\(product.appName) is already installed")
return .success(()) } else {
do {
try downloadAll([appId]).wait()
} catch {
throw error as? MASError ?? .downloadFailed(error: error as NSError)
}
} }
do {
try downloadAll([appId]).wait()
} catch {
return .failure(error as? MASError ?? .downloadFailed(error: error as NSError))
}
return .success(())
} }
} }
} }

View file

@ -25,34 +25,31 @@ extension Mas {
/// Runs the command. /// Runs the command.
func run() throws { func run() throws {
let result = run(storeSearch: MasStoreSearch(), openCommand: OpenSystemCommand()) try run(storeSearch: MasStoreSearch(), openCommand: OpenSystemCommand())
if case .failure = result {
try result.get()
}
} }
func run(storeSearch: StoreSearch, openCommand: ExternalCommand) -> Result<Void, MASError> { func run(storeSearch: StoreSearch, openCommand: ExternalCommand) throws {
do { do {
if appId == markerValue { if appId == markerValue {
// If no app ID is given, just open the MAS GUI app // If no app ID is given, just open the MAS GUI app
try openCommand.run(arguments: masScheme + "://") try openCommand.run(arguments: masScheme + "://")
return .success(()) return
} }
guard let appId = Int(appId) guard let appId = Int(appId)
else { else {
printError("Invalid app ID") printError("Invalid app ID")
return .failure(.noSearchResultsFound) throw MASError.noSearchResultsFound
} }
guard let result = try storeSearch.lookup(app: appId).wait() guard let result = try storeSearch.lookup(app: appId).wait()
else { else {
return .failure(.noSearchResultsFound) throw MASError.noSearchResultsFound
} }
guard var url = URLComponents(string: result.trackViewUrl) guard var url = URLComponents(string: result.trackViewUrl)
else { else {
return .failure(.searchFailed) throw MASError.searchFailed
} }
url.scheme = masScheme url.scheme = masScheme
@ -60,22 +57,16 @@ extension Mas {
try openCommand.run(arguments: url.string!) try openCommand.run(arguments: url.string!)
} catch { } catch {
printError("Unable to launch open command") printError("Unable to launch open command")
return .failure(.searchFailed) throw MASError.searchFailed
} }
if openCommand.failed { if openCommand.failed {
let reason = openCommand.process.terminationReason let reason = openCommand.process.terminationReason
printError("Open failed: (\(reason)) \(openCommand.stderr)") printError("Open failed: (\(reason)) \(openCommand.stderr)")
return .failure(.searchFailed) throw MASError.searchFailed
} }
} catch { } catch {
// Bubble up MASErrors throw error as? MASError ?? .searchFailed
if let error = error as? MASError {
return .failure(error)
}
return .failure(.searchFailed)
} }
return .success(())
} }
} }
} }

View file

@ -25,13 +25,10 @@ extension Mas {
/// Runs the command. /// Runs the command.
func run() throws { func run() throws {
let result = run(appLibrary: MasAppLibrary(), storeSearch: MasStoreSearch()) try run(appLibrary: MasAppLibrary(), storeSearch: MasStoreSearch())
if case .failure = result {
try result.get()
}
} }
func run(appLibrary: AppLibrary, storeSearch: StoreSearch) -> Result<Void, MASError> { func run(appLibrary: AppLibrary, storeSearch: StoreSearch) throws {
let promises = appLibrary.installedApps.map { installedApp in let promises = appLibrary.installedApps.map { installedApp in
firstly { firstly {
storeSearch.lookup(app: installedApp.itemIdentifier.intValue) storeSearch.lookup(app: installedApp.itemIdentifier.intValue)
@ -59,12 +56,11 @@ extension Mas {
} }
} }
return firstly { _ = firstly {
when(fulfilled: promises) when(fulfilled: promises)
}.map { }.map {
Result<Void, MASError>.success(()) Result<Void, MASError>.success(())
}.recover { error in }.recover { error in
// Bubble up MASErrors
.value(Result<Void, MASError>.failure(error as? MASError ?? .searchFailed)) .value(Result<Void, MASError>.failure(error as? MASError ?? .searchFailed))
}.wait() }.wait()
} }

View file

@ -20,13 +20,10 @@ extension Mas {
/// Runs the command. /// Runs the command.
func run() throws { func run() throws {
let result = run(appLibrary: MasAppLibrary()) try run(appLibrary: MasAppLibrary())
if case .failure = result {
try result.get()
}
} }
func run(appLibrary: AppLibrary) -> Result<Void, MASError> { func run(appLibrary: AppLibrary) throws {
// Try to download applications with given identifiers and collect results // Try to download applications with given identifiers and collect results
let appIds = appIds.filter { appId in let appIds = appIds.filter { appId in
if let product = appLibrary.installedApp(forId: appId) { if let product = appLibrary.installedApp(forId: appId) {
@ -40,10 +37,8 @@ extension Mas {
do { do {
try downloadAll(appIds, purchase: true).wait() try downloadAll(appIds, purchase: true).wait()
} catch { } catch {
return .failure(error as? MASError ?? .downloadFailed(error: error as NSError)) throw error as? MASError ?? .downloadFailed(error: error as NSError)
} }
return .success(())
} }
} }
} }

View file

@ -21,13 +21,6 @@ extension Mas {
/// Runs the command. /// Runs the command.
func run() throws { func run() throws {
let result = runInternal()
if case .failure = result {
try result.get()
}
}
func runInternal() -> Result<Void, MASError> {
// The "Reset Application" command in the Mac App Store debug menu performs // The "Reset Application" command in the Mac App Store debug menu performs
// the following steps // the following steps
// //
@ -81,8 +74,6 @@ extension Mas {
} }
} }
} }
return .success(())
} }
} }
} }

View file

@ -22,29 +22,20 @@ extension Mas {
var appName: String var appName: String
func run() throws { func run() throws {
let result = run(storeSearch: MasStoreSearch()) try run(storeSearch: MasStoreSearch())
if case .failure = result {
try result.get()
}
} }
func run(storeSearch: StoreSearch) -> Result<Void, MASError> { func run(storeSearch: StoreSearch) throws {
do { do {
let results = try storeSearch.search(for: appName).wait() let results = try storeSearch.search(for: appName).wait()
if results.isEmpty { if results.isEmpty {
return .failure(.noSearchResultsFound) throw MASError.noSearchResultsFound
} }
let output = SearchResultFormatter.format(results: results, includePrice: price) let output = SearchResultFormatter.format(results: results, includePrice: price)
print(output) print(output)
return .success(())
} catch { } catch {
// Bubble up MASErrors throw error as? MASError ?? .searchFailed
if let error = error as? MASError {
return .failure(error)
}
return .failure(.searchFailed)
} }
} }
} }

View file

@ -25,18 +25,10 @@ extension Mas {
/// Runs the command. /// Runs the command.
func run() throws { func run() throws {
let result = runInternal()
if case .failure = result {
try result.get()
}
}
func runInternal() -> Result<Void, MASError> {
do { do {
_ = try ISStoreAccount.signIn(username: username, password: password, systemDialog: dialog).wait() _ = try ISStoreAccount.signIn(username: username, password: password, systemDialog: dialog).wait()
return .success(())
} catch { } catch {
return .failure(error as? MASError ?? .signInFailed(error: error as NSError)) throw error as? MASError ?? MASError.signInFailed(error: error as NSError)
} }
} }
} }

View file

@ -18,13 +18,6 @@ extension Mas {
/// Runs the command. /// Runs the command.
func run() throws { func run() throws {
let result = runInternal()
if case .failure = result {
try result.get()
}
}
func runInternal() -> Result<Void, MASError> {
if #available(macOS 10.13, *) { if #available(macOS 10.13, *) {
ISServiceProxy.genericShared().accountService.signOut() ISServiceProxy.genericShared().accountService.signOut()
} else { } else {
@ -32,8 +25,6 @@ extension Mas {
// https://github.com/mas-cli/mas/issues/129 // https://github.com/mas-cli/mas/issues/129
CKAccountStore.shared().signOut() CKAccountStore.shared().signOut()
} }
return .success(())
} }
} }
} }

View file

@ -25,33 +25,26 @@ extension Mas {
/// Runs the uninstall command. /// Runs the uninstall command.
func run() throws { func run() throws {
let result = run(appLibrary: MasAppLibrary()) try run(appLibrary: MasAppLibrary())
if case .failure = result {
try result.get()
}
} }
func run(appLibrary: AppLibrary) -> Result<Void, MASError> { func run(appLibrary: AppLibrary) throws {
let appId = UInt64(appId) let appId = UInt64(appId)
guard let product = appLibrary.installedApp(forId: appId) else { guard let product = appLibrary.installedApp(forId: appId) else {
return .failure(.notInstalled) throw MASError.notInstalled
} }
if dryRun { if dryRun {
printInfo("\(product.appName) \(product.bundlePath)") printInfo("\(product.appName) \(product.bundlePath)")
printInfo("(not removed, dry run)") printInfo("(not removed, dry run)")
} else {
return .success(()) do {
try appLibrary.uninstallApp(app: product)
} catch {
throw MASError.uninstallFailed
}
} }
do {
try appLibrary.uninstallApp(app: product)
} catch {
return .failure(.uninstallFailed)
}
return .success(())
} }
} }
} }

View file

@ -10,8 +10,6 @@ import ArgumentParser
import Foundation import Foundation
import PromiseKit import PromiseKit
import enum Swift.Result
extension Mas { extension Mas {
/// Command which upgrades apps with new versions available in the Mac App Store. /// Command which upgrades apps with new versions available in the Mac App Store.
struct Upgrade: ParsableCommand { struct Upgrade: ParsableCommand {
@ -24,24 +22,20 @@ extension Mas {
/// Runs the command. /// Runs the command.
func run() throws { func run() throws {
let result = run(appLibrary: MasAppLibrary(), storeSearch: MasStoreSearch()) try run(appLibrary: MasAppLibrary(), storeSearch: MasStoreSearch())
if case .failure = result {
try result.get()
}
} }
func run(appLibrary: AppLibrary, storeSearch: StoreSearch) -> Result<Void, MASError> { func run(appLibrary: AppLibrary, storeSearch: StoreSearch) throws {
let apps: [(installedApp: SoftwareProduct, storeApp: SearchResult)] let apps: [(installedApp: SoftwareProduct, storeApp: SearchResult)]
do { do {
apps = try findOutdatedApps(appLibrary: appLibrary, storeSearch: storeSearch) apps = try findOutdatedApps(appLibrary: appLibrary, storeSearch: storeSearch)
} catch { } catch {
// Bubble up MASErrors throw error as? MASError ?? .searchFailed
return .failure(error as? MASError ?? .searchFailed)
} }
guard apps.count > 0 else { guard apps.count > 0 else {
printWarning("Nothing found to upgrade") printWarning("Nothing found to upgrade")
return .success(()) return
} }
print("Upgrading \(apps.count) outdated application\(apps.count > 1 ? "s" : ""):") print("Upgrading \(apps.count) outdated application\(apps.count > 1 ? "s" : ""):")
@ -53,10 +47,8 @@ extension Mas {
do { do {
try downloadAll(appIds).wait() try downloadAll(appIds).wait()
} catch { } catch {
return .failure(error as? MASError ?? .downloadFailed(error: error as NSError)) throw error as? MASError ?? .downloadFailed(error: error as NSError)
} }
return .success(())
} }
private func findOutdatedApps( private func findOutdatedApps(

View file

@ -21,17 +21,14 @@ extension Mas {
/// Runs the command. /// Runs the command.
func run() throws { func run() throws {
let result = run(storeSearch: MasStoreSearch(), openCommand: OpenSystemCommand()) try run(storeSearch: MasStoreSearch(), openCommand: OpenSystemCommand())
if case .failure = result {
try result.get()
}
} }
func run(storeSearch: StoreSearch, openCommand: ExternalCommand) -> Result<Void, MASError> { func run(storeSearch: StoreSearch, openCommand: ExternalCommand) throws {
do { do {
guard let result = try storeSearch.lookup(app: appId).wait() guard let result = try storeSearch.lookup(app: appId).wait()
else { else {
return .failure(.noSearchResultsFound) throw MASError.noSearchResultsFound
} }
guard let vendorWebsite = result.sellerUrl guard let vendorWebsite = result.sellerUrl
@ -41,22 +38,16 @@ extension Mas {
try openCommand.run(arguments: vendorWebsite) try openCommand.run(arguments: vendorWebsite)
} catch { } catch {
printError("Unable to launch open command") printError("Unable to launch open command")
return .failure(.searchFailed) throw MASError.searchFailed
} }
if openCommand.failed { if openCommand.failed {
let reason = openCommand.process.terminationReason let reason = openCommand.process.terminationReason
printError("Open failed: (\(reason)) \(openCommand.stderr)") printError("Open failed: (\(reason)) \(openCommand.stderr)")
return .failure(.searchFailed) throw MASError.searchFailed
} }
} catch { } catch {
// Bubble up MASErrors throw error as? MASError ?? .searchFailed
if let error = error as? MASError {
return .failure(error)
}
return .failure(.searchFailed)
} }
return .success(())
} }
} }
} }

View file

@ -17,15 +17,7 @@ extension Mas {
/// Runs the command. /// Runs the command.
func run() throws { func run() throws {
let result = runInternal()
if case .failure = result {
try result.get()
}
}
func runInternal() -> Result<Void, MASError> {
print(Package.version) print(Package.version)
return .success(())
} }
} }
} }

View file

@ -21,9 +21,9 @@ public class AccountSpec: QuickSpec {
xdescribe("Account command") { xdescribe("Account command") {
xit("displays active account") { xit("displays active account") {
expect { expect {
try Mas.Account.parse([]).runInternal() try Mas.Account.parse([]).run()
} }
.to(beSuccess()) .toNot(throwError())
} }
} }
} }

View file

@ -32,29 +32,20 @@ public class HomeSpec: QuickSpec {
expect { expect {
try Mas.Home.parse(["--", "-999"]).run(storeSearch: storeSearch, openCommand: openCommand) try Mas.Home.parse(["--", "-999"]).run(storeSearch: storeSearch, openCommand: openCommand)
} }
.to( .to(throwError(MASError.searchFailed))
beFailure { error in
expect(error) == .searchFailed
}
)
} }
it("can't find app with unknown ID") { it("can't find app with unknown ID") {
expect { expect {
try Mas.Home.parse(["999"]).run(storeSearch: storeSearch, openCommand: openCommand) try Mas.Home.parse(["999"]).run(storeSearch: storeSearch, openCommand: openCommand)
} }
.to( .to(throwError(MASError.noSearchResultsFound))
beFailure { error in
expect(error) == .noSearchResultsFound
}
)
} }
it("opens app on MAS Preview") { it("opens app on MAS Preview") {
storeSearch.apps[result.trackId] = result storeSearch.apps[result.trackId] = result
expect { expect {
try Mas.Home.parse([String(result.trackId)]).run(storeSearch: storeSearch, openCommand: openCommand) try Mas.Home.parse([String(result.trackId)]).run(storeSearch: storeSearch, openCommand: openCommand)
} }
.to(beSuccess()) .toNot(throwError())
expect(openCommand.arguments).toNot(beNil()) expect(openCommand.arguments).toNot(beNil())
expect(openCommand.arguments!.first!) == result.trackViewUrl expect(openCommand.arguments!.first!) == result.trackViewUrl
} }

View file

@ -46,30 +46,21 @@ public class InfoSpec: QuickSpec {
expect { expect {
try Mas.Info.parse(["--", "-999"]).run(storeSearch: storeSearch) try Mas.Info.parse(["--", "-999"]).run(storeSearch: storeSearch)
} }
.to( .to(throwError(MASError.searchFailed))
beFailure { error in
expect(error) == .searchFailed
}
)
} }
it("can't find app with unknown ID") { it("can't find app with unknown ID") {
expect { expect {
try Mas.Info.parse(["999"]).run(storeSearch: storeSearch) try Mas.Info.parse(["999"]).run(storeSearch: storeSearch)
} }
.to( .to(throwError(MASError.noSearchResultsFound))
beFailure { error in
expect(error) == .noSearchResultsFound
}
)
} }
it("displays app details") { it("displays app details") {
storeSearch.apps[result.trackId] = result storeSearch.apps[result.trackId] = result
let output = OutputListener() let output = OutputListener()
expect { expect {
try Mas.Info.parse([String(result.trackId)]).run(storeSearch: storeSearch) try Mas.Info.parse([String(result.trackId)]).run(storeSearch: storeSearch)
} }
.to(beSuccess()) .toNot(throwError())
expect(output.contents) == expectedOutput expect(output.contents) == expectedOutput
} }
} }

View file

@ -21,7 +21,7 @@ public class InstallSpec: QuickSpec {
expect { expect {
try Mas.Install.parse([]).run(appLibrary: AppLibraryMock()) try Mas.Install.parse([]).run(appLibrary: AppLibraryMock())
} }
.to(beSuccess()) .toNot(throwError())
} }
} }
} }

View file

@ -21,7 +21,7 @@ public class ListSpec: QuickSpec {
expect { expect {
try Mas.List.parse([]).run(appLibrary: AppLibraryMock()) try Mas.List.parse([]).run(appLibrary: AppLibraryMock())
} }
.to(beSuccess()) .toNot(throwError())
} }
} }
} }

View file

@ -24,7 +24,7 @@ public class LuckySpec: QuickSpec {
expect { expect {
try Mas.Lucky.parse(["Slack"]).run(appLibrary: AppLibraryMock(), storeSearch: storeSearch) try Mas.Lucky.parse(["Slack"]).run(appLibrary: AppLibraryMock(), storeSearch: storeSearch)
} }
.to(beSuccess()) .toNot(throwError())
} }
} }
} }

View file

@ -33,30 +33,21 @@ public class OpenSpec: QuickSpec {
expect { expect {
try Mas.Open.parse(["--", "-999"]).run(storeSearch: storeSearch, openCommand: openCommand) try Mas.Open.parse(["--", "-999"]).run(storeSearch: storeSearch, openCommand: openCommand)
} }
.to( .to(throwError(MASError.searchFailed))
beFailure { error in
expect(error) == .searchFailed
}
)
} }
it("can't find app with unknown ID") { it("can't find app with unknown ID") {
expect { expect {
try Mas.Open.parse(["999"]).run(storeSearch: storeSearch, openCommand: openCommand) try Mas.Open.parse(["999"]).run(storeSearch: storeSearch, openCommand: openCommand)
} }
.to( .to(throwError(MASError.noSearchResultsFound))
beFailure { error in
expect(error) == .noSearchResultsFound
}
)
} }
it("opens app in MAS") { it("opens app in MAS") {
storeSearch.apps[result.trackId] = result storeSearch.apps[result.trackId] = result
expect { expect {
try Mas.Open.parse([result.trackId.description]) try Mas.Open.parse([result.trackId.description])
.run(storeSearch: storeSearch, openCommand: openCommand) .run(storeSearch: storeSearch, openCommand: openCommand)
} }
.to(beSuccess()) .toNot(throwError())
expect(openCommand.arguments).toNot(beNil()) expect(openCommand.arguments).toNot(beNil())
let url = URL(string: openCommand.arguments!.first!) let url = URL(string: openCommand.arguments!.first!)
expect(url).toNot(beNil()) expect(url).toNot(beNil())
@ -66,7 +57,7 @@ public class OpenSpec: QuickSpec {
expect { expect {
try Mas.Open.parse(["appstore"]).run(storeSearch: storeSearch, openCommand: openCommand) try Mas.Open.parse(["appstore"]).run(storeSearch: storeSearch, openCommand: openCommand)
} }
.to(beSuccess()) .toNot(throwError())
expect(openCommand.arguments).toNot(beNil()) expect(openCommand.arguments).toNot(beNil())
let url = URL(string: openCommand.arguments!.first!) let url = URL(string: openCommand.arguments!.first!)
expect(url).toNot(beNil()) expect(url).toNot(beNil())

View file

@ -22,7 +22,7 @@ public class OutdatedSpec: QuickSpec {
try Mas.Outdated.parse(["--verbose"]) try Mas.Outdated.parse(["--verbose"])
.run(appLibrary: AppLibraryMock(), storeSearch: StoreSearchMock()) .run(appLibrary: AppLibraryMock(), storeSearch: StoreSearchMock())
} }
.to(beSuccess()) .toNot(throwError())
} }
} }
} }

View file

@ -19,9 +19,9 @@ public class ResetSpec: QuickSpec {
describe("reset command") { describe("reset command") {
it("resets the App Store state") { it("resets the App Store state") {
expect { expect {
try Mas.Reset.parse([]).runInternal() try Mas.Reset.parse([]).run()
} }
.to(beSuccess()) .toNot(throwError())
} }
} }
} }

View file

@ -33,17 +33,13 @@ public class SearchSpec: QuickSpec {
expect { expect {
try Mas.Search.parse(["slack"]).run(storeSearch: storeSearch) try Mas.Search.parse(["slack"]).run(storeSearch: storeSearch)
} }
.to(beSuccess()) .toNot(throwError())
} }
it("fails when searching for nonexistent app") { it("fails when searching for nonexistent app") {
expect { expect {
try Mas.Search.parse(["nonexistent"]).run(storeSearch: storeSearch) try Mas.Search.parse(["nonexistent"]).run(storeSearch: storeSearch)
} }
.to( .to(throwError(MASError.noSearchResultsFound))
beFailure { error in
expect(error) == .noSearchResultsFound
}
)
} }
} }
} }

View file

@ -21,9 +21,9 @@ public class SignInSpec: QuickSpec {
xdescribe("signin command") { xdescribe("signin command") {
xit("signs in") { xit("signs in") {
expect { expect {
try Mas.SignIn.parse(["", ""]).runInternal() try Mas.SignIn.parse(["", ""]).run()
} }
.to(beSuccess()) .toNot(throwError())
} }
} }
} }

View file

@ -19,9 +19,9 @@ public class SignOutSpec: QuickSpec {
describe("signout command") { describe("signout command") {
it("signs out") { it("signs out") {
expect { expect {
try Mas.SignOut.parse([]).runInternal() try Mas.SignOut.parse([]).run()
} }
.to(beSuccess()) .toNot(throwError())
} }
} }
} }

View file

@ -36,21 +36,16 @@ public class UninstallSpec: QuickSpec {
} }
it("can't remove a missing app") { it("can't remove a missing app") {
expect { expect {
uninstall.run(appLibrary: mockLibrary) try uninstall.run(appLibrary: mockLibrary)
} }
.to( .to(throwError(MASError.notInstalled))
beFailure { error in
expect(error) == .notInstalled
}
)
} }
it("finds an app") { it("finds an app") {
mockLibrary.installedApps.append(app) mockLibrary.installedApps.append(app)
expect { expect {
uninstall.run(appLibrary: mockLibrary) try uninstall.run(appLibrary: mockLibrary)
} }
.to(beSuccess()) .toNot(throwError())
} }
} }
context("wet run") { context("wet run") {
@ -61,35 +56,25 @@ public class UninstallSpec: QuickSpec {
} }
it("can't remove a missing app") { it("can't remove a missing app") {
expect { expect {
uninstall.run(appLibrary: mockLibrary) try uninstall.run(appLibrary: mockLibrary)
} }
.to( .to(throwError(MASError.notInstalled))
beFailure { error in
expect(error) == .notInstalled
}
)
} }
it("removes an app") { it("removes an app") {
mockLibrary.installedApps.append(app) mockLibrary.installedApps.append(app)
expect { expect {
uninstall.run(appLibrary: mockLibrary) try uninstall.run(appLibrary: mockLibrary)
} }
.to(beSuccess()) .toNot(throwError())
} }
it("fails if there is a problem with the trash command") { it("fails if there is a problem with the trash command") {
var brokenUninstall = app // make mutable copy var brokenUninstall = app // make mutable copy
brokenUninstall.bundlePath = "/dev/null" brokenUninstall.bundlePath = "/dev/null"
mockLibrary.installedApps.append(brokenUninstall) mockLibrary.installedApps.append(brokenUninstall)
expect { expect {
uninstall.run(appLibrary: mockLibrary) try uninstall.run(appLibrary: mockLibrary)
} }
.to( .to(throwError(MASError.uninstallFailed))
beFailure { error in
expect(error) == .uninstallFailed
}
)
} }
} }
} }

View file

@ -21,7 +21,7 @@ public class UpgradeSpec: QuickSpec {
expect { expect {
try Mas.Upgrade.parse([]).run(appLibrary: AppLibraryMock(), storeSearch: StoreSearchMock()) try Mas.Upgrade.parse([]).run(appLibrary: AppLibraryMock(), storeSearch: StoreSearchMock())
} }
.to(beSuccess()) .toNot(throwError())
} }
} }
} }

View file

@ -32,21 +32,13 @@ public class VendorSpec: QuickSpec {
expect { expect {
try Mas.Vendor.parse(["--", "-999"]).run(storeSearch: storeSearch, openCommand: openCommand) try Mas.Vendor.parse(["--", "-999"]).run(storeSearch: storeSearch, openCommand: openCommand)
} }
.to( .to(throwError(MASError.searchFailed))
beFailure { error in
expect(error) == .searchFailed
}
)
} }
it("can't find app with unknown ID") { it("can't find app with unknown ID") {
expect { expect {
try Mas.Vendor.parse(["999"]).run(storeSearch: storeSearch, openCommand: openCommand) try Mas.Vendor.parse(["999"]).run(storeSearch: storeSearch, openCommand: openCommand)
} }
.to( .to(throwError(MASError.noSearchResultsFound))
beFailure { error in
expect(error) == .noSearchResultsFound
}
)
} }
it("opens vendor app page in browser") { it("opens vendor app page in browser") {
storeSearch.apps[result.trackId] = result storeSearch.apps[result.trackId] = result
@ -54,7 +46,7 @@ public class VendorSpec: QuickSpec {
try Mas.Vendor.parse([String(result.trackId)]) try Mas.Vendor.parse([String(result.trackId)])
.run(storeSearch: storeSearch, openCommand: openCommand) .run(storeSearch: storeSearch, openCommand: openCommand)
} }
.to(beSuccess()) .toNot(throwError())
expect(openCommand.arguments).toNot(beNil()) expect(openCommand.arguments).toNot(beNil())
expect(openCommand.arguments!.first!) == result.sellerUrl expect(openCommand.arguments!.first!) == result.sellerUrl
} }

View file

@ -19,9 +19,9 @@ public class VersionSpec: QuickSpec {
describe("version command") { describe("version command") {
it("displays the current version") { it("displays the current version") {
expect { expect {
try Mas.Version.parse([]).runInternal() try Mas.Version.parse([]).run()
} }
.to(beSuccess()) .toNot(throwError())
} }
} }
} }

View file

@ -1,32 +0,0 @@
//
// ResultPredicates.swift
// masTests
//
// Created by Ben Chatelain on 12/27/18.
// Copyright © 2018 mas-cli. All rights reserved.
//
import Nimble
@testable import mas
/// Nimble predicate for result enum success case, no associated value
func beSuccess() -> Predicate<Result<Void, MASError>> {
Predicate.define("be <success>") { expression, message in
if case .success = try expression.evaluate() {
return PredicateResult(status: .matches, message: message)
}
return PredicateResult(status: .fail, message: message)
}
}
/// Nimble predicate for result enum failure with associated error
func beFailure(test: @escaping (MASError) -> Void = { _ in }) -> Predicate<Result<Void, MASError>> {
Predicate.define("be <failure>") { expression, message in
if case .failure(let error) = try expression.evaluate() {
test(error)
return PredicateResult(status: .matches, message: message)
}
return PredicateResult(status: .fail, message: message)
}
}