Release 0.5.1
Test / test (push) Successful in 58s
Publish release / publish (push) Successful in 3m2s

This commit is contained in:
Bendik Aagaard Lynghaug
2026-09-28 17:48:37 +02:00
parent 68471969b5
commit 6ee6551400
7 changed files with 71 additions and 18 deletions
+37 -2
View File
@@ -223,8 +223,15 @@ impl Policy {
if let Some(st) = &alt.self_transition {
// One rule per state the move may be made from: a
// person stays in dialogue with their case for as
// long as it runs, and `from` says how long that is.
for from in &st.from {
// long as it runs, and `from` says how long that
// is. Saying nothing means the bucket's own
// initial state - the bucket's, not the word
// `open`, since a bucket may start anywhere.
let initial = aggregates
.get(&st.bucket)
.map(|s| s.initial.clone())
.unwrap_or_else(|| crate::answers::OPEN_STATE.to_string());
for from in &st.from_states(&initial) {
emit(
format!("self:{}:{from}>{}", st.bucket, st.to), "the submitter, by their link and email".to_string(), "self_transition",
format!("bucket {}", st.bucket), format!("{from} -> {}", st.to),
@@ -577,6 +584,34 @@ mod tests {
assert_eq!(p.rules.iter().filter(|r| r.id.starts_with("self:subscribers:")).count(), 1);
}
// "On arrival" is the bucket's own first state, not the word
// `open`. redoal's watchers arrive at `pending` and confirm
// themselves into `open`; reading the literal made that rule
// `open -> open`, which the engine declares nowhere, so the
// confirm link in the mail could never have worked.
#[test]
fn arrival_is_wherever_the_bucket_actually_starts() {
let files = vec![(
"index.yaml".to_string(),
"name: Home\nalternatives:\n - name: Ask\n record_as: watchers\n - name: Yes, watch it\n self_transition: { bucket: watchers, to: open, label: Yes }\n".to_string(),
)];
let questions = build_questions(&files).unwrap();
let aggregates = parse_aggregates_yaml(
"aggregates:\n - bucket: watchers\n initial: pending\n states:\n pending: { event: requested }\n open: { event: confirmed }\n transitions:\n pending: [open]\n",
)
.unwrap();
let p = Policy::from_content(&questions, &aggregates);
assert_eq!(p.validate(), Ok(()), "{}", p.text);
assert!(
p.rules.iter().any(|r| r.id == "self:watchers:pending>open"),
"the rule must start where the records do: {:?}",
p.rules.iter().map(|r| &r.id).collect::<Vec<_>>()
);
let me = Principal::Submitter { record: "r".into(), email: "a@b.c".into() };
let bucket = Resource::Bucket { name: "watchers".into() };
assert!(p.decide(&me, Action::SelfTransition, &bucket, json!({ "from": "pending", "to": "open", "stored_email": "a@b.c" })).allow);
}
#[test]
fn invites_and_automation() {
let p = site();