use super::dry_run_fixture::Fixture;
use alloy::primitives::Address;
use alloy::sol_types::SolCall;
use crate::order_types::{IntentSettlerV3, UserProxyV6};
use std::process::Command;
fn check_cli(is_intent: bool, requested: bool) {
let fixture = Fixture::start();
let directory = std::env::temp_dir().join(format!("agentswap-e2e-{}-{is_intent}-{requested}", std::process::id()));
std::fs::create_dir(&directory).unwrap();
let key = directory.join("test-key");
std::fs::write(&key, "01".repeat(32)).unwrap();
for json in [true, false] {
let mut command = Command::new(std::env::var("AGENTSWAP_E2E_BIN").expect("remote CLI executable"));
command.env_clear().env("AGENTSWAP_RPC_URL_8453", &fixture.url).current_dir(&directory);
command.args(["--url", &fixture.url, "--api-key", "test", "--key-file"]).arg(&key);
if json { command.arg("--json"); }
if requested { command.arg("--allow-trade"); }
if is_intent {
command.args(["intent", "place", "--proxy-owner", &format!("{:?}", Address::repeat_byte(4)),
"--start-out", "2", "--end-out", "1", "--relay"]);
} else {
command.args(["trade", "--proxy", &format!("{:?}", Address::repeat_byte(2)), "--self-submit"]);
}
command.args(["--chainid", "8453", "--from", "USDC", "--to", "WETH", "--amount", "1"]);
if requested { command.arg("--dry-run"); }
let output = command.output().expect("CLI process");
assert!(output.status.success(), "{}", String::from_utf8_lossy(&output.stderr));
assert_preview(&String::from_utf8(output.stdout).unwrap(), json, is_intent);
}
std::fs::remove_dir_all(directory).unwrap();
let calls = fixture.calls.lock().unwrap();
let hash = if is_intent { UserProxyV6::hashIntentAuthorizationCall::SELECTOR } else { UserProxyV6::hashAgentOrderCall::SELECTOR };
assert!(calls.contains(&hex::encode(hash)), "digest parity RPC missing");
if is_intent { assert!(calls.contains(&hex::encode(IntentSettlerV3::orderHashCall::SELECTOR))); }
assert!(!calls.contains(&crate::routes::INTENT_ANNOUNCE.to_string()));
assert!(!calls.contains(&hex::encode(UserProxyV6::isIntentAuthorizedCall::SELECTOR)));
}
fn assert_preview(output: &str, json: bool, is_intent: bool) {
if json {
let value: serde_json::Value = serde_json::from_str(output).expect("CLI JSON");
assert_eq!(value["dry_run"], true);
for field in ["signature", "envelope", "self_submit"] {
assert!(value.get(field).is_none(), "CLI dry-run released {field}");
}
assert!(value["order"].is_object());
assert_eq!(value["digest"].as_str().unwrap().len(), 66);
if is_intent { assert!(value["authorization"].is_object()); }
else { assert_eq!(value["quote"]["output"], "1000"); }
return;
}
assert!(output.contains("Digest"));
assert!(output.contains("true"));
for label in ["Signature", "Envelope", "Self Submit Calldata"] {
assert!(!output.contains(label), "CLI dry-run displayed {label}");
}
}
#[test]
#[ignore = "requires remote AGENTSWAP_E2E_BIN"]
fn requested_trade_cli_dry_run() { check_cli(false, true); }
#[test]
#[ignore = "requires remote AGENTSWAP_E2E_BIN"]
fn forced_trade_cli_dry_run() { check_cli(false, false); }
#[test]
#[ignore = "requires remote AGENTSWAP_E2E_BIN"]
fn requested_intent_cli_dry_run() { check_cli(true, true); }
#[test]
#[ignore = "requires remote AGENTSWAP_E2E_BIN"]
fn forced_intent_cli_dry_run() { check_cli(true, false); }
#[test]
#[ignore = "requires a CLI binary built on an authorized remote host"]
fn arc_testnet_cli_log_windows_preserve_other_v6_chains() {
let executable = std::env::current_exe().unwrap().parent().unwrap().parent().unwrap().join("agentswap");
for alias in ["arc", "arc-mainnet", "5042", "arc-testnet", "arct", "5042002", "base", "arb", "bsc", "rh"] {
let chain_id = crate::tokens::chain_name_to_id(alias).unwrap();
for policy in [false, true] {
let fixture = Fixture::start();
let mut command = Command::new(&executable);
command.env_clear().env(format!("AGENTSWAP_RPC_URL_{chain_id}"), &fixture.url);
command.arg("--json");
if policy { command.arg("policy"); } else { command.args(["intent", "list"]); }
command.args(["--chainid", alias, "--owner", &format!("{:?}", Address::repeat_byte(4))]);
if policy { command.args(["--agent", &format!("{:?}", Address::repeat_byte(5))]); }
let output = command.output().expect("CLI process");
assert!(output.status.success(), "{alias}: {}", String::from_utf8_lossy(&output.stderr));
let value: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap();
if policy { assert_eq!(value["tokens"], serde_json::json!([])); }
else { assert_eq!(value, serde_json::json!([])); }
let calls = fixture.calls.lock().unwrap();
let queries: Vec<serde_json::Value> = calls.iter()
.filter_map(|call| serde_json::from_str(call).ok()).collect();
assert_eq!(queries.len(), 41, "{alias}: default lookback changed");
let mut next_block = 50_000;
for query in queries {
let filter = &query["params"][0];
let from = u64::from_str_radix(filter["fromBlock"].as_str().unwrap().trim_start_matches("0x"), 16).unwrap();
let to = u64::from_str_radix(filter["toBlock"].as_str().unwrap().trim_start_matches("0x"), 16).unwrap();
assert_eq!(from, next_block, "{alias}: gap or overlap");
assert!(to >= from && to - from < 5_000, "{alias}: oversized log request");
next_block = to + 1;
}
assert_eq!(next_block, 250_001, "{alias}: incomplete lookback");
}
}
}