//! `ServiceHandle::stop`: a service stopped in-process closes its ports and its locks, so //! the same process can start it again on the same port (the desktop AI Hub app's Enabled //! switch and backend toggles). use makepad_ai_hub::download::Downloader; use makepad_ai_hub::registry::Registry; use makepad_ai_hub::server::{start_service, ServiceConfig}; use std::net::{SocketAddr, TcpStream}; use std::time::Duration; fn config(dir: &std::path::Path, host: &str, port: u16) -> ServiceConfig { ServiceConfig { host: host.to_string(), port, cache_dir: dir.to_path_buf(), registry: Registry { models: Vec::new() }, downloader: Downloader::new("http://127.0.0.1:1", None).unwrap(), peer: makepad_ai_hub::peer_serve::PeerOptions { serve: Some(false), sources: Some(Vec::new()), ..Default::default() }, // A partition nobody listens for: the beacon this test sends is ignored by the fleet. fleet: "hubapp-stop-test".to_string(), } } fn answers(addr: SocketAddr) -> bool { TcpStream::connect_timeout(&addr, Duration::from_millis(500)).is_ok() } fn scratch(name: &str) -> std::path::PathBuf { let dir = std::env::temp_dir().join(format!("makepad-ai-hub-{name}-{}", std::process::id())); std::fs::create_dir_all(&dir).unwrap(); dir } #[test] fn a_stopped_service_closes_its_ports_and_starts_again() { // Loopback node. let dir = scratch("stop-loopback"); let handle = start_service(config(&dir, "127.0.0.1", 0)).unwrap(); let addr = handle.addr; assert!(answers(addr)); handle.stop(); assert!(!answers(addr), "the port is closed after stop"); let again = start_service(config(&dir, "127.0.0.1", addr.port())).expect("the locks were released"); assert!(answers(again.addr)); again.stop(); // A fleet node (TLS front + beacon) on this machine's private LAN address, when it has one. let Some(ip) = makepad_ai_hub::discovery::network_addresses().into_iter().find(|ip| ip.is_private()) else { eprintln!("no private LAN address here: the fleet-front half is skipped"); return; }; let dir = scratch("stop-fleet"); let fleet = dir.join("fleet"); std::fs::create_dir_all(&fleet).unwrap(); makepad_network::fleet::auth::FleetKey::generate().unwrap().save(&fleet.join(makepad_network::fleet::auth::FLEET_KEY)).unwrap(); let handle = start_service(config(&dir, &ip.to_string(), 0)).unwrap(); let addr = handle.addr; assert!(!addr.ip().is_loopback()); assert!(answers(addr), "the TLS front listens"); handle.stop(); assert!(!answers(addr), "the TLS front is closed after stop"); let again = start_service(config(&dir, &ip.to_string(), addr.port())).expect("same port again"); assert!(answers(again.addr)); again.stop(); }