merge: panel workspace pod filter
This commit is contained in:
commit
0ef36b4e02
|
|
@ -100,6 +100,8 @@ pub struct PodMetadata {
|
|||
pub pod_name: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub active: Option<PodActiveSegmentRef>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub workspace_root: Option<PathBuf>,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub spawned_children: Vec<PodSpawnedChild>,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
|
|
@ -116,12 +118,18 @@ impl PodMetadata {
|
|||
Self {
|
||||
pod_name: pod_name.into(),
|
||||
active,
|
||||
workspace_root: None,
|
||||
spawned_children: Vec::new(),
|
||||
reclaimed_children: Vec::new(),
|
||||
peers: Vec::new(),
|
||||
resolved_manifest_snapshot: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_workspace_root(mut self, workspace_root: PathBuf) -> Self {
|
||||
self.workspace_root = Some(workspace_root);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Sync persistence backend for Pod metadata.
|
||||
|
|
@ -157,16 +165,30 @@ pub trait PodMetadataStore: Send + Sync {
|
|||
Ok(metadata)
|
||||
}
|
||||
|
||||
/// Set the active pointer while preserving spawned children and manifest snapshot.
|
||||
/// Set the active pointer while preserving spawned children, workspace ownership, and manifest snapshot.
|
||||
fn set_active(
|
||||
&self,
|
||||
pod_name: &str,
|
||||
active: Option<PodActiveSegmentRef>,
|
||||
resolved_manifest_snapshot: Option<serde_json::Value>,
|
||||
) -> Result<PodMetadata, PodStoreError> {
|
||||
self.set_active_with_workspace_root(pod_name, active, resolved_manifest_snapshot, None)
|
||||
}
|
||||
|
||||
/// Set the active pointer and workspace ownership while preserving unrelated fields.
|
||||
fn set_active_with_workspace_root(
|
||||
&self,
|
||||
pod_name: &str,
|
||||
active: Option<PodActiveSegmentRef>,
|
||||
resolved_manifest_snapshot: Option<serde_json::Value>,
|
||||
workspace_root: Option<PathBuf>,
|
||||
) -> Result<PodMetadata, PodStoreError> {
|
||||
self.update_by_name(pod_name, |metadata| {
|
||||
metadata.active = active;
|
||||
metadata.resolved_manifest_snapshot = resolved_manifest_snapshot;
|
||||
if let Some(workspace_root) = workspace_root {
|
||||
metadata.workspace_root = Some(workspace_root);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1108,6 +1108,7 @@ mod tests {
|
|||
let parent = PodMetadata {
|
||||
pod_name: "parent".into(),
|
||||
active: None,
|
||||
workspace_root: None,
|
||||
spawned_children: vec![
|
||||
child("child-live", &live_socket),
|
||||
child("child-stale", &stale_socket),
|
||||
|
|
@ -1127,6 +1128,7 @@ mod tests {
|
|||
session_id,
|
||||
active_child_segment,
|
||||
)),
|
||||
workspace_root: None,
|
||||
spawned_children: Vec::new(),
|
||||
reclaimed_children: Vec::new(),
|
||||
peers: Vec::new(),
|
||||
|
|
@ -1140,6 +1142,7 @@ mod tests {
|
|||
session_id,
|
||||
active_child_segment,
|
||||
)),
|
||||
workspace_root: None,
|
||||
spawned_children: Vec::new(),
|
||||
reclaimed_children: Vec::new(),
|
||||
peers: Vec::new(),
|
||||
|
|
@ -1150,6 +1153,7 @@ mod tests {
|
|||
.write(&PodMetadata {
|
||||
pod_name: "child-pending".into(),
|
||||
active: Some(PodActiveSegmentRef::pending_segment(pending_session_id)),
|
||||
workspace_root: None,
|
||||
spawned_children: Vec::new(),
|
||||
reclaimed_children: Vec::new(),
|
||||
peers: Vec::new(),
|
||||
|
|
@ -1163,6 +1167,7 @@ mod tests {
|
|||
session_id,
|
||||
new_segment_id(),
|
||||
)),
|
||||
workspace_root: None,
|
||||
spawned_children: Vec::new(),
|
||||
reclaimed_children: Vec::new(),
|
||||
peers: Vec::new(),
|
||||
|
|
@ -1173,6 +1178,7 @@ mod tests {
|
|||
.write(&PodMetadata {
|
||||
pod_name: "peer".into(),
|
||||
active: None,
|
||||
workspace_root: None,
|
||||
spawned_children: Vec::new(),
|
||||
reclaimed_children: Vec::new(),
|
||||
peers: vec![pod_store::PodPeer {
|
||||
|
|
@ -1366,6 +1372,7 @@ mod tests {
|
|||
.write(&PodMetadata {
|
||||
pod_name: "source".into(),
|
||||
active: None,
|
||||
workspace_root: None,
|
||||
spawned_children: Vec::new(),
|
||||
reclaimed_children: Vec::new(),
|
||||
peers: vec![pod_store::PodPeer {
|
||||
|
|
@ -1405,6 +1412,7 @@ mod tests {
|
|||
.write(&PodMetadata {
|
||||
pod_name: "source".into(),
|
||||
active: None,
|
||||
workspace_root: None,
|
||||
spawned_children: Vec::new(),
|
||||
reclaimed_children: Vec::new(),
|
||||
peers: vec![pod_store::PodPeer {
|
||||
|
|
@ -1417,6 +1425,7 @@ mod tests {
|
|||
.write(&PodMetadata {
|
||||
pod_name: "target".into(),
|
||||
active: None,
|
||||
workspace_root: None,
|
||||
spawned_children: Vec::new(),
|
||||
reclaimed_children: Vec::new(),
|
||||
peers: vec![pod_store::PodPeer {
|
||||
|
|
@ -1519,6 +1528,7 @@ mod tests {
|
|||
.write(&PodMetadata {
|
||||
pod_name: "source".into(),
|
||||
active: None,
|
||||
workspace_root: None,
|
||||
spawned_children: Vec::new(),
|
||||
reclaimed_children: Vec::new(),
|
||||
peers: vec![pod_store::PodPeer {
|
||||
|
|
@ -1531,6 +1541,7 @@ mod tests {
|
|||
.write(&PodMetadata {
|
||||
pod_name: "target".into(),
|
||||
active: None,
|
||||
workspace_root: None,
|
||||
spawned_children: Vec::new(),
|
||||
reclaimed_children: Vec::new(),
|
||||
peers: vec![pod_store::PodPeer {
|
||||
|
|
@ -1631,6 +1642,7 @@ mod tests {
|
|||
.write(&PodMetadata {
|
||||
pod_name: "source".into(),
|
||||
active: None,
|
||||
workspace_root: None,
|
||||
spawned_children: vec![child("target", &socket)],
|
||||
reclaimed_children: Vec::new(),
|
||||
peers: Vec::new(),
|
||||
|
|
|
|||
|
|
@ -72,10 +72,11 @@ where
|
|||
let store = store.clone();
|
||||
Arc::new(move |metadata| {
|
||||
store
|
||||
.set_active(
|
||||
.set_active_with_workspace_root(
|
||||
&metadata.pod_name,
|
||||
metadata.active,
|
||||
metadata.resolved_manifest_snapshot,
|
||||
metadata.workspace_root,
|
||||
)
|
||||
.map(|_| ())
|
||||
})
|
||||
|
|
@ -924,7 +925,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
|||
}
|
||||
|
||||
fn pod_metadata(&self, active: Option<PodActiveSegmentRef>) -> PodMetadata {
|
||||
pod_metadata_for_manifest(&self.manifest, active)
|
||||
pod_metadata_for_manifest(&self.manifest, &self.workspace_root, active)
|
||||
}
|
||||
|
||||
fn write_pod_metadata_pending(&self) -> Result<(), PodError> {
|
||||
|
|
@ -4319,9 +4320,11 @@ fn request_config_from_worker_manifest(wm: &WorkerManifest) -> RequestConfig {
|
|||
|
||||
fn pod_metadata_for_manifest(
|
||||
manifest: &PodManifest,
|
||||
workspace_root: &Path,
|
||||
active: Option<PodActiveSegmentRef>,
|
||||
) -> PodMetadata {
|
||||
let mut metadata = PodMetadata::new(manifest.pod.name.clone(), active);
|
||||
let mut metadata = PodMetadata::new(manifest.pod.name.clone(), active)
|
||||
.with_workspace_root(workspace_root.to_path_buf());
|
||||
if should_persist_resolved_manifest_snapshot(manifest) {
|
||||
metadata.resolved_manifest_snapshot = serde_json::to_value(manifest).ok();
|
||||
}
|
||||
|
|
@ -5234,6 +5237,24 @@ permission = "write"
|
|||
mod pod_metadata_restore_manifest_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn metadata_writer_persists_workspace_root_through_store_update() {
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let store = pod_store::FsPodStore::new(temp.path().join("pods")).unwrap();
|
||||
let workspace_root = temp.path().join("workspace-root");
|
||||
std::fs::create_dir_all(&workspace_root).unwrap();
|
||||
let writer = pod_metadata_writer_for_store(&store);
|
||||
|
||||
writer(PodMetadata::new("runtime-pod", None).with_workspace_root(workspace_root.clone()))
|
||||
.unwrap();
|
||||
|
||||
let stored = store.read_by_name("runtime-pod").unwrap().unwrap();
|
||||
assert_eq!(
|
||||
stored.workspace_root.as_deref(),
|
||||
Some(workspace_root.as_path())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn snapshot_preserves_saved_scope_over_current_manifest() {
|
||||
let saved = PodManifest::from_toml(
|
||||
|
|
@ -5328,7 +5349,7 @@ permission = "read"
|
|||
.unwrap();
|
||||
assert!(manifest.profile.is_none());
|
||||
assert!(
|
||||
pod_metadata_for_manifest(&manifest, None)
|
||||
pod_metadata_for_manifest(&manifest, Path::new("/snapshot/workspace"), None)
|
||||
.resolved_manifest_snapshot
|
||||
.is_none()
|
||||
);
|
||||
|
|
@ -5361,7 +5382,7 @@ permission = "read"
|
|||
config: None,
|
||||
}];
|
||||
|
||||
let metadata = pod_metadata_for_manifest(&manifest, None);
|
||||
let metadata = pod_metadata_for_manifest(&manifest, Path::new("/snapshot/workspace"), None);
|
||||
let snapshot = metadata
|
||||
.resolved_manifest_snapshot
|
||||
.expect("plugin-resolved manifest should be snapshotted");
|
||||
|
|
|
|||
|
|
@ -378,6 +378,7 @@ mod tests {
|
|||
.write(&PodMetadata {
|
||||
pod_name: "orchestrator".into(),
|
||||
active: None,
|
||||
workspace_root: None,
|
||||
spawned_children: Vec::new(),
|
||||
reclaimed_children: Vec::new(),
|
||||
peers: Vec::new(),
|
||||
|
|
@ -388,6 +389,7 @@ mod tests {
|
|||
.write(&PodMetadata {
|
||||
pod_name: "companion".into(),
|
||||
active: None,
|
||||
workspace_root: None,
|
||||
spawned_children: Vec::new(),
|
||||
reclaimed_children: Vec::new(),
|
||||
peers: Vec::new(),
|
||||
|
|
|
|||
|
|
@ -3398,12 +3398,13 @@ async fn load_pod_list(
|
|||
let live = read_reachable_live_pod_infos(&store)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
Ok(PodList::from_sources(
|
||||
Ok(PodList::from_workspace_sources(
|
||||
PodVisibilitySource::ResumePicker,
|
||||
stored,
|
||||
live,
|
||||
selected_name,
|
||||
max_entries,
|
||||
¤t_workspace_root(),
|
||||
))
|
||||
}
|
||||
|
||||
|
|
@ -9390,6 +9391,7 @@ branch = "orchestration/custom-panel"
|
|||
active_session_id: None,
|
||||
active_segment_id: None,
|
||||
updated_at,
|
||||
workspace_root: None,
|
||||
preview: None,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Duration;
|
||||
|
|
@ -65,6 +65,56 @@ impl PodList {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_workspace_sources(
|
||||
source: PodVisibilitySource,
|
||||
stored: Vec<StoredPodInfo>,
|
||||
live: Vec<LivePodInfo>,
|
||||
selected_name: Option<String>,
|
||||
max_entries: usize,
|
||||
workspace_root: &Path,
|
||||
) -> Self {
|
||||
let current_workspace = workspace_root_key(workspace_root);
|
||||
let mut current_names = BTreeSet::new();
|
||||
let stored: Vec<_> = stored
|
||||
.into_iter()
|
||||
.filter(|info| {
|
||||
let matches = info
|
||||
.workspace_root
|
||||
.as_deref()
|
||||
.is_some_and(|root| workspace_root_key(root) == current_workspace);
|
||||
if matches {
|
||||
current_names.insert(info.pod_name.clone());
|
||||
}
|
||||
matches
|
||||
})
|
||||
.collect();
|
||||
let live = live
|
||||
.into_iter()
|
||||
.filter(|info| current_names.contains(&info.pod_name))
|
||||
.collect();
|
||||
Self::from_sources(source, stored, live, selected_name, max_entries)
|
||||
}
|
||||
|
||||
pub(crate) fn filter_for_workspace(&self, workspace_root: &Path) -> Self {
|
||||
let current_workspace = workspace_root_key(workspace_root);
|
||||
let entries: Vec<_> = self
|
||||
.entries
|
||||
.iter()
|
||||
.filter(|entry| entry_belongs_to_workspace(entry, ¤t_workspace))
|
||||
.cloned()
|
||||
.collect();
|
||||
let selected_name = self
|
||||
.selected_name
|
||||
.as_ref()
|
||||
.filter(|name| entries.iter().any(|entry| entry.name == **name))
|
||||
.cloned()
|
||||
.or_else(|| entries.first().map(|entry| entry.name.clone()));
|
||||
Self {
|
||||
entries,
|
||||
selected_name,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn selected_index(&self) -> usize {
|
||||
self.selected_name
|
||||
.as_ref()
|
||||
|
|
@ -82,6 +132,18 @@ impl PodList {
|
|||
}
|
||||
}
|
||||
|
||||
fn workspace_root_key(path: &Path) -> PathBuf {
|
||||
path.canonicalize().unwrap_or_else(|_| path.to_path_buf())
|
||||
}
|
||||
|
||||
fn entry_belongs_to_workspace(entry: &PodListEntry, current_workspace: &Path) -> bool {
|
||||
entry
|
||||
.stored
|
||||
.as_ref()
|
||||
.and_then(|stored| stored.workspace_root.as_deref())
|
||||
.is_some_and(|root| workspace_root_key(root) == current_workspace)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) enum PodVisibilitySource {
|
||||
ResumePicker,
|
||||
|
|
@ -210,6 +272,7 @@ pub(crate) struct StoredPodInfo {
|
|||
pub active_session_id: Option<SessionId>,
|
||||
pub active_segment_id: Option<SegmentId>,
|
||||
pub updated_at: u64,
|
||||
pub workspace_root: Option<PathBuf>,
|
||||
pub preview: Option<String>,
|
||||
}
|
||||
|
||||
|
|
@ -348,6 +411,7 @@ fn stored_info_from_metadata(
|
|||
active_session_id,
|
||||
active_segment_id,
|
||||
updated_at: summary.updated_at,
|
||||
workspace_root: metadata.workspace_root,
|
||||
preview: summary.preview,
|
||||
}
|
||||
}
|
||||
|
|
@ -359,6 +423,7 @@ fn corrupt_stored_info(pod_name: String, message: String) -> StoredPodInfo {
|
|||
active_session_id: None,
|
||||
active_segment_id: None,
|
||||
updated_at: 0,
|
||||
workspace_root: None,
|
||||
preview: Some(format!("metadata: {}", trim_one_line(&message, 48))),
|
||||
}
|
||||
}
|
||||
|
|
@ -1061,6 +1126,7 @@ mod tests {
|
|||
active_session_id: Some(session_id),
|
||||
active_segment_id: None,
|
||||
updated_at: 0,
|
||||
workspace_root: None,
|
||||
preview: Some("[pending segment]".to_string()),
|
||||
}
|
||||
}
|
||||
|
|
@ -1072,6 +1138,7 @@ mod tests {
|
|||
active_session_id: None,
|
||||
active_segment_id: None,
|
||||
updated_at,
|
||||
workspace_root: None,
|
||||
preview: None,
|
||||
}
|
||||
}
|
||||
|
|
@ -1170,4 +1237,72 @@ mod tests {
|
|||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn stopped_info_for_workspace(pod_name: &str, workspace_root: &Path) -> StoredPodInfo {
|
||||
let mut info = stopped_info_with_updated_at(pod_name, 10);
|
||||
info.workspace_root = Some(workspace_root.to_path_buf());
|
||||
info
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn workspace_sources_include_current_and_hide_external_or_unknown_pods() {
|
||||
let current = tempdir().unwrap();
|
||||
let external = tempdir().unwrap();
|
||||
|
||||
let list = PodList::from_workspace_sources(
|
||||
SOURCE,
|
||||
vec![
|
||||
stopped_info_for_workspace("current", current.path()),
|
||||
stopped_info_for_workspace("current-orchestrator", current.path()),
|
||||
stopped_info_for_workspace("other-workspace", external.path()),
|
||||
stopped_info_with_updated_at("legacy-unknown", 10),
|
||||
corrupt_stored_info("corrupt".to_string(), "invalid metadata".to_string()),
|
||||
],
|
||||
vec![
|
||||
live_info("current", PodStatus::Idle),
|
||||
live_info("current-orchestrator", PodStatus::Running),
|
||||
live_info("other-workspace", PodStatus::Idle),
|
||||
live_info("legacy-unknown", PodStatus::Idle),
|
||||
live_info("live-only", PodStatus::Idle),
|
||||
],
|
||||
None,
|
||||
10,
|
||||
current.path(),
|
||||
);
|
||||
|
||||
let names = list
|
||||
.entries
|
||||
.iter()
|
||||
.map(|entry| entry.name.as_str())
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(names, vec!["current", "current-orchestrator"]);
|
||||
assert!(list.entries.iter().all(|entry| entry.actions.can_open));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn workspace_sources_use_workspace_metadata_not_cwd_or_live_presence() {
|
||||
let current = tempdir().unwrap();
|
||||
let worktree_cwd = current.path().join(".worktree/impl");
|
||||
|
||||
let list = PodList::from_workspace_sources(
|
||||
SOURCE,
|
||||
vec![stopped_info_for_workspace("ticket-role", current.path())],
|
||||
vec![live_info("ticket-role", PodStatus::Idle)],
|
||||
None,
|
||||
10,
|
||||
&worktree_cwd,
|
||||
);
|
||||
assert!(list.entries.is_empty());
|
||||
|
||||
let list = PodList::from_workspace_sources(
|
||||
SOURCE,
|
||||
vec![stopped_info_for_workspace("ticket-role", current.path())],
|
||||
vec![live_info("ticket-role", PodStatus::Idle)],
|
||||
None,
|
||||
10,
|
||||
current.path(),
|
||||
);
|
||||
assert_eq!(list.entries[0].name, "ticket-role");
|
||||
assert!(list.entries[0].actions.can_open);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -918,6 +918,8 @@ fn build_workspace_panel_with_registry_model(
|
|||
pods: &PodList,
|
||||
registry: &PanelRegistrySnapshot,
|
||||
) -> WorkspacePanelViewModel {
|
||||
let pods = pods.filter_for_workspace(workspace_root);
|
||||
let pods = &pods;
|
||||
match ticket_config_availability(workspace_root) {
|
||||
TicketConfigAvailability::Absent => {}
|
||||
TicketConfigAvailability::Usable => {
|
||||
|
|
@ -1685,7 +1687,7 @@ fn excerpt(markdown: &str, max_chars: usize) -> Option<String> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::pod_list::{LivePodInfo, PodEntrySummary};
|
||||
use crate::pod_list::{LivePodInfo, PodEntrySummary, StoredPodInfo};
|
||||
use crate::role_session_registry::{PanelRegistryStore, RelatedTicketRef, RoleSessionOrigin};
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
|
@ -1829,10 +1831,22 @@ mod tests {
|
|||
.unwrap_or_else(|| panic!("missing row for {title}"))
|
||||
}
|
||||
|
||||
fn live_pods(names: &[&str]) -> PodList {
|
||||
fn live_pods(workspace_root: &Path, names: &[&str]) -> PodList {
|
||||
let stored = names
|
||||
.iter()
|
||||
.map(|name| StoredPodInfo {
|
||||
pod_name: (*name).to_string(),
|
||||
metadata_state: StoredMetadataState::Present,
|
||||
active_session_id: None,
|
||||
active_segment_id: None,
|
||||
updated_at: 1,
|
||||
workspace_root: Some(workspace_root.to_path_buf()),
|
||||
preview: None,
|
||||
})
|
||||
.collect();
|
||||
PodList::from_sources(
|
||||
crate::pod_list::PodVisibilitySource::ResumePicker,
|
||||
vec![],
|
||||
stored,
|
||||
names
|
||||
.iter()
|
||||
.map(|name| LivePodInfo {
|
||||
|
|
@ -1855,7 +1869,7 @@ mod tests {
|
|||
let backend = LocalTicketBackend::new(temp.path().join(".yoi/tickets"));
|
||||
create_ticket(&backend, "Hidden Without Config", |_| {});
|
||||
|
||||
let model = build_workspace_panel(temp.path(), &live_pods(&["idle"]));
|
||||
let model = build_workspace_panel(temp.path(), &live_pods(temp.path(), &["idle"]));
|
||||
|
||||
assert!(model.header.diagnostics.is_empty());
|
||||
assert_eq!(
|
||||
|
|
@ -2069,7 +2083,7 @@ mod tests {
|
|||
.unwrap();
|
||||
let model = build_workspace_panel_with_registry(
|
||||
temp.path(),
|
||||
&live_pods(&["ready-intake"]),
|
||||
&live_pods(temp.path(), &["ready-intake"]),
|
||||
®istry.snapshot().unwrap(),
|
||||
);
|
||||
|
||||
|
|
@ -2187,7 +2201,7 @@ mod tests {
|
|||
)
|
||||
.unwrap();
|
||||
|
||||
let model = build_workspace_panel(temp.path(), &live_pods(&["idle"]));
|
||||
let model = build_workspace_panel(temp.path(), &live_pods(temp.path(), &["idle"]));
|
||||
|
||||
let diagnostics = model.header.diagnostics.join("\n");
|
||||
assert!(diagnostics.contains("Ticket config is unusable"));
|
||||
|
|
@ -2420,7 +2434,10 @@ mod tests {
|
|||
)
|
||||
.unwrap();
|
||||
|
||||
let pods = live_pods(&["claimed-intake", "shared-intake", &preticket_pod]);
|
||||
let pods = live_pods(
|
||||
temp.path(),
|
||||
&["claimed-intake", "shared-intake", &preticket_pod],
|
||||
);
|
||||
let model =
|
||||
build_workspace_panel_with_registry(temp.path(), &pods, ®istry.snapshot().unwrap());
|
||||
|
||||
|
|
@ -2484,7 +2501,7 @@ mod tests {
|
|||
|
||||
let model = build_workspace_panel_with_registry(
|
||||
temp.path(),
|
||||
&live_pods(&["ticket-claimed-intake"]),
|
||||
&live_pods(temp.path(), &["ticket-claimed-intake"]),
|
||||
®istry,
|
||||
);
|
||||
let row = model
|
||||
|
|
@ -2692,4 +2709,104 @@ mod tests {
|
|||
OrchestratorLifecyclePlan::ReportLive
|
||||
);
|
||||
}
|
||||
|
||||
fn mixed_workspace_pods(current: &Path, external: &Path) -> PodList {
|
||||
let stored = vec![
|
||||
StoredPodInfo {
|
||||
pod_name: "current".to_string(),
|
||||
metadata_state: StoredMetadataState::Present,
|
||||
active_session_id: None,
|
||||
active_segment_id: None,
|
||||
updated_at: 10,
|
||||
workspace_root: Some(current.to_path_buf()),
|
||||
preview: None,
|
||||
},
|
||||
StoredPodInfo {
|
||||
pod_name: "current-coder".to_string(),
|
||||
metadata_state: StoredMetadataState::Present,
|
||||
active_session_id: None,
|
||||
active_segment_id: None,
|
||||
updated_at: 20,
|
||||
workspace_root: Some(current.to_path_buf()),
|
||||
preview: None,
|
||||
},
|
||||
StoredPodInfo {
|
||||
pod_name: "external".to_string(),
|
||||
metadata_state: StoredMetadataState::Present,
|
||||
active_session_id: None,
|
||||
active_segment_id: None,
|
||||
updated_at: 30,
|
||||
workspace_root: Some(external.to_path_buf()),
|
||||
preview: None,
|
||||
},
|
||||
StoredPodInfo {
|
||||
pod_name: "legacy".to_string(),
|
||||
metadata_state: StoredMetadataState::Present,
|
||||
active_session_id: None,
|
||||
active_segment_id: None,
|
||||
updated_at: 40,
|
||||
workspace_root: None,
|
||||
preview: None,
|
||||
},
|
||||
StoredPodInfo {
|
||||
pod_name: "corrupt".to_string(),
|
||||
metadata_state: StoredMetadataState::Corrupt("bad metadata".to_string()),
|
||||
active_session_id: None,
|
||||
active_segment_id: None,
|
||||
updated_at: 50,
|
||||
workspace_root: None,
|
||||
preview: Some("metadata: bad metadata".to_string()),
|
||||
},
|
||||
];
|
||||
let live = [
|
||||
"current",
|
||||
"current-coder",
|
||||
"external",
|
||||
"legacy",
|
||||
"live-only",
|
||||
]
|
||||
.iter()
|
||||
.map(|name| LivePodInfo {
|
||||
pod_name: (*name).to_string(),
|
||||
socket_path: PathBuf::from(format!("/tmp/{name}.sock")),
|
||||
status: Some(PodStatus::Idle),
|
||||
reachable: true,
|
||||
segment_id: None,
|
||||
summary: PodEntrySummary::default(),
|
||||
})
|
||||
.collect();
|
||||
PodList::from_sources(
|
||||
crate::pod_list::PodVisibilitySource::ResumePicker,
|
||||
stored,
|
||||
live,
|
||||
None,
|
||||
10,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn workspace_panel_filters_pod_rows_to_current_workspace_metadata() {
|
||||
let current = TempDir::new().unwrap();
|
||||
let external = TempDir::new().unwrap();
|
||||
let pods = mixed_workspace_pods(current.path(), external.path());
|
||||
|
||||
let model = build_workspace_panel(current.path(), &pods);
|
||||
let pod_names = model
|
||||
.rows
|
||||
.iter()
|
||||
.filter_map(|row| match &row.key {
|
||||
PanelRowKey::Pod(name) => Some(name.as_str()),
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(pod_names, vec!["current-coder", "current"]);
|
||||
assert!(
|
||||
model
|
||||
.rows
|
||||
.iter()
|
||||
.filter(|row| matches!(row.key, PanelRowKey::Pod(_)))
|
||||
.all(|row| row.next_action == Some(NextUserAction::OpenPod))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user