import 'package:flutter_test/flutter_test.dart'; import 'package:pluriwave/estado/estado_busqueda.dart'; import 'package:pluriwave/modelos/pais_radio.dart'; import '../helpers/fakes.dart'; /// S4-R3: EstadoBusqueda owns search query, results and loading state /// previously in EstadoRadio. void main() { test('actualizar la búsqueda notifica a los listeners', () async { final busqueda = EstadoBusqueda( radio: FakeServicioRadio( busqueda: [emisoraDemo(uuid: 'b-1', nombre: 'Resultado Uno')], ), ); addTearDown(busqueda.dispose); var notificaciones = 0; busqueda.addListener(() => notificaciones++); await busqueda.buscar(nombre: 'uno'); // At least once for the loading flag and once for the results. expect(notificaciones, greaterThanOrEqualTo(2)); expect(busqueda.cargando, isFalse); expect(busqueda.resultados.map((e) => e.uuid), contains('b-1')); }); test('cargarMas pagina resultados y acota memoria', () async { final emisoras = List.generate( 70, (i) => emisoraDemo(uuid: 'page-$i', nombre: 'Page $i'), ); final busqueda = EstadoBusqueda( radio: FakeServicioRadio(busqueda: emisoras), ); addTearDown(busqueda.dispose); await busqueda.buscar(nombre: 'page'); expect(busqueda.resultados, hasLength(30)); expect(busqueda.hayMas, isTrue); await busqueda.cargarMas(); expect(busqueda.resultados, hasLength(60)); await busqueda.cargarMas(); expect(busqueda.resultados, hasLength(70)); expect(busqueda.hayMas, isFalse); }); test( 'resultados conserva identidad entre lecturas sin cambios (S4-R5)', () async { final busqueda = EstadoBusqueda( radio: FakeServicioRadio( busqueda: [emisoraDemo(uuid: 'b-1', nombre: 'Resultado Uno')], ), ); addTearDown(busqueda.dispose); await busqueda.buscar(nombre: 'uno'); // Identity-stable getters let `context.select` skip rebuilds when the // underlying data did not change. expect(identical(busqueda.resultados, busqueda.resultados), isTrue); }, ); // --------------------------------------------------------------------- // WU7, `station-discovery-browse` spec — Países browser over // `ServicioRadio.obtenerPaises()`. In-memory cache guard: re-entering the // Países screen must NOT re-fetch (design ADR-4). // --------------------------------------------------------------------- group('EstadoBusqueda — cargarPaises', () { test( 'carga la lista de países y no vuelve a pedir en un 2º llamado', () async { final radio = FakeServicioRadio( paises: const [ PaisRadio(nombre: 'Spain', codigoIso: 'ES', numeroEmisoras: 482), PaisRadio( nombre: 'Argentina', codigoIso: 'AR', numeroEmisoras: 120, ), ], ); final busqueda = EstadoBusqueda(radio: radio); addTearDown(busqueda.dispose); expect(busqueda.paises, isEmpty); expect(busqueda.cargandoPaises, isFalse); await busqueda.cargarPaises(); expect( busqueda.paises.map((p) => p.codigoIso), containsAll(['ES', 'AR']), ); expect(busqueda.cargandoPaises, isFalse); expect(radio.obtenerPaisesCalls, 1); await busqueda.cargarPaises(); // Cache guard: a 2nd call with data already present must not refetch. expect(radio.obtenerPaisesCalls, 1); }, ); test( 'falla con gracia: no deja cargandoPaises en true ni la lista poblada', () async { final busqueda = EstadoBusqueda( radio: FakeServicioRadio(errorPaises: Exception('fallo de red')), ); addTearDown(busqueda.dispose); await busqueda.cargarPaises(); expect(busqueda.cargandoPaises, isFalse); expect(busqueda.paises, isEmpty); }, ); }); }