补齐桌面壳插件失败观测

桌面剪贴板和通知插件异常记录日志

Rust 单测覆盖稳定错误和日志边界

配置门禁和文档同步桌面插件观测约束
This commit is contained in:
2026-06-20 19:40:54 +08:00
parent 48bad28a96
commit bacb17506d
5 changed files with 83 additions and 5 deletions
@@ -2065,6 +2065,10 @@ for (const snippet of [
'write_desktop_clipboard_text(app, text)',
'read_desktop_clipboard_text(app)',
'normalize_clipboard_text(&text)',
'log_desktop_clipboard_failure',
'desktop clipboard failed for {label}: {error}',
'log_desktop_clipboard_failure("write.text", &error)',
'log_desktop_clipboard_failure("read.text", &error)',
'clipboard_write_unavailable_response',
'clipboard_read_unavailable_response',
'required_string_payload(request, "text")',
@@ -2073,6 +2077,7 @@ for (const snippet of [
'"clipboard write unavailable"',
'"clipboard read unavailable"',
'clipboard_unavailable_responses_are_stable',
'clipboard_failures_are_logged_without_exposing_native_detail',
]) {
if (!desktopHostBridgeClipboardSource.includes(snippet)) {
throw new Error(`desktop shell clipboard module is missing ${snippet}`);
@@ -2699,6 +2704,11 @@ for (const snippet of [
'desktop_notification_permission_action',
'notification_permission_unavailable_response',
'notification_delivery_unavailable_response',
'log_desktop_notification_failure',
'desktop notification failed for {label}: {error}',
'log_desktop_notification_failure("permission.state", &error.to_string())',
'log_desktop_notification_failure("permission.request", &error.to_string())',
'log_desktop_notification_failure("delivery.show", &error.to_string())',
'PermissionState::Granted',
'PermissionState::Denied',
'PermissionState::Prompt | PermissionState::PromptWithRationale',
@@ -2707,6 +2717,7 @@ for (const snippet of [
'local_notification_payload_truncates_to_shared_contract_limits',
'notification_permission_unavailable_response_is_stable',
'notification_delivery_unavailable_response_is_stable',
'notification_failures_are_logged_without_exposing_native_detail',
'"a".repeat(HOST_BRIDGE_LOCAL_NOTIFICATION_TITLE_MAX_LENGTH)',
'"b".repeat(HOST_BRIDGE_LOCAL_NOTIFICATION_BODY_MAX_LENGTH)',
'"notification permission unavailable"',
@@ -27,6 +27,14 @@ pub(crate) fn read_desktop_clipboard_text(app: &tauri::AppHandle) -> Result<Stri
.map_err(|error| error.to_string())
}
fn log_desktop_clipboard_failure(label: &str, error: &str) -> bool {
if !error.is_empty() {
eprintln!("desktop clipboard failed for {label}: {error}");
}
false
}
pub(crate) fn clipboard_write_unavailable_response(
request: &HostBridgeRequest,
) -> HostBridgeResponse {
@@ -58,7 +66,10 @@ pub(crate) fn write_desktop_host_bridge_clipboard_text(
match write_desktop_clipboard_text(app, text) {
Ok(()) => ok(request.id.clone(), json!(true)),
Err(_) => clipboard_write_unavailable_response(request),
Err(error) => {
log_desktop_clipboard_failure("write.text", &error);
clipboard_write_unavailable_response(request)
}
}
}
@@ -73,7 +84,10 @@ pub(crate) fn read_desktop_host_bridge_clipboard_text(
"text": text,
}),
),
Err(_) => clipboard_read_unavailable_response(request),
Err(error) => {
log_desktop_clipboard_failure("read.text", &error);
clipboard_read_unavailable_response(request)
}
}
}
@@ -140,4 +154,16 @@ mod tests {
assert_eq!(read_error.code, "host_error");
assert_eq!(read_error.message, "clipboard read unavailable");
}
#[test]
fn clipboard_failures_are_logged_without_exposing_native_detail() {
assert!(!log_desktop_clipboard_failure(
"write.text",
"private clipboard detail"
));
assert!(!log_desktop_clipboard_failure(
"read.text",
"private clipboard detail"
));
}
}
@@ -105,6 +105,14 @@ fn notification_delivery_unavailable_response(request: &HostBridgeRequest) -> Ho
)
}
fn log_desktop_notification_failure(label: &str, error: &str) -> bool {
if !error.is_empty() {
eprintln!("desktop notification failed for {label}: {error}");
}
false
}
pub(crate) fn show_desktop_local_notification(
app: &tauri::AppHandle,
request: &HostBridgeRequest,
@@ -116,7 +124,10 @@ pub(crate) fn show_desktop_local_notification(
let notification_manager = app.notification();
let permission_state = notification_manager
.permission_state()
.map_err(|_| notification_permission_unavailable_response(request));
.map_err(|error| {
log_desktop_notification_failure("permission.state", &error.to_string());
notification_permission_unavailable_response(request)
});
let permission_state = match permission_state {
Ok(permission_state) => permission_state,
Err(response) => return response,
@@ -125,7 +136,10 @@ pub(crate) fn show_desktop_local_notification(
if permission_action == DesktopNotificationPermissionAction::Request {
let requested_state = notification_manager
.request_permission()
.map_err(|_| notification_permission_unavailable_response(request));
.map_err(|error| {
log_desktop_notification_failure("permission.request", &error.to_string());
notification_permission_unavailable_response(request)
});
let requested_state = match requested_state {
Ok(requested_state) => requested_state,
Err(response) => return response,
@@ -150,7 +164,10 @@ pub(crate) fn show_desktop_local_notification(
request.id.clone(),
desktop_notification_delivered_to_system_result(),
),
Err(_) => notification_delivery_unavailable_response(request),
Err(error) => {
log_desktop_notification_failure("delivery.show", &error.to_string());
notification_delivery_unavailable_response(request)
}
}
}
@@ -219,6 +236,22 @@ mod tests {
assert_eq!(error.message, "notification delivery unavailable");
}
#[test]
fn notification_failures_are_logged_without_exposing_native_detail() {
assert!(!log_desktop_notification_failure(
"permission.state",
"private notification detail"
));
assert!(!log_desktop_notification_failure(
"permission.request",
"private notification detail"
));
assert!(!log_desktop_notification_failure(
"delivery.show",
"private notification detail"
));
}
#[test]
fn local_notification_payload_rejects_empty_and_control_text() {
let mut empty = request("notification.showLocal");