use fabro_test::{fabro_snapshot, test_context}; use httpmock::MockServer; use serde_json::{Value, json}; use super::support::{remote_run_summary_json, setup_local_sandbox_run}; use crate::support::unique_run_id; fn remote_run_summary(run_id: &str) -> serde_json::Value { remote_run_summary_json( run_id, "Preview Test", "preview-test", "Preview test", &json!({ "kind": "running" }), "2026-04-19T12:00:00Z", ) } #[test] fn help() { let context = test_context!(); let mut cmd = context.command(); cmd.args(["sandbox", "preview", "--help"]); fabro_snapshot!(context.filters(), cmd, @" success: true exit_code: 0 ----- stdout ----- Get a preview URL for a port on a run's sandbox Usage: fabro sandbox preview [OPTIONS] Arguments: Run ID or prefix Port number Options: --json Output as JSON [env: FABRO_JSON=] --server Fabro server target: http(s) URL or absolute Unix socket path [env: FABRO_SERVER=] --debug Enable DEBUG-level logging (default is INFO) [env: FABRO_DEBUG=] --signed Generate a signed URL (embeds auth token, no headers needed) --no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true] --ttl Signed URL expiry in seconds (default 3600, requires --signed) [default: 3600] --open Open URL in browser (implies --signed) --quiet Suppress non-essential output [env: FABRO_QUIET=] --verbose Enable verbose output [env: FABRO_VERBOSE=] -h, --help Print help ----- stderr ----- "); } #[test] fn sandbox_preview_rejects_non_daytona_run() { let context = test_context!(); let setup = setup_local_sandbox_run(&context); let mut cmd = context.preview(); cmd.args([&setup.run.run_id, "3000"]); fabro_snapshot!(context.filters(), cmd, @" success: false exit_code: 1 ----- stdout ----- ----- stderr ----- × Sandbox provider does not support this capability. "); } #[test] fn sandbox_preview_open_is_suppressed_by_json_output_format_from_home_config() { let context = test_context!(); context.write_home( ".fabro/settings.toml", "_version = 1\n\n[cli.output]\nformat = \"json\"\n", ); let server = MockServer::start(); let run_id = unique_run_id(); let resolve_run = server.mock(|when, then| { when.method("GET") .path("/api/v1/runs/resolve") .query_param("selector", run_id.as_str()); then.status(200) .header("content-type", "application/json") .body(remote_run_summary(&run_id).to_string()); }); let preview = server.mock(|when, then| { when.method("POST") .path(format!("/api/v1/runs/{run_id}/preview")) .json_body(json!({ "port": 3000, "expires_in_secs": 3600, "signed": true, })); then.status(201) .header("content-type", "application/json") .body(json!({ "url": "https://preview.example.test/app" }).to_string()); }); let mut cmd = context.preview(); cmd.args([ "--server", &format!("{}/api/v1", server.base_url()), "--open", run_id.as_str(), "3000", ]); let output = cmd.output().expect("command should run"); assert!(output.status.success(), "sandbox preview should succeed"); let value: Value = serde_json::from_slice(&output.stdout).expect("preview JSON should parse"); assert_eq!( value, json!({ "url": "https://preview.example.test/app" }) ); resolve_run.assert(); preview.assert(); }