lesavka/testing/tests/client_runtime_smoke_contract.rs

145 lines
4.3 KiB
Rust
Raw Normal View History

//! Integration smoke coverage for client runtime constructors.
//!
//! Scope: execute public client startup/media constructors through stable APIs
//! without requiring hardware-specific assertions.
//! Targets: `client/src/app.rs`, `client/src/input/camera.rs`,
//! `client/src/input/microphone.rs`, and `client/src/output/audio.rs`.
//! Why: these paths are operationally important but often depend on runtime
//! host capabilities; smoke contracts keep them exercised in CI.
use lesavka_client::LesavkaClientApp;
use lesavka_client::input::camera::{CameraCapture, CameraCodec, CameraConfig};
use lesavka_client::input::inputs::InputAggregator;
use lesavka_client::input::microphone::MicrophoneCapture;
use lesavka_client::output::audio::AudioOut;
use lesavka_client::output::video::MonitorWindow;
use lesavka_common::lesavka::AudioPacket;
use lesavka_common::lesavka::{KeyboardReport, MouseReport, VideoPacket};
use serial_test::serial;
use temp_env::with_var;
use tokio::sync::{broadcast, mpsc};
#[test]
#[serial]
fn client_app_new_supports_headless_mode() {
with_var("LESAVKA_HEADLESS", Some("1"), || {
with_var(
"LESAVKA_SERVER_ADDR",
Some("http://127.0.0.1:50051"),
|| {
let app = LesavkaClientApp::new();
assert!(app.is_ok(), "expected headless app construction to succeed");
},
);
});
}
#[test]
fn camera_capture_stub_pull_returns_none() {
gstreamer::init().ok();
let cam = CameraCapture::new_stub();
assert!(
cam.pull().is_none(),
"stub capture should not produce real frames"
);
}
#[test]
#[serial]
fn camera_capture_test_pattern_constructor_is_stable() {
with_var("LESAVKA_CAM_TEST_PATTERN", Some("ball"), || {
let result = CameraCapture::new(
Some("test"),
Some(CameraConfig {
codec: CameraCodec::Mjpeg,
width: 320,
height: 240,
fps: 5,
}),
);
match result {
Ok(_cam) => {}
Err(err) => {
assert!(
!err.to_string().trim().is_empty(),
"camera constructor returned an empty error"
);
}
}
});
}
#[test]
#[serial]
fn microphone_capture_constructor_is_stable_with_missing_source_hint() {
with_var(
"LESAVKA_MIC_SOURCE",
Some("definitely-missing-source"),
|| {
let result = MicrophoneCapture::new();
match result {
Ok(_mic) => {}
Err(err) => {
assert!(
!err.to_string().trim().is_empty(),
"microphone constructor returned an empty error"
);
}
}
},
);
}
#[test]
#[serial]
fn audio_out_constructor_and_push_are_stable() {
with_var("LESAVKA_AUDIO_SINK", Some("autoaudiosink"), || {
with_var("LESAVKA_TAP_AUDIO", Some("0"), || match AudioOut::new() {
Ok(out) => {
out.push(AudioPacket {
id: 0,
pts: 0,
data: Vec::new(),
..AudioPacket::default()
});
}
Err(err) => {
assert!(
!err.to_string().trim().is_empty(),
"audio output constructor returned an empty error"
);
}
});
});
}
#[test]
fn input_aggregator_constructor_is_stable() {
let (kbd_tx, _) = broadcast::channel::<KeyboardReport>(8);
let (mou_tx, _) = broadcast::channel::<MouseReport>(8);
let (paste_tx, _paste_rx) = mpsc::unbounded_channel::<String>();
let _agg = InputAggregator::new(false, kbd_tx, mou_tx, Some(paste_tx));
}
#[test]
#[serial]
fn monitor_window_constructor_and_push_are_stable() {
match MonitorWindow::new(0) {
Ok(win) => {
win.push_packet(VideoPacket {
id: 0,
pts: 0,
data: vec![0, 0, 0, 1, 0x65],
2026-04-16 21:18:34 -03:00
..Default::default()
});
}
Err(err) => {
assert!(
!err.to_string().trim().is_empty(),
"monitor window constructor returned an empty error"
);
}
}
}