Files
pluriwave/test/estado/estado_alarmas_fallos_nativos_test.dart
T
FreeTLab d81fabbe27 refactor(iap): make esPremium a required constructor parameter
EstadoAlarmas, EstadoGrabacion and EstadoRadio defaulted `esPremium` to
`() => true`, so any construction site that forgot to wire entitlement
compiled fine and silently ran ungated — failing OPEN to premium and
disabling the paywall with no test able to catch it.

The parameter is now required with no default. Production wiring in
app.dart was already correct and is unchanged; the 184 pre-existing test
call sites now pass `() => true` explicitly, which is exactly the old
implicit default, so every assertion is untouched.

EstadoRadio has no gate of its own but constructs EstadoGrabacion, so it
inherits the same contract.

The one test that existed to pin the old default is renamed to describe
what it still covers (the premium path through iniciar() with no
duracion); its assertions are unchanged.
2026-08-10 22:06:36 +02:00

116 lines
3.8 KiB
Dart

import 'package:flutter_test/flutter_test.dart';
import 'package:pluriwave/estado/estado_alarmas.dart';
import 'package:pluriwave/modelos/alarma_musical.dart';
import 'package:pluriwave/servicios/servicio_alarmas_android.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../helpers/fakes_alarmas.dart';
/// The native scheduler records three failures entirely on its own, outside
/// any Dart call: a pre-notice that could not be armed, a refused
/// foreground-service start when the alarm should have rung, and a per-alarm
/// reschedule that failed after a reboot.
///
/// Before this wiring existed, all three logged to logcat and stopped there.
/// The alarm stayed switched on in the list, drawn exactly as if scheduling
/// had succeeded, and simply never fired — the reported "as if there were no
/// alarm at all". These tests pin the drain path that makes them visible.
///
/// They deliberately build fixtures through [FalloProgramacionNativo.fromMap]
/// using the REAL native key names (`alarmId`/`type`/`atMillis`, see
/// `AlarmScheduler.kt:1389-1391`). An earlier draft seeded the fake with
/// guessed Spanish keys and consumed the same guess, so every entry would
/// have been silently dropped in production while the tests stayed green.
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
late FakePuertoAlarmasAndroid android;
setUp(() {
SharedPreferences.setMockInitialValues({});
android = FakePuertoAlarmasAndroid();
});
EstadoAlarmas crearEstado() => EstadoAlarmas(
android: android,
iniciarAutomaticamente: false,
esPremium: () => true,
);
/// Mirrors exactly what the native side puts on the channel.
FalloProgramacionNativo falloNativo(String alarmaId, String tipo) =>
FalloProgramacionNativo.fromMap({
'alarmId': alarmaId,
'type': tipo,
'atMillis': 1700000000000,
});
test('the fixture helper decodes the real native key names', () {
final fallo = falloNativo('a0', ExcepcionAlarma.tipoFalloPreaviso);
expect(fallo.alarmaId, 'a0');
expect(fallo.tipo, ExcepcionAlarma.tipoFalloPreaviso);
});
test('a native pre-notice failure becomes a per-alarm exception', () async {
android.fallosNativos = [
falloNativo('a1', ExcepcionAlarma.tipoFalloPreaviso),
];
final estado = crearEstado();
addTearDown(estado.dispose);
await estado.cargarFallosNativos();
final fallo = estado.ultimaExcepcionPara('a1');
expect(fallo, isNotNull);
expect(fallo!.tipo, ExcepcionAlarma.tipoFalloPreaviso);
});
test(
'a refused foreground-service start becomes a per-alarm exception',
() async {
android.fallosNativos = [
falloNativo('a2', ExcepcionAlarma.tipoFalloServicioSonido),
];
final estado = crearEstado();
addTearDown(estado.dispose);
await estado.cargarFallosNativos();
expect(
estado.ultimaExcepcionPara('a2')?.tipo,
ExcepcionAlarma.tipoFalloServicioSonido,
);
},
);
test('only the alarm whose reschedule failed is marked', () async {
android.fallosNativos = [
falloNativo('a3', ExcepcionAlarma.tipoFalloReprogramacionArranque),
];
final estado = crearEstado();
addTearDown(estado.dispose);
await estado.cargarFallosNativos();
expect(estado.ultimaExcepcionPara('a3'), isNotNull);
expect(
estado.ultimaExcepcionPara('a4'),
isNull,
reason: 'a sibling alarm that scheduled fine must stay unmarked',
);
});
test('a failing native read never surfaces as an alarm error', () async {
// A diagnostics gap must not look like a scheduling problem: an older
// native build simply has no such channel method.
android.fallaLecturaFallosNativos = true;
final estado = crearEstado();
addTearDown(estado.dispose);
await estado.cargarFallosNativos();
expect(estado.error, isNull);
});
}