use std::path::Path;
use super::{BALL, CONV, claimed_project, layout, snap, trail};
use crate::git_tree::tests::git::run_git;
use crate::science::{Attempt, Outcome, project};
use crate::workdiff::{Change, tests::Project};
struct Fan {
_dir: tempfile::TempDir,
project: Project,
xdg: balls::layout::Xdg,
balls_root: std::path::PathBuf,
ws: std::path::PathBuf,
obligation: crate::fan::Obligation,
candidates: Vec<crate::fan::Candidate>,
}
impl Fan {
fn open(n: usize) -> Fan {
let project = claimed_project();
let dir = tempfile::tempdir().unwrap();
let (xdg, balls_root) = layout(dir.path());
let ws = dir.path().join("ws");
std::fs::create_dir_all(&ws).unwrap();
let obligation = crate::fan::Obligation {
project: "proj".to_owned(),
ball: Some(BALL.to_owned()),
};
let candidates = crate::fan::open(&obligation, &project.path, &xdg, n).unwrap();
Fan {
_dir: dir,
project,
xdg,
balls_root,
ws,
obligation,
candidates,
}
}
fn work(&self, i: usize, file: &str) {
let worktree = &self.candidates[i].worktree;
std::fs::write(worktree.join(file), "fn f() {}\n").unwrap();
run_git(worktree, &["add", file]);
run_git(worktree, &["config", "user.email", "t@t.local"]);
run_git(worktree, &["config", "user.name", "Tester"]);
run_git(worktree, &["config", "commit.gpgsign", "false"]);
run_git(worktree, &["commit", "-q", "-m", "candidate work"]);
}
fn rows(&self) -> Vec<Attempt> {
let bindings: Vec<(&str, &Path)> = self
.candidates
.iter()
.map(|c| (CONV, c.worktree.as_path()))
.collect();
let entries = trail(&self.ws, &self.project.path, &bindings);
let snap = snap(&self.ws, &self.project.path, vec![], vec![]);
project(&snap, &self.ws, &entries, &self.xdg, &self.balls_root)
}
fn deliver(&self, i: usize) -> Option<String> {
crate::fan::deliver(
&self.obligation,
&self.project.path,
&self.xdg,
&self.candidates[i].handle,
"take it",
)
.unwrap()
.commit
}
}
#[test]
fn undelivered_and_unopposed_is_pending() {
let fan = Fan::open(2);
fan.work(0, "won.rs");
for row in fan.rows() {
assert_eq!(row.outcome, Outcome::Pending, "{row:?}");
}
}
#[test]
fn a_delivery_accepts_one_and_rejects_its_sibling() {
let fan = Fan::open(2);
fan.work(0, "won.rs");
let commit = fan.deliver(0).expect("a worked candidate lands a commit");
let rows = fan.rows();
assert_eq!(
rows[0].outcome,
Outcome::Pending,
"the claim: {:?}",
rows[0]
);
assert_eq!(rows[1].outcome, Outcome::Accepted { commit });
assert_eq!(
rows[2].outcome,
Outcome::Rejected {
by: Some(fan.candidates[0].handle.clone())
}
);
}
#[test]
fn incorporating_the_advanced_target_reads_as_reworked() {
let fan = Fan::open(2);
fan.work(0, "won.rs");
fan.work(1, "lost.rs");
fan.deliver(0).expect("the first candidate lands");
let stale = &fan.candidates[1].worktree;
assert!(matches!(
fan.rows()[2].outcome,
Outcome::Rejected { by: Some(_) }
));
run_git(
stale,
&[
"merge",
"--no-edit",
"-q",
&balls::delivery_path::work_branch(BALL),
],
);
assert_eq!(fan.rows()[2].outcome, Outcome::Reworked);
}
#[test]
fn a_discarded_attempt_is_rejected_by_nobody() {
let fan = Fan::open(1);
crate::fan::discard(
&fan.obligation,
&fan.project.path,
&fan.xdg,
&fan.candidates[0].handle,
)
.unwrap();
let rows = fan.rows();
assert!(
matches!(rows[1].diff.change, Change::Absent { .. }),
"{:?}",
rows[1]
);
assert_eq!(rows[1].outcome, Outcome::Rejected { by: None });
}
#[test]
fn the_claim_attempt_reads_its_own_delivery() {
let fan = Fan::open(1);
fan.project.commit("closed.rs", "fn closed() {}\n");
run_git(
&fan.project.path,
&[
"commit",
"--amend",
"-q",
"-m",
&balls::delivery_path::subject("ship it", BALL),
],
);
let rows = fan.rows();
let Outcome::Accepted { commit } = &rows[0].outcome else {
panic!("the claim reads accepted: {:?}", rows[0]);
};
assert_eq!(Some(commit.clone()), rows[0].diff.delivered);
}