//! Unit tests for attendance module from botserver //! //! These tests verify the queue priority and ordering logic //! in the attendance system. use std::cmp::Ordering; /// Priority levels matching botserver's attendance queue #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum Priority { Low = 0, Normal = 1, High = 2, Urgent = 3, } /// Queue entry for testing #[derive(Debug, Clone)] pub struct QueueEntry { pub id: u64, pub customer_id: String, pub priority: Priority, pub entered_at: u64, // Unix timestamp } impl QueueEntry { pub fn new(id: u64, customer_id: &str, priority: Priority, entered_at: u64) -> Self { Self { id, customer_id: customer_id.to_string(), priority, entered_at, } } } /// Compare queue entries: higher priority first, then earlier timestamp fn compare_queue_entries(a: &QueueEntry, b: &QueueEntry) -> Ordering { // Higher priority comes first (reverse order) match b.priority.cmp(&a.priority) { Ordering::Equal => { // Same priority: earlier timestamp comes first a.entered_at.cmp(&b.entered_at) } other => other, } } /// Sort a queue by priority and timestamp fn sort_queue(entries: &mut [QueueEntry]) { entries.sort_by(compare_queue_entries); } /// Get the next entry from the queue (highest priority, earliest time) fn get_next_in_queue(entries: &[QueueEntry]) -> Option<&QueueEntry> { if entries.is_empty() { return None; } let mut best = &entries[0]; for entry in entries.iter().skip(1) { if compare_queue_entries(entry, best) == Ordering::Less { best = entry; } } Some(best) } // ============================================================================= // Priority Comparison Tests // ============================================================================= #[test] fn test_priority_ordering() { assert!(Priority::Urgent > Priority::High); assert!(Priority::High > Priority::Normal); assert!(Priority::Normal > Priority::Low); } #[test] fn test_priority_equality() { assert_eq!(Priority::Normal, Priority::Normal); assert_ne!(Priority::Normal, Priority::High); } // ============================================================================= // Queue Entry Comparison Tests // ============================================================================= #[test] fn test_higher_priority_comes_first() { let high = QueueEntry::new(1, "customer1", Priority::High, 1000); let normal = QueueEntry::new(2, "customer2", Priority::Normal, 900); // High priority should come before normal, even if normal entered earlier assert_eq!(compare_queue_entries(&high, &normal), Ordering::Less); } #[test] fn test_same_priority_earlier_time_first() { let first = QueueEntry::new(1, "customer1", Priority::Normal, 1000); let second = QueueEntry::new(2, "customer2", Priority::Normal, 1100); // Same priority: earlier timestamp comes first assert_eq!(compare_queue_entries(&first, &second), Ordering::Less); } #[test] fn test_same_priority_same_time() { let a = QueueEntry::new(1, "customer1", Priority::Normal, 1000); let b = QueueEntry::new(2, "customer2", Priority::Normal, 1000); assert_eq!(compare_queue_entries(&a, &b), Ordering::Equal); } #[test] fn test_urgent_beats_everything() { let urgent = QueueEntry::new(1, "customer1", Priority::Urgent, 2000); let high = QueueEntry::new(2, "customer2", Priority::High, 1000); let normal = QueueEntry::new(3, "customer3", Priority::Normal, 500); let low = QueueEntry::new(4, "customer4", Priority::Low, 100); assert_eq!(compare_queue_entries(&urgent, &high), Ordering::Less); assert_eq!(compare_queue_entries(&urgent, &normal), Ordering::Less); assert_eq!(compare_queue_entries(&urgent, &low), Ordering::Less); } // ============================================================================= // Queue Sorting Tests // ============================================================================= #[test] fn test_sort_queue_by_priority() { let mut queue = vec![ QueueEntry::new(1, "low", Priority::Low, 1000), QueueEntry::new(2, "urgent", Priority::Urgent, 1000), QueueEntry::new(3, "normal", Priority::Normal, 1000), QueueEntry::new(4, "high", Priority::High, 1000), ]; sort_queue(&mut queue); assert_eq!(queue[0].priority, Priority::Urgent); assert_eq!(queue[1].priority, Priority::High); assert_eq!(queue[2].priority, Priority::Normal); assert_eq!(queue[3].priority, Priority::Low); } #[test] fn test_sort_queue_mixed_priority_and_time() { let mut queue = vec![ QueueEntry::new(1, "normal_late", Priority::Normal, 2000), QueueEntry::new(2, "high_late", Priority::High, 1500), QueueEntry::new(3, "normal_early", Priority::Normal, 1000), QueueEntry::new(4, "high_early", Priority::High, 1200), ]; sort_queue(&mut queue); // High priority entries first, ordered by time assert_eq!(queue[0].id, 4); // high_early assert_eq!(queue[1].id, 2); // high_late // Then normal priority, ordered by time assert_eq!(queue[2].id, 3); // normal_early assert_eq!(queue[3].id, 1); // normal_late } #[test] fn test_sort_empty_queue() { let mut queue: Vec = vec![]; sort_queue(&mut queue); assert!(queue.is_empty()); } #[test] fn test_sort_single_entry() { let mut queue = vec![QueueEntry::new(1, "only", Priority::Normal, 1000)]; sort_queue(&mut queue); assert_eq!(queue.len(), 1); assert_eq!(queue[0].id, 1); } // ============================================================================= // Get Next in Queue Tests // ============================================================================= #[test] fn test_get_next_returns_highest_priority() { let queue = vec![ QueueEntry::new(1, "low", Priority::Low, 100), QueueEntry::new(2, "high", Priority::High, 200), QueueEntry::new(3, "normal", Priority::Normal, 150), ]; let next = get_next_in_queue(&queue).unwrap(); assert_eq!(next.id, 2); // High priority } #[test] fn test_get_next_respects_time_within_priority() { let queue = vec![ QueueEntry::new(1, "first", Priority::Normal, 1000), QueueEntry::new(2, "second", Priority::Normal, 1100), QueueEntry::new(3, "third", Priority::Normal, 1200), ]; let next = get_next_in_queue(&queue).unwrap(); assert_eq!(next.id, 1); // First to enter } #[test] fn test_get_next_empty_queue() { let queue: Vec = vec![]; assert!(get_next_in_queue(&queue).is_none()); } #[test] fn test_get_next_single_entry() { let queue = vec![QueueEntry::new(42, "only_customer", Priority::Normal, 1000)]; let next = get_next_in_queue(&queue).unwrap(); assert_eq!(next.id, 42); } // ============================================================================= // Real-world Scenario Tests // ============================================================================= #[test] fn test_scenario_customer_support_queue() { // Simulate a real customer support queue scenario let mut queue = vec![ // Regular customers entering over time QueueEntry::new(1, "alice", Priority::Normal, 1000), QueueEntry::new(2, "bob", Priority::Normal, 1100), QueueEntry::new(3, "charlie", Priority::Normal, 1200), // VIP customer enters later QueueEntry::new(4, "vip_dave", Priority::High, 1300), // Urgent issue reported QueueEntry::new(5, "urgent_eve", Priority::Urgent, 1400), ]; sort_queue(&mut queue); // Service order should be: urgent_eve, vip_dave, alice, bob, charlie assert_eq!(queue[0].customer_id, "urgent_eve"); assert_eq!(queue[1].customer_id, "vip_dave"); assert_eq!(queue[2].customer_id, "alice"); assert_eq!(queue[3].customer_id, "bob"); assert_eq!(queue[4].customer_id, "charlie"); } #[test] fn test_scenario_multiple_urgent_fifo() { // Multiple urgent requests should still be FIFO within that priority let mut queue = vec![ QueueEntry::new(1, "urgent1", Priority::Urgent, 1000), QueueEntry::new(2, "urgent2", Priority::Urgent, 1100), QueueEntry::new(3, "urgent3", Priority::Urgent, 1050), ]; sort_queue(&mut queue); // Should be ordered by entry time within urgent priority assert_eq!(queue[0].customer_id, "urgent1"); // 1000 assert_eq!(queue[1].customer_id, "urgent3"); // 1050 assert_eq!(queue[2].customer_id, "urgent2"); // 1100 } #[test] fn test_scenario_priority_upgrade() { // Simulate upgrading a customer's priority let mut entry = QueueEntry::new(1, "customer", Priority::Normal, 1000); // Verify initial priority assert_eq!(entry.priority, Priority::Normal); // Upgrade priority (customer complained, escalation, etc.) entry.priority = Priority::High; assert_eq!(entry.priority, Priority::High); } #[test] fn test_queue_position_calculation() { let queue = vec![ QueueEntry::new(1, "first", Priority::Normal, 1000), QueueEntry::new(2, "second", Priority::Normal, 1100), QueueEntry::new(3, "third", Priority::Normal, 1200), QueueEntry::new(4, "fourth", Priority::Normal, 1300), ]; // Find position of customer with id=3 let position = queue.iter().position(|e| e.id == 3).map(|p| p + 1); // 1-based position assert_eq!(position, Some(3)); } #[test] fn test_estimated_wait_time() { let avg_service_time_minutes = 5; let _queue = vec![ QueueEntry::new(1, "first", Priority::Normal, 1000), QueueEntry::new(2, "second", Priority::Normal, 1100), QueueEntry::new(3, "third", Priority::Normal, 1200), ]; // Customer at position 3 has 2 people ahead let position = 3; let people_ahead = position - 1; let estimated_wait = people_ahead * avg_service_time_minutes; assert_eq!(estimated_wait, 10); // 2 people * 5 minutes each } // ============================================================================= // Edge Case Tests // ============================================================================= #[test] fn test_large_queue() { let mut queue: Vec = (0..1000) .map(|i| { let priority = match i % 4 { 0 => Priority::Low, 1 => Priority::Normal, 2 => Priority::High, _ => Priority::Urgent, }; QueueEntry::new(i, &format!("customer_{}", i), priority, 1000 + i) }) .collect(); sort_queue(&mut queue); // First entry should be urgent (i % 4 == 3, first one is i=3) assert_eq!(queue[0].priority, Priority::Urgent); // Last entry should be low priority assert_eq!(queue[999].priority, Priority::Low); } #[test] fn test_all_same_priority_and_time() { let queue = vec![ QueueEntry::new(1, "a", Priority::Normal, 1000), QueueEntry::new(2, "b", Priority::Normal, 1000), QueueEntry::new(3, "c", Priority::Normal, 1000), ]; // All equal, any is valid as "next" let next = get_next_in_queue(&queue); assert!(next.is_some()); }