mod derive;
mod confirm;
mod follow;
mod governing;
use crate::app::Snapshot;
use super::*;
use crate::boundary::tests::{agent, bound_row, snapshot};
use crate::cli_outbound::Cli;
use crate::git_tree::AgentState;
use crate::opslog::{OpRow, Origin};
use crate::projects::join::JoinState;
use std::path::{Path, PathBuf};
use std::sync::Arc;
pub(super) fn ui() -> UiState {
UiState::open(PathBuf::from("/nonexistent/ui.json"))
}
pub(super) fn ws() -> PathBuf {
PathBuf::from("/names/alba")
}
pub(super) fn deps(snap: Snapshot) -> Deps {
Deps {
litany: Cli::new("/no/such/litany"),
bl: Cli::new("/no/such/bl"),
state_root: PathBuf::from("/nonexistent/state"),
yog_binary: PathBuf::from("/no/such/yog"),
world: crate::test_support::no_world(),
home: PathBuf::from("/home/x"),
yog_data_root: PathBuf::from("/data"),
snapshot: Arc::new(snap),
caller: crate::boundary::dispatch::Caller::default(),
}
}
#[test]
fn the_four_query_families_answer_from_one_snapshot() {
let project = PathBuf::from("/proj");
let mut snap = snapshot(
&ws(),
"alba",
vec![agent("c-1", AgentState::Live, 100)],
vec![bound_row(&project, "bl-1", &ws(), "alba")],
);
let op = |ts: &str| OpRow {
ts: ts.into(),
argv: "x".into(),
cwd: String::new(),
exit: 0,
stdout: String::new(),
stderr: String::new(),
origin: Origin::World,
client: crate::registry::Client::default(),
};
snap.ops = vec![op("1"), op("2"), op("3")];
let join_rows = snap.join_rows.clone();
let board_snap = snap.clone();
let d = deps(snap);
let Ok(Reply::Workspaces(view)) = answer(&Query::Workspaces, &d, &ui(), 200) else {
panic!("workspaces answers workspaces");
};
assert_eq!(view.rows.len(), 1);
assert_eq!(view.rows[0].agents, 1);
assert!(view.rows[0].running);
assert_eq!(view.stale.as_deref(), Some("derivation 200 s behind"));
assert_eq!(view.growth, None, "a quiet world says nothing");
let Ok(Reply::Conversations(rows)) = answer(
&Query::Conversations {
workspace: crate::naming::leaf(&ws()),
},
&d,
&ui(),
200,
) else {
panic!("conversations answers conversations");
};
assert_eq!(rows.len(), 1);
let Ok(Reply::Balls(rows)) = answer(&Query::Balls, &d, &ui(), 200) else {
panic!("balls answers balls");
};
assert_eq!(rows, join_rows);
let Ok(Reply::Board(board)) = answer(&Query::Board, &d, &ui(), 200) else {
panic!("board answers board");
};
assert_eq!(board, crate::board::build(&board_snap, &ui(), 0));
let Ok(Reply::Ops(rows)) = answer(&Query::Ops { max: 2 }, &d, &ui(), 200) else {
panic!("ops answers ops");
};
assert_eq!(
rows.iter().map(|r| r.row.ts.as_str()).collect::<Vec<_>>(),
["2", "3"],
"the tail, newest last"
);
let Ok(Reply::Ops(all)) = answer(&Query::Ops { max: 99 }, &d, &ui(), 200) else {
panic!();
};
assert_eq!(all.len(), 3, "a max past the tail takes everything");
}
#[test]
fn help_is_answered_without_reading_the_world() {
let d = deps(snapshot(&ws(), "alba", vec![], vec![]));
let all = answer(&Query::Help { verb: None }, &d, &ui(), 0);
let Ok(Reply::Help(rows)) = all else {
panic!("not help");
};
assert_eq!(rows.len(), crate::boundary::help::table().len());
let one = answer(
&Query::Help {
verb: Some("ack".to_owned()),
},
&d,
&ui(),
0,
);
let Ok(Reply::Help(rows)) = one else {
panic!("not help");
};
assert_eq!(rows.len(), 1);
assert_eq!(rows.first().map(|row| row.verb), Some("ack"));
}
#[test]
fn search_is_answered_here_too_because_every_seat_that_reaches_this_is_off_frame() {
let mut snap = snapshot(&ws(), "alba", vec![], vec![]);
snap.projects.push(PathBuf::from("/proj"));
snap.balls_by_project.insert(
PathBuf::from("/proj"),
vec![crate::projects::balls::Ball {
id: "bl-1f2a".to_owned(),
title: "the kraken".to_owned(),
body: String::new(),
claimant: None,
blockers: vec![],
parent: None,
priority: 3,
tags: vec![],
created: None,
updated: None,
root_commit: None,
}],
);
let d = deps(snap);
let ask = |text: &str| {
let Ok(Reply::Search(found)) = answer(
&Query::Search {
text: text.to_owned(),
},
&d,
&ui(),
200,
) else {
panic!("search answers search");
};
found
};
let found = ask("kraken");
assert_eq!(found.hits.len(), 1);
assert_eq!(
found.hits[0].at,
crate::search::Address::Ball {
project: "proj".to_owned(),
id: "bl-1f2a".to_owned(),
}
);
assert_eq!(ask(""), crate::search::Found::default());
}
#[test]
fn the_governing_read_refuses_and_the_routing_reads_name_no_world() {
let d = deps(snapshot(
&ws(),
"alba",
vec![agent("c-1", AgentState::Live, 100)],
vec![],
));
let governing = answer(
&Query::Governing {
workspace: "alba".to_owned(),
agent: "c-1".to_owned(),
at: None,
},
&d,
&ui(),
200,
);
assert!(governing.is_err(), "got {governing:?}");
let held = answer(&Query::Invocations, &d, &ui(), 200)
.expect_err("an in-world caller carries no certificate to be addressed by");
assert!(held.contains("no client identity"), "{held}");
let capture = answer(
&Query::Capture {
invocation: "inv-404".to_owned(),
},
&d,
&ui(),
200,
)
.expect_err("a handle nobody posted");
assert!(
capture.contains("inv-404"),
"the handle is named: {capture}"
);
}