feat(shorebird_cli): expose user (#224)

This commit is contained in:
Felix Angelov
2023-04-04 11:02:25 -05:00
committed by GitHub
parent efdb675a87
commit 8a2ec108ef
30 changed files with 224 additions and 113 deletions
+32 -5
View File
@@ -4,9 +4,11 @@ import 'dart:io';
import 'package:googleapis_auth/auth_io.dart';
import 'package:http/http.dart' as http;
import 'package:path/path.dart' as p;
import 'package:shorebird_cli/src/auth/jwt.dart';
import 'package:shorebird_cli/src/auth/models/models.dart';
import 'package:shorebird_cli/src/config/config.dart';
export 'package:googleapis_auth/googleapis_auth.dart' show AccessCredentials;
export 'package:shorebird_cli/src/auth/models/models.dart' show User;
final _clientId = ClientId(
/// Shorebird CLI's OAuth 2.0 identifier.
@@ -50,12 +52,12 @@ class Auth {
final credentialsFilePath = p.join(shorebirdConfigDir, _credentialsFileName);
http.Client get client {
if (credentials == null) return _httpClient;
return autoRefreshingClient(_clientId, credentials!, _httpClient);
if (_credentials == null) return _httpClient;
return autoRefreshingClient(_clientId, _credentials!, _httpClient);
}
Future<void> login(void Function(String) prompt) async {
if (credentials != null) return;
if (_credentials != null) return;
final client = http.Client();
try {
@@ -65,6 +67,7 @@ class Auth {
client,
prompt,
);
_user = _credentials?.toUser();
_flushCredentials(_credentials!);
} finally {
client.close();
@@ -75,7 +78,11 @@ class Auth {
AccessCredentials? _credentials;
AccessCredentials? get credentials => _credentials;
User? _user;
User? get user => _user;
bool get isAuthenticated => _user != null;
void _loadCredentials() {
final credentialsFile = File(credentialsFilePath);
@@ -86,6 +93,7 @@ class Auth {
_credentials = AccessCredentials.fromJson(
json.decode(contents) as Map<String, dynamic>,
);
_user = _credentials?.toUser();
} catch (_) {}
}
}
@@ -98,6 +106,7 @@ class Auth {
void _clearCredentials() {
_credentials = null;
_user = null;
final credentialsFile = File(credentialsFilePath);
if (credentialsFile.existsSync()) {
@@ -109,3 +118,21 @@ class Auth {
_httpClient.close();
}
}
extension on AccessCredentials {
User toUser() {
final token = idToken;
if (token == null) throw Exception('Missing JWT');
final claims = Jwt.decodeClaims(token);
if (claims == null) throw Exception('Invalid JWT');
try {
return User(email: claims['email'] as String);
} catch (_) {
throw Exception('Malformed claims');
}
}
}
@@ -0,0 +1,22 @@
import 'dart:convert';
/// Jwt Utilities
class Jwt {
/// Decode and extract claims from a JWT token.
static Map<String, dynamic>? decodeClaims(String value) {
final parts = value.split('.');
if (parts.length != 3) return null;
try {
return _decodePart(parts[1]);
} catch (_) {}
return null;
}
}
Map<String, dynamic> _decodePart(String part) {
final normalized = base64.normalize(part);
final base64Decoded = base64.decode(normalized);
final utf8Decoded = utf8.decode(base64Decoded);
final jsonDecoded = json.decode(utf8Decoded) as Map<String, dynamic>;
return jsonDecoded;
}
@@ -0,0 +1 @@
export 'user.dart';
@@ -0,0 +1,10 @@
/// {@template user}
/// A shorebird user account.
/// {@endtemplate}
class User {
/// {@macro user}
const User({required this.email});
/// The user's email address.
final String email;
}
@@ -35,7 +35,7 @@ Defaults to the name in "pubspec.yaml".''',
@override
Future<int>? run() async {
if (auth.credentials == null) {
if (!auth.isAuthenticated) {
logger.err('You must be logged in.');
return ExitCode.noUser.code;
}
@@ -32,7 +32,7 @@ Defaults to the app_id in "shorebird.yaml".''',
@override
Future<int>? run() async {
if (auth.credentials == null) {
if (!auth.isAuthenticated) {
logger.err('You must be logged in.');
return ExitCode.noUser.code;
}
@@ -30,7 +30,7 @@ class ListAppsCommand extends ShorebirdCommand with ShorebirdConfigMixin {
@override
Future<int>? run() async {
if (auth.credentials == null) {
if (!auth.isAuthenticated) {
logger.err('You must be logged in.');
return ExitCode.noUser.code;
}
@@ -35,7 +35,7 @@ class BuildCommand extends ShorebirdCommand
@override
Future<int> run() async {
if (auth.credentials == null) {
if (!auth.isAuthenticated) {
logger
..err('You must be logged in to build.')
..err("Run 'shorebird login' to log in and try again.");
@@ -37,7 +37,7 @@ class CreateChannelsCommand extends ShorebirdCommand with ShorebirdConfigMixin {
@override
Future<int>? run() async {
if (auth.credentials == null) {
if (!auth.isAuthenticated) {
logger.err('You must be logged in to view channels.');
return ExitCode.noUser.code;
}
@@ -36,7 +36,7 @@ class ListChannelsCommand extends ShorebirdCommand with ShorebirdConfigMixin {
@override
Future<int>? run() async {
if (auth.credentials == null) {
if (!auth.isAuthenticated) {
logger.err('You must be logged in to view channels.');
return ExitCode.noUser.code;
}
@@ -24,7 +24,7 @@ class InitCommand extends ShorebirdCommand
@override
Future<int> run() async {
if (auth.credentials == null) {
if (!auth.isAuthenticated) {
logger.err('You must be logged in.');
return ExitCode.noUser.code;
}
@@ -18,10 +18,9 @@ class LoginCommand extends ShorebirdCommand {
@override
Future<int> run() async {
final credentials = auth.credentials;
if (credentials != null) {
if (auth.isAuthenticated) {
logger
..info('You are already logged in.')
..info('You are already logged in as <${auth.user!.email}>.')
..info("Run 'shorebird logout' to log out and try again.");
return ExitCode.success.code;
}
@@ -30,7 +29,7 @@ class LoginCommand extends ShorebirdCommand {
await auth.login(prompt);
logger.info('''
🎉 ${lightGreen.wrap('Welcome to Shorebird! You are now logged in.')}
🎉 ${lightGreen.wrap('Welcome to Shorebird! You are now logged in as <${auth.user!.email}>.')}
🔑 Credentials are stored in ${lightCyan.wrap(auth.credentialsFilePath)}.
🚪 To logout use: "${lightCyan.wrap('shorebird logout')}".''');
@@ -18,7 +18,7 @@ class LogoutCommand extends ShorebirdCommand {
@override
Future<int> run() async {
if (auth.credentials == null) {
if (!auth.isAuthenticated) {
logger.info('You are already logged out.');
return ExitCode.success.code;
}
@@ -93,7 +93,7 @@ class PatchCommand extends ShorebirdCommand
return ExitCode.config.code;
}
if (auth.credentials == null) {
if (!auth.isAuthenticated) {
logger.err('You must be logged in to publish.');
return ExitCode.noUser.code;
}
@@ -71,7 +71,7 @@ make smaller updates to your app.
return ExitCode.config.code;
}
if (auth.credentials == null) {
if (!auth.isAuthenticated) {
logger.err('You must be logged in to release.');
return ExitCode.noUser.code;
}
@@ -34,7 +34,7 @@ class RunCommand extends ShorebirdCommand
@override
Future<int> run() async {
if (auth.credentials == null) {
if (!auth.isAuthenticated) {
logger
..err('You must be logged in to run.')
..err("Run 'shorebird login' to log in and try again.");
@@ -6,19 +6,27 @@ import 'package:test/test.dart';
class _MockHttpClient extends Mock implements http.Client {}
class _MockAccessCredentials extends Mock implements AccessCredentials {}
void main() {
group('Auth', () {
const idToken =
'''eyJhbGciOiJSUzI1NiIsImN0eSI6IkpXVCJ9.eyJlbWFpbCI6InRlc3RAZW1haWwuY29tIn0.pD47BhF3MBLyIpfsgWCzP9twzC1HJxGukpcR36DqT6yfiOMHTLcjDbCjRLAnklWEHiT0BQTKTfhs8IousU90Fm5bVKObudfKu8pP5iZZ6Ls4ohDjTrXky9j3eZpZjwv8CnttBVgRfMJG-7YASTFRYFcOLUpnb4Zm5R6QdoCDUYg''';
const email = 'test@email.com';
final credentials = AccessCredentials(
AccessToken('Bearer', 'token', DateTime.now().toUtc()),
'refreshToken',
AccessToken('Bearer', 'accessToken', DateTime.now().toUtc()),
'',
[],
idToken: idToken,
);
late http.Client httpClient;
late AccessCredentials accessCredentials;
late Auth auth;
setUp(() {
httpClient = _MockHttpClient();
accessCredentials = _MockAccessCredentials();
auth = Auth(
httpClient: httpClient,
obtainAccessCredentials: (clientId, scopes, client, userPrompt) async {
@@ -47,42 +55,95 @@ void main() {
});
group('login', () {
test('should set the credentials', () async {
test('should set the user when claims are valid', () async {
when(() => accessCredentials.idToken).thenReturn(idToken);
await auth.login((_) {});
expect(auth.user, isA<User>().having((u) => u.email, 'email', email));
expect(auth.isAuthenticated, isTrue);
expect(
auth.credentials,
isA<AccessCredentials>().having(
(c) => c.accessToken.data,
'accessToken',
credentials.accessToken.data,
Auth().user,
isA<User>().having((u) => u.email, 'email', email),
);
expect(Auth().isAuthenticated, isTrue);
});
test('should not set the user when token is null', () async {
when(() => accessCredentials.idToken).thenReturn(null);
auth = Auth(
httpClient: httpClient,
obtainAccessCredentials:
(clientId, scopes, client, userPrompt) async => accessCredentials,
);
await expectLater(
auth.login((_) {}),
throwsA(
isA<Exception>().having(
(e) => '$e',
'description',
'Exception: Missing JWT',
),
),
);
expect(
Auth().credentials,
isA<AccessCredentials>().having(
(c) => c.accessToken.data,
'accessToken',
credentials.accessToken.data,
expect(auth.user, isNull);
expect(auth.isAuthenticated, isFalse);
});
test('should not set the user when token is empty', () async {
when(() => accessCredentials.idToken).thenReturn('');
auth = Auth(
httpClient: httpClient,
obtainAccessCredentials:
(clientId, scopes, client, userPrompt) async => accessCredentials,
);
await expectLater(
auth.login((_) {}),
throwsA(
isA<Exception>().having(
(e) => '$e',
'description',
'Exception: Invalid JWT',
),
),
);
expect(auth.user, isNull);
expect(auth.isAuthenticated, isFalse);
});
test('should not set the user when token claims are malformed', () async {
when(() => accessCredentials.idToken).thenReturn(
'''eyJhbGciOiJSUzI1NiIsImN0eSI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.LaR0JfOiDrS1AuABC38kzxpSjRLJ_OtfOkZ8hL6I1GPya-cJYwsmqhi5eMBwEbpYHcJhguG5l56XM6dW8xjdK7JbUN6_53gHBosSnL-Ccf29oW71Ado9sxO17YFQyihyMofJ_v78BPVy2H5O10hNjRn_M0JnnAe0Fvd2VrInlIE''',
);
auth = Auth(
httpClient: httpClient,
obtainAccessCredentials:
(clientId, scopes, client, userPrompt) async => accessCredentials,
);
await expectLater(
auth.login((_) {}),
throwsA(
isA<Exception>().having(
(e) => '$e',
'description',
'Exception: Malformed claims',
),
),
);
expect(auth.user, isNull);
expect(auth.isAuthenticated, isFalse);
});
});
group('logout', () {
test('clears session and wipes state', () async {
await auth.login((_) {});
expect(
auth.credentials,
isA<AccessCredentials>().having(
(c) => c.accessToken.data,
'accessToken',
credentials.accessToken.data,
),
);
expect(auth.user, isA<User>().having((u) => u.email, 'email', email));
expect(auth.isAuthenticated, isTrue);
auth.logout();
expect(auth.credentials, isNull);
expect(Auth().credentials, isNull);
expect(auth.user, isNull);
expect(auth.isAuthenticated, isFalse);
expect(Auth().user, isNull);
expect(Auth().isAuthenticated, isFalse);
});
});
@@ -0,0 +1,25 @@
import 'package:shorebird_cli/src/auth/jwt.dart';
import 'package:test/test.dart';
void main() {
group('Jwt', () {
group('decodeClaims', () {
test('returns null jwt does not contain 3 segments', () {
expect(Jwt.decodeClaims('invalid'), isNull);
});
test('returns null when jwt payload segment is malformed', () {
expect(Jwt.decodeClaims('this.is.invalid'), isNull);
});
test('returns correct claims when jwt payload segment is valid', () {
expect(
Jwt.decodeClaims(
'''eyJhbGciOiJSUzI1NiIsImN0eSI6IkpXVCJ9.eyJlbWFpbCI6InRlc3RAZW1haWwuY29tIn0.pD47BhF3MBLyIpfsgWCzP9twzC1HJxGukpcR36DqT6yfiOMHTLcjDbCjRLAnklWEHiT0BQTKTfhs8IousU90Fm5bVKObudfKu8pP5iZZ6Ls4ohDjTrXky9j3eZpZjwv8CnttBVgRfMJG-7YASTFRYFcOLUpnb4Zm5R6QdoCDUYg''',
),
equals({'email': 'test@email.com'}),
);
});
});
});
}
@@ -17,13 +17,10 @@ class _MockCodePushClient extends Mock implements CodePushClient {}
class _MockLogger extends Mock implements Logger {}
class _MockAccessCredentials extends Mock implements AccessCredentials {}
void main() {
group('create', () {
const appId = 'app-id';
const displayName = 'Example App';
final credentials = _MockAccessCredentials();
late ArgResults argResults;
late http.Client httpClient;
@@ -49,7 +46,7 @@ void main() {
logger: logger,
)..testArgResults = argResults;
when(() => auth.credentials).thenReturn(credentials);
when(() => auth.isAuthenticated).thenReturn(true);
when(() => auth.client).thenReturn(httpClient);
});
@@ -58,7 +55,7 @@ void main() {
});
test('returns no user error when not logged in', () async {
when(() => auth.credentials).thenReturn(null);
when(() => auth.isAuthenticated).thenReturn(false);
final result = await command.run();
expect(result, ExitCode.noUser.code);
});
@@ -7,8 +7,6 @@ import 'package:shorebird_cli/src/commands/commands.dart';
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
import 'package:test/test.dart';
class _MockAccessCredentials extends Mock implements AccessCredentials {}
class _MockArgResults extends Mock implements ArgResults {}
class _MockHttpClient extends Mock implements http.Client {}
@@ -22,7 +20,6 @@ class _MockLogger extends Mock implements Logger {}
void main() {
group('delete', () {
const appId = 'example';
final credentials = _MockAccessCredentials();
late ArgResults argResults;
late http.Client httpClient;
@@ -48,7 +45,7 @@ void main() {
logger: logger,
)..testArgResults = argResults;
when(() => auth.credentials).thenReturn(credentials);
when(() => auth.isAuthenticated).thenReturn(true);
when(() => auth.client).thenReturn(httpClient);
});
@@ -60,7 +57,7 @@ void main() {
});
test('returns no user error when not logged in', () async {
when(() => auth.credentials).thenReturn(null);
when(() => auth.isAuthenticated).thenReturn(false);
final result = await command.run();
expect(result, ExitCode.noUser.code);
});
@@ -6,8 +6,6 @@ import 'package:shorebird_cli/src/commands/commands.dart';
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
import 'package:test/test.dart';
class _MockAccessCredentials extends Mock implements AccessCredentials {}
class _MockHttpClient extends Mock implements http.Client {}
class _MockAuth extends Mock implements Auth {}
@@ -18,8 +16,6 @@ class _MockLogger extends Mock implements Logger {}
void main() {
group('list', () {
final credentials = _MockAccessCredentials();
late http.Client httpClient;
late Auth auth;
late CodePushClient codePushClient;
@@ -43,7 +39,7 @@ void main() {
logger: logger,
);
when(() => auth.credentials).thenReturn(credentials);
when(() => auth.isAuthenticated).thenReturn(true);
when(() => auth.client).thenReturn(httpClient);
});
@@ -52,7 +48,7 @@ void main() {
});
test('returns ExitCode.noUser when not logged in', () async {
when(() => auth.credentials).thenReturn(null);
when(() => auth.isAuthenticated).thenReturn(false);
expect(await command.run(), ExitCode.noUser.code);
});
@@ -14,8 +14,6 @@ import 'package:test/test.dart';
class _MockArgResults extends Mock implements ArgResults {}
class _MockAccessCredentials extends Mock implements AccessCredentials {}
class _MockHttpClient extends Mock implements http.Client {}
class _MockAuth extends Mock implements Auth {}
@@ -30,8 +28,6 @@ class _MockCodePushClient extends Mock implements CodePushClient {}
void main() {
group('build', () {
final credentials = _MockAccessCredentials();
late ArgResults argResults;
late Directory applicationConfigHome;
late http.Client httpClient;
@@ -70,7 +66,7 @@ void main() {
testApplicationConfigHome = (_) => applicationConfigHome.path;
when(() => argResults.rest).thenReturn([]);
when(() => auth.credentials).thenReturn(credentials);
when(() => auth.isAuthenticated).thenReturn(true);
when(() => auth.client).thenReturn(httpClient);
when(
() => codePushClient.downloadEngine(revision: any(named: 'revision')),
@@ -79,7 +75,7 @@ void main() {
});
test('exits with no user when not logged in', () async {
when(() => auth.credentials).thenReturn(null);
when(() => auth.isAuthenticated).thenReturn(false);
final result = await buildCommand.run();
expect(result, equals(ExitCode.noUser.code));
@@ -7,8 +7,6 @@ import 'package:shorebird_cli/src/commands/commands.dart';
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
import 'package:test/test.dart';
class _MockAccessCredentials extends Mock implements AccessCredentials {}
class _MockArgResults extends Mock implements ArgResults {}
class _MockHttpClient extends Mock implements http.Client {}
@@ -27,8 +25,6 @@ void main() {
const channelName = 'my-channel';
const channel = Channel(id: 0, appId: appId, name: channelName);
final credentials = _MockAccessCredentials();
late ArgResults argResults;
late http.Client httpClient;
late Auth auth;
@@ -57,7 +53,7 @@ void main() {
when(() => argResults['app-id']).thenReturn(appId);
when(() => argResults['name']).thenReturn(channelName);
when(() => auth.credentials).thenReturn(credentials);
when(() => auth.isAuthenticated).thenReturn(true);
when(() => auth.client).thenReturn(httpClient);
when(() => logger.confirm(any())).thenReturn(true);
when(() => logger.progress(any())).thenReturn(progress);
@@ -71,7 +67,7 @@ void main() {
});
test('returns ExitCode.noUser when not logged in', () async {
when(() => auth.credentials).thenReturn(null);
when(() => auth.isAuthenticated).thenReturn(false);
expect(await command.run(), ExitCode.noUser.code);
});
@@ -7,8 +7,6 @@ import 'package:shorebird_cli/src/commands/commands.dart';
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
import 'package:test/test.dart';
class _MockAccessCredentials extends Mock implements AccessCredentials {}
class _MockArgResults extends Mock implements ArgResults {}
class _MockHttpClient extends Mock implements http.Client {}
@@ -22,7 +20,6 @@ class _MockLogger extends Mock implements Logger {}
void main() {
group('list', () {
const appId = 'test-app-id';
final credentials = _MockAccessCredentials();
late ArgResults argResults;
late http.Client httpClient;
@@ -49,7 +46,7 @@ void main() {
)..testArgResults = argResults;
when(() => argResults['app-id']).thenReturn(appId);
when(() => auth.credentials).thenReturn(credentials);
when(() => auth.isAuthenticated).thenReturn(true);
when(() => auth.client).thenReturn(httpClient);
});
@@ -61,7 +58,7 @@ void main() {
});
test('returns ExitCode.noUser when not logged in', () async {
when(() => auth.credentials).thenReturn(null);
when(() => auth.isAuthenticated).thenReturn(false);
expect(await command.run(), ExitCode.noUser.code);
});
@@ -9,8 +9,6 @@ import 'package:shorebird_cli/src/commands/init_command.dart';
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
import 'package:test/test.dart';
class _MockAccessCredentials extends Mock implements AccessCredentials {}
class _MockHttpClient extends Mock implements http.Client {}
class _MockAuth extends Mock implements Auth {}
@@ -34,8 +32,6 @@ version: $version
environment:
sdk: ">=2.19.0 <3.0.0"''';
final credentials = _MockAccessCredentials();
late http.Client httpClient;
late Auth auth;
late CodePushClient codePushClient;
@@ -60,7 +56,7 @@ environment:
logger: logger,
);
when(() => auth.credentials).thenReturn(credentials);
when(() => auth.isAuthenticated).thenReturn(true);
when(() => auth.client).thenReturn(httpClient);
when(
() => codePushClient.createApp(displayName: any(named: 'displayName')),
@@ -75,7 +71,7 @@ environment:
});
test('returns no user error when not logged in', () async {
when(() => auth.credentials).thenReturn(null);
when(() => auth.isAuthenticated).thenReturn(false);
final result = await command.run();
expect(result, ExitCode.noUser.code);
});
@@ -8,15 +8,13 @@ import 'package:shorebird_cli/src/commands/login_command.dart';
import 'package:shorebird_cli/src/config/config.dart';
import 'package:test/test.dart';
class _MockAccessCredentials extends Mock implements AccessCredentials {}
class _MockAuth extends Mock implements Auth {}
class _MockLogger extends Mock implements Logger {}
void main() {
group('login', () {
final credentials = _MockAccessCredentials();
const user = User(email: 'test@email.com');
late Directory applicationConfigHome;
late Logger logger;
@@ -34,15 +32,19 @@ void main() {
when(() => auth.credentialsFilePath).thenReturn(
p.join(applicationConfigHome.path, 'credentials.json'),
);
when(() => auth.isAuthenticated).thenReturn(false);
});
test('exits with code 0 when already logged in', () async {
when(() => auth.credentials).thenReturn(credentials);
when(() => auth.isAuthenticated).thenReturn(true);
when(() => auth.user).thenReturn(user);
final result = await loginCommand.run();
expect(result, equals(ExitCode.success.code));
verify(() => logger.info('You are already logged in.')).called(1);
verify(
() => logger.info('You are already logged in as <${user.email}>.'),
).called(1);
verify(
() => logger.info("Run 'shorebird logout' to log out and try again."),
).called(1);
@@ -61,13 +63,16 @@ void main() {
test('exits with code 0 when logged in successfully', () async {
when(() => auth.login(any())).thenAnswer((_) async {});
when(() => auth.user).thenReturn(user);
final result = await loginCommand.run();
expect(result, equals(ExitCode.success.code));
verify(() => auth.login(any())).called(1);
verify(
() => logger.info(any(that: contains('You are now logged in.'))),
() => logger.info(
any(that: contains('You are now logged in as <${user.email}>.')),
),
).called(1);
});
@@ -4,8 +4,6 @@ import 'package:shorebird_cli/src/auth/auth.dart';
import 'package:shorebird_cli/src/commands/logout_command.dart';
import 'package:test/test.dart';
class _MockAccessCredentials extends Mock implements AccessCredentials {}
class _MockLogger extends Mock implements Logger {}
class _MockAuth extends Mock implements Auth {}
@@ -27,6 +25,7 @@ void main() {
});
test('exits with code 0 when already logged out', () async {
when(() => auth.isAuthenticated).thenReturn(false);
final result = await logoutCommand.run();
expect(result, equals(ExitCode.success.code));
@@ -36,8 +35,7 @@ void main() {
});
test('exits with code 0 when logged out successfully', () async {
final credentials = _MockAccessCredentials();
when(() => auth.credentials).thenReturn(credentials);
when(() => auth.isAuthenticated).thenReturn(true);
final progress = _MockProgress();
when(() => progress.complete(any())).thenAnswer((invocation) {});
@@ -13,8 +13,6 @@ import 'package:shorebird_cli/src/config/config.dart';
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
import 'package:test/test.dart';
class _MockAccessCredentials extends Mock implements AccessCredentials {}
class _FakeBaseRequest extends Fake implements http.BaseRequest {}
class _MockArgResults extends Mock implements ArgResults {}
@@ -76,8 +74,6 @@ flutter:
assets:
- shorebird.yaml''';
final credentials = _MockAccessCredentials();
late ArgResults argResults;
late Directory applicationConfigHome;
late Auth auth;
@@ -145,7 +141,7 @@ flutter:
when(() => argResults['channel']).thenReturn(channelName);
when(() => argResults['dry-run']).thenReturn(false);
when(() => argResults['force']).thenReturn(false);
when(() => auth.credentials).thenReturn(credentials);
when(() => auth.isAuthenticated).thenReturn(true);
when(() => auth.client).thenReturn(httpClient);
when(() => logger.progress(any())).thenReturn(progress);
when(
@@ -218,8 +214,8 @@ flutter:
expect(exitCode, ExitCode.config.code);
});
test('throws no user error when session does not exist', () async {
when(() => auth.credentials).thenReturn(null);
test('throws no user error when user is not logged in', () async {
when(() => auth.isAuthenticated).thenReturn(false);
final tempDir = setUpTempDir();
final exitCode = await IOOverrides.runZoned(
() => command.run(),
@@ -13,8 +13,6 @@ import 'package:shorebird_cli/src/config/config.dart';
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
import 'package:test/test.dart';
class _MockAccessCredentials extends Mock implements AccessCredentials {}
class _MockArgResults extends Mock implements ArgResults {}
class _MockHttpClient extends Mock implements http.Client {}
@@ -63,8 +61,6 @@ flutter:
assets:
- shorebird.yaml''';
final credentials = _MockAccessCredentials();
late ArgResults argResults;
late Directory applicationConfigHome;
late http.Client httpClient;
@@ -120,7 +116,7 @@ flutter:
when(() => argResults.rest).thenReturn([]);
when(() => argResults['arch']).thenReturn(arch);
when(() => argResults['platform']).thenReturn(platform);
when(() => auth.credentials).thenReturn(credentials);
when(() => auth.isAuthenticated).thenReturn(true);
when(() => auth.client).thenReturn(httpClient);
when(() => logger.progress(any())).thenReturn(progress);
when(() => logger.confirm(any())).thenReturn(true);
@@ -168,8 +164,8 @@ flutter:
expect(exitCode, ExitCode.config.code);
});
test('throws no user error when session does not exist', () async {
when(() => auth.credentials).thenReturn(null);
test('throws no user error when user is not logged in', () async {
when(() => auth.isAuthenticated).thenReturn(false);
final tempDir = setUpTempDir();
final exitCode = await IOOverrides.runZoned(
() => command.run(),
@@ -15,8 +15,6 @@ import 'package:shorebird_cli/src/config/config.dart';
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
import 'package:test/test.dart';
class _MockAccessCredentials extends Mock implements AccessCredentials {}
class _MockArgResults extends Mock implements ArgResults {}
class _MockHttpClient extends Mock implements http.Client {}
@@ -33,8 +31,6 @@ class _MockCodePushClient extends Mock implements CodePushClient {}
void main() {
group('run', () {
final credentials = _MockAccessCredentials();
late ArgResults argResults;
late Directory applicationConfigHome;
late http.Client httpClient;
@@ -69,13 +65,13 @@ void main() {
testApplicationConfigHome = (_) => applicationConfigHome.path;
when(() => argResults.rest).thenReturn([]);
when(() => auth.credentials).thenReturn(credentials);
when(() => auth.isAuthenticated).thenReturn(true);
when(() => auth.client).thenReturn(httpClient);
when(() => logger.progress(any())).thenReturn(_MockProgress());
});
test('exits with no user when not logged in', () async {
when(() => auth.credentials).thenReturn(null);
when(() => auth.isAuthenticated).thenReturn(false);
final result = await runCommand.run();
expect(result, equals(ExitCode.noUser.code));