tui: prioritize live pending pods in picker
This commit is contained in:
parent
08397f3f3b
commit
56bb46634c
|
|
@ -360,4 +360,37 @@ mod tests {
|
||||||
fn picker_title_names_pods_not_sessions() {
|
fn picker_title_names_pods_not_sessions() {
|
||||||
assert_eq!(picker_title(), "resume pod pick a pod");
|
assert_eq!(picker_title(), "resume pod pick a pod");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn picker_row_shows_live_pending_preview_and_runtime_segment_id() {
|
||||||
|
let segment_id = session_store::new_segment_id();
|
||||||
|
let entry = PodList::from_sources(
|
||||||
|
PodVisibilitySource::ResumePicker,
|
||||||
|
vec![],
|
||||||
|
vec![crate::pod_list::LivePodInfo {
|
||||||
|
pod_name: "pending".to_string(),
|
||||||
|
socket_path: PathBuf::from("/tmp/pending.sock"),
|
||||||
|
status: Some(protocol::PodStatus::Idle),
|
||||||
|
reachable: true,
|
||||||
|
segment_id: Some(segment_id),
|
||||||
|
summary: crate::pod_list::PodEntrySummary::default(),
|
||||||
|
}],
|
||||||
|
None,
|
||||||
|
10,
|
||||||
|
)
|
||||||
|
.entries
|
||||||
|
.into_iter()
|
||||||
|
.next()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let text = row_line(&entry, false)
|
||||||
|
.spans
|
||||||
|
.iter()
|
||||||
|
.map(|span| span.content.as_ref())
|
||||||
|
.collect::<String>();
|
||||||
|
|
||||||
|
assert!(text.contains("[live]"));
|
||||||
|
assert!(text.contains("[live, pending segment]"));
|
||||||
|
assert!(text.contains(&format!("g:{}", short_id(segment_id))));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,9 +48,9 @@ impl PodList {
|
||||||
entry.finalize();
|
entry.finalize();
|
||||||
}
|
}
|
||||||
entries.sort_by(|a, b| {
|
entries.sort_by(|a, b| {
|
||||||
b.summary
|
b.has_reachable_live()
|
||||||
.updated_at
|
.cmp(&a.has_reachable_live())
|
||||||
.cmp(&a.summary.updated_at)
|
.then_with(|| b.summary.updated_at.cmp(&a.summary.updated_at))
|
||||||
.then_with(|| a.name.cmp(&b.name))
|
.then_with(|| a.name.cmp(&b.name))
|
||||||
});
|
});
|
||||||
entries.truncate(max_entries);
|
entries.truncate(max_entries);
|
||||||
|
|
@ -164,10 +164,27 @@ impl PodListEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finalize(&mut self) {
|
fn finalize(&mut self) {
|
||||||
|
self.fill_live_pending_preview();
|
||||||
self.diagnostics = build_diagnostics(self);
|
self.diagnostics = build_diagnostics(self);
|
||||||
self.actions = build_actions(self);
|
self.actions = build_actions(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn has_reachable_live(&self) -> bool {
|
||||||
|
self.live.as_ref().is_some_and(|live| live.reachable)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fill_live_pending_preview(&mut self) {
|
||||||
|
if !self.has_reachable_live() || self.summary.updated_at != 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let preview_is_pending = self.summary.preview.as_deref() == Some("[pending segment]");
|
||||||
|
let preview_is_incomplete = self.summary.preview.is_none() || preview_is_pending;
|
||||||
|
if preview_is_incomplete && (self.summary.active_segment_id.is_some() || preview_is_pending)
|
||||||
|
{
|
||||||
|
self.summary.preview = Some("[live, pending segment]".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn attach_socket_path(&self) -> Option<&Path> {
|
pub(crate) fn attach_socket_path(&self) -> Option<&Path> {
|
||||||
self.live
|
self.live
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|
@ -593,6 +610,98 @@ mod tests {
|
||||||
assert_eq!(entries[1].name, "older");
|
assert_eq!(entries[1].name, "older");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reachable_live_rows_sort_before_stopped_rows_before_truncation() {
|
||||||
|
let stopped = (0..10)
|
||||||
|
.map(|index| stopped_info_with_updated_at(&format!("stopped-{index}"), 1_000 - index))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let live = live_info_with_updated_at("live-pending", PodStatus::Idle, 0);
|
||||||
|
|
||||||
|
let entries = PodList::from_sources(SOURCE, stopped, vec![live], None, 10).entries;
|
||||||
|
|
||||||
|
assert_eq!(entries.len(), 10);
|
||||||
|
assert_eq!(entries[0].name, "live-pending");
|
||||||
|
assert!(entries.iter().all(|entry| entry.name != "stopped-9"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reachable_live_sort_does_not_promote_unreachable_registry_allocations() {
|
||||||
|
let mut unreachable = live_info_with_updated_at("unreachable", PodStatus::Idle, 0);
|
||||||
|
unreachable.reachable = false;
|
||||||
|
unreachable.status = None;
|
||||||
|
|
||||||
|
let entries = PodList::from_sources(
|
||||||
|
SOURCE,
|
||||||
|
vec![stopped_info_with_updated_at("stopped", 100)],
|
||||||
|
vec![unreachable],
|
||||||
|
None,
|
||||||
|
10,
|
||||||
|
)
|
||||||
|
.entries;
|
||||||
|
|
||||||
|
assert_eq!(entries[0].name, "stopped");
|
||||||
|
assert_eq!(entries[1].name, "unreachable");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn live_pending_with_runtime_segment_is_attach_only_and_gets_pending_preview() {
|
||||||
|
let session_id = new_session_id();
|
||||||
|
let runtime_segment_id = new_segment_id();
|
||||||
|
let entry = single_entry(PodList::from_sources(
|
||||||
|
SOURCE,
|
||||||
|
vec![pending_metadata_info("pending", session_id)],
|
||||||
|
vec![live_info_with_segment(
|
||||||
|
"pending",
|
||||||
|
PodStatus::Idle,
|
||||||
|
runtime_segment_id,
|
||||||
|
)],
|
||||||
|
None,
|
||||||
|
10,
|
||||||
|
));
|
||||||
|
|
||||||
|
assert_eq!(entry.name, "pending");
|
||||||
|
assert_eq!(entry.summary.active_session_id, Some(session_id));
|
||||||
|
assert_eq!(entry.summary.active_segment_id, Some(runtime_segment_id));
|
||||||
|
assert_eq!(
|
||||||
|
entry.summary.preview.as_deref(),
|
||||||
|
Some("[live, pending segment]")
|
||||||
|
);
|
||||||
|
assert!(entry.actions.can_open);
|
||||||
|
assert!(!entry.actions.can_restore);
|
||||||
|
assert_eq!(
|
||||||
|
entry.attach_socket_path(),
|
||||||
|
Some(Path::new("/tmp/pending.sock"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn live_only_runtime_segment_is_attach_only_and_not_restorable() {
|
||||||
|
let runtime_segment_id = new_segment_id();
|
||||||
|
let entry = single_entry(PodList::from_sources(
|
||||||
|
SOURCE,
|
||||||
|
vec![],
|
||||||
|
vec![live_info_with_segment(
|
||||||
|
"runtime-only",
|
||||||
|
PodStatus::Idle,
|
||||||
|
runtime_segment_id,
|
||||||
|
)],
|
||||||
|
None,
|
||||||
|
10,
|
||||||
|
));
|
||||||
|
|
||||||
|
assert_eq!(entry.summary.active_segment_id, Some(runtime_segment_id));
|
||||||
|
assert_eq!(
|
||||||
|
entry.summary.preview.as_deref(),
|
||||||
|
Some("[live, pending segment]")
|
||||||
|
);
|
||||||
|
assert!(entry.actions.can_open);
|
||||||
|
assert!(!entry.actions.can_restore);
|
||||||
|
assert_eq!(
|
||||||
|
entry.attach_socket_path(),
|
||||||
|
Some(Path::new("/tmp/runtime-only.sock"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn stored_only_row_can_restore_and_open_but_not_direct_send() {
|
fn stored_only_row_can_restore_and_open_but_not_direct_send() {
|
||||||
let dir = tempdir().unwrap();
|
let dir = tempdir().unwrap();
|
||||||
|
|
@ -820,10 +929,42 @@ mod tests {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pending_metadata_info(pod_name: &str, session_id: SessionId) -> StoredPodInfo {
|
||||||
|
StoredPodInfo {
|
||||||
|
pod_name: pod_name.to_string(),
|
||||||
|
metadata_state: StoredMetadataState::Present,
|
||||||
|
active_session_id: Some(session_id),
|
||||||
|
active_segment_id: None,
|
||||||
|
updated_at: 0,
|
||||||
|
preview: Some("[pending segment]".to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stopped_info_with_updated_at(pod_name: &str, updated_at: u64) -> StoredPodInfo {
|
||||||
|
StoredPodInfo {
|
||||||
|
pod_name: pod_name.to_string(),
|
||||||
|
metadata_state: StoredMetadataState::Present,
|
||||||
|
active_session_id: None,
|
||||||
|
active_segment_id: None,
|
||||||
|
updated_at,
|
||||||
|
preview: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn live_info(pod_name: &str, status: PodStatus) -> LivePodInfo {
|
fn live_info(pod_name: &str, status: PodStatus) -> LivePodInfo {
|
||||||
live_info_with_updated_at(pod_name, status, 0)
|
live_info_with_updated_at(pod_name, status, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn live_info_with_segment(
|
||||||
|
pod_name: &str,
|
||||||
|
status: PodStatus,
|
||||||
|
segment_id: SegmentId,
|
||||||
|
) -> LivePodInfo {
|
||||||
|
let mut info = live_info(pod_name, status);
|
||||||
|
info.segment_id = Some(segment_id);
|
||||||
|
info
|
||||||
|
}
|
||||||
|
|
||||||
fn live_info_with_updated_at(
|
fn live_info_with_updated_at(
|
||||||
pod_name: &str,
|
pod_name: &str,
|
||||||
status: PodStatus,
|
status: PodStatus,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user