feat: add URL reachability checks to shorebird doctor (#2537)

This commit is contained in:
Bryan Oltman
2024-10-15 14:49:53 -04:00
committed by GitHub
parent 8f43bb5d15
commit cec1bf5b6e
6 changed files with 139 additions and 2 deletions
@@ -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);
});
});
});
});
}