//! Tests for the `loopd serve` command. Do not edit. mod support; use std::os::unix::fs::PermissionsExt; use std::process::{Command, Stdio}; use std::time::Duration; use support::{FakeServer, Home, Reply}; fn config_file(home: &Home, server: &FakeServer, expect_slots: u32) -> std::path::PathBuf { let text = format!( r#" [infer] socket = "{}" model = "test-model" [slots] main = 0 background = 1 [expect] template_sha256 = "f55f52930aa8bf44ab5cb85f99370fcc3c56e9a85640b812086d5330bce5d86b" n_ctx = 131072 slots = {expect_slots} [limits] poll_ms = 40 liveness_ms = 500 retry_backoff_ms = [10] [paths] home = "{}" "#, server.socket.display(), home.dir.display() ); let path = home.dir.join("config.toml"); std::fs::write(&path, text).unwrap(); path } fn healthy_routes(server: &FakeServer) { server.route("/props", vec![Reply::fixture("props")]); server.route( "/v1/chat/completions", vec![ Reply::fixture("tool_call"), Reply::fixture("turn1"), Reply::fixture("turn2"), Reply::fixture("plain"), ], ); } #[test] fn serve_runs_the_self_test_then_binds_the_socket_with_mode_0600() { let home = Home::new(); let server = FakeServer::start(); healthy_routes(&server); let config = config_file(&home, &server, 2); let socket = home.dir.join("run").join("loop").join("loop.sock"); let mut child = Command::new(env!("CARGO_BIN_EXE_loopd")) .args(["serve", "--config"]) .arg(&config) .stderr(Stdio::piped()) .spawn() .unwrap(); let mut up = false; for _ in 0..200 { if socket.exists() { up = true; break; } std::thread::sleep(Duration::from_millis(50)); } let mode = std::fs::metadata(&socket).map(|m| m.permissions().mode() & 0o777); let _ = child.kill(); let output = child.wait_with_output().unwrap(); let stderr = String::from_utf8_lossy(&output.stderr); assert!(up, "the socket never appeared; stderr: {stderr}"); assert_eq!(mode.unwrap(), 0o600, "{stderr}"); assert!(stderr.contains("selftest: ok"), "{stderr}"); assert!(stderr.contains("serving on"), "{stderr}"); assert_eq!( server.requests_to("/v1/chat/completions").len(), 3, "the three self-test completions ran" ); } #[test] fn serve_refuses_to_start_when_the_self_test_fails() { let home = Home::new(); let server = FakeServer::start(); healthy_routes(&server); let config = config_file(&home, &server, 3); // the fixture reports 2 slots let output = Command::new(env!("CARGO_BIN_EXE_loopd")) .args(["serve", "--config"]) .arg(&config) .output() .unwrap(); let stderr = String::from_utf8_lossy(&output.stderr); assert_eq!(output.status.code(), Some(1), "{stderr}"); assert!(stderr.contains("selftest: FAILED"), "{stderr}"); assert!(stderr.contains("slot count"), "{stderr}"); assert!( !home.dir.join("run").join("loop").join("loop.sock").exists(), "no socket was left behind" ); } #[test] fn usage_and_a_bad_config_are_reported() { let output = Command::new(env!("CARGO_BIN_EXE_loopd")) .arg("dance") .output() .unwrap(); assert_eq!(output.status.code(), Some(2)); assert!(String::from_utf8_lossy(&output.stderr).contains("usage")); let output = Command::new(env!("CARGO_BIN_EXE_loopd")) .args(["serve", "--config", "/nonexistent/config.toml"]) .output() .unwrap(); assert_eq!(output.status.code(), Some(1)); assert!(String::from_utf8_lossy(&output.stderr).contains("config.toml")); }