Skip to main content

update_kit/checker/
background.rs

1use std::path::Path;
2use std::process::Command;
3
4use crate::errors::UpdateKitError;
5
6/// Spawn a background process to check for updates.
7///
8/// Launches the current executable (or the given exe) as a detached process
9/// with a special flag and serialized config JSON. The process runs
10/// independently (fire and forget).
11pub fn spawn_background_check(exe_path: &Path, config_json: &str) -> Result<(), UpdateKitError> {
12    let exe = exe_path.to_str().ok_or_else(|| {
13        UpdateKitError::CommandSpawnFailed("Invalid executable path".into())
14    })?;
15
16    let mut cmd = Command::new(exe);
17    cmd.arg("--update-kit-background-check")
18        .arg(config_json)
19        .stdin(std::process::Stdio::null())
20        .stdout(std::process::Stdio::null())
21        .stderr(std::process::Stdio::null());
22
23    // On Unix, use process group detachment
24    #[cfg(unix)]
25    {
26        use std::os::unix::process::CommandExt;
27        cmd.process_group(0);
28    }
29
30    // On Windows, use creation flags for detached process
31    #[cfg(windows)]
32    {
33        use std::os::windows::process::CommandExt;
34        const DETACHED_PROCESS: u32 = 0x00000008;
35        const CREATE_NO_WINDOW: u32 = 0x08000000;
36        cmd.creation_flags(DETACHED_PROCESS | CREATE_NO_WINDOW);
37    }
38
39    cmd.spawn().map_err(|e| {
40        UpdateKitError::CommandSpawnFailed(format!(
41            "Failed to spawn background check: {}",
42            e
43        ))
44    })?;
45
46    Ok(())
47}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52    use std::path::PathBuf;
53
54    #[test]
55    fn test_spawn_background_check_nonexistent_exe() {
56        let result = spawn_background_check(
57            &PathBuf::from("/nonexistent/binary"),
58            "{}",
59        );
60        assert!(result.is_err());
61    }
62
63    #[test]
64    fn spawn_with_current_exe_succeeds() {
65        // Use the test binary itself — it will exit quickly since args won't match
66        let exe = std::env::current_exe().unwrap();
67        let result = spawn_background_check(&exe, "{}");
68        assert!(result.is_ok());
69    }
70
71    #[test]
72    fn spawn_with_complex_config_json() {
73        let exe = std::env::current_exe().unwrap();
74        let config = r#"{"app_name":"test","version":"1.0.0","sources":[]}"#;
75        let result = spawn_background_check(&exe, config);
76        assert!(result.is_ok());
77    }
78
79    #[test]
80    fn spawn_with_empty_config() {
81        let exe = std::env::current_exe().unwrap();
82        let result = spawn_background_check(&exe, "");
83        assert!(result.is_ok()); // Empty string is valid as argument
84    }
85
86    #[test]
87    fn spawn_with_special_chars_in_config() {
88        let exe = std::env::current_exe().unwrap();
89        let config = r#"{"name":"test \"quoted\" & special <chars>"}"#;
90        let result = spawn_background_check(&exe, config);
91        assert!(result.is_ok());
92    }
93}