Skip to content

Commit 9ef2295

Browse files
authored
Merge pull request #6 from rynfar/feat/prime-session-host-7238
feat(harness): own bounded native Prime sessions
2 parents de66c08 + 3ba88e5 commit 9ef2295

14 files changed

Lines changed: 6853 additions & 47 deletions

File tree

crates/harness/src/prime/contract.rs

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,16 @@ impl PrimeServerCapabilities {
9191
}
9292
}
9393

94+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
95+
#[serde(rename_all = "kebab-case")]
96+
pub(super) enum OwnedSessionCleanupStatus {
97+
Active,
98+
Stopping,
99+
Settled,
100+
}
101+
94102
#[derive(Debug, Deserialize)]
95-
#[serde(tag = "kind", rename_all = "kebab-case")]
103+
#[serde(tag = "kind", rename_all = "kebab-case", deny_unknown_fields)]
96104
pub(super) enum BridgeResponse {
97105
#[serde(rename_all = "camelCase")]
98106
Loaded {
@@ -107,6 +115,12 @@ pub(super) enum BridgeResponse {
107115
protocol_version: u64,
108116
capabilities: Vec<String>,
109117
},
118+
#[serde(rename_all = "camelCase")]
119+
OwnedSessionCleanup {
120+
v: u8,
121+
id: u64,
122+
status: OwnedSessionCleanupStatus,
123+
},
110124
Shutdown {
111125
v: u8,
112126
id: u64,
@@ -124,6 +138,7 @@ impl BridgeResponse {
124138
let (version, id) = match self {
125139
Self::Loaded { v, id, .. }
126140
| Self::Ready { v, id, .. }
141+
| Self::OwnedSessionCleanup { v, id, .. }
127142
| Self::Shutdown { v, id, .. }
128143
| Self::Error { v, id, .. } => (*v, *id),
129144
};
@@ -193,4 +208,39 @@ mod tests {
193208
};
194209
assert!(response.validate_meta(1).is_err());
195210
}
211+
212+
#[test]
213+
fn cleanup_status_is_bounded_and_metadata_is_correlated() {
214+
for (wire, expected) in [
215+
("active", OwnedSessionCleanupStatus::Active),
216+
("stopping", OwnedSessionCleanupStatus::Stopping),
217+
("settled", OwnedSessionCleanupStatus::Settled),
218+
] {
219+
let bytes = format!(
220+
r#"{{"v":{CONTROL_VERSION},"id":7,"kind":"owned-session-cleanup","status":"{wire}"}}"#
221+
);
222+
let response: BridgeResponse = serde_json::from_str(&bytes).unwrap();
223+
assert!(response.validate_meta(7).is_ok());
224+
assert!(matches!(
225+
response,
226+
BridgeResponse::OwnedSessionCleanup { status, .. } if status == expected
227+
));
228+
}
229+
230+
let unknown = format!(
231+
r#"{{"v":{CONTROL_VERSION},"id":7,"kind":"owned-session-cleanup","status":"/private/native-id"}}"#
232+
);
233+
assert!(serde_json::from_str::<BridgeResponse>(&unknown).is_err());
234+
let injected = format!(
235+
r#"{{"v":{CONTROL_VERSION},"id":7,"kind":"owned-session-cleanup","status":"settled","raw":"/private/native-id"}}"#
236+
);
237+
assert!(serde_json::from_str::<BridgeResponse>(&injected).is_err());
238+
239+
let response = BridgeResponse::OwnedSessionCleanup {
240+
v: CONTROL_VERSION,
241+
id: 7,
242+
status: OwnedSessionCleanupStatus::Settled,
243+
};
244+
assert!(response.validate_meta(8).is_err());
245+
}
196246
}

crates/harness/src/prime/error.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,15 @@ pub enum PrimeDaemonError {
3232
},
3333
#[error("Prime Agent bridge emitted an invalid control frame ({reason})")]
3434
InvalidBridgeFrame { reason: &'static str },
35+
#[error("Prime Agent session host emitted an invalid control frame ({reason})")]
36+
InvalidSessionHostFrame { reason: &'static str },
37+
#[error("Prime Agent session {stage} failed ({code})")]
38+
Session {
39+
stage: &'static str,
40+
code: &'static str,
41+
},
42+
#[error("Prime Agent owned-session cleanup is uncertain")]
43+
CleanupUncertain,
3544
}
3645

3746
impl PrimeDaemonError {
@@ -51,3 +60,31 @@ impl From<PrimeDaemonError> for crate::HarnessError {
5160
}
5261
}
5362
}
63+
64+
#[cfg(test)]
65+
mod tests {
66+
use super::*;
67+
68+
#[test]
69+
fn session_errors_are_fixed_sanitized_protocol_failures() {
70+
let errors = [
71+
PrimeDaemonError::InvalidSessionHostFrame {
72+
reason: "response kind is invalid",
73+
},
74+
PrimeDaemonError::Session {
75+
stage: "attach",
76+
code: "host-operation-failed",
77+
},
78+
PrimeDaemonError::CleanupUncertain,
79+
];
80+
for error in errors {
81+
let message = error.to_string();
82+
assert!(!message.contains("/private/"));
83+
assert!(!message.contains("active-session"));
84+
assert!(matches!(
85+
crate::HarnessError::from(error),
86+
crate::HarnessError::Protocol(_)
87+
));
88+
}
89+
}
90+
}

0 commit comments

Comments
 (0)