agentmux_srv/muxbus/wan_presence/
policy.rs1use crate::backend::rpc_types::PresenceOffReason;
13use crate::backend::wconfig::SettingsType;
14
15pub(crate) const FORCE_ENV: &str = "AGENTMUX_PUBLISH_PRESENCE";
18
19pub(crate) const TEST_HARNESS_ENV: &str = "AGENTMUX_TEST_HARNESS";
22
23#[derive(Debug, Clone, Default, PartialEq, Eq)]
26pub(crate) struct Install {
27 pub headless: bool,
28 pub home_override: bool,
29 pub channel: String,
30 pub test_harness: bool,
31 pub forced: bool,
32}
33
34impl Install {
35 pub(crate) fn from_env(headless: bool) -> Self {
37 Self::from_vars(headless, crate::backend::reactive::registry::local_channel_id(), |name| {
38 std::env::var(name).ok()
39 })
40 }
41
42 pub(crate) fn from_vars(headless: bool, channel: String, var: impl Fn(&str) -> Option<String>) -> Self {
45 Self {
46 headless,
47 home_override: var("AGENTMUX_HOME_OVERRIDE").is_some_and(|v| !v.is_empty()),
48 channel,
49 test_harness: var(TEST_HARNESS_ENV).is_some() || var("AGENTMUX_DISABLE_CLOUD_SUBSCRIBER").is_some(),
51 forced: var(FORCE_ENV).as_deref() == Some("1"),
52 }
53 }
54
55 fn default_off(&self) -> Option<PresenceOffReason> {
57 if self.headless {
61 Some(PresenceOffReason::Headless)
62 } else if self.test_harness {
63 Some(PresenceOffReason::TestHarness)
64 } else if self.home_override {
65 Some(PresenceOffReason::IsolatedHome)
66 } else if self.channel.starts_with("dev-") {
67 Some(PresenceOffReason::DevBuild)
68 } else {
69 None
70 }
71 }
72
73 pub(crate) fn off_reason(&self, setting_on: bool) -> Option<PresenceOffReason> {
77 if !setting_on {
78 return Some(PresenceOffReason::Setting);
79 }
80 if self.forced {
81 return None;
82 }
83 self.default_off()
84 }
85}
86
87pub(crate) fn setting_on(settings: &SettingsType) -> bool {
91 settings.extra.get("cloud:publishpresence").and_then(|v| v.as_bool()).unwrap_or(true)
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97
98 fn normal() -> Install {
99 Install { channel: "stable".into(), ..Install::default() }
100 }
101
102 #[test]
103 fn a_normal_install_publishes_and_the_others_are_off_by_default() {
104 let cases: [(&str, Install, Option<PresenceOffReason>); 7] = [
105 ("stable", normal(), None),
106 ("a local package build", Install { channel: "agentmux-0-59-12-k3j9".into(), ..normal() }, None),
107 ("a dev build", Install { channel: "dev-feature-x".into(), ..normal() }, Some(PresenceOffReason::DevBuild)),
108 ("headless", Install { headless: true, ..normal() }, Some(PresenceOffReason::Headless)),
109 ("an isolated home", Install { home_override: true, ..normal() }, Some(PresenceOffReason::IsolatedHome)),
110 ("a test harness", Install { test_harness: true, ..normal() }, Some(PresenceOffReason::TestHarness)),
111 (
112 "a headless dev build under test",
113 Install { headless: true, test_harness: true, channel: "dev-x".into(), ..normal() },
114 Some(PresenceOffReason::Headless),
115 ),
116 ];
117 for (what, install, expect) in cases {
118 assert_eq!(install.off_reason(true), expect, "{what}");
119 }
120 }
121
122 #[test]
123 fn the_setting_wins_and_the_override_only_lifts_the_default() {
124 assert_eq!(normal().off_reason(false), Some(PresenceOffReason::Setting));
125 let dev = Install { channel: "dev-x".into(), ..normal() };
126 let forced = Install { forced: true, ..dev.clone() };
127 assert_eq!(dev.off_reason(true), Some(PresenceOffReason::DevBuild));
128 assert_eq!(forced.off_reason(true), None, "a dev build under test publishes");
129 assert_eq!(forced.off_reason(false), Some(PresenceOffReason::Setting), "the setting still wins");
130 let everything = Install { headless: true, home_override: true, test_harness: true, ..forced };
131 assert_eq!(everything.off_reason(true), None);
132 }
133
134 #[test]
135 fn the_setting_is_on_unless_it_is_false() {
136 let read = |json: serde_json::Value| setting_on(&serde_json::from_value::<SettingsType>(json).unwrap());
137 assert!(read(serde_json::json!({})));
138 assert!(read(serde_json::json!({ "cloud:publishpresence": true })));
139 assert!(!read(serde_json::json!({ "cloud:publishpresence": false })));
140 assert!(read(serde_json::json!({ "cloud:publishpresence": "no" })), "not a boolean: the default");
141 }
142
143 fn with(vars: &[(&str, &str)]) -> Install {
144 let vars: Vec<(String, String)> = vars.iter().map(|(k, v)| (k.to_string(), v.to_string())).collect();
145 Install::from_vars(false, "stable".into(), move |name| {
146 vars.iter().find(|(k, _)| k == name).map(|(_, v)| v.clone())
147 })
148 }
149
150 #[test]
151 fn the_environment_is_read() {
152 assert_eq!(with(&[]), normal());
153 assert!(Install::from_vars(true, "stable".into(), |_| None).headless);
154 assert!(with(&[(TEST_HARNESS_ENV, "1")]).test_harness);
155 assert!(with(&[("AGENTMUX_DISABLE_CLOUD_SUBSCRIBER", "")]).test_harness, "present is enough");
156 assert!(with(&[("AGENTMUX_HOME_OVERRIDE", "/tmp/h")]).home_override);
157 assert!(!with(&[("AGENTMUX_HOME_OVERRIDE", "")]).home_override, "empty is unset");
158 assert!(with(&[(FORCE_ENV, "1")]).forced);
159 assert!(!with(&[(FORCE_ENV, "true")]).forced, "only 1");
160 }
161
162 #[test]
165 fn a_headless_srv_says_headless_not_test_harness() {
166 let install = Install::from_vars(true, "stable".into(), |name| {
167 (name == "AGENTMUX_DISABLE_CLOUD_SUBSCRIBER").then(|| "1".to_string())
168 });
169 assert_eq!(install.off_reason(true), Some(PresenceOffReason::Headless));
170 }
171}