lesavka/testing/tests/server_main_rpc_contract.rs

122 lines
4.3 KiB
Rust
Raw Normal View History

//! Integration coverage for server main RPC handler branches.
//!
//! Scope: include `server/src/main.rs` and exercise additional RPC paths that
//! are awkward to hit from process-level tests.
//! Targets: `server/src/main.rs`.
//! Why: keep handler-side error/reply behavior stable without HID hardware.
#[allow(warnings)]
mod server_main_rpc {
include!(env!("LESAVKA_SERVER_MAIN_SRC"));
use serial_test::serial;
use temp_env::with_var;
use tempfile::tempdir;
fn build_handler_for_tests() -> (tempfile::TempDir, Handler) {
let dir = tempdir().expect("tempdir");
let kb_path = dir.path().join("hidg0.bin");
let ms_path = dir.path().join("hidg1.bin");
std::fs::write(&kb_path, []).expect("create kb file");
std::fs::write(&ms_path, []).expect("create ms file");
let kb = tokio::fs::File::from_std(
std::fs::OpenOptions::new()
.read(true)
.write(true)
.open(&kb_path)
.expect("open kb"),
);
let ms = tokio::fs::File::from_std(
std::fs::OpenOptions::new()
.read(true)
.write(true)
.open(&ms_path)
.expect("open ms"),
);
(
dir,
Handler {
kb: std::sync::Arc::new(tokio::sync::Mutex::new(kb)),
ms: std::sync::Arc::new(tokio::sync::Mutex::new(ms)),
gadget: UsbGadget::new("lesavka"),
did_cycle: std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),
camera_rt: std::sync::Arc::new(CameraRuntime::new()),
},
)
}
#[test]
#[serial]
fn reopen_hid_returns_error_without_hid_endpoints() {
let (_dir, handler) = build_handler_for_tests();
let rt = tokio::runtime::Runtime::new().expect("runtime");
let result = rt.block_on(handler.reopen_hid());
assert!(result.is_err(), "reopen_hid should fail without /dev/hidg*");
}
#[test]
#[serial]
fn capture_video_valid_monitor_surfaces_internal_error_without_device() {
let (_dir, handler) = build_handler_for_tests();
let rt = tokio::runtime::Runtime::new().expect("runtime");
let result = rt.block_on(async {
handler
.capture_video(tonic::Request::new(MonitorRequest {
id: 0,
max_bitrate: 3_000,
}))
.await
});
let err = match result {
Ok(_) => panic!("missing camera device should fail"),
Err(err) => err,
};
assert_eq!(err.code(), tonic::Code::Internal);
}
#[test]
#[serial]
fn paste_text_accepts_encrypted_payload_and_returns_reply() {
let (_dir, handler) = build_handler_for_tests();
with_var(
"LESAVKA_PASTE_KEY",
Some("hex:00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"),
|| {
with_var("LESAVKA_PASTE_DELAY_MS", Some("0"), || {
let req =
lesavka_client::paste::build_paste_request("hello").expect("build request");
let rt = tokio::runtime::Runtime::new().expect("runtime");
let reply = rt
.block_on(async { handler.paste_text(tonic::Request::new(req)).await })
.expect("paste rpc should return reply")
.into_inner();
assert!(
reply.ok || !reply.error.is_empty(),
"paste path should execute and return a structured reply"
);
});
},
);
}
#[test]
#[serial]
fn capture_audio_accepts_secondary_monitor_id_and_fails_internally_without_sink() {
let (_dir, handler) = build_handler_for_tests();
let req = MonitorRequest {
id: 1,
max_bitrate: 0,
};
let rt = tokio::runtime::Runtime::new().expect("runtime");
let result = rt.block_on(async { handler.capture_audio(tonic::Request::new(req)).await });
let err = match result {
Ok(_) => panic!("missing ALSA source should fail"),
Err(err) => err,
};
assert_eq!(err.code(), tonic::Code::Internal);
}
}