lesavka/tests/contract/client/app/client_app_support_contract.rs

174 lines
5.1 KiB
Rust

// Integration coverage for client app support helpers.
//
// Scope: exercise server-address, camera-capability, and retry-delay helpers
// against deterministic local types.
// Targets: `client/src/app_support.rs`.
// Why: these startup decisions shape live camera codec negotiation and should
// stay covered without adding source-file LOC to the hygiene ratchet.
mod handshake {
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct PeerCaps {
pub camera: bool,
pub microphone: bool,
pub bundled_webcam_media: bool,
pub server_version: Option<String>,
pub camera_output: Option<String>,
pub camera_codec: Option<String>,
pub camera_width: Option<u32>,
pub camera_height: Option<u32>,
pub camera_fps: Option<u32>,
pub eye_width: Option<u32>,
pub eye_height: Option<u32>,
pub eye_fps: Option<u32>,
}
}
mod input {
pub mod camera {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CameraCodec {
Mjpeg,
H264,
Hevc,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct CameraConfig {
pub codec: CameraCodec,
pub width: u32,
pub height: u32,
pub fps: u32,
}
}
}
#[path = "../../../../client/src/app_support.rs"]
mod app_support;
use app_support::{
DEFAULT_SERVER_ADDR, camera_config_from_caps, next_delay, resolve_server_addr,
sanitize_video_queue,
};
use handshake::PeerCaps;
use input::camera::CameraCodec;
use serial_test::serial;
use std::time::Duration;
fn peer_caps(codec: &str) -> PeerCaps {
PeerCaps {
camera: true,
microphone: false,
bundled_webcam_media: false,
server_version: None,
camera_output: Some(String::from("uvc")),
camera_codec: Some(codec.to_string()),
camera_width: Some(1280),
camera_height: Some(720),
camera_fps: Some(25),
eye_width: None,
eye_height: None,
eye_fps: None,
}
}
#[test]
fn resolve_server_addr_prefers_cli_then_env_then_default() {
assert_eq!(
resolve_server_addr(&[String::from("http://cli:1")], Some("http://env:2")),
"http://cli:1"
);
assert_eq!(
resolve_server_addr(
&[
String::from("--no-launcher"),
String::from("--server"),
String::from("http://cli-flag:3"),
],
Some("http://env:2"),
),
"http://cli-flag:3"
);
assert_eq!(
resolve_server_addr(&[String::from("--launcher")], Some("http://env:2")),
"http://env:2"
);
assert_eq!(
resolve_server_addr(&[], Some("http://env:2")),
"http://env:2"
);
assert_eq!(resolve_server_addr(&[], None), DEFAULT_SERVER_ADDR);
}
#[test]
#[serial]
fn camera_config_from_caps_requires_complete_profile() {
temp_env::with_var("LESAVKA_CAM_CODEC", None::<&str>, || {
let mut caps = peer_caps("mjpeg");
let config = camera_config_from_caps(&caps).expect("complete caps should map");
assert!(matches!(config.codec, CameraCodec::Mjpeg));
assert_eq!(config.width, 1280);
caps.camera_codec = Some(String::from("h265"));
let config = camera_config_from_caps(&caps).expect("h265 alias should map");
assert!(matches!(config.codec, CameraCodec::Hevc));
caps.camera_codec = Some(String::from("vp9"));
assert!(camera_config_from_caps(&caps).is_none());
});
}
#[test]
#[serial]
fn camera_config_from_caps_uses_negotiated_codec_over_launcher_default() {
temp_env::with_var("LESAVKA_CAM_CODEC", Some("mjpeg"), || {
let config = camera_config_from_caps(&peer_caps("hevc")).expect("caps codec should map");
assert!(matches!(config.codec, CameraCodec::Hevc));
});
}
#[test]
#[serial]
fn camera_config_from_caps_allows_explicit_forced_codec_override() {
temp_env::with_vars(
[
("LESAVKA_CAM_CODEC", Some("mjpeg")),
("LESAVKA_CAM_CODEC_FORCE", Some("1")),
],
|| {
let config = camera_config_from_caps(&peer_caps("hevc")).expect("forced override");
assert!(matches!(config.codec, CameraCodec::Mjpeg));
},
);
}
#[test]
#[serial]
fn camera_config_force_flag_ignores_unknown_override() {
temp_env::with_vars(
[
("LESAVKA_CAM_CODEC", Some("vp9")),
("LESAVKA_CAM_CODEC_FORCE", Some("1")),
],
|| {
let config = camera_config_from_caps(&peer_caps("hevc")).expect("fallback codec");
assert!(matches!(config.codec, CameraCodec::Hevc));
},
);
}
#[test]
fn sanitize_video_queue_enforces_floor() {
assert_eq!(sanitize_video_queue(None), 8);
assert_eq!(sanitize_video_queue(Some(1)), 4);
assert_eq!(sanitize_video_queue(Some(32)), 32);
}
#[test]
fn next_delay_doubles_until_capped() {
assert_eq!(next_delay(Duration::from_secs(1)), Duration::from_secs(2));
assert_eq!(next_delay(Duration::from_secs(15)), Duration::from_secs(30));
assert_eq!(next_delay(Duration::from_secs(31)), Duration::from_secs(30));
}