agent_tui/ipc/
version.rs

1//! Version checking for CLI/daemon compatibility.
2
3use crate::common::ValueExt;
4
5use crate::ipc::client::DaemonClient;
6
7/// Version mismatch information.
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct VersionMismatch {
10    /// CLI version.
11    pub cli_version: String,
12    /// Daemon version.
13    pub daemon_version: String,
14}
15
16/// Result of version check operation.
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub enum VersionCheckResult {
19    /// CLI and daemon versions match.
20    Match,
21    /// CLI and daemon versions differ.
22    Mismatch(VersionMismatch),
23    /// Could not check version (daemon not running or RPC error).
24    CheckFailed(String),
25}
26
27/// Check for version mismatch between CLI and daemon.
28///
29/// Returns `VersionCheckResult::Match` if versions are the same,
30/// `VersionCheckResult::Mismatch` with details if different,
31/// or `VersionCheckResult::CheckFailed` if the check could not be performed.
32pub fn check_version<C: DaemonClient>(client: &mut C, cli_version: &str) -> VersionCheckResult {
33    match client.call("health", None) {
34        Err(e) => VersionCheckResult::CheckFailed(e.to_string()),
35        Ok(health) => {
36            let daemon_version = health.str_or("version", "unknown");
37            if cli_version != daemon_version {
38                VersionCheckResult::Mismatch(VersionMismatch {
39                    cli_version: cli_version.to_string(),
40                    daemon_version: daemon_version.to_string(),
41                })
42            } else {
43                VersionCheckResult::Match
44            }
45        }
46    }
47}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52    use crate::ipc::mock_client::MockClient;
53    use serde_json::json;
54
55    #[test]
56    fn test_version_match_returns_match() {
57        let mut client = MockClient::new();
58        client.set_response(
59            "health",
60            json!({
61                "status": "healthy",
62                "version": "1.0.0"
63            }),
64        );
65
66        let result = check_version(&mut client, "1.0.0");
67        assert_eq!(result, VersionCheckResult::Match);
68    }
69
70    #[test]
71    fn test_version_mismatch_returns_mismatch() {
72        let mut client = MockClient::new();
73        client.set_response(
74            "health",
75            json!({
76                "status": "healthy",
77                "version": "2.0.0"
78            }),
79        );
80
81        let result = check_version(&mut client, "1.0.0");
82        match result {
83            VersionCheckResult::Mismatch(mismatch) => {
84                assert_eq!(mismatch.cli_version, "1.0.0");
85                assert_eq!(mismatch.daemon_version, "2.0.0");
86            }
87            _ => panic!("Expected Mismatch, got {:?}", result),
88        }
89    }
90
91    #[test]
92    fn test_daemon_not_running_returns_check_failed() {
93        let mut client = MockClient::new_strict();
94        // new_strict() returns error for unconfigured methods
95
96        let result = check_version(&mut client, "1.0.0");
97        match result {
98            VersionCheckResult::CheckFailed(msg) => {
99                assert!(!msg.is_empty(), "Error message should not be empty");
100            }
101            _ => panic!("Expected CheckFailed, got {:?}", result),
102        }
103    }
104
105    #[test]
106    fn test_unknown_daemon_version_reports_mismatch() {
107        let mut client = MockClient::new();
108        client.set_response(
109            "health",
110            json!({
111                "status": "healthy"
112                // No version field
113            }),
114        );
115
116        let result = check_version(&mut client, "1.0.0");
117        match result {
118            VersionCheckResult::Mismatch(mismatch) => {
119                assert_eq!(mismatch.daemon_version, "unknown");
120            }
121            _ => panic!("Expected Mismatch, got {:?}", result),
122        }
123    }
124}