use cellos_core::{
command_completed_data_v1, lifecycle_destroyed_data_v1, AuthorityBundle, ExecutionCellSpec,
LifecycleDestroyOutcome, LifecycleTerminalState, Lifetime,
};
use serde_json::Value;
fn fixture_spec(spec_id: &str) -> ExecutionCellSpec {
ExecutionCellSpec {
id: spec_id.into(),
correlation: None,
ingress: None,
environment: None,
placement: None,
policy: None,
identity: None,
run: None,
authority: AuthorityBundle::default(),
lifetime: Lifetime { ttl_seconds: 60 },
export: None,
telemetry: None,
}
}
fn assert_no_authentic_exit_code(data: &Value, ctx: &str) {
let obj = data
.as_object()
.unwrap_or_else(|| panic!("{ctx}: destroyed data must be a JSON object"));
match obj.get("exitCode") {
None => {}
Some(Value::Null) => {}
Some(other) => panic!(
"{ctx}: destroyed payload carried a non-null exitCode ({other}); the \
exitCode field is reserved for vsock-authenticated exits and MUST NOT \
appear on a Forced terminal-state event (FC-23)"
),
}
}
#[test]
fn forced_terminal_state_failed_outcome_omits_exit_code() {
let spec = fixture_spec("fc23-forced-failed");
let data = lifecycle_destroyed_data_v1(
&spec,
"cell-fc23-forced-failed",
Some("run-fc23-forced-failed"),
LifecycleDestroyOutcome::Failed,
Some("in-VM exit bridge: vsock closed before exit code"),
Some(LifecycleTerminalState::Forced),
None,
None,
)
.expect("destroyed payload must serialize");
assert_eq!(
data["terminalState"], "forced",
"FC-23 fixture must actually exercise the Forced branch"
);
assert_no_authentic_exit_code(&data, "Forced + Failed");
}
#[test]
fn forced_terminal_state_succeeded_outcome_omits_exit_code() {
let spec = fixture_spec("fc23-forced-succeeded");
let data = lifecycle_destroyed_data_v1(
&spec,
"cell-fc23-forced-succeeded",
Some("run-fc23-forced-succeeded"),
LifecycleDestroyOutcome::Succeeded,
None,
Some(LifecycleTerminalState::Forced),
None,
None,
)
.expect("destroyed payload must serialize");
assert_eq!(data["terminalState"], "forced");
assert_no_authentic_exit_code(&data, "Forced + Succeeded");
}
#[test]
fn clean_terminal_state_with_command_completed_carries_numeric_exit_code() {
let spec = fixture_spec("fc23-clean");
let destroyed = lifecycle_destroyed_data_v1(
&spec,
"cell-fc23-clean",
Some("run-fc23-clean"),
LifecycleDestroyOutcome::Succeeded,
None,
Some(LifecycleTerminalState::Clean),
None,
None,
)
.expect("destroyed payload must serialize");
assert_eq!(destroyed["terminalState"], "clean");
assert_no_authentic_exit_code(&destroyed, "Clean destroyed");
let completed = command_completed_data_v1(
&spec,
"cell-fc23-clean",
Some("run-fc23-clean"),
&[
"/bin/sh".to_string(),
"-c".to_string(),
"exit 42".to_string(),
],
42,
128,
None,
)
.expect("command-completed payload must serialize");
let exit_code = completed
.get("exitCode")
.expect("Clean / vsock-authenticated path must carry exitCode");
assert!(
exit_code.is_number(),
"exitCode must serialize as a JSON number on the authenticated path; \
got {exit_code}"
);
assert_eq!(
exit_code.as_i64(),
Some(42),
"command_completed_data_v1 must round-trip the supplied exit code so \
the Forced-path no-exitCode assertion is non-trivial"
);
}