diff --git a/packages/shorebird_cli/lib/src/auth/auth.dart b/packages/shorebird_cli/lib/src/auth/auth.dart index c56ea403..22785604 100644 --- a/packages/shorebird_cli/lib/src/auth/auth.dart +++ b/packages/shorebird_cli/lib/src/auth/auth.dart @@ -237,42 +237,6 @@ class Auth { void logout() => _clearCredentials(); - Future signUp({ - required void Function(String) authPrompt, - required String Function() namePrompt, - }) async { - if (_credentials != null) { - throw UserAlreadyLoggedInException(email: _credentials!.email!); - } - - final client = http.Client(); - final User newUser; - try { - _credentials = await _obtainAccessCredentials( - _clientId, - _scopes, - client, - authPrompt, - ); - - final codePushClient = _buildCodePushClient(httpClient: this.client); - - final existingUser = await codePushClient.getCurrentUser(); - if (existingUser != null) { - throw UserAlreadyExistsException(existingUser); - } - - newUser = await codePushClient.createUser(name: namePrompt()); - - _email = newUser.email; - _flushCredentials(_credentials!); - } finally { - client.close(); - } - - return newUser; - } - oauth2.AccessCredentials? _credentials; String? _email; @@ -356,14 +320,3 @@ class UserNotFoundException implements Exception { /// credentials. final String email; } - -/// {@template user_already_exists_exception} -/// Thrown when an attempt to create a User object results in a 409. -/// {@endtemplate} -class UserAlreadyExistsException implements Exception { - /// {@macro user_already_exists_exception} - UserAlreadyExistsException(this.user); - - /// The existing user. - final User user; -} diff --git a/packages/shorebird_cli/lib/src/code_push_client_wrapper.dart b/packages/shorebird_cli/lib/src/code_push_client_wrapper.dart index 9ecacfb5..cf5a3183 100644 --- a/packages/shorebird_cli/lib/src/code_push_client_wrapper.dart +++ b/packages/shorebird_cli/lib/src/code_push_client_wrapper.dart @@ -615,16 +615,4 @@ aar artifact already exists, continuing...''', await promotePatch(appId: appId, patchId: patch.id, channel: channel); } - - Future getUsage() async { - final progress = logger.progress('Fetching usage'); - try { - final usage = await codePushClient.getUsage(); - progress.complete(); - return usage; - } catch (error) { - progress.fail(error.toString()); - exit(ExitCode.software.code); - } - } } diff --git a/packages/shorebird_cli/lib/src/commands/account/account.dart b/packages/shorebird_cli/lib/src/commands/account/account.dart index f30e7e80..cca9cfc7 100644 --- a/packages/shorebird_cli/lib/src/commands/account/account.dart +++ b/packages/shorebird_cli/lib/src/commands/account/account.dart @@ -1,5 +1,3 @@ export 'account_command.dart'; -export 'account_usage_command.dart'; -export 'create_account_command.dart'; export 'downgrade_account_command.dart'; export 'upgrade_account_command.dart'; diff --git a/packages/shorebird_cli/lib/src/commands/account/account_command.dart b/packages/shorebird_cli/lib/src/commands/account/account_command.dart index b18a5730..024af358 100644 --- a/packages/shorebird_cli/lib/src/commands/account/account_command.dart +++ b/packages/shorebird_cli/lib/src/commands/account/account_command.dart @@ -8,8 +8,6 @@ import 'package:shorebird_cli/src/commands/commands.dart'; class AccountCommand extends ShorebirdCommand { /// {@macro account_command} AccountCommand() { - addSubcommand(AccountUsageCommand()); - addSubcommand(CreateAccountCommand()); addSubcommand(DowngradeAccountCommand()); addSubcommand(UpgradeAccountCommand()); } diff --git a/packages/shorebird_cli/lib/src/commands/account/account_usage_command.dart b/packages/shorebird_cli/lib/src/commands/account/account_usage_command.dart deleted file mode 100644 index 75c01ef1..00000000 --- a/packages/shorebird_cli/lib/src/commands/account/account_usage_command.dart +++ /dev/null @@ -1,104 +0,0 @@ -import 'dart:async'; - -import 'package:barbecue/barbecue.dart'; -import 'package:intl/intl.dart'; -import 'package:mason_logger/mason_logger.dart'; -import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; -import 'package:shorebird_cli/src/command.dart'; -import 'package:shorebird_cli/src/logger.dart'; -import 'package:shorebird_cli/src/shorebird_config_mixin.dart'; -import 'package:shorebird_cli/src/shorebird_validation_mixin.dart'; -import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; - -/// {@template account_usage_command} -/// `shorebird account usage` -/// Get usage information for your Shorebird account. -/// {@endtemplate} -class AccountUsageCommand extends ShorebirdCommand - with ShorebirdConfigMixin, ShorebirdValidationMixin { - @override - String get description => 'Get usage information for your Shorebird account.'; - - @override - String get name => 'usage'; - - @override - Future run() async { - try { - await validatePreconditions(checkUserIsAuthenticated: true); - } on PreconditionFailedException catch (e) { - return e.exitCode.code; - } - - final usage = await codePushClientWrapper.getUsage(); - - logger - ..info('📈 Usage') - ..info(usage.prettyPrint()); - - return ExitCode.success.code; - } -} - -extension on GetUsageResponse { - String prettyPrint() { - const cellStyle = CellStyle( - paddingLeft: 1, - paddingRight: 1, - borderBottom: true, - borderTop: true, - borderLeft: true, - borderRight: true, - ); - var totalPatchInstalls = 0; - for (final appUsage in apps) { - totalPatchInstalls += appUsage.patchInstallCount; - } - - final remainingPatchInstalls = plan.patchInstallLimit == null - ? '∞' - : '${plan.patchInstallLimit! - totalPatchInstalls}'; - - return ''' - -You are on the ${lightCyan.wrap(plan.name)} plan. - -${Table( - cellStyle: cellStyle, - header: const TableSection( - rows: [ - Row( - cells: [ - Cell('App'), - Cell('Patch Installs'), - ], - ) - ], - ), - body: TableSection( - rows: [ - for (final appUsage in apps) - Row( - cells: [ - Cell(appUsage.name), - Cell('${appUsage.patchInstallCount}'), - ], - ), - Row( - cells: [ - const Cell('Total'), - Cell('$totalPatchInstalls'), - ], - ), - ], - ), - ).render()} - -${styleBold.wrap('${lightCyan.wrap(remainingPatchInstalls)} patch installs remaining in the current billing period.')} - -Current Billing Period: ${lightCyan.wrap(DateFormat.yMMMd().format(currentPeriodStart))} - ${lightCyan.wrap(DateFormat.yMMMd().format(currentPeriodEnd))} -Month-to-date cost: ${lightCyan.wrap(currentPeriodCost.toString())} - -${styleBold.wrap('*Usage data is not reported in real-time and may be delayed by up to 48 hours.')}'''; - } -} diff --git a/packages/shorebird_cli/lib/src/commands/account/create_account_command.dart b/packages/shorebird_cli/lib/src/commands/account/create_account_command.dart deleted file mode 100644 index 4e509c9b..00000000 --- a/packages/shorebird_cli/lib/src/commands/account/create_account_command.dart +++ /dev/null @@ -1,79 +0,0 @@ -import 'dart:async'; - -import 'package:mason_logger/mason_logger.dart'; -import 'package:shorebird_cli/src/auth/auth.dart'; -import 'package:shorebird_cli/src/command.dart'; -import 'package:shorebird_cli/src/logger.dart'; -import 'package:shorebird_cli/src/shorebird_config_mixin.dart'; -import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; - -/// {@template create_account_command} -/// `shorebird account create` -/// Create a new Shorebird account. -/// {@endtemplate} -class CreateAccountCommand extends ShorebirdCommand with ShorebirdConfigMixin { - @override - String get description => 'Create a new Shorebird account.'; - - @override - String get name => 'create'; - - @override - Future run() async { - final User newUser; - try { - newUser = await auth.signUp( - authPrompt: authPrompt, - namePrompt: namePrompt, - ); - } on UserAlreadyLoggedInException catch (error) { - logger - ..info('You are already logged in as <${error.email}>.') - ..info( - 'Run ${lightCyan.wrap('shorebird logout')} to log out and try again.', - ); - return ExitCode.success.code; - } on UserAlreadyExistsException catch (error) { - // TODO(bryanoltman): change this message based on the user's subscription - // status. - logger.info(''' -You already have an account, ${error.user.displayName}! -To login, run ${lightCyan.wrap('shorebird login')}. -'''); - return ExitCode.success.code; - } catch (error) { - logger.err(error.toString()); - return ExitCode.software.code; - } - - logger.info( - ''' - -🎉 ${lightGreen.wrap('Welcome to Shorebird, ${newUser.displayName}!')} -🔑 Credentials are stored in ${lightCyan.wrap(auth.credentialsFilePath)}. -🚪 To logout, use: "${lightCyan.wrap('shorebird logout')}". - -Your current plan is ${lightCyan.wrap('Hobby')}. -To upgrade, run ${lightCyan.wrap('shorebird account upgrade')}. - -Please let us know via Discord if we can help! -${lightCyan.wrap('https://discord.gg/shorebird')}. -''', - ); - return ExitCode.success.code; - } - - void authPrompt(String url) { - logger.info(''' -Shorebird currently requires a Google account for authentication. If you'd like to use a different kind of auth, please let us know: ${lightCyan.wrap('https://github.com/shorebirdtech/shorebird/issues/335')}. - -Follow the link below to authenticate: - -${styleBold.wrap(styleUnderlined.wrap(lightCyan.wrap(url)))} - -Waiting for your authorization...'''); - } - - String namePrompt() => logger.prompt(''' -Tell us your name to finish creating your account:'''); -} diff --git a/packages/shorebird_cli/test/src/auth/auth_test.dart b/packages/shorebird_cli/test/src/auth/auth_test.dart index 5fbd65a0..47d992d5 100644 --- a/packages/shorebird_cli/test/src/auth/auth_test.dart +++ b/packages/shorebird_cli/test/src/auth/auth_test.dart @@ -41,7 +41,6 @@ void main() { const idToken = '''eyJhbGciOiJSUzI1NiIsImN0eSI6IkpXVCJ9.eyJlbWFpbCI6InRlc3RAZW1haWwuY29tIn0.pD47BhF3MBLyIpfsgWCzP9twzC1HJxGukpcR36DqT6yfiOMHTLcjDbCjRLAnklWEHiT0BQTKTfhs8IousU90Fm5bVKObudfKu8pP5iZZ6Ls4ohDjTrXky9j3eZpZjwv8CnttBVgRfMJG-7YASTFRYFcOLUpnb4Zm5R6QdoCDUYg'''; const email = 'test@email.com'; - const name = 'Jane Doe'; const user = User(id: 42, email: email); const refreshToken = ''; const scopes = []; @@ -403,78 +402,6 @@ void main() { }); }); - group('signUp', () { - test( - 'should set the email when claims are valid and user is successfully ' - 'created', () async { - when(() => codePushClient.getCurrentUser()) - .thenAnswer((_) async => null); - when(() => codePushClient.createUser(name: any(named: 'name'))) - .thenAnswer((_) async => user); - - final newUser = await auth.signUp( - authPrompt: (_) {}, - namePrompt: () => name, - ); - expect(user, newUser); - expect(auth.email, email); - expect(auth.isAuthenticated, isTrue); - expect(buildAuth().email, email); - expect(buildAuth().isAuthenticated, isTrue); - }); - - test('throws UserAlreadyLoggedInException if user is authenticated', - () async { - writeCredentials(); - auth = buildAuth(); - - await expectLater( - auth.signUp( - authPrompt: (_) {}, - namePrompt: () => name, - ), - throwsA(isA()), - ); - - expect(auth.email, isNotNull); - expect(auth.isAuthenticated, isTrue); - }); - - test('throws UserAlreadyExistsException if user already exists', - () async { - when(() => codePushClient.getCurrentUser()) - .thenAnswer((_) async => user); - - await expectLater( - auth.signUp( - authPrompt: (_) {}, - namePrompt: () => name, - ), - throwsA(isA()), - ); - verifyNever(() => codePushClient.createUser(name: any(named: 'name'))); - expect(auth.email, isNull); - expect(auth.isAuthenticated, isFalse); - }); - - test('throws exception if createUser fails', () async { - when(() => codePushClient.getCurrentUser()) - .thenAnswer((_) async => null); - when(() => codePushClient.createUser(name: any(named: 'name'))) - .thenThrow(Exception('oh no!')); - - await expectLater( - auth.signUp( - authPrompt: (_) {}, - namePrompt: () => name, - ), - throwsA(isA()), - ); - expect(auth.email, isNull); - expect(auth.isAuthenticated, isFalse); - }); - }); - group('logout', () { test('clears session and wipes state', () async { await auth.login((_) {}); diff --git a/packages/shorebird_cli/test/src/code_push_client_wrapper_test.dart b/packages/shorebird_cli/test/src/code_push_client_wrapper_test.dart index 013ada40..c9b99ebb 100644 --- a/packages/shorebird_cli/test/src/code_push_client_wrapper_test.dart +++ b/packages/shorebird_cli/test/src/code_push_client_wrapper_test.dart @@ -1849,48 +1849,6 @@ Please bump your version number and try again.''', ).called(1); }); }); - - group('getUsage', () { - test('exits with code 70 when getUsage throws an exception', () async { - when(() => codePushClient.getUsage()).thenThrow(Exception('oh no!')); - - await expectLater( - () => runWithOverrides(codePushClientWrapper.getUsage), - exitsWithCode(ExitCode.software), - ); - - verify(() => progress.fail(any(that: contains('oh no!')))).called(1); - }); - - test('returns usage when succeeds', () async { - final usage = GetUsageResponse( - plan: ShorebirdPlan( - name: 'Hobby', - monthlyCost: Money.fromIntWithCurrency(0, usd), - patchInstallLimit: 1000, - maxTeamSize: 1, - ), - apps: const [ - AppUsage( - id: 'test-app-id', - name: 'test app', - patchInstallCount: 42, - ), - ], - currentPeriodCost: Money.fromIntWithCurrency(0, usd), - currentPeriodStart: DateTime(2023), - currentPeriodEnd: DateTime(2023, 2), - ); - when(() => codePushClient.getUsage()).thenAnswer((_) async => usage); - - await expectLater( - runWithOverrides(codePushClientWrapper.getUsage), - completion(equals(usage)), - ); - - verify(() => progress.complete()).called(1); - }); - }); }); }); } diff --git a/packages/shorebird_cli/test/src/commands/account/account_usage_command_test.dart b/packages/shorebird_cli/test/src/commands/account/account_usage_command_test.dart deleted file mode 100644 index e6920175..00000000 --- a/packages/shorebird_cli/test/src/commands/account/account_usage_command_test.dart +++ /dev/null @@ -1,189 +0,0 @@ -import 'package:intl/intl.dart'; -import 'package:mason_logger/mason_logger.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:scoped/scoped.dart'; -import 'package:shorebird_cli/src/auth/auth.dart'; -import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; -import 'package:shorebird_cli/src/commands/account/account.dart'; -import 'package:shorebird_cli/src/logger.dart'; -import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; -import 'package:test/test.dart'; - -class _MockAuth extends Mock implements Auth {} - -class _MockCodePushClientWrapper extends Mock - implements CodePushClientWrapper {} - -class _MockLogger extends Mock implements Logger {} - -class _MockProgress extends Mock implements Progress {} - -void main() { - late Auth auth; - late CodePushClientWrapper codePushClientWrapper; - late Logger logger; - late Progress progress; - - late AccountUsageCommand command; - - group(AccountUsageCommand, () { - R runWithOverrides(R Function() body) { - return runScoped( - body, - values: { - authRef.overrideWith(() => auth), - loggerRef.overrideWith(() => logger), - codePushClientWrapperRef.overrideWith(() => codePushClientWrapper) - }, - ); - } - - setUp(() { - auth = _MockAuth(); - codePushClientWrapper = _MockCodePushClientWrapper(); - logger = _MockLogger(); - progress = _MockProgress(); - - when(() => auth.isAuthenticated).thenReturn(true); - when(() => logger.progress(any())).thenReturn(progress); - - command = runWithOverrides(AccountUsageCommand.new); - }); - - test('has a description', () { - expect(command.description, isNotEmpty); - }); - - test('exits with code 67 when user is not logged in', () async { - when(() => auth.isAuthenticated).thenReturn(false); - - final result = await runWithOverrides(command.run); - - expect(result, ExitCode.noUser.code); - - verify( - () => logger.err(any(that: contains('You must be logged in to run'))), - ).called(1); - }); - - test('exits with code 0 when usage is fetched.', () async { - final usage = GetUsageResponse( - plan: ShorebirdPlan( - name: 'Team', - monthlyCost: Money.fromIntWithCurrency(2000, usd), - patchInstallLimit: 20000, - maxTeamSize: 1, - ), - apps: const [ - AppUsage( - id: 'test-app-id', - name: 'test app 2', - patchInstallCount: 42, - ), - AppUsage( - id: 'test-app-id', - name: 'test app 2', - patchInstallCount: 42, - ), - ], - currentPeriodCost: Money.fromIntWithCurrency(2000, usd), - currentPeriodStart: DateTime(2023), - currentPeriodEnd: DateTime(2023, 2), - ); - when( - () => codePushClientWrapper.getUsage(), - ).thenAnswer((_) async => usage); - - final result = await runWithOverrides(command.run); - - expect(result, ExitCode.success.code); - verify(() => logger.info('📈 Usage')).called(1); - verify( - () => logger.info( - any( - that: contains(''' - -You are on the ${lightCyan.wrap('Team')} plan. - -┌────────────┬────────────────┐ -│ App │ Patch Installs │ -├────────────┼────────────────┤ -│ test app 2 │ 42 │ -├────────────┼────────────────┤ -│ test app 2 │ 42 │ -├────────────┼────────────────┤ -│ Total │ 84 │ -└────────────┴────────────────┘ - -${styleBold.wrap('${lightCyan.wrap('19916')} patch installs remaining in the current billing period.')} - -Current Billing Period: ${lightCyan.wrap(DateFormat.yMMMd().format(usage.currentPeriodStart))} - ${lightCyan.wrap(DateFormat.yMMMd().format(usage.currentPeriodEnd))} -Month-to-date cost: ${lightCyan.wrap(r'$20.00')} - -${styleBold.wrap('*Usage data is not reported in real-time and may be delayed by up to 48 hours.')}'''), - ), - ), - ).called(1); - }); - - test('exits with code 0 when usage is fetched (unlimited).', () async { - final usage = GetUsageResponse( - plan: ShorebirdPlan( - name: 'Hobby', - monthlyCost: Money.fromIntWithCurrency(0, usd), - patchInstallLimit: null, - maxTeamSize: 1, - ), - apps: const [ - AppUsage( - id: 'test-app-id', - name: 'test app 2', - patchInstallCount: 42, - ), - AppUsage( - id: 'test-app-id', - name: 'test app 2', - patchInstallCount: 42, - ), - ], - currentPeriodCost: Money.fromIntWithCurrency(0, usd), - currentPeriodStart: DateTime(2023), - currentPeriodEnd: DateTime(2023, 2), - ); - when( - () => codePushClientWrapper.getUsage(), - ).thenAnswer((_) async => usage); - - final result = await runWithOverrides(command.run); - - expect(result, ExitCode.success.code); - verify(() => logger.info('📈 Usage')).called(1); - verify( - () => logger.info( - any( - that: contains(''' - -You are on the ${lightCyan.wrap('Hobby')} plan. - -┌────────────┬────────────────┐ -│ App │ Patch Installs │ -├────────────┼────────────────┤ -│ test app 2 │ 42 │ -├────────────┼────────────────┤ -│ test app 2 │ 42 │ -├────────────┼────────────────┤ -│ Total │ 84 │ -└────────────┴────────────────┘ - -${styleBold.wrap('${lightCyan.wrap('∞')} patch installs remaining in the current billing period.')} - -Current Billing Period: ${lightCyan.wrap(DateFormat.yMMMd().format(usage.currentPeriodStart))} - ${lightCyan.wrap(DateFormat.yMMMd().format(usage.currentPeriodEnd))} -Month-to-date cost: ${lightCyan.wrap(r'$0.00')} - -${styleBold.wrap('*Usage data is not reported in real-time and may be delayed by up to 48 hours.')}'''), - ), - ), - ).called(1); - }); - }); -} diff --git a/packages/shorebird_cli/test/src/commands/account/create_account_command_test.dart b/packages/shorebird_cli/test/src/commands/account/create_account_command_test.dart deleted file mode 100644 index c543aaf3..00000000 --- a/packages/shorebird_cli/test/src/commands/account/create_account_command_test.dart +++ /dev/null @@ -1,168 +0,0 @@ -import 'package:http/http.dart' as http; -import 'package:mason_logger/mason_logger.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:scoped/scoped.dart'; -import 'package:shorebird_cli/src/auth/auth.dart'; -import 'package:shorebird_cli/src/commands/account/account.dart'; -import 'package:shorebird_cli/src/logger.dart' hide logger; -import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; -import 'package:test/test.dart'; - -class _MockAuth extends Mock implements Auth {} - -class _MockHttpClient extends Mock implements http.Client {} - -class _MockLogger extends Mock implements Logger {} - -class _MockUser extends Mock implements User {} - -void main() { - group(CreateAccountCommand, () { - const userName = 'John Doe'; - const email = 'tester@shorebird.dev'; - - late Auth auth; - late http.Client httpClient; - late Logger logger; - late User user; - - late CreateAccountCommand createAccountCommand; - - R runWithOverrides(R Function() body) { - return runScoped( - body, - values: { - authRef.overrideWith(() => auth), - loggerRef.overrideWith(() => logger) - }, - ); - } - - setUp(() { - auth = _MockAuth(); - httpClient = _MockHttpClient(); - logger = _MockLogger(); - user = _MockUser(); - - when(() => auth.client).thenReturn(httpClient); - when(() => auth.credentialsFilePath).thenReturn('credentials.json'); - - when(() => logger.prompt(any())).thenReturn(userName); - - when(() => user.displayName).thenReturn(userName); - when(() => user.email).thenReturn(email); - - createAccountCommand = runWithOverrides(CreateAccountCommand.new); - }); - - test('has a description', () { - expect(createAccountCommand.description, isNotEmpty); - }); - - test('login prompt is correct', () { - runWithOverrides( - () => createAccountCommand.authPrompt('https://shorebird.dev'), - ); - verify( - () => logger.info(''' -Shorebird currently requires a Google account for authentication. If you'd like to use a different kind of auth, please let us know: ${lightCyan.wrap('https://github.com/shorebirdtech/shorebird/issues/335')}. - -Follow the link below to authenticate: - -${styleBold.wrap(styleUnderlined.wrap(lightCyan.wrap('https://shorebird.dev')))} - -Waiting for your authorization...'''), - ).called(1); - }); - - test('namePrompt asks user for name', () { - final name = runWithOverrides(() => createAccountCommand.namePrompt()); - expect(name, userName); - verify( - () => logger.prompt( - 'Tell us your name to finish creating your account:', - ), - ).called(1); - }); - - test('exits with code 0 if user is logged in', () async { - when( - () => auth.signUp( - authPrompt: any(named: 'authPrompt'), - namePrompt: any(named: 'namePrompt'), - ), - ).thenThrow(UserAlreadyLoggedInException(email: email)); - - final result = await runWithOverrides(createAccountCommand.run); - - expect(result, ExitCode.success.code); - - verify( - () => logger.info(any(that: contains('You are already logged in '))), - ).called(1); - }); - - test( - 'exits with code 0 and prints message and exits if user already has an ' - 'account', () async { - when( - () => auth.signUp( - authPrompt: any(named: 'authPrompt'), - namePrompt: any(named: 'namePrompt'), - ), - ).thenThrow(UserAlreadyExistsException(user)); - - final result = await runWithOverrides(createAccountCommand.run); - - expect(result, ExitCode.success.code); - verify( - () => logger.info(any(that: contains('You already have an account'))), - ).called(1); - }); - - test('exits with code 70 when signUp fails', () async { - when( - () => auth.signUp( - authPrompt: any(named: 'authPrompt'), - namePrompt: any(named: 'namePrompt'), - ), - ).thenThrow(Exception('login failed')); - - final result = await runWithOverrides(createAccountCommand.run); - - expect(result, ExitCode.software.code); - verify(() => logger.err(any(that: contains('login failed')))).called(1); - }); - - test('exits with code 0, creates account with name provided by user', - () async { - when( - () => auth.signUp( - authPrompt: any(named: 'authPrompt'), - namePrompt: any(named: 'namePrompt'), - ), - ).thenAnswer((_) async => user); - - final result = await runWithOverrides(createAccountCommand.run); - - expect(result, ExitCode.success.code); - verify( - () => auth.signUp( - authPrompt: any(named: 'authPrompt'), - namePrompt: any(named: 'namePrompt'), - ), - ).called(1); - verify( - () => logger.info( - any( - that: stringContainsInOrder([ - 'Welcome to Shorebird', - userName, - 'shorebird account upgrade', - ]), - ), - ), - ).called(1); - }); - }); -}