mirror of
https://github.com/devmobasa/wayscriber.git
synced 2026-11-01 07:36:42 +01:00
228 lines
8.1 KiB
Rust
228 lines
8.1 KiB
Rust
use wayscriber::config::{ConfigSectionError, ConfigSource, ConfigValidationReport};
|
|
|
|
use super::*;
|
|
use std::path::PathBuf;
|
|
|
|
use crate::models::session::SessionArtifactSummary;
|
|
use crate::models::{
|
|
DesktopEnvironment, LightShortcutApplyCapability, ShortcutApplyCapability, ShortcutBackend,
|
|
};
|
|
use crate::test_env::EnvGuard;
|
|
use crate::test_temp;
|
|
use wayscriber::env_vars::XDG_RUNTIME_DIR_ENV;
|
|
|
|
fn daemon_status(active: bool) -> DaemonRuntimeStatus {
|
|
DaemonRuntimeStatus {
|
|
desktop: DesktopEnvironment::Unknown,
|
|
shortcut_backend: ShortcutBackend::Manual,
|
|
shortcut_apply_capability: ShortcutApplyCapability::Manual,
|
|
light_shortcut_apply_capability: LightShortcutApplyCapability::Manual,
|
|
systemctl_available: false,
|
|
gsettings_available: false,
|
|
service_installed: active,
|
|
service_enabled: active,
|
|
service_active: active,
|
|
service_unit_path: None,
|
|
configured_shortcut: None,
|
|
light_controls_configured: false,
|
|
light_controls_config_path: None,
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn clear_policy_blocks_running_daemon() {
|
|
let temp = test_temp::tempdir().unwrap();
|
|
let _env = EnvGuard::set(&[(XDG_RUNTIME_DIR_ENV, temp.path().as_os_str())]);
|
|
let status = daemon_status(true);
|
|
|
|
let blocker = SessionCatalogOperation::Clear
|
|
.cached_status_blocker(Some(&status))
|
|
.expect("daemon should block clear");
|
|
|
|
assert!(blocker.contains("background service"));
|
|
}
|
|
|
|
#[test]
|
|
fn clear_policy_allows_inactive_daemon() {
|
|
let temp = test_temp::tempdir().unwrap();
|
|
let _env = EnvGuard::set(&[(XDG_RUNTIME_DIR_ENV, temp.path().as_os_str())]);
|
|
let status = daemon_status(false);
|
|
|
|
let blocker = SessionCatalogOperation::Clear.cached_status_blocker(Some(&status));
|
|
|
|
assert!(blocker.is_none(), "{blocker:?}");
|
|
}
|
|
|
|
#[test]
|
|
fn duplicate_policy_uses_duplicate_status_message() {
|
|
let temp = test_temp::tempdir().unwrap();
|
|
let _env = EnvGuard::set(&[(XDG_RUNTIME_DIR_ENV, temp.path().as_os_str())]);
|
|
let blocker = SessionCatalogOperation::Duplicate
|
|
.cached_status_blocker(None)
|
|
.expect("unknown status should block duplicate");
|
|
|
|
assert!(blocker.contains("Duplicate Session"));
|
|
}
|
|
|
|
#[test]
|
|
fn move_policy_uses_move_status_message() {
|
|
let temp = test_temp::tempdir().unwrap();
|
|
let _env = EnvGuard::set(&[(XDG_RUNTIME_DIR_ENV, temp.path().as_os_str())]);
|
|
let blocker = SessionCatalogOperation::Move
|
|
.cached_status_blocker(None)
|
|
.expect("unknown status should block move");
|
|
|
|
assert!(blocker.contains("Move Session"));
|
|
}
|
|
|
|
#[test]
|
|
fn clear_policy_blocks_unknown_daemon_status() {
|
|
let temp = test_temp::tempdir().unwrap();
|
|
let _env = EnvGuard::set(&[(XDG_RUNTIME_DIR_ENV, temp.path().as_os_str())]);
|
|
let blocker = SessionCatalogOperation::Clear
|
|
.cached_status_blocker(None)
|
|
.expect("unknown status should block clear");
|
|
|
|
assert!(blocker.contains("status finishes loading"));
|
|
}
|
|
|
|
#[test]
|
|
fn clear_tool_state_policy_uses_tool_state_status_message() {
|
|
let temp = test_temp::tempdir().unwrap();
|
|
let _env = EnvGuard::set(&[(XDG_RUNTIME_DIR_ENV, temp.path().as_os_str())]);
|
|
let blocker = SessionCatalogOperation::ClearToolState
|
|
.cached_status_blocker(None)
|
|
.expect("unknown status should block tool reset");
|
|
|
|
assert!(blocker.contains("Clear saved tool state"));
|
|
assert!(blocker.contains("status finishes loading"));
|
|
}
|
|
|
|
#[test]
|
|
fn session_clear_transaction_guard_blocks_manual_daemon_lock() {
|
|
let temp = test_temp::tempdir().unwrap();
|
|
let _env = EnvGuard::set(&[(XDG_RUNTIME_DIR_ENV, temp.path().as_os_str())]);
|
|
let _daemon_lock = acquire_runtime_lock_for_clear(RuntimeLockKind::Daemon).unwrap();
|
|
let error = acquire_runtime_lock_for_inactive_operation(
|
|
RuntimeLockKind::Daemon,
|
|
SessionCatalogOperation::Clear,
|
|
)
|
|
.expect_err("held daemon lock should block the transaction");
|
|
|
|
assert!(error.contains("manually started daemon"));
|
|
}
|
|
|
|
#[test]
|
|
fn inactive_session_reservation_holds_the_overlay_lock_but_not_the_daemon_lock() {
|
|
let temp = test_temp::tempdir().unwrap();
|
|
let _env = EnvGuard::set(&[(XDG_RUNTIME_DIR_ENV, temp.path().as_os_str())]);
|
|
|
|
let reservation = reserve_inactive_session_operation(SessionCatalogOperation::Clear).unwrap();
|
|
|
|
let daemon_lock = acquire_runtime_lock_for_clear(RuntimeLockKind::Daemon)
|
|
.expect("a daemon starting during the operation must get its lock");
|
|
assert!(matches!(
|
|
runtime_lock_active(RuntimeLockKind::Overlay, SessionCatalogOperation::Clear),
|
|
Ok(true)
|
|
));
|
|
drop(daemon_lock);
|
|
drop(reservation);
|
|
assert!(matches!(
|
|
runtime_lock_active(RuntimeLockKind::Overlay, SessionCatalogOperation::Clear),
|
|
Ok(false)
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn inactive_session_reservation_refuses_a_running_daemon() {
|
|
let temp = test_temp::tempdir().unwrap();
|
|
let _env = EnvGuard::set(&[(XDG_RUNTIME_DIR_ENV, temp.path().as_os_str())]);
|
|
let _daemon_lock = acquire_runtime_lock_for_clear(RuntimeLockKind::Daemon).unwrap();
|
|
|
|
let error = match reserve_inactive_session_operation(SessionCatalogOperation::Move) {
|
|
Ok(_) => panic!("a held daemon lock must refuse the operation"),
|
|
Err(error) => error,
|
|
};
|
|
|
|
assert!(error.contains("manually started daemon"), "{error}");
|
|
assert!(matches!(
|
|
runtime_lock_active(RuntimeLockKind::Overlay, SessionCatalogOperation::Move),
|
|
Ok(false)
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn cached_status_blocker_does_not_probe_runtime_locks() {
|
|
let temp = test_temp::tempdir().unwrap();
|
|
let _env = EnvGuard::set(&[(XDG_RUNTIME_DIR_ENV, temp.path().as_os_str())]);
|
|
let _daemon_lock = acquire_runtime_lock_for_clear(RuntimeLockKind::Daemon).unwrap();
|
|
let _overlay_lock = acquire_runtime_lock_for_clear(RuntimeLockKind::Overlay).unwrap();
|
|
let status = daemon_status(false);
|
|
|
|
let blocker = SessionCatalogOperation::Clear.cached_status_blocker(Some(&status));
|
|
|
|
assert!(blocker.is_none(), "{blocker:?}");
|
|
let blocker = SessionCatalogOperation::Move.cached_status_blocker(Some(&status));
|
|
|
|
assert!(blocker.is_none(), "{blocker:?}");
|
|
}
|
|
|
|
#[test]
|
|
fn clear_runtime_guards_hold_daemon_and_overlay_locks() {
|
|
let temp = test_temp::tempdir().unwrap();
|
|
let _env = EnvGuard::set(&[(XDG_RUNTIME_DIR_ENV, temp.path().as_os_str())]);
|
|
let _daemon_lock = acquire_runtime_lock_for_clear(RuntimeLockKind::Daemon).unwrap();
|
|
let _overlay_lock = acquire_runtime_lock_for_clear(RuntimeLockKind::Overlay).unwrap();
|
|
|
|
assert!(matches!(
|
|
runtime_lock_active(RuntimeLockKind::Daemon, SessionCatalogOperation::Clear),
|
|
Ok(true)
|
|
));
|
|
assert!(matches!(
|
|
runtime_lock_active(RuntimeLockKind::Overlay, SessionCatalogOperation::Clear),
|
|
Ok(true)
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn session_artifact_status_reports_size_when_present() {
|
|
let item = SessionCatalogItem {
|
|
id: "s-1".to_string(),
|
|
display_name: "Lecture".to_string(),
|
|
path: PathBuf::from("/tmp/lecture.wayscriber-session"),
|
|
path_label: "/tmp/lecture.wayscriber-session".to_string(),
|
|
canonical_path_label: None,
|
|
created_at_millis: 1_767_261_600_000,
|
|
last_opened_at_millis: None,
|
|
last_saved_at_millis: None,
|
|
artifacts: SessionArtifactSummary {
|
|
primary_exists: true,
|
|
backup_exists: false,
|
|
recovery_exists: false,
|
|
unreadable_copy_exists: false,
|
|
clear_marker_exists: false,
|
|
lock_exists: false,
|
|
non_lock_size_bytes: 4096,
|
|
},
|
|
};
|
|
|
|
assert_eq!(item.artifacts.status_label(), "primary · 4.0 KiB");
|
|
}
|
|
|
|
#[test]
|
|
fn clear_tool_state_rejects_defaulted_session_config() {
|
|
let loaded = LoadedConfig {
|
|
config: Config::default(),
|
|
source: ConfigSource::Primary,
|
|
validation: ConfigValidationReport::default(),
|
|
section_errors: vec![ConfigSectionError {
|
|
section: "session".to_string(),
|
|
error: "invalid value".to_string(),
|
|
}],
|
|
};
|
|
|
|
let err = ensure_session_config_available_for_destructive_action(&loaded)
|
|
.expect_err("a defaulted session section must block destructive catalog edits");
|
|
assert!(err.contains("[session] could not be read"));
|
|
}
|