use fabro_test::{fabro_snapshot, test_context}; #[test] fn start_status_stop_lifecycle() { let context = test_context!(); let storage_root = tempfile::tempdir_in("/tmp").unwrap(); let storage_dir = storage_root.path().join("storage"); std::fs::create_dir_all(&storage_dir).unwrap(); context.write_home( ".fabro/settings.toml", "[server.auth]\nmethods = [\"dev-token\"]\n", ); let runtime_directory = fabro_config::Storage::new(&storage_dir).runtime_directory(); let server_env_path = runtime_directory.env_path(); fabro_config::envfile::merge_env_file(&server_env_path, [ ( "FABRO_DEV_TOKEN", "fabro_dev_abababababababababababababababababababababababababababababababab", ), ( "SESSION_SECRET", "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", ), ]) .unwrap(); fabro_util::dev_token::write_dev_token( &runtime_directory.dev_token_path(), "fabro_dev_abababababababababababababababababababababababababababababababab", ) .unwrap(); 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(), )); filters.push(( r"fabro_dev_[0-9a-f]{64}".to_string(), "fabro_dev_[DEV_TOKEN]".to_string(), )); let mut cmd = context.command(); cmd.env("FABRO_STORAGE_DIR", &storage_dir); cmd.args(["server", "start", "--bind", &bind_str]); fabro_snapshot!(filters.clone(), cmd, @" success: true exit_code: 0 ----- stdout ----- ----- stderr ----- Server started (pid [PID]) on [SOCKET_PATH] Auth: dev-token "); let mut cmd = context.command(); cmd.env("FABRO_STORAGE_DIR", &storage_dir); 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 "); let status_output = context .command() .env("FABRO_STORAGE_DIR", &storage_dir) .args(["server", "status", "--json"]) .assert() .success(); let stdout = std::str::from_utf8(&status_output.get_output().stdout) .expect("status --json stdout should be valid UTF-8"); 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" ); let mut cmd = context.command(); cmd.env("FABRO_STORAGE_DIR", &storage_dir); cmd.args(["server", "stop"]); fabro_snapshot!(filters.clone(), cmd, @" success: true exit_code: 0 ----- stdout ----- ----- stderr ----- Server stopped "); let mut cmd = context.command(); cmd.env("FABRO_STORAGE_DIR", &storage_dir); cmd.args(["server", "status"]); fabro_snapshot!(filters, cmd, @" success: false exit_code: 1 ----- stdout ----- ----- stderr ----- Server is not running "); assert!( !storage_dir.join("server.json").exists(), "server.json should be removed after stop" ); }