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.
143 lines
4.7 KiB
Dart
143 lines
4.7 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.dart';
|
|
import 'package:pluriwave/servicios/servicio_alarmas_android.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
import '../helpers/fakes_alarmas.dart';
|
|
|
|
/// fix/alarmas-fallos-silenciosos, item 3: "verify the alarm is actually
|
|
/// registered, and say so if it is not". `android.programar` returning
|
|
/// without throwing is not proof enough by itself -- this is the check that
|
|
/// would have caught the reported case immediately (the native side can
|
|
/// silently fail to persist the registration even when the channel call
|
|
/// itself reports success).
|
|
void main() {
|
|
setUp(() {
|
|
SharedPreferences.setMockInitialValues({});
|
|
});
|
|
|
|
test('guardarAlarma detecta que el conteo nativo no refleja la alarma '
|
|
'guardada, aunque android.programar no haya lanzado', () async {
|
|
// Fixed at 0 regardless of what programar() does internally --
|
|
// simulates the native side accepting the channel call but never
|
|
// actually persisting the registration.
|
|
final android = FakePuertoAlarmasAndroid()..alarmasNativasPendientes = 0;
|
|
final estado = EstadoAlarmas(
|
|
esPremium: () => true,
|
|
servicio: ServicioAlarmas(reloj: () => DateTime(2026, 5, 25, 7)),
|
|
android: android,
|
|
iniciarAutomaticamente: false,
|
|
);
|
|
addTearDown(estado.dispose);
|
|
addTearDown(android.dispose);
|
|
|
|
await estado.guardarAlarma(
|
|
const AlarmaMusical(
|
|
id: 'silenciosa1',
|
|
nombre: 'Diaria',
|
|
hora: 7,
|
|
minuto: 30,
|
|
tipoProgramacion: TipoProgramacionAlarma.diaria,
|
|
diasSemana: [],
|
|
),
|
|
);
|
|
|
|
final excepcion = estado.ultimaExcepcionPara('silenciosa1');
|
|
expect(excepcion, isNotNull);
|
|
expect(excepcion!.tipo, ExcepcionAlarma.tipoFalloProgramacion);
|
|
expect(estado.error, isNotNull);
|
|
});
|
|
|
|
test('guardarAlarma en el camino feliz (conteo nativo coincide) no registra '
|
|
'fallo alguno', () async {
|
|
final android = FakePuertoAlarmasAndroid();
|
|
final estado = EstadoAlarmas(
|
|
esPremium: () => true,
|
|
servicio: ServicioAlarmas(reloj: () => DateTime(2026, 5, 25, 7)),
|
|
android: android,
|
|
iniciarAutomaticamente: false,
|
|
);
|
|
addTearDown(estado.dispose);
|
|
addTearDown(android.dispose);
|
|
|
|
await estado.guardarAlarma(
|
|
const AlarmaMusical(
|
|
id: 'sana1',
|
|
nombre: 'Diaria',
|
|
hora: 7,
|
|
minuto: 30,
|
|
tipoProgramacion: TipoProgramacionAlarma.diaria,
|
|
diasSemana: [],
|
|
),
|
|
);
|
|
|
|
expect(estado.ultimaExcepcionPara('sana1'), isNull);
|
|
expect(estado.error, isNull);
|
|
});
|
|
|
|
test(
|
|
'inicializar importa los fallos nativos (pre-aviso, servicio en primer '
|
|
'plano, reprogramacion tras arranque) como excepciones por alarma',
|
|
() async {
|
|
final android =
|
|
FakePuertoAlarmasAndroid()
|
|
..fallosProgramacionNativos.addAll([
|
|
FalloProgramacionNativo(
|
|
alarmaId: 'con-preaviso-roto',
|
|
tipo: ExcepcionAlarma.tipoFalloPreaviso,
|
|
ocurridoEn: DateTime(2026, 5, 25, 7),
|
|
),
|
|
FalloProgramacionNativo(
|
|
alarmaId: 'servicio-rechazado',
|
|
tipo: ExcepcionAlarma.tipoFalloServicioSonido,
|
|
ocurridoEn: DateTime(2026, 5, 25, 7),
|
|
),
|
|
]);
|
|
final servicio = ServicioAlarmas(
|
|
reloj: () => DateTime(2026, 5, 25, 7, 30),
|
|
);
|
|
await servicio.guardarAlarma(
|
|
AlarmaMusical(
|
|
id: 'con-preaviso-roto',
|
|
nombre: 'Diaria',
|
|
hora: 7,
|
|
minuto: 30,
|
|
tipoProgramacion: TipoProgramacionAlarma.diaria,
|
|
diasSemana: const [],
|
|
),
|
|
);
|
|
await servicio.guardarAlarma(
|
|
AlarmaMusical(
|
|
id: 'servicio-rechazado',
|
|
nombre: 'Diaria',
|
|
hora: 8,
|
|
minuto: 0,
|
|
tipoProgramacion: TipoProgramacionAlarma.diaria,
|
|
diasSemana: const [],
|
|
),
|
|
);
|
|
|
|
final estado = EstadoAlarmas(
|
|
esPremium: () => true,
|
|
servicio: servicio,
|
|
android: android,
|
|
iniciarAutomaticamente: false,
|
|
);
|
|
addTearDown(estado.dispose);
|
|
addTearDown(android.dispose);
|
|
|
|
await estado.inicializar();
|
|
|
|
final falloPreaviso = estado.ultimaExcepcionPara('con-preaviso-roto');
|
|
expect(falloPreaviso, isNotNull);
|
|
expect(falloPreaviso!.tipo, ExcepcionAlarma.tipoFalloPreaviso);
|
|
|
|
final falloServicio = estado.ultimaExcepcionPara('servicio-rechazado');
|
|
expect(falloServicio, isNotNull);
|
|
expect(falloServicio!.tipo, ExcepcionAlarma.tipoFalloServicioSonido);
|
|
},
|
|
);
|
|
}
|