Files
shorebird/test/src/command_runner_test.dart
T
2023-03-02 11:37:56 -06:00

179 lines
6.1 KiB
Dart

import 'dart:io';
import 'package:args/command_runner.dart';
import 'package:cli_completion/cli_completion.dart';
import 'package:mason_logger/mason_logger.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pub_updater/pub_updater.dart';
import 'package:shorebird_cli/src/command_runner.dart';
import 'package:shorebird_cli/src/version.dart';
import 'package:test/test.dart';
class _MockLogger extends Mock implements Logger {}
class _MockProcessResult extends Mock implements ProcessResult {}
class _MockProgress extends Mock implements Progress {}
class _MockPubUpdater extends Mock implements PubUpdater {}
const latestVersion = '0.0.0';
final updatePrompt = '''
${lightYellow.wrap('Update available!')} ${lightCyan.wrap(packageVersion)} \u2192 ${lightCyan.wrap(latestVersion)}
Run ${lightCyan.wrap('$executableName update')} to update''';
void main() {
group('ShorebirdCliCommandRunner', () {
late PubUpdater pubUpdater;
late Logger logger;
late ProcessResult processResult;
late ShorebirdCliCommandRunner commandRunner;
setUp(() {
pubUpdater = _MockPubUpdater();
when(
() => pubUpdater.getLatestVersion(any()),
).thenAnswer((_) async => packageVersion);
logger = _MockLogger();
processResult = _MockProcessResult();
when(() => processResult.exitCode).thenReturn(ExitCode.success.code);
commandRunner = ShorebirdCliCommandRunner(
logger: logger,
pubUpdater: pubUpdater,
);
});
test('shows update message when newer version exists', () async {
when(
() => pubUpdater.getLatestVersion(any()),
).thenAnswer((_) async => latestVersion);
final result = await commandRunner.run(['--version']);
expect(result, equals(ExitCode.success.code));
verify(() => logger.info(updatePrompt)).called(1);
});
test(
'Does not show update message when the shell calls the '
'completion command',
() async {
when(
() => pubUpdater.getLatestVersion(any()),
).thenAnswer((_) async => latestVersion);
final result = await commandRunner.run(['completion']);
expect(result, equals(ExitCode.success.code));
verifyNever(() => logger.info(updatePrompt));
},
);
test('does not show update message when using update command', () async {
when(
() => pubUpdater.getLatestVersion(any()),
).thenAnswer((_) async => latestVersion);
when(
() => pubUpdater.update(
packageName: packageName,
versionConstraint: any(named: 'versionConstraint'),
),
).thenAnswer((_) async => processResult);
when(
() => pubUpdater.isUpToDate(
packageName: any(named: 'packageName'),
currentVersion: any(named: 'currentVersion'),
),
).thenAnswer((_) async => true);
final progress = _MockProgress();
final progressLogs = <String>[];
when(() => progress.complete(any())).thenAnswer((_) {
final message = _.positionalArguments.elementAt(0) as String?;
if (message != null) progressLogs.add(message);
});
when(() => logger.progress(any())).thenReturn(progress);
final result = await commandRunner.run(['update']);
expect(result, equals(ExitCode.success.code));
verifyNever(() => logger.info(updatePrompt));
});
test('can be instantiated without an explicit analytics/logger instance',
() {
final commandRunner = ShorebirdCliCommandRunner();
expect(commandRunner, isNotNull);
expect(commandRunner, isA<CompletionCommandRunner<int>>());
});
test('handles FormatException', () async {
const exception = FormatException('oops!');
var isFirstInvocation = true;
when(() => logger.info(any())).thenAnswer((_) {
if (isFirstInvocation) {
isFirstInvocation = false;
throw exception;
}
});
final result = await commandRunner.run(['--version']);
expect(result, equals(ExitCode.usage.code));
verify(() => logger.err(exception.message)).called(1);
verify(() => logger.info(commandRunner.usage)).called(1);
});
test('handles UsageException', () async {
final exception = UsageException('oops!', 'exception usage');
var isFirstInvocation = true;
when(() => logger.info(any())).thenAnswer((_) {
if (isFirstInvocation) {
isFirstInvocation = false;
throw exception;
}
});
final result = await commandRunner.run(['--version']);
expect(result, equals(ExitCode.usage.code));
verify(() => logger.err(exception.message)).called(1);
verify(() => logger.info('exception usage')).called(1);
});
group('--version', () {
test('outputs current version', () async {
final result = await commandRunner.run(['--version']);
expect(result, equals(ExitCode.success.code));
verify(() => logger.info(packageVersion)).called(1);
});
});
group('--verbose', () {
test('enables verbose logging', () async {
final result = await commandRunner.run(['--verbose']);
expect(result, equals(ExitCode.success.code));
verify(() => logger.detail('Argument information:')).called(1);
verify(() => logger.detail(' Top level options:')).called(1);
verify(() => logger.detail(' - verbose: true')).called(1);
verifyNever(() => logger.detail(' Command options:'));
});
test('enables verbose logging for sub commands', () async {
final result = await commandRunner.run([
'--verbose',
'sample',
'--cyan',
]);
expect(result, equals(ExitCode.success.code));
verify(() => logger.detail('Argument information:')).called(1);
verify(() => logger.detail(' Top level options:')).called(1);
verify(() => logger.detail(' - verbose: true')).called(1);
verify(() => logger.detail(' Command: sample')).called(1);
verify(() => logger.detail(' Command options:')).called(1);
verify(() => logger.detail(' - cyan: true')).called(1);
});
});
});
}