feature(updater): Implement auto-updating system using AppCast protocol
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:moonwell_launcher/features/launcher/data/launcher_log_service.dart';
|
||||
import 'package:moonwell_launcher/features/launcher/data/launcher_update_service.dart';
|
||||
|
||||
void main() {
|
||||
group('LauncherUpdateService', () {
|
||||
test('does nothing on unsupported platforms', () async {
|
||||
final client = _FakeLauncherUpdateClient();
|
||||
final logger = _FakeLauncherLogService();
|
||||
final service = LauncherUpdateService.forTesting(
|
||||
launcherLogService: logger,
|
||||
updateClient: client,
|
||||
isWindows: false,
|
||||
);
|
||||
|
||||
await service.checkForUpdates(
|
||||
feedUrl: 'https://moon-well.online/launcher/updates/appcast.xml',
|
||||
);
|
||||
|
||||
expect(client.calls, isEmpty);
|
||||
expect(logger.entries, isEmpty);
|
||||
});
|
||||
|
||||
test('logs disabled state when the feed URL is empty', () async {
|
||||
final client = _FakeLauncherUpdateClient();
|
||||
final logger = _FakeLauncherLogService();
|
||||
final service = LauncherUpdateService.forTesting(
|
||||
launcherLogService: logger,
|
||||
updateClient: client,
|
||||
isWindows: true,
|
||||
);
|
||||
|
||||
await service.checkForUpdates(feedUrl: ' ');
|
||||
|
||||
expect(client.calls, isEmpty);
|
||||
expect(logger.entries.single, startsWith('INFO:'));
|
||||
});
|
||||
|
||||
test('rejects a non-HTTPS feed URL without invoking the updater', () async {
|
||||
final client = _FakeLauncherUpdateClient();
|
||||
final logger = _FakeLauncherLogService();
|
||||
final service = LauncherUpdateService.forTesting(
|
||||
launcherLogService: logger,
|
||||
updateClient: client,
|
||||
isWindows: true,
|
||||
);
|
||||
|
||||
await service.checkForUpdates(
|
||||
feedUrl: 'http://moon-well.online/appcast.xml',
|
||||
);
|
||||
|
||||
expect(client.calls, isEmpty);
|
||||
expect(logger.entries.single, startsWith('ERROR:'));
|
||||
});
|
||||
|
||||
test('rejects an HTTPS URL without a host', () async {
|
||||
final client = _FakeLauncherUpdateClient();
|
||||
final logger = _FakeLauncherLogService();
|
||||
final service = LauncherUpdateService.forTesting(
|
||||
launcherLogService: logger,
|
||||
updateClient: client,
|
||||
isWindows: true,
|
||||
);
|
||||
|
||||
await service.checkForUpdates(feedUrl: 'https:appcast.xml');
|
||||
|
||||
expect(client.calls, isEmpty);
|
||||
expect(logger.entries.single, startsWith('ERROR:'));
|
||||
});
|
||||
|
||||
test(
|
||||
'configures WinSparkle before starting one background check',
|
||||
() async {
|
||||
final client = _FakeLauncherUpdateClient();
|
||||
final logger = _FakeLauncherLogService();
|
||||
final service = LauncherUpdateService.forTesting(
|
||||
launcherLogService: logger,
|
||||
updateClient: client,
|
||||
isWindows: true,
|
||||
);
|
||||
const feedUrl = 'https://moon-well.online/launcher/updates/appcast.xml';
|
||||
|
||||
await service.checkForUpdates(feedUrl: feedUrl);
|
||||
await service.checkForUpdates(feedUrl: feedUrl);
|
||||
|
||||
expect(client.calls, <String>[
|
||||
'setFeedUrl:$feedUrl',
|
||||
'disableScheduledChecks',
|
||||
'checkInBackground',
|
||||
]);
|
||||
expect(logger.entries.single, startsWith('INFO:'));
|
||||
},
|
||||
);
|
||||
|
||||
test('logs updater failures without propagating them', () async {
|
||||
final client = _FakeLauncherUpdateClient(failure: Exception('offline'));
|
||||
final logger = _FakeLauncherLogService();
|
||||
final service = LauncherUpdateService.forTesting(
|
||||
launcherLogService: logger,
|
||||
updateClient: client,
|
||||
isWindows: true,
|
||||
);
|
||||
|
||||
await expectLater(
|
||||
service.checkForUpdates(
|
||||
feedUrl: 'https://moon-well.online/launcher/updates/appcast.xml',
|
||||
),
|
||||
completes,
|
||||
);
|
||||
|
||||
expect(logger.entries.single, contains('offline'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class _FakeLauncherUpdateClient implements LauncherUpdateClient {
|
||||
_FakeLauncherUpdateClient({this.failure});
|
||||
|
||||
final Object? failure;
|
||||
final List<String> calls = <String>[];
|
||||
|
||||
@override
|
||||
Future<void> setFeedUrl(String feedUrl) async {
|
||||
calls.add('setFeedUrl:$feedUrl');
|
||||
if (failure case final error?) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> disableScheduledChecks() async {
|
||||
calls.add('disableScheduledChecks');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> checkInBackground() async {
|
||||
calls.add('checkInBackground');
|
||||
}
|
||||
}
|
||||
|
||||
class _FakeLauncherLogService extends LauncherLogService {
|
||||
final List<String> entries = <String>[];
|
||||
|
||||
@override
|
||||
Future<void> info(
|
||||
String message, {
|
||||
String? installationDir,
|
||||
Map<String, Object?> details = const <String, Object?>{},
|
||||
}) async {
|
||||
entries.add('INFO:$message');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> error(
|
||||
String message, {
|
||||
String? installationDir,
|
||||
Map<String, Object?> details = const <String, Object?>{},
|
||||
Object? error,
|
||||
StackTrace? stackTrace,
|
||||
}) async {
|
||||
entries.add('ERROR:$message:$error');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user