refactor(shorebird_cli): publish command updates and tests (#1)
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import 'package:args/args.dart';
|
||||
import 'package:args/command_runner.dart';
|
||||
import 'package:cli_completion/cli_completion.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:mason_logger/mason_logger.dart';
|
||||
import 'package:pub_updater/pub_updater.dart';
|
||||
import 'package:shorebird_cli/src/commands/commands.dart';
|
||||
@@ -21,6 +22,7 @@ class ShorebirdCliCommandRunner extends CompletionCommandRunner<int> {
|
||||
/// {@macro shorebird_cli_command_runner}
|
||||
ShorebirdCliCommandRunner({
|
||||
Logger? logger,
|
||||
http.Client? httpClient,
|
||||
PubUpdater? pubUpdater,
|
||||
}) : _logger = logger ?? Logger(),
|
||||
_pubUpdater = pubUpdater ?? PubUpdater(),
|
||||
@@ -39,7 +41,7 @@ class ShorebirdCliCommandRunner extends CompletionCommandRunner<int> {
|
||||
);
|
||||
|
||||
// Add sub commands
|
||||
addCommand(PublishCommand(logger: _logger));
|
||||
addCommand(PublishCommand(logger: _logger, httpClient: httpClient));
|
||||
addCommand(UpdateCommand(logger: _logger, pubUpdater: _pubUpdater));
|
||||
}
|
||||
|
||||
@@ -85,27 +87,6 @@ class ShorebirdCliCommandRunner extends CompletionCommandRunner<int> {
|
||||
return ExitCode.success.code;
|
||||
}
|
||||
|
||||
// Verbose logs
|
||||
_logger
|
||||
..detail('Argument information:')
|
||||
..detail(' Top level options:');
|
||||
for (final option in topLevelResults.options) {
|
||||
if (topLevelResults.wasParsed(option)) {
|
||||
_logger.detail(' - $option: ${topLevelResults[option]}');
|
||||
}
|
||||
}
|
||||
if (topLevelResults.command != null) {
|
||||
final commandResult = topLevelResults.command!;
|
||||
_logger
|
||||
..detail(' Command: ${commandResult.name}')
|
||||
..detail(' Command options:');
|
||||
for (final option in commandResult.options) {
|
||||
if (commandResult.wasParsed(option)) {
|
||||
_logger.detail(' - $option: ${commandResult[option]}');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run the command or show version
|
||||
final int? exitCode;
|
||||
if (topLevelResults['version'] == true) {
|
||||
|
||||
@@ -39,20 +39,20 @@ class PublishCommand extends Command<int> {
|
||||
|
||||
final request = http.MultipartRequest(
|
||||
'POST',
|
||||
Uri.parse('http://localhost:8080/deploy'),
|
||||
Uri.parse('http://localhost:8080/api/v1/releases'),
|
||||
);
|
||||
final file = await http.MultipartFile.fromPath('file', artifact.path);
|
||||
request.files.add(file);
|
||||
final response = await _httpClient.send(request);
|
||||
|
||||
if (response.statusCode != HttpStatus.ok) {
|
||||
if (response.statusCode != HttpStatus.created) {
|
||||
_logger.err(
|
||||
'Deploy failed: ${response.statusCode} ${response.reasonPhrase}',
|
||||
'Failed to deploy: ${response.statusCode} ${response.reasonPhrase}',
|
||||
);
|
||||
return ExitCode.software.code;
|
||||
}
|
||||
|
||||
_logger.success('Deployed ${artifact.path} successfully!');
|
||||
_logger.success('Deployed ${artifact.path}!');
|
||||
|
||||
return ExitCode.success.code;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ dependencies:
|
||||
cli_completion: ^0.2.0
|
||||
http: ^0.13.5
|
||||
mason_logger: ^0.2.4
|
||||
path: ^1.8.3
|
||||
pub_updater: ^0.2.4
|
||||
|
||||
dev_dependencies:
|
||||
|
||||
@@ -5,5 +5,8 @@ import 'package:build_verify/build_verify.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
test('ensure_build', expectBuildClean);
|
||||
test(
|
||||
'ensure_build',
|
||||
() => expectBuildClean(packageRelativeDirectory: 'packages/shorebird_cli'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -151,27 +151,6 @@ void main() {
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,26 +1,91 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:mason_logger/mason_logger.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:shorebird_cli/src/command_runner.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
class _MockLogger extends Mock implements Logger {}
|
||||
|
||||
class _MockHttpClient extends Mock implements http.Client {}
|
||||
|
||||
class _FakeBaseRequest extends Fake implements http.BaseRequest {}
|
||||
|
||||
void main() {
|
||||
group('publish', () {
|
||||
late Logger logger;
|
||||
late http.Client httpClient;
|
||||
late ShorebirdCliCommandRunner commandRunner;
|
||||
|
||||
setUpAll(() {
|
||||
registerFallbackValue(_FakeBaseRequest());
|
||||
});
|
||||
|
||||
setUp(() {
|
||||
logger = _MockLogger();
|
||||
commandRunner = ShorebirdCliCommandRunner(logger: logger);
|
||||
httpClient = _MockHttpClient();
|
||||
commandRunner = ShorebirdCliCommandRunner(
|
||||
logger: logger,
|
||||
httpClient: httpClient,
|
||||
);
|
||||
});
|
||||
|
||||
test('outputs coming soon...', () async {
|
||||
test('throws usage error when no file path is specified.', () async {
|
||||
final exitCode = await commandRunner.run(['publish']);
|
||||
verify(
|
||||
() => logger.err('A single file path must be specified.'),
|
||||
).called(1);
|
||||
expect(exitCode, ExitCode.usage.code);
|
||||
});
|
||||
|
||||
test('throws usage error when multiple args are passed.', () async {
|
||||
final exitCode = await commandRunner.run(['publish', 'arg1', 'arg2']);
|
||||
verify(
|
||||
() => logger.err('A single file path must be specified.'),
|
||||
).called(1);
|
||||
expect(exitCode, ExitCode.usage.code);
|
||||
});
|
||||
|
||||
test('throws no input error when file is not found.', () async {
|
||||
final exitCode = await commandRunner.run([
|
||||
'publish',
|
||||
'missing.txt',
|
||||
]);
|
||||
verify(() => logger.err('File not found: missing.txt')).called(1);
|
||||
expect(exitCode, ExitCode.noInput.code);
|
||||
});
|
||||
|
||||
test('throws error when release fails.', () async {
|
||||
const statusCode = HttpStatus.internalServerError;
|
||||
const reasonPhrase = 'something went wrong';
|
||||
when(() => httpClient.send(any())).thenAnswer(
|
||||
(_) async => http.StreamedResponse(
|
||||
const Stream.empty(),
|
||||
statusCode,
|
||||
reasonPhrase: reasonPhrase,
|
||||
),
|
||||
);
|
||||
final release = p.join('test', 'fixtures', 'release.txt');
|
||||
final exitCode = await commandRunner.run(['publish', release]);
|
||||
verify(
|
||||
() => logger.err('Failed to deploy: $statusCode $reasonPhrase'),
|
||||
).called(1);
|
||||
expect(exitCode, ExitCode.software.code);
|
||||
});
|
||||
|
||||
test('succeeds when release is successful.', () async {
|
||||
when(() => httpClient.send(any())).thenAnswer(
|
||||
(_) async => http.StreamedResponse(
|
||||
const Stream.empty(),
|
||||
HttpStatus.created,
|
||||
),
|
||||
);
|
||||
final release = p.join('test', 'fixtures', 'release.txt');
|
||||
final exitCode = await commandRunner.run(['publish', release]);
|
||||
verify(() => logger.success('Deployed $release!')).called(1);
|
||||
expect(exitCode, ExitCode.success.code);
|
||||
|
||||
verify(() => logger.info('Coming soon...')).called(1);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user