eclaw 0.0.13

Personal AI assistant powered by Elph
use std::io::{Read, Write};
use std::net::TcpStream;
use std::process::Command;
use std::time::{Duration, Instant};

fn health_ready(port: u16) -> bool {
    let mut stream = match TcpStream::connect(format!("127.0.0.1:{port}")) {
        Ok(stream) => stream,
        Err(_) => return false,
    };
    let _ = stream.set_read_timeout(Some(Duration::from_millis(500)));
    let request = format!("GET /api/health HTTP/1.1\r\nHost: 127.0.0.1:{port}\r\nConnection: close\r\n\r\n");
    if stream.write_all(request.as_bytes()).is_err() {
        return false;
    }

    let mut buf = [0u8; 512];
    let Ok(n) = stream.read(&mut buf) else {
        return false;
    };
    std::str::from_utf8(&buf[..n])
        .ok()
        .is_some_and(|response| response.contains("200"))
}

fn eclaw_cmd() -> Command {
    Command::new(env!("CARGO_BIN_EXE_eclaw"))
}

fn with_isolated_home(mut cmd: Command) -> (Command, tempfile::TempDir) {
    let tmp = tempfile::tempdir().expect("tempdir");
    let home = tmp.path().join("home");
    let data = tmp.path().join("data");
    cmd.env("ECLAW_HOME", &home);
    cmd.env("ECLAW_DATA_DIR", &data);
    (cmd, tmp)
}

#[test]
fn default_run_starts_server() {
    let port = {
        let listener = std::net::TcpListener::bind("127.0.0.1:0").expect("failed to bind ephemeral port");
        listener.local_addr().expect("failed to read ephemeral port").port()
    };

    let (mut cmd, _tmp) = with_isolated_home(eclaw_cmd());
    let mut child = cmd
        .args(["--port", &port.to_string()])
        .spawn()
        .expect("failed to spawn eclaw");

    let started = Instant::now();
    let mut ready = false;
    while started.elapsed() < Duration::from_secs(5) {
        if health_ready(port) {
            ready = true;
            break;
        }
        std::thread::sleep(Duration::from_millis(50));
    }

    let _ = child.kill();
    let _ = child.wait();

    assert!(ready, "timed out waiting for eclaw /api/health");
}

#[test]
fn doctor_exits_successfully() {
    let (mut cmd, _tmp) = with_isolated_home(eclaw_cmd());
    let output = cmd.arg("doctor").output().expect("failed to run eclaw doctor");
    assert!(output.status.success());
    let stdout = String::from_utf8_lossy(&output.stdout);
    assert!(stdout.contains("not yet implemented"));
}

#[test]
fn unknown_subcommand_fails() {
    let (mut cmd, _tmp) = with_isolated_home(eclaw_cmd());
    let output = cmd
        .arg("not-a-command")
        .output()
        .expect("failed to run eclaw not-a-command");
    assert!(!output.status.success());
}