use super::*;
use crate::app::Roots;
use crate::projects::join::JoinState;
use crate::test_support::FakeClock;
use std::collections::{HashMap, HashSet};
use std::fs;
use std::sync::{Arc, Mutex};
use tempfile::{TempDir, tempdir};
mod ack;
mod delete;
mod glue;
mod prepare;
mod world;
pub(crate) use world::*;
mod refresh;
mod ops;
fn banner(m: &AppModel, origin: crate::opslog::Origin) -> Option<crate::opslog::OpRow> {
crate::opslog::standings(&m.snap.ops)
.into_iter()
.rev()
.find(|v| v.row.origin == origin && v.standing == crate::opslog::Standing::Live)
.map(|v| v.row)
}
fn row_for(m: &AppModel, ws: &Path) -> Option<crate::projects::join::JoinRow> {
let name = crate::naming::leaf(ws);
m.snap
.join_rows
.iter()
.find(|r| r.workspace.as_deref() == Some(name.as_str()))
.cloned()
}
#[test]
fn construction_fetches_balls_and_builds_the_claimant_join() {
let w = world();
let (_c, m) = model(&w);
let bound = row_for(&m, &w.ws_cobalt).unwrap();
assert_eq!(bound.state, JoinState::Bound, "claimant = workspace name");
assert_eq!(bound.ball_id, "bl-work");
let spare = row_for(&m, &w.ws_spare).unwrap();
assert_eq!(spare.state, JoinState::UnassignedWorkspace);
let cobalt_balls = crate::test_support::chrome::ws_balls(&m, &w.ws_cobalt);
assert_eq!(cobalt_balls.len(), 1);
assert_eq!(cobalt_balls[0].id, "bl-work");
assert_eq!(cobalt_balls[0].badge, None);
assert!(crate::test_support::chrome::ws_balls(&m, &w.ws_spare).is_empty());
assert!(crate::test_support::chrome::ws_balls(&m, Path::new("/nope")).is_empty());
}
#[test]
fn a_bound_ball_gets_one_roster_row_not_two() {
let w = world();
let (_c, m) = model(&w);
let rows = crate::test_support::chrome::ws_balls(&m, &w.ws_cobalt);
assert_eq!(rows.len(), 1, "cobalt holds bl-work");
assert!(
crate::nav::balls::roster(&rows).is_empty(),
"one ball, one row"
);
let ball = crate::nav::balls::bound(&rows, "bl-work").unwrap();
assert_eq!(ball.owner, "cobalt");
assert_eq!(ball.state, JoinState::Bound);
assert!(crate::nav::balls::bound(&rows, "bl-nope").is_none());
}
#[test]
fn identity_and_state_root_are_exposed() {
let w = world();
let (_c, m) = model(&w);
assert_eq!(
m.identity(),
"me",
"no recorded identity ⇒ the $USER fallback"
);
assert_eq!(m.state_root(), w.roots.yog_state.as_path());
}
#[test]
fn an_unlistable_clone_renders_orphaned() {
let w = world();
fs::create_dir_all(w.roots.balls_clones.join("%2Fproj%2Fgone")).unwrap();
let bad = PathBuf::from("/proj/gone");
w.fail.lock().unwrap().insert(bad.clone());
let (_c, m) = model(&w);
let orphan = m
.snap
.join_rows
.iter()
.find(|r| r.project == crate::naming::leaf(&bad))
.expect("the unlistable clone has a row");
assert_eq!(orphan.state, JoinState::OrphanedProject);
}
pub(super) fn empty_model() -> (TempDir, AppModel) {
let root = tempdir().unwrap();
let roots = Roots {
yog_data: root.path().join("yog"),
litany_data: root.path().join("litany"),
yog_state: root.path().join("state"),
balls_clones: root.path().join("clones"),
home: root.path().join("home"),
world: crate::test_support::no_world(),
};
let empty = FakeBl {
live: Arc::new(Mutex::new(HashMap::new())),
closed: Arc::new(Mutex::new(HashMap::new())),
fail: Arc::new(Mutex::new(HashSet::new())),
};
let (m, _deriver) = AppModel::boot(roots, FakeClock::new().arc(), Box::new(empty), None);
(root, m)
}
#[test]
fn an_empty_world_answers_nothing_and_names_no_identity() {
let (_root, m) = empty_model();
assert!(m.snap.workspaces.is_empty());
assert!(m.snap.ops.is_empty());
assert_eq!(m.identity(), "", "no recorded identity, no user ⇒ empty");
}