Completes the bridge the native side already exposed. AlarmScheduler and PluriWaveAlarmService record a pre-notice that could not be armed, a refused foreground-service start, and a per-alarm reschedule that failed after a reboot -- but nothing read them, so all three still ended at logcat. EstadoAlarmas now drains them at startup and turns each into a per-alarm exception, which the card already knows how to mark. An alarm that never reached the OS stops looking identical to one that did. The read is deliberately tolerant: a failure to read is logged and swallowed, never surfaced as an alarm error, so a diagnostics gap cannot masquerade as a scheduling problem.
185 lines
5.4 KiB
Dart
185 lines
5.4 KiB
Dart
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',
|
|
ExcepcionAlarma.tipoFalloProgramacion,
|
|
);
|
|
|
|
expect(config.excepciones, isEmpty);
|
|
});
|
|
|
|
test('limpiarFalloProgramacion NO limpia un fallo de un tipo distinto '
|
|
'(cada subsistema se limpia por su cuenta)', () 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.limpiarFalloProgramacion(
|
|
'a1',
|
|
ExcepcionAlarma.tipoFalloProgramacion,
|
|
);
|
|
|
|
expect(config.excepciones, hasLength(1));
|
|
expect(config.excepciones.single.tipo, ExcepcionAlarma.tipoFalloPreaviso);
|
|
});
|
|
|
|
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',
|
|
ExcepcionAlarma.tipoFalloProgramacion,
|
|
);
|
|
|
|
expect(config.excepciones, isEmpty);
|
|
});
|
|
}
|