1use std::time::Duration;
2
3use crate::types::ApiRuntimeConfig;
4
5const NOTIFY_TIMEOUT: Duration = Duration::from_millis(250);
6
7pub fn notify_task_changed(api: &ApiRuntimeConfig) -> Result<(), ureq::Error> {
20 let url = format!("http://127.0.0.1:{}/api/events/tasks-changed", api.port);
21 let agent: ureq::Agent = ureq::Agent::config_builder()
22 .timeout_global(Some(NOTIFY_TIMEOUT))
23 .build()
24 .into();
25
26 agent.post(&url).send_empty()?;
27 Ok(())
28}
29
30#[cfg(test)]
31mod tests {
32 use std::io::{Read, Write};
33 use std::net::TcpListener;
34 use std::sync::mpsc;
35 use std::thread;
36 use std::time::Duration;
37
38 use crate::types::ApiRuntimeConfig;
39
40 use super::notify_task_changed;
41
42 #[test]
43 fn posts_task_change_notification_to_the_local_api() {
44 let listener = TcpListener::bind("127.0.0.1:0").expect("listener should bind");
45 let port = listener
46 .local_addr()
47 .expect("listener should expose local address")
48 .port();
49 let (sender, receiver) = mpsc::channel();
50
51 let server = thread::spawn(move || {
52 let (mut stream, _) = listener.accept().expect("listener should accept");
53 let mut buffer = [0u8; 1024];
54 let bytes_read = stream
55 .read(&mut buffer)
56 .expect("request should be readable");
57 stream
58 .write_all(
59 b"HTTP/1.1 204 No Content\r\nContent-Length: 0\r\nConnection: close\r\n\r\n",
60 )
61 .expect("response should be writable");
62 sender
63 .send(String::from_utf8_lossy(&buffer[..bytes_read]).into_owned())
64 .expect("request should reach the test thread");
65 });
66
67 notify_task_changed(&ApiRuntimeConfig { port }).expect("notification should succeed");
68
69 let request = receiver
70 .recv_timeout(Duration::from_secs(1))
71 .expect("notification should be received");
72
73 assert!(request.starts_with("POST /api/events/tasks-changed HTTP/1.1"));
77 server.join().expect("server thread should exit cleanly");
78 }
79}