mod collection;
mod tracker;
pub use collection::{run, Asked, Awaited, Collection, Wanted};
#[cfg(test)]
mod fixtures {
use chrono::{DateTime, Utc};
use crate::collect::agents::testing::{pane, titled, Fake as Provider};
use crate::collect::bd::parse_beads;
use crate::collect::run::{FailureKind, RunFailure};
use crate::collect::tracker::testing::{Fake, Fakes};
use crate::config::Config;
use crate::model::snapshot::{Node, Tree};
use crate::model::types::{Bead, PaneStatus};
pub(super) const ORBITAL: &str = "/srv/work/orbital";
pub(super) const FERRY: &str = "/srv/work/ferry";
pub(super) const ORBITAL_TREE: &str = r#"[
{"id":"orb-7","title":"lift the ground station","status":"in_progress",
"priority":1,"issue_type":"epic"},
{"id":"orb-7.1","title":"re-point the dish","status":"in_progress","parent":"orb-7",
"dependencies":[{"depends_on_id":"orb-7","type":"parent-child"}],
"priority":2,"issue_type":"task",
"metadata":{"agent_pane":"w:p1"}},
{"id":"orb-7.2","title":"lay the feeder cable","status":"open","parent":"orb-7",
"dependencies":[{"depends_on_id":"orb-7","type":"parent-child"}],
"priority":2,"issue_type":"task"}
]"#;
pub(super) const COLLIDING_TREE: &str = r#"[
{"id":"x-1","title":"the shared prefix","status":"in_progress",
"priority":1,"issue_type":"epic"},
{"id":"x-1.1","title":"the colliding id","status":"in_progress","parent":"x-1",
"dependencies":[{"depends_on_id":"x-1","type":"parent-child"}],
"priority":2,"issue_type":"task"}
]"#;
pub(super) fn panes() -> Provider {
Provider::holding(vec![
titled(pane("w:p1", ORBITAL, PaneStatus::Working), "the dish"),
pane("w:p9", ORBITAL, PaneStatus::Idle),
])
}
pub(super) fn no_panes() -> Provider {
Provider::holding(Vec::new())
}
pub(super) fn beads(rows: &str) -> Vec<Bead> {
parse_beads(rows).expect("the rows parse")
}
pub(super) fn now() -> DateTime<Utc> {
"2026-08-30T12:00:00Z".parse().expect("the instant parses")
}
pub(super) fn one_project() -> Config {
Config::from_toml(&format!(
r#"
[[projects]]
name = "orbital"
path = "{ORBITAL}"
"#
))
.expect("the config parses")
}
pub(super) fn one_project_reached_with_a_credential() -> Config {
Config::from_toml(&format!(
r#"
[[projects]]
name = "orbital"
path = "{ORBITAL}"
credential_command = "pass show orbital"
"#
))
.expect("the config parses")
}
pub(super) fn one_project_with_a_root_named() -> Config {
Config::from_toml(&format!(
r#"
[[projects]]
name = "orbital"
path = "{ORBITAL}"
[roots.explicit]
orbital = ["orb-7.1"]
"#
))
.expect("the config parses")
}
pub(super) fn two_projects() -> Config {
Config::from_toml(&format!(
r#"
[[projects]]
name = "orbital"
path = "{ORBITAL}"
[[projects]]
name = "ferry"
path = "{FERRY}"
"#
))
.expect("the config parses")
}
pub(super) fn orbital_holding(rows: &str) -> Fake {
Fake::holding(beads(rows))
.ready(["orb-7.2"])
.blocked("orb-7.1", &["orb-9"])
}
pub(super) fn orbital_tracker() -> Fake {
orbital_holding(ORBITAL_TREE)
}
pub(super) fn orbital_with(tracker: Fake) -> Fakes {
Fakes::default().with("orbital", tracker)
}
pub(super) fn orbital() -> Fakes {
orbital_with(orbital_tracker())
}
pub(super) fn colliding_tracker() -> Fake {
Fake::holding(beads(COLLIDING_TREE))
}
pub(super) fn colliding_trackers() -> Fakes {
Fakes::default()
.with("orbital", colliding_tracker())
.with("ferry", colliding_tracker())
}
pub(super) fn failing(kind: FailureKind) -> RunFailure {
RunFailure {
kind,
program: "bd".to_string(),
detail: "bd could not read the tracker".to_string(),
}
}
pub(super) fn node<'a>(tree: &'a Tree, id: &str) -> &'a Node {
tree.beads
.iter()
.find(|n| n.id == id)
.unwrap_or_else(|| panic!("{id} is among the beads"))
}
}