use super::*;
use crate::api::Polled;
use crate::live::{Ask, Probe};
fn next_ask(s: &mut State) -> (Vec<Ask>, u64) {
let timer = timers(s).into_iter().find_map(|cmd| match cmd {
Cmd::Timer(Timer::Watch(id), ms) => Some((id, ms)),
_ => None,
});
let (id, ms) = timer.expect("an ask scheduled");
let cmds = update(s, Msg::Timer(Timer::Watch(id)));
match &cmds[..] {
[Cmd::Api(Api::Poll { asks, .. })] => (asks.clone(), ms),
_ => panic!("{cmds:?}"),
}
}
fn changed(etag: &str, body: &str) -> Result<Polled, ApiError> {
Ok(Polled::Changed {
etag: etag.into(),
body: body.into(),
})
}
#[test]
fn polling_asks_with_etags_and_marks_new_threads() {
let (mut s, pr) = diff_state(120);
s.viewer = Some("octocat".into());
let old = thread("old", Some(1), false, false);
diff_msg(&mut s, &pr, DiffMsg::ThreadsLoaded(Ok(vec![old.clone()])));
let (asks, wait) = next_ask(&mut s);
assert_eq!(wait, crate::live::FIRST_MS);
let probes: Vec<&Probe> = asks.iter().map(|a| &a.probe).collect();
assert_eq!(probes, [&Probe::Pull, &Probe::ReviewComments]);
assert!(asks.iter().all(|a| a.etag.is_none()));
let first = vec![
(Probe::Pull, changed("\"p1\"", r#"{"head":{"sha":"abc"}}"#)),
(Probe::ReviewComments, changed("\"c1\"", "[]")),
];
let cmds = update(&mut s, Msg::Polled(pr.clone(), first));
assert!(cmds.is_empty(), "{cmds:?}");
let (asks, wait) = next_ask(&mut s);
assert!(wait > crate::live::FIRST_MS);
let etag = |probe: &Probe| asks.iter().find(|a| a.probe == *probe)?.etag.clone();
assert_eq!(etag(&Probe::Pull).as_deref(), Some("\"p1\""));
assert_eq!(etag(&Probe::ReviewComments).as_deref(), Some("\"c1\""));
assert!(
asks.iter()
.any(|a| a.probe == Probe::CheckRuns("abc".into()))
);
let answers = vec![
(Probe::Pull, Ok(Polled::Same)),
(Probe::ReviewComments, changed("\"c2\"", "[]")),
];
let cmds = update(&mut s, Msg::Polled(pr.clone(), answers));
assert!(
cmds.contains(&Cmd::Api(Api::FetchThreads(pr.clone()))),
"{cmds:?}"
);
let new = thread("new", Some(14), false, false);
diff_msg(&mut s, &pr, DiffMsg::ThreadsLoaded(Ok(vec![old, new])));
let anns = s.diffs[&DiffOf::Pr(pr.clone())].doc.annotations();
let marked = |id: &str| {
let ann = anns
.iter()
.find(|a| a.key == AnnotationKey::Thread(NodeId::new(id)));
ann.is_some_and(|a| a.new && a.comments.iter().all(|c| c.new))
};
assert!(marked("new") && !marked("old"));
says(&s, "New on o/r#7: 1 thread");
}
#[test]
fn a_comment_before_the_first_answer_is_fetched() {
let (mut s, pr) = diff_state(120);
diff_msg(
&mut s,
&pr,
DiffMsg::ThreadsLoaded(Ok(vec![thread("old", Some(1), false, false)])),
);
let _ = next_ask(&mut s);
let seen = vec![(
Probe::ReviewComments,
changed("\"c1\"", r#"[{"node_id":"old-c"}]"#),
)];
assert!(update(&mut s, Msg::Polled(pr.clone(), seen)).is_empty());
let (mut s, pr) = diff_state(120);
diff_msg(
&mut s,
&pr,
DiffMsg::ThreadsLoaded(Ok(vec![thread("old", Some(1), false, false)])),
);
let _ = next_ask(&mut s);
let came = vec![(
Probe::ReviewComments,
changed("\"c1\"", r#"[{"node_id":"new-c"}]"#),
)];
let cmds = update(&mut s, Msg::Polled(pr.clone(), came));
assert!(cmds.contains(&Cmd::Api(Api::FetchThreads(pr))), "{cmds:?}");
}
#[test]
fn nothing_else_is_polled() {
let (mut s, _) = diff_state(120);
next_ask(&mut s);
press(&mut s, "h");
let scheduled = timers(&mut s)
.into_iter()
.any(|cmd| matches!(cmd, Cmd::Timer(Timer::Watch(_), _)));
assert!(!scheduled && s.watch.is_none());
}