mod rollup;
mod fixture;
use fixture::{NOW, WS_A, WS_B, agent, ball, blocks, join, ui_doc, world};
use super::{Board, Column, build, column, descendants};
use crate::app::Snapshot;
use crate::projects::balls::Status;
use crate::projects::join::{JoinRow, JoinState};
use std::collections::HashMap;
use std::path::Path;
#[test]
fn the_four_columns_are_the_ladder_crossed_with_the_close_gate() {
assert_eq!(column(Status::Ready, false), Column::Ready);
assert_eq!(column(Status::Ready, true), Column::Gated);
assert_eq!(column(Status::Blocked, false), Column::Blocked);
assert_eq!(column(Status::Blocked, true), Column::Blocked);
assert_eq!(column(Status::Claimed, false), Column::Claimed);
assert_eq!(
column(Status::Claimed, true),
Column::Claimed,
"a drone holds it and is working; the gate renders on the row, not as its bucket"
);
for status in Status::ALL {
assert_eq!(
column(status, false).word(),
status.word(),
"the board speaks balls' own vocabulary"
);
}
assert_eq!(Column::ALL.len(), 4);
assert_eq!(Column::Gated.word(), "gated");
}
#[test]
fn every_ball_lands_in_its_column_and_a_gate_names_what_mints_it() {
let w = world(
vec![
ball("bl-ready", None, vec![]),
ball("bl-gated", None, vec![blocks("bl-claim", "close")]),
ball("bl-blockd", None, vec![blocks("bl-claim", "claim")]),
ball("bl-claim", Some("alfa"), vec![]),
],
vec![
join("bl-ready", JoinState::ReadyStartable, None, None),
join("bl-gated", JoinState::ReadyStartable, None, None),
join("bl-blockd", JoinState::Blocked, None, None),
join("bl-claim", JoinState::Bound, Some(WS_A), Some("alfa")),
],
vec![(WS_A, vec![agent("conv1", Some("bl-claim"), Some("Cobalt"))])],
);
let board = w.board();
let word = |id: &str| {
board
.rows
.iter()
.find(|r| r.id == id)
.map(|r| r.column.word())
};
assert_eq!(word("bl-ready"), Some("ready"));
assert_eq!(word("bl-gated"), Some("gated"));
assert_eq!(word("bl-blockd"), Some("blocked"));
assert_eq!(word("bl-claim"), Some("claimed"));
for column in Column::ALL {
assert_eq!(board.count(column), 1, "{column:?}");
assert_eq!(board.in_column(column).len(), 1);
}
let gated = board.rows.iter().find(|r| r.id == "bl-gated").unwrap();
assert_eq!(gated.gates.len(), 1);
assert_eq!(gated.gates[0].id, "bl-claim");
assert_eq!(
gated.gates[0].title, "bl-claim title",
"what mints the gate"
);
let claimed = board.rows.iter().find(|r| r.id == "bl-claim").unwrap();
assert_eq!(claimed.drones.len(), 1);
assert_eq!(claimed.drones[0].root_id, "conv1");
assert_eq!(claimed.drones[0].name, "Cobalt");
assert_eq!(claimed.state, JoinState::Bound);
assert_eq!(claimed.spend.as_ref().unwrap().cost.unwrap().usd(), "$1.00");
assert!(
board
.rows
.iter()
.find(|r| r.id == "bl-ready")
.unwrap()
.spend
.is_none(),
"an unclaimed ball is bound to no workspace and has spent nothing"
);
}
#[test]
fn a_gate_onto_a_closed_ball_is_no_gate_at_all() {
let w = world(
vec![ball("bl-x", None, vec![blocks("bl-gone", "close")])],
vec![join("bl-x", JoinState::ReadyStartable, None, None)],
vec![],
);
let row = w.board().rows.into_iter().next().unwrap();
assert!(row.gates.is_empty());
assert_eq!(row.column, Column::Ready);
}
#[test]
fn the_board_is_the_join_filtered_to_live_balls_and_ordered() {
let mut low = ball("bl-aaa", None, vec![]);
low.priority = 1;
let w = world(
vec![low, ball("bl-zzz", None, vec![])],
vec![
join("bl-zzz", JoinState::ReadyStartable, None, None),
join("bl-aaa", JoinState::ReadyStartable, None, None),
join("bl-dead", JoinState::Delivered, Some(WS_A), Some("alfa")),
JoinRow {
project: String::new(),
ball_id: String::new(),
state: JoinState::UnassignedWorkspace,
workspace: Some(crate::naming::leaf(Path::new(WS_A))),
claimant: None,
title: None,
},
],
vec![],
);
let ids: Vec<String> = w.board().rows.into_iter().map(|r| r.id).collect();
assert_eq!(ids, vec!["bl-aaa", "bl-zzz"], "priority first, then id");
}
#[test]
fn an_empty_world_is_an_empty_board() {
let snap = Snapshot::empty(0);
assert_eq!(build(&snap, &ui_doc("{}"), NOW), Board::default());
assert_eq!(
super::stamped_roots(&HashMap::new(), Path::new(WS_A), "bl-x"),
Vec::<String>::new()
);
}
#[test]
fn a_parent_cycle_terminates() {
let mut a = ball("bl-a", None, vec![]);
a.parent = Some("bl-b".to_owned());
let mut b = ball("bl-b", None, vec![]);
b.parent = Some("bl-a".to_owned());
let by_id = HashMap::from([("bl-a", &a), ("bl-b", &b)]);
assert_eq!(descendants("bl-a", &by_id), vec!["bl-a", "bl-b"]);
}