feat(alarmas): record and clear per-alarm scheduling failures

Adds ServicioAlarmas.registrarFalloProgramacion/limpiarFalloProgramacion,
persisting a scheduling-reliability failure through the same
ExcepcionAlarma model saltarProxima already uses. Only one failure
record is kept per alarm (latest attempt wins) and skipNext entries
for any alarm are never touched. EstadoAlarmas wiring follows next.
This commit is contained in:
2026-07-31 21:00:51 +02:00
parent 88bd251eba
commit 47d0b8a053
2 changed files with 206 additions and 0 deletions
@@ -0,0 +1,150 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:pluriwave/modelos/alarma_musical.dart';
import 'package:pluriwave/servicios/servicio_alarmas.dart';
import 'package:shared_preferences/shared_preferences.dart';
/// Coverage for the scheduling-reliability failure records (fix/alarmas-
/// fallos-silenciosos): a failed `android.programar` call is no longer only
/// a transient, alarm-agnostic `EstadoAlarmas.error` string -- it is also
/// recorded per-alarm through the SAME `ExcepcionAlarma` model `saltarProxima`
/// already uses, so `EstadoAlarmas.ultimaExcepcionPara` can surface it on the
/// exact card affected.
void main() {
setUp(() {
SharedPreferences.setMockInitialValues({});
});
test('registrarFalloProgramacion agrega una excepcion de fallo para la '
'alarma indicada', () async {
final servicio = ServicioAlarmas(reloj: () => DateTime(2026, 5, 25, 7));
await servicio.guardarAlarma(
AlarmaMusical(
id: 'a1',
nombre: 'Diaria',
hora: 7,
minuto: 30,
tipoProgramacion: TipoProgramacionAlarma.diaria,
diasSemana: const [],
),
);
final config = await servicio.registrarFalloProgramacion(
'a1',
DateTime(2026, 5, 25, 7, 30),
ExcepcionAlarma.tipoFalloProgramacion,
);
expect(config.excepciones, hasLength(1));
expect(config.excepciones.single.alarmaId, 'a1');
expect(
config.excepciones.single.tipo,
ExcepcionAlarma.tipoFalloProgramacion,
);
});
test('registrarFalloProgramacion reemplaza un fallo previo de la MISMA '
'alarma en vez de acumular', () async {
final servicio = ServicioAlarmas(reloj: () => DateTime(2026, 5, 25, 7));
await servicio.guardarAlarma(
AlarmaMusical(
id: 'a1',
nombre: 'Diaria',
hora: 7,
minuto: 30,
tipoProgramacion: TipoProgramacionAlarma.diaria,
diasSemana: const [],
),
);
await servicio.registrarFalloProgramacion(
'a1',
DateTime(2026, 5, 25, 7, 30),
ExcepcionAlarma.tipoFalloPreaviso,
);
final config = await servicio.registrarFalloProgramacion(
'a1',
DateTime(2026, 5, 26, 7, 30),
ExcepcionAlarma.tipoFalloProgramacion,
);
expect(config.excepciones, hasLength(1));
expect(
config.excepciones.single.tipo,
ExcepcionAlarma.tipoFalloProgramacion,
);
});
test('registrarFalloProgramacion NUNCA toca las excepciones skipNext de '
'otras alarmas ni de la misma', () async {
final servicio = ServicioAlarmas(reloj: () => DateTime(2026, 5, 25, 7));
await servicio.guardarAlarma(
AlarmaMusical(
id: 'a1',
nombre: 'Diaria',
hora: 7,
minuto: 30,
tipoProgramacion: TipoProgramacionAlarma.diaria,
diasSemana: const [],
),
);
await servicio.saltarProxima('a1');
final config = await servicio.registrarFalloProgramacion(
'a1',
DateTime(2026, 5, 26, 7, 30),
ExcepcionAlarma.tipoFalloProgramacion,
);
expect(config.excepciones, hasLength(2));
expect(
config.excepciones.map((e) => e.tipo),
containsAll(<String>[
ExcepcionAlarma.tipoSaltoSiguiente,
ExcepcionAlarma.tipoFalloProgramacion,
]),
);
});
test('limpiarFalloProgramacion elimina el fallo registrado para esa '
'alarma', () async {
final servicio = ServicioAlarmas(reloj: () => DateTime(2026, 5, 25, 7));
await servicio.guardarAlarma(
AlarmaMusical(
id: 'a1',
nombre: 'Diaria',
hora: 7,
minuto: 30,
tipoProgramacion: TipoProgramacionAlarma.diaria,
diasSemana: const [],
),
);
await servicio.registrarFalloProgramacion(
'a1',
DateTime(2026, 5, 25, 7, 30),
ExcepcionAlarma.tipoFalloProgramacion,
);
final config = await servicio.limpiarFalloProgramacion('a1');
expect(config.excepciones, isEmpty);
});
test('limpiarFalloProgramacion sin fallo previo no rompe y no persiste '
'cambios', () async {
final servicio = ServicioAlarmas(reloj: () => DateTime(2026, 5, 25, 7));
await servicio.guardarAlarma(
AlarmaMusical(
id: 'a1',
nombre: 'Diaria',
hora: 7,
minuto: 30,
tipoProgramacion: TipoProgramacionAlarma.diaria,
diasSemana: const [],
),
);
final config = await servicio.limpiarFalloProgramacion('a1');
expect(config.excepciones, isEmpty);
});
}