import 'package:audio_service/audio_service.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pluriwave/modelos/emisora.dart'; import 'package:pluriwave/servicios/navegacion_auto.dart'; void main() { group('ConstructorArbolAuto.raiz', () { test('devuelve exactamente 3 carpetas no reproducibles con los ids ' 'esperados', () { final raiz = ConstructorArbolAuto().raiz(); expect(raiz, hasLength(3)); final ids = raiz.map((item) => item.id).toSet(); expect( ids, equals({ ConstructorArbolAuto.idFavoritos, ConstructorArbolAuto.idTodas, ConstructorArbolAuto.idMisEmisoras, }), ); for (final item in raiz) { expect(item.playable, isFalse); expect(item.title, isNotEmpty); } }); }); group('ConstructorArbolAuto.itemEmisora', () { test('usa el favicon remoto como artUri cuando existe', () { final emisora = _emisora( uuid: 'uuid-logo', nombre: 'Radio Con Logo', favicon: 'https://cdn.example.com/logo.png', ); final item = ConstructorArbolAuto().itemEmisora(emisora); expect(item.id, 'emisora:${emisora.uuid}'); expect(item.title, emisora.nombre); expect(item.artUri.toString(), emisora.favicon); expect(item.playable, isTrue); }); test('cae al arte por defecto cuando el favicon es null o vacío', () { final sinFavicon = _emisora( uuid: 'uuid-null', nombre: 'Radio Sin Logo', favicon: null, ); final faviconVacio = _emisora( uuid: 'uuid-vacio', nombre: 'Radio Logo Vacio', favicon: '', ); const esperado = 'android.resource://es.freetimelab.pluriwave/drawable/default_station_art'; final builder = ConstructorArbolAuto(); expect(builder.itemEmisora(sinFavicon).artUri.toString(), esperado); expect(builder.itemEmisora(faviconVacio).artUri.toString(), esperado); }); }); group('ConstructorArbolAuto.hijos', () { test('limita a 50 items y respeta el orden de ordenarEmisoras', () { final emisoras = List.generate( 60, (i) => _emisora(uuid: 'uuid-$i', nombre: 'Radio $i', bitrate: i), ); final hijos = ConstructorArbolAuto().hijos( ConstructorArbolAuto.idFavoritos, emisoras: emisoras, ); expect(hijos, hasLength(50)); // ordenarEmisoras por calidad ordena por bitrate descendente: el // primer item debe ser el de mayor bitrate (59) y el ultimo (el #50) // el de bitrate 10 (59..10 son 50 valores). expect(hijos.first.id, 'emisora:uuid-59'); expect(hijos.last.id, 'emisora:uuid-10'); }); test('lista vacía cuando no hay emisoras, sin lanzar', () { final hijos = ConstructorArbolAuto().hijos( ConstructorArbolAuto.idFavoritos, emisoras: const [], ); expect(hijos, isEmpty); }); test('parentId desconocido devuelve lista vacía', () { final emisoras = [_emisora(uuid: 'uuid-1', nombre: 'Radio 1')]; final hijos = ConstructorArbolAuto().hijos( 'carpeta-inexistente', emisoras: emisoras, ); expect(hijos, isEmpty); }); }); group('ConstructorArbolAuto.resolver', () { test('resuelve un uuid conocido a su Emisora', () { final emisora = _emisora(uuid: 'uuid-conocido', nombre: 'Conocida'); final universo = [emisora, _emisora(uuid: 'otra', nombre: 'Otra')]; final resultado = ConstructorArbolAuto().resolver( 'emisora:uuid-conocido', universo, ); expect(resultado, equals(emisora)); }); test( 'devuelve null para id sin prefijo, id malformado o uuid sin match, ' 'sin lanzar', () { final universo = [_emisora(uuid: 'uuid-conocido', nombre: 'Conocida')]; final builder = ConstructorArbolAuto(); expect(builder.resolver('favoritos', universo), isNull); expect(builder.resolver('emisora:', universo), isNull); expect( builder.resolver('emisora:uuid-desconocido', universo), isNull, ); }, ); }); group('reproducirPorMediaId', () { test( 'resuelve el id y delega a reproducir con un MediaItem con forma de ' 'telefono (id=url, extras.uuid)', () async { final emisora = _emisora( uuid: 'uuid-play', nombre: 'Radio a reproducir', url: 'https://stream.demo/play', ); final fuente = _FakeFuenteEmisorasAuto( porUuidResultado: {emisora.uuid: emisora}, ); MediaItem? recibido; await reproducirPorMediaId( 'emisora:${emisora.uuid}', fuente: fuente, reproducir: (item) async { recibido = item; }, ); expect(recibido, isNotNull); expect(recibido!.id, emisora.url); expect(recibido!.extras?['uuid'], emisora.uuid); }, ); test( 'id obsoleto/desconocido no llama a reproducir ni lanza excepción', () async { final fuente = _FakeFuenteEmisorasAuto(porUuidResultado: const {}); var llamadas = 0; await reproducirPorMediaId( 'emisora:uuid-fantasma', fuente: fuente, reproducir: (item) async { llamadas++; }, ); expect(llamadas, 0); }, ); }); } Emisora _emisora({ required String uuid, required String nombre, String url = 'https://stream.demo/radio', String? favicon, int? bitrate, }) { return Emisora( uuid: uuid, nombre: nombre, url: url, favicon: favicon, bitrate: bitrate, ); } class _FakeFuenteEmisorasAuto implements FuenteEmisorasAuto { _FakeFuenteEmisorasAuto({required Map porUuidResultado}) : _porUuidResultado = porUuidResultado; final Map _porUuidResultado; @override Future> favoritos() async => const []; @override Future> misEmisoras() async => const []; @override Future> todas() async => const []; @override Future porUuid(String uuid) async => _porUuidResultado[uuid]; @override void actualizarSnapshot({ List? favoritos, List? misEmisoras, List? todas, }) {} }