tokio_ipc 0.1.0

Multi-protocol RPC framework built on top of tokio
Documentation
// Integration tests that spawn examples as separate server/client processes

use anyhow::Result;
use std::process::{Command, Stdio};
use std::time::Duration;
use tokio::time::sleep;

/// Test basic example by spawning server and client as separate processes
#[tokio::test]
async fn test_rpc_simple_separate_processes() -> Result<()> {
    println!("Testing basic with separate server/client processes");

    // Build the example first
    let build_status = Command::new("cargo")
        .args(&["build", "--example", "basic"])
        .current_dir(env!("CARGO_MANIFEST_DIR"))
        .status()?;

    assert!(build_status.success(), "Failed to build basic example");

    // Get the example binary path
    let manifest_dir = std::env::var("CARGO_MANIFEST_DIR")?;
    let example_path = format!("{}/../../target/debug/examples/basic", manifest_dir);

    println!("Starting server process...");

    // Start server in background
    let mut server = Command::new(&example_path)
        .arg("--server")
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .spawn()?;

    // Give server time to start
    sleep(Duration::from_millis(500)).await;

    // Check if server is still running
    if let Some(status) = server.try_wait()? {
        panic!("Server exited early with status: {}", status);
    }

    println!("Starting client process...");

    // Run client
    let client_output = Command::new(&example_path)
        .arg("--client")
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .output()?;

    // Check client succeeded
    assert!(
        client_output.status.success(),
        "Client failed with status: {}\nstdout: {}\nstderr: {}",
        client_output.status,
        String::from_utf8_lossy(&client_output.stdout),
        String::from_utf8_lossy(&client_output.stderr)
    );

    // Verify client output contains expected messages
    let stdout = String::from_utf8_lossy(&client_output.stdout);
    assert!(stdout.contains("Connected!"), "Client didn't connect");
    assert!(stdout.contains("Echo response:"), "Client didn't get echo response");
    assert!(stdout.contains("Add response:"), "Client didn't get add response");
    assert!(stdout.contains("Total calls"), "Client didn't get stats");
    assert!(
        stdout.contains("All requests completed successfully"),
        "Client didn't complete all requests"
    );

    println!("Client completed successfully");

    // Kill server
    server.kill()?;
    server.wait()?;

    println!("Test passed!");

    Ok(())
}

/// Test that demo mode works
#[tokio::test]
async fn test_rpc_simple_demo_mode() -> Result<()> {
    println!("Testing basic in demo mode");

    let manifest_dir = std::env::var("CARGO_MANIFEST_DIR")?;
    let example_path = format!("{}/../../target/debug/examples/basic", manifest_dir);

    // Run in demo mode (no arguments)
    let output = Command::new(&example_path).output()?;

    assert!(
        output.status.success(),
        "Demo mode failed: {}",
        String::from_utf8_lossy(&output.stderr)
    );

    let stdout = String::from_utf8_lossy(&output.stdout);
    assert!(stdout.contains("Demo Mode"), "Not in demo mode");
    assert!(stdout.contains("Echo:"), "Demo didn't echo");
    assert!(stdout.contains("Sum:"), "Demo didn't add");

    println!("Demo mode passed!");

    Ok(())
}

/// Test server handles multiple sequential clients
#[tokio::test]
async fn test_rpc_simple_multiple_clients() -> Result<()> {
    println!("Testing basic with multiple sequential clients");

    let manifest_dir = std::env::var("CARGO_MANIFEST_DIR")?;
    let example_path = format!("{}/../../target/debug/examples/basic", manifest_dir);

    // Start server
    let mut server = Command::new(&example_path)
        .arg("--server")
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .spawn()?;

    sleep(Duration::from_millis(500)).await;

    // Run first client
    println!("Running first client...");
    let client1 = Command::new(&example_path).arg("--client").output()?;

    assert!(client1.status.success(), "First client failed");

    // Run second client
    println!("Running second client...");
    let client2 = Command::new(&example_path).arg("--client").output()?;

    assert!(client2.status.success(), "Second client failed");

    // Verify second client shows increased call count
    let stdout2 = String::from_utf8_lossy(&client2.stdout);
    // Second client should see at least 2 calls from first client
    assert!(stdout2.contains("Total calls"), "Second client didn't get stats");

    println!("Multiple clients passed!");

    // Kill server
    server.kill()?;
    server.wait()?;

    Ok(())
}

/// Test server handles shutdown RPC (though server stays running for Ctrl+C)
#[tokio::test]
async fn test_rpc_simple_shutdown_rpc() -> Result<()> {
    println!("Testing basic shutdown RPC");

    let manifest_dir = std::env::var("CARGO_MANIFEST_DIR")?;
    let example_path = format!("{}/../../target/debug/examples/basic", manifest_dir);

    // Start server
    let mut server = Command::new(&example_path)
        .arg("--server")
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .spawn()?;

    sleep(Duration::from_millis(500)).await;

    // Run client (which sends shutdown request)
    let client_output = Command::new(&example_path).arg("--client").output()?;

    assert!(client_output.status.success(), "Client failed");

    // Verify client got shutdown response
    let stdout = String::from_utf8_lossy(&client_output.stdout);
    assert!(
        stdout.contains("Shutdown response:"),
        "Client didn't get shutdown response"
    );

    // Note: The server receives shutdown request but doesn't actually shut down
    // because it's waiting for Ctrl+C. This is expected behavior.
    // Server should still be running
    sleep(Duration::from_millis(100)).await;
    assert!(
        server.try_wait()?.is_none(),
        "Server should still be running after shutdown RPC"
    );

    println!("Shutdown RPC test passed!");

    // Kill server
    server.kill()?;
    server.wait()?;

    Ok(())
}