needs: also_moved_by - a need carried by more than one group's desk
Test / test (push) Successful in 27s
Test / test (push) Successful in 27s
Roles overlap on one aggregate: a project manager prices a change, a site manager completes it. A need may list the other groups whose desks hold some of its moves; their buttons count toward reaching `done_when` and toward the strand check exactly as `handled_by`'s do. `handled_by` must still hold a desk of its own, so that someone is answerable for the record. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L4jrCgLiKKHAEFuZUJjckH
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
6efd7a0c6b
commit
d7f60eaa31
+34
-2
@@ -86,6 +86,13 @@ pub struct Need {
|
|||||||
/// becoming open -> quoted) fails instead of passing.
|
/// becoming open -> quoted) fails instead of passing.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub stages: Vec<String>,
|
pub stages: Vec<String>,
|
||||||
|
/// Other groups whose desks carry some of this need's moves: a
|
||||||
|
/// site manager closing what a project manager opened. Their
|
||||||
|
/// buttons count toward reaching `done_when` exactly as
|
||||||
|
/// `handled_by`'s do. `handled_by` must still hold a desk of its
|
||||||
|
/// own - someone has to be answerable for the record.
|
||||||
|
#[serde(default)]
|
||||||
|
pub also_moved_by: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn root() -> String {
|
fn root() -> String {
|
||||||
@@ -392,9 +399,11 @@ pub fn check(
|
|||||||
let desks = desks_over(questions, &need.lands_in);
|
let desks = desks_over(questions, &need.lands_in);
|
||||||
let own: Vec<&Desk> = desks
|
let own: Vec<&Desk> = desks
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|d| d.group.as_deref() == Some(need.handled_by.as_str()))
|
.filter(|d| {
|
||||||
|
d.group.as_deref().is_some_and(|g| g == need.handled_by || need.also_moved_by.iter().any(|m| m == g))
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
if own.is_empty() {
|
if !desks.iter().any(|d| d.group.as_deref() == Some(need.handled_by.as_str())) {
|
||||||
let others: Vec<String> = desks
|
let others: Vec<String> = desks
|
||||||
.iter()
|
.iter()
|
||||||
.map(|d| format!("{} ({})", d.page, d.group.as_deref().unwrap_or("ungated")))
|
.map(|d| format!("{} ({})", d.page, d.group.as_deref().unwrap_or("ungated")))
|
||||||
@@ -883,6 +892,29 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn a_need_can_be_carried_by_more_than_one_groups_desk() {
|
||||||
|
// owners may only quote or lose; closing a quoted job is the
|
||||||
|
// site managers' button, on a desk of their own.
|
||||||
|
let (mut questions, aggregates) = site(" - { to: quoted, label: Quote }\n - { to: lost, label: Lose }\n");
|
||||||
|
let more = build_questions(&[(
|
||||||
|
"site/index.yaml".to_string(),
|
||||||
|
"name: Site desk\nqualifies: site_managers\nalternatives:\n - name: Projects\n features:\n - name: \"\"\n resource:\n source: { kind: kv, bucket: projects }\n requires_group: site_managers\n transitions:\n - { from: quoted, to: won, label: Win }\n - { from: quoted, to: lost, label: Lose }\n".to_string(),
|
||||||
|
)])
|
||||||
|
.unwrap();
|
||||||
|
questions.extend(more);
|
||||||
|
|
||||||
|
let mut file = needs("");
|
||||||
|
// Alone, owners cannot finish it...
|
||||||
|
assert_eq!(fails(&check(&file, &questions, &aggregates)).len(), 2);
|
||||||
|
// ...with the site managers' buttons counted, they can.
|
||||||
|
file.needs[0].also_moved_by = vec!["site_managers".to_string()];
|
||||||
|
assert!(check(&file, &questions, &aggregates).is_empty());
|
||||||
|
// But someone has to be answerable: handled_by needs its own desk.
|
||||||
|
file.needs[0].handled_by = "nobody".to_string();
|
||||||
|
assert!(fails(&check(&file, &questions, &aggregates))[0].starts_with("no desk gated on \"nobody\""));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn the_wrong_group_holding_the_desk_is_a_failure() {
|
fn the_wrong_group_holding_the_desk_is_a_failure() {
|
||||||
let (questions, aggregates) = site(FULL_DESK);
|
let (questions, aggregates) = site(FULL_DESK);
|
||||||
|
|||||||
Reference in New Issue
Block a user