feat: add URL reachability checks to shorebird doctor (#2537)
This commit is contained in:
@@ -15,6 +15,7 @@ import 'package:shorebird_cli/src/engine_config.dart';
|
||||
import 'package:shorebird_cli/src/executables/executables.dart';
|
||||
import 'package:shorebird_cli/src/http_client/http_client.dart';
|
||||
import 'package:shorebird_cli/src/logger.dart';
|
||||
import 'package:shorebird_cli/src/network_checker.dart';
|
||||
import 'package:shorebird_cli/src/os/os.dart';
|
||||
import 'package:shorebird_cli/src/patch_diff_checker.dart';
|
||||
import 'package:shorebird_cli/src/platform.dart';
|
||||
@@ -63,6 +64,7 @@ Future<void> main(List<String> args) async {
|
||||
iosRef,
|
||||
javaRef,
|
||||
loggerRef,
|
||||
networkCheckerRef,
|
||||
osInterfaceRef,
|
||||
patchExecutableRef,
|
||||
patchDiffCheckerRef,
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:shorebird_cli/src/android_studio.dart';
|
||||
import 'package:shorebird_cli/src/doctor.dart';
|
||||
import 'package:shorebird_cli/src/executables/executables.dart';
|
||||
import 'package:shorebird_cli/src/logger.dart';
|
||||
import 'package:shorebird_cli/src/network_checker.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_command.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_env.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_flutter.dart';
|
||||
@@ -94,6 +95,11 @@ Android Toolchain
|
||||
|
||||
logger.info(output.toString());
|
||||
|
||||
// ignore: cascade_invocations
|
||||
logger.info('URL reachability:');
|
||||
await networkChecker.checkReachability();
|
||||
logger.info('');
|
||||
|
||||
await doctor.runValidators(doctor.generalValidators, applyFixes: shouldFix);
|
||||
|
||||
return ExitCode.success.code;
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import 'package:scoped_deps/scoped_deps.dart';
|
||||
import 'package:shorebird_cli/src/http_client/http_client.dart';
|
||||
import 'package:shorebird_cli/src/logger.dart';
|
||||
|
||||
/// A reference to a [NetworkChecker] instance.
|
||||
final networkCheckerRef = create(NetworkChecker.new);
|
||||
|
||||
/// The [NetworkChecker] instance available in the current zone.
|
||||
NetworkChecker get networkChecker => read(networkCheckerRef);
|
||||
|
||||
/// {@template network_checker}
|
||||
/// Checks reachability of various Shorebird-related endpoints and logs the
|
||||
/// results.
|
||||
/// {@endtemplate}
|
||||
class NetworkChecker {
|
||||
/// The URLs to check for network reachability.
|
||||
static final urlsToCheck = [
|
||||
'https://api.shorebird.dev',
|
||||
'https://console.shorebird.dev',
|
||||
'https://oauth2.googleapis.com',
|
||||
].map(Uri.parse).toList();
|
||||
|
||||
/// Verify that each of [urlsToCheck] responds to an HTTP GET request.
|
||||
Future<void> checkReachability() async {
|
||||
for (final url in urlsToCheck) {
|
||||
final progress = logger.progress('Checking reachability of $url');
|
||||
|
||||
try {
|
||||
await httpClient.get(url);
|
||||
progress.complete('$url OK');
|
||||
} catch (e) {
|
||||
progress.fail('$url unreachable');
|
||||
logger.detail('Failed to reach $url: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import 'package:shorebird_cli/src/commands/commands.dart';
|
||||
import 'package:shorebird_cli/src/doctor.dart';
|
||||
import 'package:shorebird_cli/src/executables/executables.dart';
|
||||
import 'package:shorebird_cli/src/logger.dart';
|
||||
import 'package:shorebird_cli/src/network_checker.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_env.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_flutter.dart';
|
||||
import 'package:shorebird_cli/src/validators/validators.dart';
|
||||
@@ -30,6 +31,7 @@ void main() {
|
||||
late Doctor doctor;
|
||||
late Gradlew gradlew;
|
||||
late Java java;
|
||||
late NetworkChecker networkChecker;
|
||||
late ShorebirdLogger logger;
|
||||
late ShorebirdEnv shorebirdEnv;
|
||||
late ShorebirdFlutter shorebirdFlutter;
|
||||
@@ -46,6 +48,7 @@ void main() {
|
||||
gradlewRef.overrideWith(() => gradlew),
|
||||
javaRef.overrideWith(() => java),
|
||||
loggerRef.overrideWith(() => logger),
|
||||
networkCheckerRef.overrideWith(() => networkChecker),
|
||||
shorebirdEnvRef.overrideWith(() => shorebirdEnv),
|
||||
shorebirdFlutterRef.overrideWith(() => shorebirdFlutter),
|
||||
},
|
||||
@@ -61,6 +64,7 @@ void main() {
|
||||
logsDirectory = Directory.systemTemp.createTempSync('shorebird_logs');
|
||||
java = MockJava();
|
||||
logger = MockShorebirdLogger();
|
||||
networkChecker = MockNetworkChecker();
|
||||
shorebirdEnv = MockShorebirdEnv();
|
||||
shorebirdFlutter = MockShorebirdFlutter();
|
||||
validator = MockValidator();
|
||||
@@ -72,6 +76,9 @@ void main() {
|
||||
when(() => androidSdk.adbPath).thenReturn(null);
|
||||
when(() => gradlew.exists(any())).thenReturn(false);
|
||||
when(() => java.home).thenReturn(null);
|
||||
when(
|
||||
() => networkChecker.checkReachability(),
|
||||
).thenAnswer((_) async => {});
|
||||
when(
|
||||
() => shorebirdEnv.shorebirdEngineRevision,
|
||||
).thenReturn(shorebirdEngineRevision);
|
||||
@@ -104,8 +111,8 @@ Engine • revision $shorebirdEngineRevision
|
||||
});
|
||||
|
||||
test(
|
||||
'prints shorebird version, flutter revision, '
|
||||
'flutter version, and engine revision', () async {
|
||||
'''prints shorebird version, flutter revision, flutter version, and engine revision''',
|
||||
() async {
|
||||
const flutterVersion = '1.2.3';
|
||||
when(
|
||||
() => shorebirdFlutter.getVersionString(),
|
||||
@@ -120,6 +127,7 @@ Flutter $flutterVersion • revision ${shorebirdEnv.flutterRevision}
|
||||
Engine • revision $shorebirdEngineRevision
|
||||
'''),
|
||||
).called(1);
|
||||
verify(() => networkChecker.checkReachability()).called(1);
|
||||
});
|
||||
|
||||
group('--verbose', () {
|
||||
@@ -197,6 +205,8 @@ Android Toolchain
|
||||
''',
|
||||
),
|
||||
);
|
||||
|
||||
verify(() => networkChecker.checkReachability()).called(1);
|
||||
});
|
||||
|
||||
group('when a gradlew executable exists', () {
|
||||
|
||||
@@ -26,6 +26,7 @@ import 'package:shorebird_cli/src/engine_config.dart';
|
||||
import 'package:shorebird_cli/src/executables/devicectl/apple_device.dart';
|
||||
import 'package:shorebird_cli/src/executables/executables.dart';
|
||||
import 'package:shorebird_cli/src/logger.dart';
|
||||
import 'package:shorebird_cli/src/network_checker.dart';
|
||||
import 'package:shorebird_cli/src/os/os.dart';
|
||||
import 'package:shorebird_cli/src/patch_diff_checker.dart';
|
||||
import 'package:shorebird_cli/src/platform/platform.dart';
|
||||
@@ -112,6 +113,8 @@ class MockJwtHeader extends Mock implements JwtHeader {}
|
||||
|
||||
class MockJwtPayload extends Mock implements JwtPayload {}
|
||||
|
||||
class MockNetworkChecker extends Mock implements NetworkChecker {}
|
||||
|
||||
class MockOperatingSystemInterface extends Mock
|
||||
implements OperatingSystemInterface {}
|
||||
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:mason_logger/mason_logger.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:scoped_deps/scoped_deps.dart';
|
||||
import 'package:shorebird_cli/src/http_client/http_client.dart';
|
||||
import 'package:shorebird_cli/src/logger.dart';
|
||||
import 'package:shorebird_cli/src/network_checker.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'fakes.dart';
|
||||
import 'mocks.dart';
|
||||
|
||||
void main() {
|
||||
group(NetworkChecker, () {
|
||||
late http.Client httpClient;
|
||||
late ShorebirdLogger logger;
|
||||
late Progress progress;
|
||||
late NetworkChecker networkChecker;
|
||||
|
||||
R runWithOverrides<R>(R Function() body) {
|
||||
return runScoped(
|
||||
() => body(),
|
||||
values: {
|
||||
httpClientRef.overrideWith(() => httpClient),
|
||||
loggerRef.overrideWith(() => logger),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
setUpAll(() {
|
||||
registerFallbackValue(FakeBaseRequest());
|
||||
registerFallbackValue(Uri());
|
||||
});
|
||||
|
||||
setUp(() {
|
||||
httpClient = MockHttpClient();
|
||||
logger = MockShorebirdLogger();
|
||||
progress = MockProgress();
|
||||
|
||||
when(() => logger.progress(any())).thenReturn(progress);
|
||||
|
||||
networkChecker = NetworkChecker();
|
||||
});
|
||||
|
||||
group('checkReachability', () {
|
||||
group('when endpoints are reachable', () {
|
||||
setUp(() {
|
||||
when(() => httpClient.get(any())).thenAnswer(
|
||||
(_) async => http.Response('', HttpStatus.ok),
|
||||
);
|
||||
});
|
||||
|
||||
test('logs reachability for each checked url', () async {
|
||||
await runWithOverrides(networkChecker.checkReachability);
|
||||
|
||||
verify(
|
||||
() => progress.complete(any(that: contains('OK'))),
|
||||
).called(NetworkChecker.urlsToCheck.length);
|
||||
});
|
||||
});
|
||||
|
||||
group('when endpoints are not reachable', () {
|
||||
setUp(() {
|
||||
when(() => httpClient.send(any())).thenThrow(Exception('oops'));
|
||||
});
|
||||
|
||||
test('logs reachability for each checked url', () async {
|
||||
await runWithOverrides(networkChecker.checkReachability);
|
||||
|
||||
verify(
|
||||
() => progress.fail(any(that: contains('unreachable'))),
|
||||
).called(NetworkChecker.urlsToCheck.length);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user