import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pluriwave/modelos/alarma_musical.dart'; import 'package:pluriwave/modelos/emisora.dart'; import 'package:pluriwave/servicios/servicio_alarmas_android.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); const channel = MethodChannel('pluriwave/alarm_scheduler'); late List llamadas; setUp(() { llamadas = []; TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler(channel, (call) async { llamadas.add(call); switch (call.method) { case 'scheduleAlarm': return true; case 'requestIgnoreBatteryOptimizations': return true; case 'openNotificationSettings': return true; case 'getActiveRingingAlarmId': return 'ring1'; case 'stopActiveAlarm': return { 'stopped': true, 'wasRinging': true, 'activeAlarmId': 'ring1', }; } return null; }); }); tearDown(() { TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler(channel, null); }); test( 'programar incluye emisora de respaldo y fade en el payload nativo', () async { final servicio = ServicioAlarmasAndroid(channel: channel); final alarma = AlarmaMusical( id: 'a1', nombre: 'Con respaldo', hora: 7, minuto: 30, tipoProgramacion: TipoProgramacionAlarma.diaria, diasSemana: const [], proximaEjecucion: DateTime(2099, 1, 1, 7, 30), emisora: const Emisora( uuid: 'uuid-principal', nombre: 'Principal FM', url: 'https://principal.example/stream', ), emisoraFallback: const Emisora( uuid: 'uuid-respaldo', nombre: 'Respaldo FM', url: 'https://respaldo.example/stream', ), fadeInSegundos: 12, ); await servicio.programar(alarma); final llamada = llamadas.singleWhere((c) => c.method == 'scheduleAlarm'); final args = llamada.arguments as Map; expect(args['fallbackStationName'], 'Respaldo FM'); expect(args['fallbackStationUrl'], 'https://respaldo.example/stream'); expect(args['fadeInSegundos'], 12); expect(args['fallbackSound'], SonidoInternoAlarma.amanecer.name); }, ); test( 'programar sin emisora de respaldo envia campos de respaldo nulos', () async { final servicio = ServicioAlarmasAndroid(channel: channel); final alarma = AlarmaMusical( id: 'a2', nombre: 'Sin respaldo', hora: 8, minuto: 0, tipoProgramacion: TipoProgramacionAlarma.diaria, diasSemana: const [], proximaEjecucion: DateTime(2099, 1, 1, 8, 0), ); await servicio.programar(alarma); final llamada = llamadas.singleWhere((c) => c.method == 'scheduleAlarm'); final args = llamada.arguments as Map; expect(args.containsKey('fallbackStationName'), isTrue); expect(args['fallbackStationName'], isNull); expect(args.containsKey('fallbackStationUrl'), isTrue); expect(args['fallbackStationUrl'], isNull); expect(args['fadeInSegundos'], 0); }, ); test( 'solicitarExencionBateria invoca requestIgnoreBatteryOptimizations', () async { final servicio = ServicioAlarmasAndroid(channel: channel); final abierto = await servicio.solicitarExencionBateria(); expect(abierto, isTrue); expect( llamadas.map((c) => c.method), contains('requestIgnoreBatteryOptimizations'), ); }, ); test( 'abrirConfiguracionNotificaciones invoca openNotificationSettings ' '(deep link a Settings, distinto del permiso runtime de la primera vez)', () async { final servicio = ServicioAlarmasAndroid(channel: channel); final abierto = await servicio.abrirConfiguracionNotificaciones(); expect(abierto, isTrue); expect( llamadas.map((c) => c.method), contains('openNotificationSettings'), ); }, ); test( 'detenerSonidoActivo mapea el resultado nativo confirmado a ResultadoDetencion', () async { final servicio = ServicioAlarmasAndroid(channel: channel); final resultado = await servicio.detenerSonidoActivo(); final sonando = await servicio.alarmaSonandoId(); expect(resultado.detenido, isTrue); expect(resultado.estabaSonando, isTrue); expect(resultado.alarmaId, 'ring1'); expect(sonando, 'ring1'); }, ); test( 'detenerSonidoActivo retorna un resultado fallido cuando el canal lanza error', () async { TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler(channel, (call) async { llamadas.add(call); throw PlatformException(code: 'STOP_FAILED', message: 'boom'); }); final servicio = ServicioAlarmasAndroid(channel: channel); final resultado = await servicio.detenerSonidoActivo(); expect(resultado.detenido, isFalse); expect(resultado.estabaSonando, isFalse); expect(resultado.alarmaId, isNull); }, ); test('alarmaSonandoId propaga el error del canal (fail-toward-silence, ' 'Finding 2)', () async { TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler(channel, (call) async { llamadas.add(call); throw PlatformException(code: 'QUERY_FAILED', message: 'boom'); }); final servicio = ServicioAlarmasAndroid(channel: channel); expect(() => servicio.alarmaSonandoId(), throwsA(isA())); }); }