use super::{banner, model, world};
use crate::AppModel;
use crate::opslog::{self, Origin};
fn ack(m: &crate::app::tests::Rig) {
let deps = m.boundary_deps(
&crate::cli_outbound::Cli::new("litany"),
&crate::cli_outbound::Cli::new("bl"),
);
let mut ui = crate::ui_state::UiState::open(m.ui_json_path());
crate::boundary::dispatch::dispatch(&deps, &mut ui, "TS", &crate::boundary::Action::Ack)
.expect("the ack lands");
announce(m);
}
fn clear(m: &crate::app::tests::Rig) {
let deps = m.boundary_deps(
&crate::cli_outbound::Cli::new("litany"),
&crate::cli_outbound::Cli::new("bl"),
);
let mut ui = crate::ui_state::UiState::open(m.ui_json_path());
crate::boundary::dispatch::dispatch(&deps, &mut ui, "TS", &crate::boundary::Action::ClearTrail)
.expect("the clear lands");
announce(m);
}
fn announce(m: &crate::app::tests::Rig) {
m.deriver
.dirty_handle()
.mark_all([(m.state_root().to_path_buf(), crate::watch::Mark::Watch)]);
}
fn fail(m: &AppModel, origin: Origin) {
opslog::append(
m.state_root(),
&opslog::OpEntry::synthetic_failure(
"TS".into(),
vec!["litany".into(), "prime".into()],
"/proj".into(),
"unrecognized subcommand\n".into(),
origin,
),
)
.unwrap();
}
fn drift(m: &AppModel) {
opslog::append(
m.state_root(),
&opslog::OpEntry::drift("TS".into(), "unannounced", "/state".into(), "/root".into()),
)
.unwrap();
}
#[test]
fn an_ack_quiets_the_banners_and_the_chip_without_hiding_a_row() {
let w = world();
let (_c, mut m) = model(&w);
fail(&m, Origin::Balls);
fail(&m, Origin::Conversation);
drift(&m);
m.after_litany_verb();
m.tick();
assert!(banner(&m, Origin::Balls).is_some());
assert!(banner(&m, Origin::Conversation).is_some());
assert_eq!(m.activity().errors, 2);
assert_eq!(m.activity().drifts, 1);
assert!(m.activity().alarming(), "and the pane offers its Dismiss");
ack(&m);
m.tick();
assert!(
banner(&m, Origin::Balls).is_none(),
"every surface's banner is quiet, not just the one it was clicked on"
);
assert!(banner(&m, Origin::Conversation).is_none());
assert_eq!(m.activity().errors, 0);
assert_eq!(
m.activity().drifts,
0,
"drift is an alarm too, and is acknowledged with the rest (§7.2)"
);
assert!(
!m.activity().alarming(),
"so the Dismiss control retires with them"
);
assert_eq!(
m.activity().total,
4,
"three rows plus the ack — the chip's op count is the trail's"
);
let rows = &m.snap.ops;
assert_eq!(rows.len(), 4, "ack quiets alarms; it hides no history");
assert!(
rows.iter().filter(|r| r.failed()).count() == 2,
"the acked failures are still failures in the expanded trail"
);
let ack_row = rows.last().unwrap();
assert_eq!(ack_row.argv, "yog-step ack-failures");
assert_eq!(ack_row.origin, Origin::World);
assert!(!ack_row.failed(), "and the ack row is not itself an alarm");
}
#[test]
fn a_new_failure_after_an_ack_banners_again() {
let w = world();
let (_c, mut m) = model(&w);
fail(&m, Origin::Balls);
ack(&m);
m.tick();
assert!(banner(&m, Origin::Balls).is_none());
fail(&m, Origin::Conversation);
m.after_litany_verb();
m.tick();
assert_eq!(
banner(&m, Origin::Conversation).unwrap().argv,
"litany prime"
);
assert!(
banner(&m, Origin::Balls).is_none(),
"and the acked one stays acknowledged — bl-48f8's per-surface rule holds"
);
assert_eq!(m.activity().errors, 1);
}
#[test]
fn clear_leaves_a_one_row_trail_whose_row_is_the_clear() {
let w = world();
let (_c, mut m) = model(&w);
fail(&m, Origin::Balls);
drift(&m);
m.after_litany_verb();
m.tick();
assert_eq!(m.snap.ops.len(), 2);
clear(&m);
m.tick();
let rows = &m.snap.ops;
assert_eq!(rows.len(), 1);
let row = rows.first().unwrap();
assert_eq!(row.argv, "yog-step clear-trail");
assert_eq!(row.origin, Origin::World);
assert!(!row.failed());
assert_eq!(row.exit_label(), "exit 0");
assert!(banner(&m, Origin::Balls).is_none());
assert!(!m.activity().alarming());
assert_eq!(m.activity().total, 1, "the chip counts the new trail only");
assert_eq!(
opslog::tail(m.state_root(), 64).len(),
1,
"and the file itself is the new trail, not a longer one filtered down"
);
}