use super::{ask, deps_at, fire, quiet, script, seeing};
use crate::boundary::config::{Read, Write};
use crate::boundary::reply::{ConfigAnswer, Reply};
use crate::boundary::{Action, Query};
use crate::proposals::{Settle, Verdict};
use crate::test_support::workspace::{seed_proposal, seed_workspace_config};
use std::path::Path;
use tempfile::TempDir;
fn staged(root: &Path) -> (TempDir, crate::boundary::dispatch::Deps) {
let ws = tempfile::tempdir().expect("tmp");
seed_workspace_config(ws.path(), &[("workflow.yaml", "events: {}\n")]);
seed_proposal(
ws.path(),
"20260906T090000Z-r001",
"config/default",
"souls: the apk cache is worth keeping",
&[("souls/worker.md", "keep it\n")],
);
let deps = seeing(&quiet(root), &[ws.path()]);
(ws, deps)
}
#[test]
fn the_staged_proposals_answer_at_the_chokepoint() {
let root = tempfile::tempdir().expect("tmp");
let (ws, deps) = staged(root.path());
let name = crate::naming::leaf(ws.path());
let Ok(Reply::Config(ConfigAnswer::Proposals(listing))) = ask(
&deps,
&Query::Config(Read::Proposals {
workspace: name.clone(),
id: None,
}),
) else {
panic!("a proposals read answers the family's carrier");
};
assert_eq!(listing.rows.len(), 1, "{:?}", listing.rows);
assert!(listing.rows[0].fresh, "the lineage still stands there");
assert_eq!(listing.whole, None, "the bare read names none");
let Ok(Reply::Config(ConfigAnswer::Proposals(one))) = ask(
&deps,
&Query::Config(Read::Proposals {
workspace: name,
id: Some("20260906T090000Z-r001".to_owned()),
}),
) else {
panic!("naming one answers the same carrier");
};
assert!(
one.whole.is_some_and(|w| w.contains("apk cache")),
"the proposal itself, message and diff"
);
}
#[test]
fn a_workspace_with_nothing_staged_answers_emptily() {
let root = tempfile::tempdir().expect("tmp");
let ws = tempfile::tempdir().expect("tmp");
seed_workspace_config(ws.path(), &[("workflow.yaml", "events: {}\n")]);
let deps = seeing(&quiet(root.path()), &[ws.path()]);
let Ok(Reply::Config(ConfigAnswer::Proposals(view))) = ask(
&deps,
&Query::Config(Read::Proposals {
workspace: crate::naming::leaf(ws.path()),
id: None,
}),
) else {
panic!("an empty listing is still a listing");
};
assert!(view.rows.is_empty());
}
#[test]
fn an_unreadable_workspace_refuses_rather_than_reading_empty() {
let root = tempfile::tempdir().expect("tmp");
let ws = tempfile::tempdir().expect("tmp");
let deps = seeing(&quiet(root.path()), &[ws.path()]);
assert!(
ask(
&deps,
&Query::Config(Read::Proposals {
workspace: crate::naming::leaf(ws.path()),
id: None,
}),
)
.is_err()
);
}
#[test]
fn the_settle_spawns_litany_proposal_with_the_verdicts_flag() {
let root = tempfile::tempdir().expect("tmp");
let bin = tempfile::tempdir().expect("tmp");
let litany = script(bin.path(), "litany", "printf '%s\\n' \"$*\"\nexit 0\n");
let ws = tempfile::tempdir().expect("tmp");
let deps = seeing(
&deps_at(root.path(), &litany, Path::new("/definitely/not/a/bl-xyz")),
&[ws.path()],
);
for (verdict, flag) in [(Verdict::Accept, "--accept"), (Verdict::Reject, "--reject")] {
let reply = fire(
&deps,
&Action::Config(Write::Proposal(Settle {
workspace: crate::naming::leaf(ws.path()),
id: "20260906T090000Z-r001".to_owned(),
verdict,
})),
)
.expect("the verb ran");
let Reply::Outcome(outcome) = reply else {
panic!("a §8.2 verb answers its captured run");
};
assert!(outcome.ok(), "{outcome:?}");
assert!(outcome.stdout.contains("proposal"), "{}", outcome.stdout);
assert!(
outcome.stdout.contains("20260906T090000Z-r001"),
"{}",
outcome.stdout
);
assert!(outcome.stdout.contains(flag), "{}", outcome.stdout);
}
}
#[test]
fn litanys_refusal_is_the_answer() {
let root = tempfile::tempdir().expect("tmp");
let bin = tempfile::tempdir().expect("tmp");
let litany = script(
bin.path(),
"litany",
"printf 'proposal x is stale\\n' 1>&2\nexit 1\n",
);
let ws = tempfile::tempdir().expect("tmp");
let deps = seeing(
&deps_at(root.path(), &litany, Path::new("/definitely/not/a/bl-xyz")),
&[ws.path()],
);
let reply = fire(
&deps,
&Action::Config(Write::Proposal(Settle {
workspace: crate::naming::leaf(ws.path()),
id: "20260906T090000Z-r001".to_owned(),
verdict: Verdict::Accept,
})),
)
.expect("a captured run, however it exited");
let Reply::Outcome(outcome) = reply else {
panic!("a §8.2 verb answers its captured run");
};
assert!(!outcome.ok(), "{outcome:?}");
assert!(outcome.stderr.contains("stale"), "{}", outcome.stderr);
}