#![allow(dead_code)]
use super::cross_tab::CrossTabLink;
use super::delegator::{DelegationResult, TaskDelegator};
use super::meeting::{Meeting, MeetingDecision, MeetingManager, MeetingMessage};
use super::{Priority, TabId, TabMetadata, TabStatus, TabType};
use std::collections::HashMap;
pub const MAX_TABS: usize = 9;
#[derive(Debug, Clone)]
pub struct TabState {
pub metadata: TabMetadata,
pub status: TabStatus,
pub pending_tasks: Vec<String>,
}
impl TabState {
pub fn new(id: TabId, title: String, tab_type: TabType) -> Self {
Self {
metadata: TabMetadata::new(id, title, tab_type),
status: TabStatus::Idle,
pending_tasks: Vec::new(),
}
}
}
pub struct TabManager {
tabs: Vec<TabState>,
active_tab: Option<usize>,
max_tabs: usize,
cross_tab_links: HashMap<TabId, Vec<CrossTabLink>>,
delegator: TaskDelegator,
meeting_manager: MeetingManager,
groups: super::group::TabGroupManager,
next_tab_id: u64,
}
impl TabManager {
pub fn new() -> Self {
Self {
tabs: Vec::new(),
active_tab: None,
max_tabs: MAX_TABS,
cross_tab_links: HashMap::new(),
delegator: TaskDelegator::new(),
meeting_manager: MeetingManager::new(),
groups: super::group::TabGroupManager::new(),
next_tab_id: 1,
}
}
pub fn len(&self) -> usize {
self.tabs.len()
}
pub fn is_empty(&self) -> bool {
self.tabs.is_empty()
}
pub fn active_index(&self) -> Option<usize> {
self.active_tab
}
pub fn active_id(&self) -> Option<TabId> {
self.active_tab
.and_then(|i| self.tabs.get(i))
.map(|t| t.metadata.id)
}
pub fn all_tabs(&self) -> Vec<&TabMetadata> {
self.tabs.iter().map(|t| &t.metadata).collect()
}
pub fn get(&self, index: usize) -> Option<&TabState> {
self.tabs.get(index)
}
pub fn get_mut(&mut self, index: usize) -> Option<&mut TabState> {
self.tabs.get_mut(index)
}
pub fn iter(&self) -> impl Iterator<Item = (usize, &TabState)> {
self.tabs.iter().enumerate()
}
pub fn create_tab(&mut self, title: String, tab_type: TabType) -> Option<TabId> {
if self.tabs.len() >= self.max_tabs {
return None;
}
let id = TabId::new(self.next_tab_id);
self.next_tab_id += 1;
let tab = TabState::new(id, title, tab_type);
self.tabs.push(tab);
self.active_tab = Some(self.tabs.len() - 1);
Some(id)
}
pub fn create_default_chat_tab(&mut self) -> Option<TabId> {
let title = format!("Tab {}", self.tabs.len() + 1);
self.create_tab(title, TabType::Chat)
}
pub fn groups_mut(&mut self) -> &mut super::group::TabGroupManager {
&mut self.groups
}
pub fn groups(&self) -> &super::group::TabGroupManager {
&self.groups
}
pub fn create_group(&mut self, name: String, color: super::group::GroupColor) -> String {
self.groups.create_group(name, color)
}
pub fn delete_group(&mut self, group_id: &str) -> bool {
self.groups.delete_group(group_id)
}
pub fn assign_tab_to_group(&mut self, tab_id: TabId, group_id: &str) -> bool {
self.groups.assign_tab(tab_id, group_id)
}
pub fn tab_group(&self, tab_id: TabId) -> Option<&super::group::TabGroup> {
self.groups.group_of(tab_id)
}
pub fn all_groups(&self) -> Vec<&super::group::TabGroup> {
self.groups.all_groups()
}
pub fn cycle_tab_group(&mut self, tab_id: TabId) {
self.groups.cycle_tab_group(tab_id);
}
pub fn snapshot(&self) -> super::persistence::PersistedTabState {
use super::persistence::PersistedTab;
let tabs: Vec<PersistedTab> = self
.tabs
.iter()
.map(|t| super::persistence::from_metadata(&t.metadata))
.collect();
let delegations: Vec<super::persistence::PersistedDelegation> = self
.delegator
.all_pending()
.iter()
.map(|t| super::persistence::PersistedDelegation {
task_id: t.task_id.clone(),
from_tab: t.from_tab.0,
to_tab: t.to_tab.0,
description: t.description.clone(),
priority: t.priority,
status: t.status,
created_at: t.created_at,
completed_at: t.completed_at,
result: t.result.clone(),
was_successful: None,
})
.collect();
let groups: Vec<super::persistence::PersistedGroup> = self
.groups
.all_groups()
.into_iter()
.map(|g| super::persistence::PersistedGroup {
id: g.id.clone(),
name: g.name.clone(),
color: g.color,
tab_ids: g.tab_ids.clone(),
created_at: g.created_at,
})
.collect();
super::persistence::PersistedTabState {
version: 1,
saved_at: chrono::Utc::now(),
active_tab_index: self.active_tab,
tabs,
delegations,
groups,
}
}
pub fn save_to_file(&self, path: &std::path::Path) -> std::io::Result<()> {
let state = self.snapshot();
super::persistence::save_to_file(&state, path)
}
pub fn restore_from_snapshot(&mut self, state: &super::persistence::PersistedTabState) {
self.tabs.clear();
self.active_tab = None;
self.cross_tab_links.clear();
self.delegator = super::delegator::TaskDelegator::new();
self.meeting_manager = super::meeting::MeetingManager::new();
self.groups = super::group::TabGroupManager::new();
self.next_tab_id = 1;
let mut max_seen_id: u64 = 0;
for p in &state.tabs {
let meta = super::persistence::to_metadata(p);
if meta.id.0 > max_seen_id {
max_seen_id = meta.id.0;
}
let tab = TabState {
metadata: meta,
status: TabStatus::Idle,
pending_tasks: Vec::new(),
};
self.tabs.push(tab);
}
self.next_tab_id = max_seen_id + 1;
for d in &state.delegations {
let task = super::delegator::DelegationTask {
task_id: d.task_id.clone(),
from_tab: TabId(d.from_tab),
to_tab: TabId(d.to_tab),
description: d.description.clone(),
priority: d.priority,
status: d.status,
created_at: d.created_at,
deadline: None,
completed_at: d.completed_at,
result: d.result.clone(),
};
self.delegator.pending_tasks.push(task);
}
self.delegator.advance_next_id_past_existing_tasks();
for g in &state.groups {
let group = super::group::TabGroup {
id: g.id.clone(),
name: g.name.clone(),
color: g.color,
tab_ids: g.tab_ids.clone(),
created_at: g.created_at,
};
self.groups.groups.insert(group.id.clone(), group);
for tab_id in &g.tab_ids {
self.groups.tab_to_group.insert(*tab_id, g.id.clone());
}
}
self.groups.advance_next_id_past_existing_groups();
if let Some(idx) = state.active_tab_index {
if idx < self.tabs.len() {
self.active_tab = Some(idx);
} else if !self.tabs.is_empty() {
self.active_tab = Some(self.tabs.len() - 1);
}
} else if !self.tabs.is_empty() {
self.active_tab = Some(0);
}
}
pub fn restore_from_file(&mut self, path: &std::path::Path) -> std::io::Result<()> {
let state = super::persistence::load_from_file(path)?;
self.restore_from_snapshot(&state);
Ok(())
}
pub fn close_tab(&mut self, index: usize) -> bool {
if index >= self.tabs.len() {
return false;
}
let tab_id = self.tabs[index].metadata.id;
self.groups.unassign_tab(tab_id);
self.tabs.remove(index);
if let Some(active) = self.active_tab {
if index < active {
self.active_tab = Some(active - 1);
} else if index == active {
self.active_tab = if self.tabs.is_empty() {
None
} else if active >= self.tabs.len() {
Some(self.tabs.len() - 1)
} else {
Some(active)
};
}
}
true
}
pub fn close_tab_by_id(&mut self, id: TabId) -> bool {
if let Some(index) = self.tabs.iter().position(|t| t.metadata.id == id) {
self.close_tab(index)
} else {
false
}
}
pub fn switch_to(&mut self, index: usize) -> bool {
if index < self.tabs.len() {
self.active_tab = Some(index);
if let Some(tab) = self.tabs.get_mut(index) {
tab.metadata.touch();
tab.metadata.clear_unread();
}
true
} else {
false
}
}
pub fn switch_to_next(&mut self) -> bool {
if self.tabs.is_empty() {
return false;
}
let next = match self.active_tab {
Some(i) => (i + 1) % self.tabs.len(),
None => 0,
};
self.switch_to(next)
}
pub fn switch_to_prev(&mut self) -> bool {
if self.tabs.is_empty() {
return false;
}
let prev = match self.active_tab {
Some(i) => {
if i == 0 {
self.tabs.len() - 1
} else {
i - 1
}
}
None => self.tabs.len() - 1,
};
self.switch_to(prev)
}
pub fn switch_to_by_id(&mut self, id: TabId) -> bool {
if let Some(index) = self.tabs.iter().position(|t| t.metadata.id == id) {
self.switch_to(index)
} else {
false
}
}
pub fn update_title(&mut self, index: usize, title: &str) -> bool {
if let Some(tab) = self.tabs.get_mut(index) {
tab.metadata.title = title.to_string();
true
} else {
false
}
}
pub fn update_status(&mut self, index: usize, status: TabStatus) -> bool {
if let Some(tab) = self.tabs.get_mut(index) {
tab.status = status;
true
} else {
false
}
}
pub fn mark_unread(&mut self, index: usize) -> bool {
if let Some(tab) = self.tabs.get_mut(index) {
if self.active_tab != Some(index) {
tab.metadata.increment_unread();
}
true
} else {
false
}
}
pub fn completed_delegations(&self, id: TabId) -> Vec<&DelegationResult> {
self.delegator.results_for_tab(id)
}
pub fn create_link(&mut self, from: TabId, to: TabId) {
let link = CrossTabLink {
from,
to,
created_at: chrono::Utc::now(),
};
self.cross_tab_links.entry(from).or_default().push(link);
}
pub fn remove_link(&mut self, from: TabId, to: TabId) -> bool {
if let Some(links) = self.cross_tab_links.get_mut(&from) {
let initial_len = links.len();
links.retain(|l| l.to != to);
links.len() < initial_len
} else {
false
}
}
pub fn get_links(&self, from: TabId) -> Vec<&CrossTabLink> {
self.cross_tab_links
.get(&from)
.map(|l| l.iter().collect())
.unwrap_or_default()
}
pub fn delegate_task(
&mut self,
from: TabId,
to: TabId,
description: String,
priority: Priority,
) -> Option<String> {
let has_from = self.tabs.iter().any(|t| t.metadata.id == from);
let has_to = self.tabs.iter().any(|t| t.metadata.id == to);
if !has_from || !has_to {
return None;
}
self.delegator
.create_delegation(from, to, description, priority)
}
pub fn complete_delegation(&mut self, task_id: &str, result: String) {
self.delegator.complete(task_id, result);
}
pub fn pending_delegations(&self, tab_id: TabId) -> Vec<&super::delegator::DelegationTask> {
self.delegator.pending_for_tab(tab_id)
}
pub fn take_next_delegation(
&mut self,
tab_id: TabId,
) -> Option<super::delegator::DelegationTask> {
self.delegator.take_pending_for_tab(tab_id)
}
pub fn peek_next_delegation(&self, tab_id: TabId) -> Option<&super::delegator::DelegationTask> {
self.delegator.peek_pending_for_tab(tab_id)
}
pub fn has_pending_delegations(&self, tab_id: TabId) -> bool {
self.delegator.peek_pending_for_tab(tab_id).is_some()
}
pub fn start_meeting(&mut self, topic: String, participants: Vec<TabId>) -> Option<String> {
for p in &participants {
if !self.tabs.iter().any(|t| t.metadata.id == *p) {
return None;
}
}
self.meeting_manager.start_meeting(topic, participants)
}
pub fn end_meeting(&mut self, meeting_id: &str) -> Option<super::meeting::MeetingSummary> {
self.meeting_manager.end_meeting(meeting_id)
}
pub fn add_meeting_message(&mut self, meeting_id: &str, msg: MeetingMessage) {
self.meeting_manager.add_message(meeting_id, msg);
}
pub fn add_meeting_decision(&mut self, meeting_id: &str, decision: MeetingDecision) {
self.meeting_manager.add_decision(meeting_id, decision);
}
pub fn active_meeting(&self, tab_id: TabId) -> Option<&Meeting> {
self.meeting_manager.active_meeting_for(tab_id)
}
pub fn meeting(&self, meeting_id: &str) -> Option<&Meeting> {
self.meeting_manager.get_meeting(meeting_id)
}
}
impl Default for TabManager {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_and_close_tab() {
let mut manager = TabManager::new();
assert!(manager.is_empty());
let id1 = manager.create_tab("Tab 1".to_string(), TabType::Chat);
assert!(id1.is_some());
assert_eq!(manager.len(), 1);
assert_eq!(manager.active_index(), Some(0));
let id2 = manager.create_tab("Tab 2".to_string(), TabType::Chat);
assert!(id2.is_some());
assert_eq!(manager.len(), 2);
assert_eq!(manager.active_index(), Some(1));
assert!(manager.close_tab(1));
assert_eq!(manager.len(), 1);
assert!(manager.close_tab(0));
assert!(manager.is_empty());
}
#[test]
fn test_switch_tabs() {
let mut manager = TabManager::new();
manager.create_tab("Tab 1".to_string(), TabType::Chat);
manager.create_tab("Tab 2".to_string(), TabType::Chat);
manager.create_tab("Tab 3".to_string(), TabType::Chat);
assert_eq!(manager.active_index(), Some(2));
assert!(manager.switch_to_next());
assert_eq!(manager.active_index(), Some(0));
assert!(manager.switch_to_prev());
assert_eq!(manager.active_index(), Some(2));
}
#[test]
fn test_max_tabs() {
let mut manager = TabManager::new();
for i in 0..MAX_TABS {
let result = manager.create_tab(format!("Tab {}", i), TabType::Chat);
assert!(result.is_some(), "Tab {} should be created", i);
}
let result = manager.create_tab("Extra".to_string(), TabType::Chat);
assert!(result.is_none(), "Should not exceed max tabs");
}
#[test]
fn test_delegation_take_priority_order() {
use super::super::Priority;
let mut manager = TabManager::new();
let from = manager
.create_tab("Source".to_string(), TabType::Chat)
.unwrap();
let to = manager
.create_tab("Target".to_string(), TabType::Chat)
.unwrap();
manager.delegate_task(from, to, "Low task".to_string(), Priority::Low);
manager.delegate_task(from, to, "Urgent task".to_string(), Priority::Urgent);
manager.delegate_task(from, to, "Normal task".to_string(), Priority::Normal);
let task = manager.take_next_delegation(to).unwrap();
assert_eq!(task.description, "Urgent task");
assert_eq!(task.priority, Priority::Urgent);
assert!(manager.has_pending_delegations(to));
let task = manager.take_next_delegation(to).unwrap();
assert_eq!(task.description, "Normal task");
assert_eq!(task.priority, Priority::Normal);
let task = manager.take_next_delegation(to).unwrap();
assert_eq!(task.description, "Low task");
assert_eq!(task.priority, Priority::Low);
assert!(manager.take_next_delegation(to).is_none());
assert!(!manager.has_pending_delegations(to));
}
#[test]
fn test_delegation_peek() {
use super::super::Priority;
let mut manager = TabManager::new();
let from = manager
.create_tab("Source".to_string(), TabType::Chat)
.unwrap();
let to = manager
.create_tab("Target".to_string(), TabType::Chat)
.unwrap();
manager.delegate_task(from, to, "Test task".to_string(), Priority::High);
let peek1 = manager.peek_next_delegation(to).unwrap();
let peek2 = manager.peek_next_delegation(to).unwrap();
assert_eq!(peek1.task_id, peek2.task_id);
assert_eq!(manager.delegator.pending_count(to), 1);
}
#[test]
fn test_tab_type_icons() {
use super::super::TabType;
assert_eq!(TabType::Chat.icon(), "💬");
assert_eq!(TabType::Delegation.icon(), "📤");
assert_eq!(TabType::Review.icon(), "🔍");
assert_eq!(TabType::Meeting.icon(), "👥");
assert_eq!(TabType::Chat.ascii_icon(), "[C]");
assert_eq!(TabType::Delegation.ascii_icon(), "[D]");
assert_eq!(TabType::Review.ascii_icon(), "[R]");
assert_eq!(TabType::Meeting.ascii_icon(), "[M]");
assert_eq!(TabType::Chat.display_name(), "Chat");
assert_eq!(TabType::Delegation.display_name(), "Delegation");
assert_eq!(TabType::Review.display_name(), "Review");
assert_eq!(TabType::Meeting.display_name(), "Meeting");
}
#[test]
fn test_snapshot_and_restore() {
let mut manager = TabManager::new();
let id1 = manager
.create_tab("Tab A".to_string(), TabType::Chat)
.unwrap();
let _id2 = manager
.create_tab("Tab B".to_string(), TabType::Review)
.unwrap();
assert!(manager.switch_to_by_id(id1));
let snapshot = manager.snapshot();
assert_eq!(snapshot.tabs.len(), 2);
assert_eq!(snapshot.active_tab_index, Some(0));
assert_eq!(snapshot.tabs[0].title, "Tab A");
assert_eq!(snapshot.tabs[1].tab_type, TabType::Review);
manager.close_tab(0);
assert_eq!(manager.len(), 1);
manager.restore_from_snapshot(&snapshot);
assert_eq!(manager.len(), 2);
assert_eq!(manager.active_index(), Some(0));
assert_eq!(manager.all_tabs()[0].title, "Tab A");
}
#[test]
fn test_snapshot_restore_preserves_delegations() {
use super::super::Priority;
let mut manager = TabManager::new();
let from = manager
.create_tab("Source".to_string(), TabType::Chat)
.unwrap();
let to = manager
.create_tab("Target".to_string(), TabType::Chat)
.unwrap();
manager.delegate_task(from, to, "review the patch".to_string(), Priority::High);
assert_eq!(manager.pending_delegations(to).len(), 1);
let snapshot = manager.snapshot();
assert_eq!(snapshot.delegations.len(), 1);
assert_eq!(snapshot.delegations[0].description, "review the patch");
let mut restored = TabManager::new();
restored.restore_from_snapshot(&snapshot);
assert_eq!(restored.pending_delegations(to).len(), 1);
assert_eq!(
restored.pending_delegations(to)[0].description,
"review the patch"
);
assert_eq!(restored.pending_delegations(to)[0].priority, Priority::High);
}
#[test]
fn test_create_tab_id_does_not_collide_after_restore() {
let mut manager = TabManager::new();
let _a = manager.create_tab("A".to_string(), TabType::Chat).unwrap();
let _b = manager.create_tab("B".to_string(), TabType::Chat).unwrap();
let _c = manager.create_tab("C".to_string(), TabType::Chat).unwrap();
let max_id_before = manager.all_tabs().iter().map(|t| t.id.0).max().unwrap();
let snapshot = manager.snapshot();
manager.close_tab(0);
manager.close_tab(0);
manager.close_tab(0);
assert!(manager.is_empty());
manager.restore_from_snapshot(&snapshot);
let d = manager.create_tab("D".to_string(), TabType::Chat).unwrap();
assert!(
d.0 > max_id_before,
"new tab id {} must exceed restored max {}",
d.0,
max_id_before
);
for tab in manager.all_tabs() {
if tab.title == "D" {
continue;
}
assert_ne!(tab.id, d, "newly created tab collided with restored tab");
}
}
#[test]
fn test_persistence_roundtrip() {
let dir = std::env::temp_dir().join("codewhale_tabs_roundtrip");
let path = dir.join("tabs.json");
let _ = std::fs::remove_file(&path);
let mut manager = TabManager::new();
manager
.create_tab("First".to_string(), TabType::Chat)
.unwrap();
manager
.create_tab("Second".to_string(), TabType::Meeting)
.unwrap();
manager.switch_to(1);
manager.save_to_file(&path).unwrap();
assert!(path.exists());
let mut restored = TabManager::new();
restored.restore_from_file(&path).unwrap();
assert_eq!(restored.len(), 2);
assert_eq!(restored.active_index(), Some(1));
assert_eq!(restored.all_tabs()[0].title, "First");
assert_eq!(restored.all_tabs()[1].tab_type, TabType::Meeting);
let _ = std::fs::remove_file(&path);
let _ = std::fs::remove_dir(&dir);
}
#[test]
fn test_manager_group_operations() {
use super::super::group::GroupColor;
let mut manager = TabManager::new();
let tab1 = manager
.create_tab("Tab 1".to_string(), TabType::Chat)
.unwrap();
let tab2 = manager
.create_tab("Tab 2".to_string(), TabType::Review)
.unwrap();
let group_id = manager.create_group("Frontend".to_string(), GroupColor::Blue);
assert_eq!(manager.all_groups().len(), 1);
assert!(manager.assign_tab_to_group(tab1, &group_id));
assert!(manager.assign_tab_to_group(tab2, &group_id));
let group = manager.tab_group(tab1).unwrap();
assert_eq!(group.name, "Frontend");
assert_eq!(group.len(), 2);
manager.cycle_tab_group(tab1);
assert!(manager.tab_group(tab1).is_none());
manager.assign_tab_to_group(tab1, &group_id);
manager.create_group("Alpha".to_string(), GroupColor::Red);
manager.cycle_tab_group(tab1);
assert!(manager.tab_group(tab1).is_none());
}
#[test]
fn test_close_tab_unassigns_group() {
use super::super::group::GroupColor;
let mut manager = TabManager::new();
let tab1 = manager
.create_tab("Tab 1".to_string(), TabType::Chat)
.unwrap();
let group_id = manager.create_group("Test".to_string(), GroupColor::Cyan);
manager.assign_tab_to_group(tab1, &group_id);
assert!(manager.tab_group(tab1).is_some());
manager.close_tab(0);
let group = manager.groups().all_groups();
assert_eq!(group.len(), 1);
assert_eq!(group[0].len(), 0);
}
#[test]
fn test_delegate_task_rejects_unknown_tabs() {
use super::super::Priority;
let mut manager = TabManager::new();
let from = manager
.create_tab("Source".to_string(), TabType::Chat)
.unwrap();
let bogus_to = TabId(9999);
assert!(
manager
.delegate_task(from, bogus_to, "x".to_string(), Priority::Normal)
.is_none()
);
let bogus_from = TabId(9998);
let to = manager
.create_tab("Target".to_string(), TabType::Chat)
.unwrap();
assert!(
manager
.delegate_task(bogus_from, to, "x".to_string(), Priority::Normal)
.is_none()
);
let id = manager.delegate_task(from, to, "real".to_string(), Priority::Normal);
assert!(id.is_some());
}
#[test]
fn test_start_meeting_rejects_unknown_participants() {
let mut manager = TabManager::new();
let a = manager.create_tab("A".to_string(), TabType::Chat).unwrap();
let _b = manager.create_tab("B".to_string(), TabType::Chat).unwrap();
let bogus = TabId(4242);
assert!(
manager
.start_meeting("topic".to_string(), vec![a, bogus])
.is_none()
);
assert!(
manager
.start_meeting("topic".to_string(), vec![bogus])
.is_none()
);
let meeting_id = manager.start_meeting("topic".to_string(), vec![a, _b]);
assert!(meeting_id.is_some());
}
#[test]
fn test_snapshot_restore_preserves_in_progress_status() {
use super::super::Priority;
use super::super::delegator::DelegationStatus;
let mut manager = TabManager::new();
let from = manager
.create_tab("Source".to_string(), TabType::Chat)
.unwrap();
let to = manager
.create_tab("Target".to_string(), TabType::Chat)
.unwrap();
let task_id = manager
.delegate_task(from, to, "work".to_string(), Priority::High)
.unwrap();
manager.delegator.start_task(&task_id);
assert_eq!(
manager.delegator.all_pending()[0].status,
DelegationStatus::InProgress
);
let snapshot = manager.snapshot();
let mut restored = TabManager::new();
restored.restore_from_snapshot(&snapshot);
let restored_tasks = restored.delegator.all_pending();
assert_eq!(restored_tasks.len(), 1);
assert_eq!(restored_tasks[0].status, DelegationStatus::InProgress);
assert_eq!(restored_tasks[0].task_id, task_id);
}
#[test]
fn test_restore_advances_delegation_ids() {
use super::super::Priority;
let mut manager = TabManager::new();
let from = manager
.create_tab("Source".to_string(), TabType::Chat)
.unwrap();
let to = manager
.create_tab("Target".to_string(), TabType::Chat)
.unwrap();
let restored_id = manager
.delegate_task(from, to, "restored".to_string(), Priority::Normal)
.unwrap();
assert_eq!(restored_id, "delegation_1");
let snapshot = manager.snapshot();
let mut restored = TabManager::new();
restored.restore_from_snapshot(&snapshot);
let fresh_id = restored
.delegate_task(from, to, "fresh".to_string(), Priority::Normal)
.unwrap();
assert_eq!(fresh_id, "delegation_2");
}
#[test]
fn test_restore_advances_group_ids() {
use super::super::group::GroupColor;
let mut manager = TabManager::new();
let tab = manager
.create_tab("Grouped".to_string(), TabType::Chat)
.unwrap();
let restored_group = manager.create_group("Restored".to_string(), GroupColor::Blue);
assert_eq!(restored_group, "group_1");
assert!(manager.assign_tab_to_group(tab, &restored_group));
let snapshot = manager.snapshot();
let mut restored = TabManager::new();
restored.restore_from_snapshot(&snapshot);
let fresh_group = restored.create_group("Fresh".to_string(), GroupColor::Green);
assert_eq!(fresh_group, "group_2");
assert!(
restored
.groups()
.all_groups()
.iter()
.any(|g| g.id == "group_1")
);
assert!(
restored
.groups()
.all_groups()
.iter()
.any(|g| g.id == "group_2")
);
assert_eq!(
restored.tab_group(tab).map(|g| g.id.as_str()),
Some("group_1")
);
}
}