fix(alarm): recalculate every alarm on each mutation, not just the touched one
guardarAlarma only recomputed proximaEjecucion for the alarm being saved; every other alarm kept whatever snapshot the last periodic recalculation left, which can be stale or already past-due. Since EstadoAlarmas.proximaAlarma just sorts by proximaProgramable, a stale sibling could wrongly outrank a freshly activated/created/edited alarm in the "Próxima alarma" panel until the next 1-minute tick. Extended the same full-list recalculation guardarVacaciones already did to guardarAlarma, eliminarAlarma, completarEjecucion, sincronizarEjecucionesNativas, saltarProxima and posponerEjecucionHasta, via a shared _recalcularLista helper.
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
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';
|
||||
|
||||
/// SharedPreferences spy: only the members ServicioAlarmas touches are
|
||||
/// implemented; everything else throws via noSuchMethod.
|
||||
class _PrefsEspia implements SharedPreferences {
|
||||
final Map<String, Object> _datos = {};
|
||||
|
||||
@override
|
||||
String? getString(String key) => _datos[key] as String?;
|
||||
|
||||
@override
|
||||
Future<bool> setString(String key, String value) async {
|
||||
_datos[key] = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
|
||||
}
|
||||
|
||||
void main() {
|
||||
// Regression for: activating/creating/deleting/firing an alarm must never
|
||||
// leave a sibling stuck with a stale (already past-due) proximaEjecucion
|
||||
// snapshot, since EstadoAlarmas.proximaAlarma just sorts by
|
||||
// proximaProgramable and a stale past value would incorrectly outrank a
|
||||
// freshly computed future one.
|
||||
test(
|
||||
'guardarAlarma recalcula la proximaEjecucion de TODAS las alarmas, no solo la guardada',
|
||||
() async {
|
||||
final prefs = _PrefsEspia();
|
||||
var reloj = DateTime(2026, 1, 1, 6, 0);
|
||||
final servicio = ServicioAlarmas(prefs: prefs, reloj: () => reloj);
|
||||
|
||||
final a = servicio.crearAlarma(
|
||||
nombre: 'A',
|
||||
hora: 7,
|
||||
minuto: 0,
|
||||
tipoProgramacion: TipoProgramacionAlarma.diaria,
|
||||
diasSemana: const [],
|
||||
);
|
||||
final configTrasA = await servicio.guardarAlarma(a);
|
||||
final aGuardada = configTrasA.alarmas.single;
|
||||
expect(aGuardada.proximaEjecucion, DateTime(2026, 1, 1, 7, 0));
|
||||
|
||||
// Time moves forward well past A's stored occurrence, simulating the
|
||||
// periodic 1-minute refresh not having ticked yet.
|
||||
reloj = DateTime(2026, 1, 2, 8, 0);
|
||||
|
||||
final b = servicio.crearAlarma(
|
||||
nombre: 'B',
|
||||
hora: 9,
|
||||
minuto: 0,
|
||||
tipoProgramacion: TipoProgramacionAlarma.diaria,
|
||||
diasSemana: const [],
|
||||
);
|
||||
final configTrasB = await servicio.guardarAlarma(b);
|
||||
|
||||
final aTrasGuardarB = configTrasB.alarmas.firstWhere(
|
||||
(alarma) => alarma.id == a.id,
|
||||
);
|
||||
expect(
|
||||
aTrasGuardarB.proximaEjecucion!.isAfter(reloj),
|
||||
isTrue,
|
||||
reason:
|
||||
'A debe recalcularse al guardar B, no quedar con el snapshot '
|
||||
'viejo (ya vencido) que la haria ganar la comparacion de '
|
||||
'"proxima alarma" por error',
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user