fix(alarmas): decode native failures with the real channel key names

The first pass read 'alarmaId'/'tipo' from the channel payload while the
native side sends 'alarmId'/'type'/'atMillis' (AlarmScheduler.kt:1389).
Every entry would have been dropped silently in production.

The tests passed because the fake was seeded with the same guessed keys,
so they confirmed the mistake instead of catching it. Decoding now goes
through FalloProgramacionNativo.fromMap -- the single place native key
names appear -- and the fixtures build through that same constructor.
This commit is contained in:
2026-07-31 23:27:45 +02:00
parent a8dca83cd9
commit f2528c930b
4 changed files with 39 additions and 28 deletions
+1 -4
View File
@@ -556,10 +556,7 @@ class EstadoAlarmas extends ChangeNotifier {
try {
final fallos = await android.fallosNativosProgramacion();
for (final fallo in fallos) {
final alarmaId = fallo['alarmaId'] as String?;
final tipo = fallo['tipo'] as String?;
if (alarmaId == null || tipo == null) continue;
await _registrarFalloProgramacion(alarmaId, tipo: tipo);
await _registrarFalloProgramacion(fallo.alarmaId, tipo: fallo.tipo);
}
if (fallos.isNotEmpty) {
debugPrint(
+11 -3
View File
@@ -209,7 +209,14 @@ abstract class PuertoAlarmasAndroid {
/// Before this existed every one of those paths logged to logcat and
/// stopped there, so an alarm could sit switched on in the list having
/// never reached the OS at all — the user's "as if there were no alarm".
Future<List<Map<String, Object?>>> fallosNativosProgramacion();
///
/// Returns the typed model rather than raw maps ON PURPOSE:
/// [FalloProgramacionNativo.fromMap] is the single place the native key
/// names (`alarmId`/`type`/`atMillis`) appear. Consuming raw maps here
/// once silently dropped every entry, because the caller guessed Spanish
/// key names and the fake was seeded with the same guess — the test
/// confirmed the mistake instead of catching it.
Future<List<FalloProgramacionNativo>> fallosNativosProgramacion();
Future<void> ocultarNotificacionAlarma(String alarmaId);
/// Notification-only dismissal (RES-1): hides the fire notification for
@@ -439,7 +446,7 @@ class ServicioAlarmasAndroid implements PuertoAlarmasAndroid {
}
@override
Future<List<Map<String, Object?>>> fallosNativosProgramacion() async {
Future<List<FalloProgramacionNativo>> fallosNativosProgramacion() async {
try {
final raw = await _channel.invokeMethod<List<Object?>>(
'getNativeSchedulingFailures',
@@ -447,7 +454,8 @@ class ServicioAlarmasAndroid implements PuertoAlarmasAndroid {
if (raw == null) return const [];
return raw
.whereType<Map<Object?, Object?>>()
.map((m) => m.map((k, v) => MapEntry(k.toString(), v)))
.map(FalloProgramacionNativo.fromMap)
.where((f) => f.alarmaId.isNotEmpty && f.tipo.isNotEmpty)
.toList();
} catch (e) {
// Never let a diagnostics read break alarm handling: an older build