feat: Implement startup retry mechanism for custom stations and equalizer persistence

- Added state management for startup retry and custom station handling in `EstadoRadio`.
- Created tasks for implementing strict TDD with RED tests for HTTP failure retries and EQ persistence.
- Developed verification report to ensure compliance with TDD practices.
- Introduced fake services for testing, including `FakeServicioAudio`, `FakeServicioFavoritos`, and `FakeServicioRadio`.
- Implemented widget tests for `PantallaInicio` and `PantallaFavoritos` to validate UI behavior with custom stations.
- Enhanced `ServicioRadio` to support host rotation and retry logic for API calls.
- Established a new configuration file to enforce project constraints and testing rules.
This commit is contained in:
Javier Bautista Fernández
2026-04-27 17:34:04 +02:00
parent 922b3b4859
commit d579a0e107
21 changed files with 1902 additions and 156 deletions

View File

@@ -0,0 +1,72 @@
import 'dart:convert';
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
import 'package:http/testing.dart';
import 'package:pluriwave/servicios/servicio_radio.dart';
void main() {
group('ServicioRadio retry + rotación', () {
test(
'reintenta con otro host cuando el primero falla y recupera en el segundo',
() async {
final hostsSolicitados = <String>[];
final servicio = ServicioRadio(
cliente: MockClient((request) async {
hostsSolicitados.add(request.url.host);
if (request.url.host == 'host-1.api.radio-browser.info') {
return http.Response('fallo', 500);
}
return http.Response(
jsonEncode([
{
'stationuuid': 'uuid-ok',
'name': 'Radio Recuperada',
'url_resolved': 'https://stream.recuperada/audio',
},
]),
200,
headers: {'content-type': 'application/json'},
);
}),
servidores: const [
'host-1.api.radio-browser.info',
'host-2.api.radio-browser.info',
],
maxIntentos: 3,
retryDelay: Duration.zero,
);
final emisoras = await servicio.obtenerPopulares(limit: 1);
expect(emisoras, hasLength(1));
expect(emisoras.first.uuid, 'uuid-ok');
expect(
hostsSolicitados,
equals([
'host-1.api.radio-browser.info',
'host-2.api.radio-browser.info',
]),
);
});
test('corta al llegar al tope de intentos y propaga error final', () async {
var intentos = 0;
final servicio = ServicioRadio(
cliente: MockClient((request) async {
intentos += 1;
throw http.ClientException('sin red', request.url);
}),
servidores: const ['host-unico.api.radio-browser.info'],
maxIntentos: 2,
retryDelay: Duration.zero,
);
expect(
() => servicio.obtenerPopulares(limit: 1),
throwsA(isA<Exception>()),
);
expect(intentos, 2);
});
});
}