use coapum::{StateUpdateError, observer::memory::MemObserver, router::CoapRouter};
use std::collections::HashMap;
#[derive(Clone, Debug, PartialEq)]
struct TestAppState {
counter: i32,
data: HashMap<String, String>,
enabled: bool,
}
impl TestAppState {
fn new() -> Self {
TestAppState {
counter: 0,
data: HashMap::new(),
enabled: true,
}
}
}
#[tokio::test]
async fn test_state_update_handle_creation() {
let state = TestAppState::new();
let observer = MemObserver::new();
let mut router = CoapRouter::new(state, observer);
assert!(router.state_update_handle().is_none());
let state_handle = router.enable_state_updates(100);
assert!(router.state_update_handle().is_some());
let result = state_handle
.update(|state: &mut TestAppState| {
state.counter += 1;
})
.await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_external_state_updates() {
let state = TestAppState::new();
let observer = MemObserver::new();
let mut router = CoapRouter::new(state, observer);
let state_handle = router.enable_state_updates(100);
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
state_handle
.update(|state: &mut TestAppState| {
state.counter = 42;
})
.await
.unwrap();
state_handle
.update(|state: &mut TestAppState| {
state
.data
.insert("test_key".to_string(), "test_value".to_string());
})
.await
.unwrap();
state_handle
.update(|state: &mut TestAppState| {
state.enabled = false;
})
.await
.unwrap();
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
}
#[tokio::test]
async fn test_multiple_state_handles() {
let state = TestAppState::new();
let observer = MemObserver::new();
let mut router = CoapRouter::new(state, observer);
let state_handle1 = router.enable_state_updates(100);
let state_handle2 = router.state_update_handle().unwrap();
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
state_handle1
.update(|state: &mut TestAppState| {
state.counter += 10;
})
.await
.unwrap();
state_handle2
.update(|state: &mut TestAppState| {
state.counter += 5;
})
.await
.unwrap();
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
}
#[tokio::test]
async fn test_try_update_functionality() {
let state = TestAppState::new();
let observer = MemObserver::new();
let mut router = CoapRouter::new(state, observer);
let state_handle = router.enable_state_updates(2);
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
let result = state_handle.try_update(|state: &mut TestAppState| {
state.counter = 1;
});
assert!(result.is_ok());
let result = state_handle.try_update(|state: &mut TestAppState| {
state.counter = 2;
});
assert!(result.is_ok());
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
}
#[tokio::test]
async fn test_state_update_with_complex_operations() {
let state = TestAppState::new();
let observer = MemObserver::new();
let mut router = CoapRouter::new(state, observer);
let state_handle = router.enable_state_updates(100);
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
state_handle
.update(|state: &mut TestAppState| {
for i in 0..10 {
state
.data
.insert(format!("key_{}", i), format!("value_{}", i));
}
state.counter = state.data.len() as i32;
state.enabled = state.counter > 5;
})
.await
.unwrap();
for i in 10..20 {
state_handle
.update(move |state: &mut TestAppState| {
state
.data
.insert(format!("batch_key_{}", i), format!("batch_value_{}", i));
state.counter += 1;
})
.await
.unwrap();
}
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
}
#[tokio::test]
async fn test_state_handle_cloning() {
let state = TestAppState::new();
let observer = MemObserver::new();
let mut router = CoapRouter::new(state, observer);
let state_handle = router.enable_state_updates(100);
let cloned_handle = state_handle.clone();
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
state_handle
.update(|state: &mut TestAppState| {
state.counter = 100;
})
.await
.unwrap();
cloned_handle
.update(|state: &mut TestAppState| {
state.counter += 50;
})
.await
.unwrap();
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
}
#[tokio::test]
async fn test_state_update_error_types() {
let state = TestAppState::new();
let observer = MemObserver::new();
let mut router = CoapRouter::new(state, observer);
let state_handle = router.enable_state_updates(1);
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
let result = state_handle
.update(|state: &mut TestAppState| {
state.counter = 1;
})
.await;
assert!(result.is_ok());
let error = StateUpdateError::ChannelFull;
assert_eq!(error.to_string(), "State update channel is full");
let error = StateUpdateError::ChannelClosed;
assert_eq!(error.to_string(), "State update channel is closed");
}
#[tokio::test]
async fn test_concurrent_state_updates() {
let state = TestAppState::new();
let observer = MemObserver::new();
let mut router = CoapRouter::new(state, observer);
let state_handle = router.enable_state_updates(1000);
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
let mut handles = Vec::new();
for i in 0..10 {
let handle = state_handle.clone();
let task = tokio::spawn(async move {
handle
.update(move |state: &mut TestAppState| {
state
.data
.insert(format!("concurrent_{}", i), format!("value_{}", i));
state.counter += 1;
})
.await
.unwrap();
});
handles.push(task);
}
for handle in handles {
handle.await.unwrap();
}
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
}