mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-07 08:27:12 +00:00
Add integration tests for server daemon management
- cmd/server_start: help snapshot, start-already-running error - cmd/server_stop: help snapshot, stop-when-not-running error - cmd/server_status: help snapshot, status-when-not-running error - scenario/server_lifecycle: full start → status → status --json → stop cycle - Remove stale server.rs help test (replaced by per-command files) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e0ac11f1d5
commit
4e84152c39
7 changed files with 271 additions and 49 deletions
|
|
@ -52,6 +52,9 @@ mod secret_set;
|
|||
mod send_analytics;
|
||||
mod send_panic;
|
||||
mod server;
|
||||
mod server_start;
|
||||
mod server_status;
|
||||
mod server_stop;
|
||||
mod start;
|
||||
mod store;
|
||||
mod store_dump;
|
||||
|
|
|
|||
|
|
@ -1,50 +1 @@
|
|||
#[test]
|
||||
#[cfg(feature = "server")]
|
||||
fn help() {
|
||||
use fabro_test::{fabro_snapshot, test_context};
|
||||
|
||||
let context = test_context!();
|
||||
let mut cmd = context.command();
|
||||
cmd.args(["server", "start", "--help"]);
|
||||
fabro_snapshot!(context.filters(), cmd, @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
Start the HTTP API server
|
||||
|
||||
Usage: fabro server start [OPTIONS]
|
||||
|
||||
Options:
|
||||
--debug
|
||||
Enable DEBUG-level logging (default is INFO) [env: FABRO_DEBUG=]
|
||||
--port <PORT>
|
||||
Port to listen on [default: 3000]
|
||||
--host <HOST>
|
||||
Host address to bind to [default: 127.0.0.1]
|
||||
--no-upgrade-check
|
||||
Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
|
||||
--model <MODEL>
|
||||
Override default LLM model
|
||||
--quiet
|
||||
Suppress non-essential output [env: FABRO_QUIET=]
|
||||
--provider <PROVIDER>
|
||||
Override default LLM provider
|
||||
--verbose
|
||||
Enable verbose output [env: FABRO_VERBOSE=]
|
||||
--dry-run
|
||||
Execute with simulated LLM backend
|
||||
--storage-dir <STORAGE_DIR>
|
||||
Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
|
||||
--sandbox <SANDBOX>
|
||||
Sandbox for agent tools
|
||||
--server-url <SERVER_URL>
|
||||
Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
|
||||
--max-concurrent-runs <MAX_CONCURRENT_RUNS>
|
||||
Maximum number of concurrent run executions
|
||||
--config <CONFIG>
|
||||
Path to server config file (default: ~/.fabro/server.toml)
|
||||
-h, --help
|
||||
Print help
|
||||
----- stderr -----
|
||||
");
|
||||
}
|
||||
|
|
|
|||
90
lib/crates/fabro-cli/tests/it/cmd/server_start.rs
Normal file
90
lib/crates/fabro-cli/tests/it/cmd/server_start.rs
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
use fabro_test::{fabro_snapshot, test_context};
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "server")]
|
||||
fn help() {
|
||||
let context = test_context!();
|
||||
let mut cmd = context.command();
|
||||
cmd.args(["server", "start", "--help"]);
|
||||
fabro_snapshot!(context.filters(), cmd, @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
Start the HTTP API server
|
||||
|
||||
Usage: fabro server start [OPTIONS]
|
||||
|
||||
Options:
|
||||
--foreground
|
||||
Run in the foreground instead of daemonizing
|
||||
--json
|
||||
Output as JSON [env: FABRO_JSON=]
|
||||
--bind <BIND>
|
||||
Address to bind to (host:port for TCP, or path containing / for Unix socket)
|
||||
--debug
|
||||
Enable DEBUG-level logging (default is INFO) [env: FABRO_DEBUG=]
|
||||
--model <MODEL>
|
||||
Override default LLM model
|
||||
--no-upgrade-check
|
||||
Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
|
||||
--provider <PROVIDER>
|
||||
Override default LLM provider
|
||||
--quiet
|
||||
Suppress non-essential output [env: FABRO_QUIET=]
|
||||
--dry-run
|
||||
Execute with simulated LLM backend
|
||||
--verbose
|
||||
Enable verbose output [env: FABRO_VERBOSE=]
|
||||
--sandbox <SANDBOX>
|
||||
Sandbox for agent tools
|
||||
--storage-dir <STORAGE_DIR>
|
||||
Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
|
||||
--max-concurrent-runs <MAX_CONCURRENT_RUNS>
|
||||
Maximum number of concurrent run executions
|
||||
--server-url <SERVER_URL>
|
||||
Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
|
||||
--config <CONFIG>
|
||||
Path to server config file (default: ~/.fabro/server.toml)
|
||||
-h, --help
|
||||
Print help
|
||||
----- stderr -----
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "server")]
|
||||
fn start_already_running_exits_with_error() {
|
||||
let context = test_context!();
|
||||
|
||||
let sock_dir = tempfile::tempdir_in("/tmp").unwrap();
|
||||
let bind_addr = sock_dir.path().join("test.sock");
|
||||
let bind_str = bind_addr.to_string_lossy().to_string();
|
||||
|
||||
// Start the daemon
|
||||
context
|
||||
.command()
|
||||
.args(["server", "start", "--dry-run", "--bind", &bind_str])
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
// Try to start again -- should fail with "already running"
|
||||
let mut filters = context.filters();
|
||||
filters.push((r"pid \d+".to_string(), "pid [PID]".to_string()));
|
||||
filters.push((regex::escape(&bind_str), "[SOCKET_PATH]".to_string()));
|
||||
let mut cmd = context.command();
|
||||
cmd.args(["server", "start", "--dry-run", "--bind", &bind_str]);
|
||||
fabro_snapshot!(filters, cmd, @"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
----- stderr -----
|
||||
error: Server already running (pid [PID]) on [SOCKET_PATH]
|
||||
");
|
||||
|
||||
// Clean up: stop the server
|
||||
context
|
||||
.command()
|
||||
.args(["server", "stop"])
|
||||
.assert()
|
||||
.success();
|
||||
}
|
||||
43
lib/crates/fabro-cli/tests/it/cmd/server_status.rs
Normal file
43
lib/crates/fabro-cli/tests/it/cmd/server_status.rs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
use fabro_test::{fabro_snapshot, test_context};
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "server")]
|
||||
fn help() {
|
||||
let context = test_context!();
|
||||
let mut cmd = context.command();
|
||||
cmd.args(["server", "status", "--help"]);
|
||||
fabro_snapshot!(context.filters(), cmd, @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
Show server status
|
||||
|
||||
Usage: fabro server status [OPTIONS]
|
||||
|
||||
Options:
|
||||
--json Output as JSON
|
||||
--debug Enable DEBUG-level logging (default is INFO) [env: FABRO_DEBUG=]
|
||||
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
|
||||
--quiet Suppress non-essential output [env: FABRO_QUIET=]
|
||||
--verbose Enable verbose output [env: FABRO_VERBOSE=]
|
||||
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
|
||||
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
|
||||
-h, --help Print help
|
||||
----- stderr -----
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "server")]
|
||||
fn status_when_not_running() {
|
||||
let context = test_context!();
|
||||
let mut cmd = context.command();
|
||||
cmd.args(["server", "status"]);
|
||||
fabro_snapshot!(context.filters(), cmd, @"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
----- stderr -----
|
||||
Server is not running
|
||||
");
|
||||
}
|
||||
44
lib/crates/fabro-cli/tests/it/cmd/server_stop.rs
Normal file
44
lib/crates/fabro-cli/tests/it/cmd/server_stop.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
use fabro_test::{fabro_snapshot, test_context};
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "server")]
|
||||
fn help() {
|
||||
let context = test_context!();
|
||||
let mut cmd = context.command();
|
||||
cmd.args(["server", "stop", "--help"]);
|
||||
fabro_snapshot!(context.filters(), cmd, @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
Stop the HTTP API server
|
||||
|
||||
Usage: fabro server stop [OPTIONS]
|
||||
|
||||
Options:
|
||||
--json Output as JSON [env: FABRO_JSON=]
|
||||
--timeout <TIMEOUT> Seconds to wait for graceful shutdown before SIGKILL [default: 10]
|
||||
--debug Enable DEBUG-level logging (default is INFO) [env: FABRO_DEBUG=]
|
||||
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
|
||||
--quiet Suppress non-essential output [env: FABRO_QUIET=]
|
||||
--verbose Enable verbose output [env: FABRO_VERBOSE=]
|
||||
--storage-dir <STORAGE_DIR> Storage directory (default: ~/.fabro) [env: FABRO_STORAGE_DIR=[STORAGE_DIR]]
|
||||
--server-url <SERVER_URL> Server URL (overrides server.base_url from user.toml) [env: FABRO_SERVER_URL=]
|
||||
-h, --help Print help
|
||||
----- stderr -----
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "server")]
|
||||
fn stop_when_not_running() {
|
||||
let context = test_context!();
|
||||
let mut cmd = context.command();
|
||||
cmd.args(["server", "stop"]);
|
||||
fabro_snapshot!(context.filters(), cmd, @"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
----- stderr -----
|
||||
Server is not running
|
||||
");
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
mod exec;
|
||||
mod lifecycle;
|
||||
mod recovery;
|
||||
mod server_lifecycle;
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
|
|
|
|||
90
lib/crates/fabro-cli/tests/it/scenario/server_lifecycle.rs
Normal file
90
lib/crates/fabro-cli/tests/it/scenario/server_lifecycle.rs
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
use fabro_test::{fabro_snapshot, test_context};
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "server")]
|
||||
fn start_status_stop_lifecycle() {
|
||||
let context = test_context!();
|
||||
|
||||
let sock_dir = tempfile::tempdir_in("/tmp").unwrap();
|
||||
let bind_addr = sock_dir.path().join("test.sock");
|
||||
let bind_str = bind_addr.to_string_lossy().to_string();
|
||||
|
||||
let mut filters = context.filters();
|
||||
filters.push((r"pid \d+".to_string(), "pid [PID]".to_string()));
|
||||
filters.push((regex::escape(&bind_str), "[SOCKET_PATH]".to_string()));
|
||||
filters.push((
|
||||
r"started \d+[hms] (?:\d+[hms] )*ago".to_string(),
|
||||
"started [UPTIME] ago".to_string(),
|
||||
));
|
||||
|
||||
// Start the server as a daemon with a Unix socket in a short path
|
||||
let mut cmd = context.command();
|
||||
cmd.args(["server", "start", "--dry-run", "--bind", &bind_str]);
|
||||
fabro_snapshot!(filters.clone(), cmd, @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
----- stderr -----
|
||||
Server started (pid [PID]) on [SOCKET_PATH]
|
||||
");
|
||||
|
||||
// Verify server.json is written in storage_dir
|
||||
assert!(
|
||||
context.storage_dir.join("server.json").exists(),
|
||||
"server.json should exist after start"
|
||||
);
|
||||
|
||||
// Status should report running
|
||||
let mut cmd = context.command();
|
||||
cmd.args(["server", "status"]);
|
||||
fabro_snapshot!(filters.clone(), cmd, @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
----- stderr -----
|
||||
Server running (pid [PID]) on [SOCKET_PATH], started [UPTIME] ago
|
||||
");
|
||||
|
||||
// Status --json should produce valid JSON with "running" status
|
||||
let status_output = context
|
||||
.command()
|
||||
.args(["server", "status", "--json"])
|
||||
.assert()
|
||||
.success();
|
||||
let stdout = String::from_utf8(status_output.get_output().stdout.clone()).unwrap();
|
||||
let json: serde_json::Value =
|
||||
serde_json::from_str(&stdout).expect("status --json should be valid JSON");
|
||||
assert_eq!(
|
||||
json["status"].as_str(),
|
||||
Some("running"),
|
||||
"status should be running"
|
||||
);
|
||||
|
||||
// Stop the server
|
||||
let mut cmd = context.command();
|
||||
cmd.args(["server", "stop"]);
|
||||
fabro_snapshot!(filters.clone(), cmd, @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
----- stderr -----
|
||||
Server stopped
|
||||
");
|
||||
|
||||
// Status should report not running
|
||||
let mut cmd = context.command();
|
||||
cmd.args(["server", "status"]);
|
||||
fabro_snapshot!(filters.clone(), cmd, @"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
----- stderr -----
|
||||
Server is not running
|
||||
");
|
||||
|
||||
// Verify server.json is cleaned up
|
||||
assert!(
|
||||
!context.storage_dir.join("server.json").exists(),
|
||||
"server.json should be removed after stop"
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue