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, [ '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 calls = []; @override Future setFeedUrl(String feedUrl) async { calls.add('setFeedUrl:$feedUrl'); if (failure case final error?) { throw error; } } @override Future disableScheduledChecks() async { calls.add('disableScheduledChecks'); } @override Future checkInBackground() async { calls.add('checkInBackground'); } } class _FakeLauncherLogService extends LauncherLogService { final List entries = []; @override Future info( String message, { String? installationDir, Map details = const {}, }) async { entries.add('INFO:$message'); } @override Future error( String message, { String? installationDir, Map details = const {}, Object? error, StackTrace? stackTrace, }) async { entries.add('ERROR:$message:$error'); } }