feat(quality): harden lint rules and add quality-gate tests
This commit is contained in:
@@ -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();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user