use super::*;
use crate::actions::{close_enabled, unclaim_enabled};
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 world;
pub(crate) use world::*;
mod internal;
mod refresh;
mod ops;
mod ack;
mod convball;
mod delete;
mod prepare;
mod glue;
mod line;
#[test]
fn construction_fetches_balls_and_builds_the_claimant_join() {
let w = world();
let (_c, m) = model(&w);
let bound = m.row_for(&w.ws_cobalt).unwrap();
assert_eq!(bound.state, JoinState::Bound, "claimant = workspace name");
assert_eq!(bound.ball_id, "bl-work");
let spare = m.row_for(&w.ws_spare).unwrap();
assert_eq!(spare.state, JoinState::UnassignedWorkspace);
let cobalt_balls = m.ws_balls(&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!(m.ws_balls(&w.ws_spare).is_empty());
assert!(m.ws_balls(Path::new("/nope")).is_empty());
}
#[test]
fn focused_join_targets_the_focused_workspace_ball() {
let w = world();
let (_c, mut m) = model(&w);
m.focus_workspace(&w.ws_cobalt);
let row = m.focused_join().unwrap();
assert_eq!(row.ball_id, "bl-work");
assert!(close_enabled(row.state), "Bound ⇒ Close offered");
assert!(unclaim_enabled(row.state));
m.focus_workspace(&w.ws_spare);
assert!(
m.focused_join().is_none(),
"a workspace with no ball focuses no ball"
);
let spare = m.row_for(&w.ws_spare).unwrap().state;
assert_eq!(spare, JoinState::UnassignedWorkspace);
assert!(!close_enabled(spare), "unassigned workspace ⇒ no Close");
assert!(!unclaim_enabled(spare));
}
#[test]
fn a_bound_ball_gets_one_roster_row_not_two() {
let w = world();
let (_c, m) = model(&w);
assert_eq!(m.ws_balls(&w.ws_cobalt).len(), 1, "cobalt holds bl-work");
assert_eq!(m.resumable().len(), 1, "▶ Continue renders it in full");
assert!(
m.roster_ball_rows(&w.ws_cobalt).is_empty(),
"one ball, one row"
);
let ball = m.bound_ball(&w.ws_cobalt, "bl-work").unwrap();
assert_eq!(ball.owner, "cobalt");
assert_eq!(ball.state, JoinState::Bound);
assert!(m.bound_ball(&w.ws_cobalt, "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 == 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"),
lernie_data: root.path().join("lernie"),
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, None, FakeClock::new().arc(), Box::new(empty), None);
(root, m)
}
#[test]
fn focused_join_is_none_without_a_focused_workspace() {
let (_root, m) = empty_model();
assert!(m.focused_join().is_none());
assert!(m.ops_rows().is_empty());
assert_eq!(m.identity(), "", "no recorded identity, no user ⇒ empty");
}