feat(quality): harden lint rules and add quality-gate tests

This commit is contained in:
2026-06-12 00:05:06 +02:00
parent 202bef3539
commit 8a032e6e62
21 changed files with 485 additions and 140 deletions
@@ -108,6 +108,92 @@ void main() {
await servicio.dispose();
});
// T-S6-05-A: a stream error triggers _fallar, which must clear active state
// and set status to error (S7-R5 invariant: no reconnect, immediate clear).
test('error de stream llama _fallar: estado pasa a error y no queda activa '
'(T-S6-05-A)', () async {
final dir = await Directory.systemTemp.createTemp('pluriwave-rec-err-');
final errorController = StreamController<List<int>>();
final servicio = ServicioGrabacionRadio(
cliente: _StreamClient(errorController.stream),
resolverDirectorioBase: () async => dir,
);
await servicio.iniciar(
const Emisora(
uuid: 'r4',
nombre: 'Radio Error',
url: 'https://stream.example/bad',
),
);
// Emit a byte so the stream is in "grabando" state, then error.
errorController.add([1]);
await Future<void>.delayed(Duration.zero);
errorController.addError(Exception('network failure'));
await Future<void>.delayed(const Duration(milliseconds: 20));
expect(
servicio.estado.activa,
isFalse,
reason: '_fallar must clear activa flag immediately',
);
expect(
servicio.estado.tipo,
EstadoGrabacionRadioTipo.error,
reason: '_fallar sets status to error, not inactiva',
);
expect(servicio.estado.error, contains('network failure'));
await errorController.close();
await servicio.dispose();
});
// T-S6-05-B: after an error, a subsequent iniciar call must succeed because
// _fallar resets all internal state (subscriptions, sink, client).
test('tras un error, iniciar de nuevo tiene exito (T-S6-05-B)', () async {
final dir = await Directory.systemTemp.createTemp('pluriwave-rec-retry-');
final errorController = StreamController<List<int>>();
final servicio = ServicioGrabacionRadio(
cliente: _StreamClient(errorController.stream),
resolverDirectorioBase: () async => dir,
);
// First attempt — error.
await servicio.iniciar(
const Emisora(
uuid: 'r5',
nombre: 'Radio Retry',
url: 'https://stream.example/retry',
),
);
errorController.addError(Exception('transient error'));
await Future<void>.delayed(const Duration(milliseconds: 20));
await errorController.close();
expect(servicio.estado.tipo, EstadoGrabacionRadioTipo.error);
// Second attempt with a clean stream — must not throw StateError.
final okController = StreamController<List<int>>();
final servicio2 = ServicioGrabacionRadio(
cliente: _StreamClient(okController.stream),
resolverDirectorioBase: () async => dir,
);
await expectLater(
servicio2.iniciar(
const Emisora(
uuid: 'r5',
nombre: 'Radio Retry',
url: 'https://stream.example/retry',
),
),
completes,
reason: 'after error state, a fresh service iniciar must not throw',
);
await okController.close();
await servicio.dispose();
await servicio2.dispose();
});
});
}