use std::sync::Arc;
use macp_core::session::{Session, SessionState};
use macp_modes::mode::decision::DecisionMode;
use macp_modes::mode::Mode;
use macp_modes::step::{step, StepOutcome};
use macp_pb::decision_pb::ProposalPayload;
use macp_pb::pb::{CommitmentPayload, Envelope};
use prost::Message;
const SESSION_ID: &str = "11111111-1111-4111-8111-111111111111";
const MODE: &str = "macp.mode.decision.v1";
const INITIATOR: &str = "agent://orchestrator";
fn session() -> Session {
Session::builder(SESSION_ID, MODE, INITIATOR)
.ttl_ms(60_000)
.participants(vec![INITIATOR.into(), "agent://fraud".into()])
.mode_version("1.0.0")
.configuration_version("cfg-1")
.build()
}
fn env(sender: &str, message_type: &str, message_id: &str, payload: Vec<u8>) -> Envelope {
Envelope {
macp_version: "1.0".into(),
mode: MODE.into(),
message_type: message_type.into(),
message_id: message_id.into(),
session_id: SESSION_ID.into(),
sender: sender.into(),
timestamp_unix_ms: 0,
payload,
}
}
#[test]
fn drives_a_full_decision_session_without_the_runtime() {
let mode = DecisionMode::new(Arc::new(macp_policy::DefaultPolicyEvaluator));
let mut s = session();
let resp = mode
.on_session_start(&s, &env(INITIATOR, "SessionStart", "start", vec![]))
.expect("session start");
s.apply_mode_response(resp);
assert_eq!(s.state, SessionState::Open);
let proposal = ProposalPayload {
proposal_id: "p1".into(),
option: "ship-it".into(),
rationale: "ready".into(),
supporting_data: vec![],
}
.encode_to_vec();
let out = step(
&mut s,
&env(INITIATOR, "Proposal", "m-prop", proposal),
&mode,
1,
)
.expect("proposal accepted");
assert_eq!(
out,
StepOutcome::Accepted {
state: SessionState::Open
}
);
let commitment = CommitmentPayload {
commitment_id: "c1".into(),
action: "decision.selected".into(),
authority_scope: "release".into(),
reason: "bound".into(),
mode_version: s.mode_version.clone(),
policy_version: s.policy_version.clone(),
configuration_version: s.configuration_version.clone(),
outcome_positive: true,
supersedes: None,
}
.encode_to_vec();
let out = step(
&mut s,
&env(INITIATOR, "Commitment", "m-commit", commitment),
&mode,
2,
)
.expect("commitment accepted");
assert_eq!(
out,
StepOutcome::Accepted {
state: SessionState::Resolved
}
);
assert_eq!(s.state, SessionState::Resolved);
assert!(s.resolution.is_some());
}
#[test]
fn rejected_message_does_not_consume_a_dedup_slot() {
let mode = DecisionMode::new(Arc::new(macp_policy::DefaultPolicyEvaluator));
let mut s = session();
let resp = mode
.on_session_start(&s, &env(INITIATOR, "SessionStart", "start", vec![]))
.unwrap();
s.apply_mode_response(resp);
let proposal = ProposalPayload {
proposal_id: "p1".into(),
option: "ship-it".into(),
rationale: "ready".into(),
supporting_data: vec![],
}
.encode_to_vec();
assert!(step(
&mut s,
&env("agent://stranger", "Proposal", "m-prop", proposal.clone()),
&mode,
1,
)
.is_err());
assert!(!s.seen_message_ids.contains("m-prop"));
let out = step(
&mut s,
&env(INITIATOR, "Proposal", "m-prop", proposal),
&mode,
1,
)
.expect("re-used id accepted from a participant");
assert_eq!(
out,
StepOutcome::Accepted {
state: SessionState::Open
}
);
}