commit 83264c4da582d810e0142921f1c841bd92652b15
parent 69fe855a4a5aac65435916f3b20c8c859f964086
Author: triesap <tyson@radroots.org>
Date: Mon, 27 Apr 2026 07:17:03 +0000
tests: share cli integration harness
Diffstat:
3 files changed, 258 insertions(+), 287 deletions(-)
diff --git a/tests/signer_runtime_modes.rs b/tests/signer_runtime_modes.rs
@@ -1,12 +1,15 @@
mod support;
use std::fs;
-use std::path::{Path, PathBuf};
+use std::path::Path;
use radroots_events::kinds::KIND_LISTING;
-use radroots_identity::{RadrootsIdentity, RadrootsIdentityPublic};
+use radroots_identity::RadrootsIdentityPublic;
use serde_json::{Value, json};
-use support::RadrootsCliSandbox;
+use support::{
+ RadrootsCliSandbox, assert_contains, assert_hex_len, create_listing_draft, identity_public,
+ make_listing_publishable, shell_single_quoted, toml_string, write_public_identity_profile,
+};
#[test]
fn local_signer_status_reports_unconfigured_without_account() {
@@ -667,116 +670,3 @@ fn remote_session(
"permissions": permissions
})
}
-
-fn identity_public(seed: u8) -> RadrootsIdentityPublic {
- let secret = [seed; 32];
- RadrootsIdentity::from_secret_key_bytes(&secret)
- .expect("fixture identity")
- .to_public()
-}
-
-fn write_public_identity_profile(
- sandbox: &RadrootsCliSandbox,
- name: &str,
- identity: &RadrootsIdentityPublic,
-) -> PathBuf {
- let path = sandbox.root().join(format!("{name}.json"));
- fs::write(
- &path,
- serde_json::to_string_pretty(identity).expect("public identity json"),
- )
- .expect("write public identity");
- path
-}
-
-fn shell_single_quoted(value: &str) -> String {
- value.replace('\'', "'\"'\"'")
-}
-
-fn toml_string(value: &str) -> String {
- value.replace('\\', "\\\\").replace('"', "\\\"")
-}
-
-fn assert_contains(value: &Value, needle: &str) {
- let value = value.as_str().expect("string value");
- assert!(
- value.contains(needle),
- "expected `{value}` to contain `{needle}`"
- );
-}
-
-fn assert_hex_len(value: &Value, expected_len: usize) {
- let value = value.as_str().expect("hex string");
- assert_eq!(value.len(), expected_len);
- assert!(value.chars().all(|ch| ch.is_ascii_hexdigit()));
-}
-
-fn create_listing_draft(sandbox: &RadrootsCliSandbox, key: &str) -> PathBuf {
- let listing_file = sandbox.root().join(format!("{key}.toml"));
- let listing_file_arg = listing_file.to_string_lossy();
- let value = sandbox.json_success(&[
- "--format",
- "json",
- "listing",
- "create",
- "--output",
- listing_file_arg.as_ref(),
- "--key",
- key,
- "--title",
- "Eggs",
- "--category",
- "eggs",
- "--summary",
- "Fresh eggs",
- "--bin-id",
- "bin-1",
- "--quantity-amount",
- "1",
- "--quantity-unit",
- "each",
- "--price-amount",
- "6",
- "--price-currency",
- "USD",
- "--price-per-amount",
- "1",
- "--price-per-unit",
- "each",
- "--available",
- "10",
- ]);
- assert_eq!(value["operation_id"], "listing.create");
- listing_file
-}
-
-fn make_listing_publishable(path: &Path, farm_d_tag: &str) {
- let raw = fs::read_to_string(path).expect("listing draft");
- let mut seller_pubkey_present = false;
- let patched = raw
- .lines()
- .map(|line| {
- let trimmed = line.trim_start();
- if trimmed.starts_with("seller_pubkey =") {
- seller_pubkey_present = !trimmed.ends_with("\"\"");
- line.to_owned()
- } else if trimmed.starts_with("farm_d_tag =") {
- format!("{}farm_d_tag = \"{}\"", line_indent(line), farm_d_tag)
- } else if trimmed.starts_with("method =") {
- format!("{}method = \"pickup\"", line_indent(line))
- } else if trimmed.starts_with("primary =") {
- format!("{}primary = \"farmstand\"", line_indent(line))
- } else {
- line.to_owned()
- }
- })
- .collect::<Vec<_>>()
- .join("\n");
- assert!(seller_pubkey_present, "listing draft seller pubkey");
- fs::write(path, format!("{patched}\n")).expect("write listing draft");
-}
-
-fn line_indent(line: &str) -> &str {
- let trimmed = line.trim_start();
- &line[..line.len() - trimmed.len()]
-}
diff --git a/tests/support/mod.rs b/tests/support/mod.rs
@@ -6,6 +6,7 @@ use std::process::{Command, Output};
use std::sync::Mutex;
use assert_cmd::prelude::*;
+use radroots_identity::{RadrootsIdentity, RadrootsIdentityPublic};
use serde_json::Value;
use tempfile::TempDir;
@@ -101,3 +102,140 @@ impl RadrootsCliSandbox {
command.env("RADROOTS_ACCOUNT_SECRET_FALLBACK", "none");
}
}
+
+pub fn assert_no_removed_command_reference(value: &Value, args: &[&str]) {
+ let raw = serde_json::to_string(value).expect("json value");
+ for removed in [
+ "radroots setup",
+ "radroots status",
+ "radroots doctor",
+ "radroots sell",
+ "radroots find",
+ "radroots local",
+ "radroots net",
+ "radroots myc",
+ "radroots rpc",
+ "radroots product",
+ "radroots message",
+ "radroots approval",
+ "radroots agent",
+ ] {
+ assert!(
+ !raw.contains(removed),
+ "`{args:?}` output should not contain removed command reference `{removed}`: {raw}"
+ );
+ }
+}
+
+pub fn assert_contains(value: &Value, needle: &str) {
+ let value = value.as_str().expect("string value");
+ assert!(
+ value.contains(needle),
+ "expected `{value}` to contain `{needle}`"
+ );
+}
+
+pub fn assert_hex_len(value: &Value, expected_len: usize) {
+ let value = value.as_str().expect("hex string");
+ assert_eq!(value.len(), expected_len);
+ assert!(value.chars().all(|ch| ch.is_ascii_hexdigit()));
+}
+
+pub fn create_listing_draft(sandbox: &RadrootsCliSandbox, key: &str) -> PathBuf {
+ let listing_file = sandbox.root().join(format!("{key}.toml"));
+ let listing_file_arg = listing_file.to_string_lossy();
+ let value = sandbox.json_success(&[
+ "--format",
+ "json",
+ "listing",
+ "create",
+ "--output",
+ listing_file_arg.as_ref(),
+ "--key",
+ key,
+ "--title",
+ "Eggs",
+ "--category",
+ "eggs",
+ "--summary",
+ "Fresh eggs",
+ "--bin-id",
+ "bin-1",
+ "--quantity-amount",
+ "1",
+ "--quantity-unit",
+ "each",
+ "--price-amount",
+ "6",
+ "--price-currency",
+ "USD",
+ "--price-per-amount",
+ "1",
+ "--price-per-unit",
+ "each",
+ "--available",
+ "10",
+ ]);
+ assert_eq!(value["operation_id"], "listing.create");
+ listing_file
+}
+
+pub fn identity_public(seed: u8) -> RadrootsIdentityPublic {
+ let secret = [seed; 32];
+ RadrootsIdentity::from_secret_key_bytes(&secret)
+ .expect("fixture identity")
+ .to_public()
+}
+
+pub fn make_listing_publishable(path: &Path, farm_d_tag: &str) {
+ let raw = fs::read_to_string(path).expect("listing draft");
+ let mut seller_pubkey_present = false;
+ let patched = raw
+ .lines()
+ .map(|line| {
+ let trimmed = line.trim_start();
+ if trimmed.starts_with("seller_pubkey =") {
+ seller_pubkey_present = !trimmed.ends_with("\"\"");
+ line.to_owned()
+ } else if trimmed.starts_with("farm_d_tag =") {
+ format!("{}farm_d_tag = \"{}\"", line_indent(line), farm_d_tag)
+ } else if trimmed.starts_with("method =") {
+ format!("{}method = \"pickup\"", line_indent(line))
+ } else if trimmed.starts_with("primary =") {
+ format!("{}primary = \"farmstand\"", line_indent(line))
+ } else {
+ line.to_owned()
+ }
+ })
+ .collect::<Vec<_>>()
+ .join("\n");
+ assert!(seller_pubkey_present, "listing draft seller pubkey");
+ fs::write(path, format!("{patched}\n")).expect("write listing draft");
+}
+
+pub fn shell_single_quoted(value: &str) -> String {
+ value.replace('\'', "'\"'\"'")
+}
+
+pub fn toml_string(value: &str) -> String {
+ value.replace('\\', "\\\\").replace('"', "\\\"")
+}
+
+pub fn write_public_identity_profile(
+ sandbox: &RadrootsCliSandbox,
+ name: &str,
+ identity: &RadrootsIdentityPublic,
+) -> PathBuf {
+ let path = sandbox.root().join(format!("{name}.json"));
+ fs::write(
+ &path,
+ serde_json::to_string_pretty(identity).expect("public identity json"),
+ )
+ .expect("write public identity");
+ path
+}
+
+fn line_indent(line: &str) -> &str {
+ let trimmed = line.trim_start();
+ &line[..line.len() - trimmed.len()]
+}
diff --git a/tests/target_cli.rs b/tests/target_cli.rs
@@ -2,15 +2,11 @@ mod support;
use serde_json::Value;
-use support::{RadrootsCliSandbox, radroots};
+use support::{RadrootsCliSandbox, assert_no_removed_command_reference, radroots};
const LISTING_ADDR: &str =
"30402:1111111111111111111111111111111111111111111111111111111111111111:AAAAAAAAAAAAAAAAAAAAAg";
-fn json_success(sandbox: &RadrootsCliSandbox, args: &[&str]) -> Value {
- sandbox.json_success(args)
-}
-
#[test]
fn root_help_exposes_only_target_namespaces() {
let output = radroots().arg("--help").output().expect("run root help");
@@ -114,35 +110,11 @@ fn target_outputs_do_not_suggest_removed_command_families() {
]
.as_slice(),
] {
- let value = json_success(&sandbox, args);
+ let value = sandbox.json_success(args);
assert_no_removed_command_reference(&value, args);
}
}
-fn assert_no_removed_command_reference(value: &Value, args: &[&str]) {
- let raw = serde_json::to_string(value).expect("json value");
- for removed in [
- "radroots setup",
- "radroots status",
- "radroots doctor",
- "radroots sell",
- "radroots find",
- "radroots local",
- "radroots net",
- "radroots myc",
- "radroots rpc",
- "radroots product",
- "radroots message",
- "radroots approval",
- "radroots agent",
- ] {
- assert!(
- !raw.contains(removed),
- "`{args:?}` output should not contain removed command reference `{removed}`: {raw}"
- );
- }
-}
-
#[test]
fn account_id_global_populates_envelope_actor() {
let output = radroots()
@@ -220,18 +192,15 @@ fn offline_allows_supported_external_dry_run() {
let listing_file = sandbox.root().join("listing.toml");
let listing_file = listing_file.to_string_lossy().into_owned();
- let publish = json_success(
- &sandbox,
- &[
- "--format",
- "json",
- "--offline",
- "--dry-run",
- "listing",
- "publish",
- listing_file.as_str(),
- ],
- );
+ let publish = sandbox.json_success(&[
+ "--format",
+ "json",
+ "--offline",
+ "--dry-run",
+ "listing",
+ "publish",
+ listing_file.as_str(),
+ ]);
assert_eq!(publish["operation_id"], "listing.publish");
assert_eq!(publish["result"]["state"], "dry_run");
@@ -255,10 +224,13 @@ fn online_requires_relay_for_external_network_operations() {
#[test]
fn online_allows_local_diagnostics() {
- let value = json_success(
- &RadrootsCliSandbox::new(),
- &["--format", "json", "--online", "workspace", "get"],
- );
+ let value = RadrootsCliSandbox::new().json_success(&[
+ "--format",
+ "json",
+ "--online",
+ "workspace",
+ "get",
+ ]);
assert_eq!(value["operation_id"], "workspace.get");
assert_eq!(value["errors"].as_array().expect("errors").len(), 0);
@@ -283,58 +255,46 @@ fn required_approval_missing_token_returns_structured_error() {
fn buyer_target_flow_acceptance_uses_target_operations() {
let sandbox = RadrootsCliSandbox::new();
- let search = json_success(
- &sandbox,
- &["--format", "json", "market", "product", "search", "eggs"],
- );
+ let search = sandbox.json_success(&["--format", "json", "market", "product", "search", "eggs"]);
assert_eq!(search["operation_id"], "market.product.search");
assert_eq!(search["errors"].as_array().expect("errors").len(), 0);
- let create = json_success(
- &sandbox,
- &["--format", "json", "basket", "create", "basket_flow"],
- );
+ let create = sandbox.json_success(&["--format", "json", "basket", "create", "basket_flow"]);
assert_eq!(create["operation_id"], "basket.create");
assert_eq!(create["result"]["basket_id"], "basket_flow");
- let add = json_success(
- &sandbox,
- &[
- "--format",
- "json",
- "basket",
- "item",
- "add",
- "basket_flow",
- "--listing-addr",
- LISTING_ADDR,
- "--bin-id",
- "bin-1",
- "--quantity",
- "2",
- ],
- );
+ let add = sandbox.json_success(&[
+ "--format",
+ "json",
+ "basket",
+ "item",
+ "add",
+ "basket_flow",
+ "--listing-addr",
+ LISTING_ADDR,
+ "--bin-id",
+ "bin-1",
+ "--quantity",
+ "2",
+ ]);
assert_eq!(add["operation_id"], "basket.item.add");
assert_eq!(add["result"]["ready_for_quote"], true);
- let quote = json_success(
- &sandbox,
- &[
- "--format",
- "json",
- "basket",
- "quote",
- "create",
- "basket_flow",
- ],
- );
+ let quote = sandbox.json_success(&[
+ "--format",
+ "json",
+ "basket",
+ "quote",
+ "create",
+ "basket_flow",
+ ]);
assert_eq!(quote["operation_id"], "basket.quote.create");
assert_eq!(quote["result"]["state"], "quoted");
let order_id = quote["result"]["quote"]["order_id"]
.as_str()
.expect("order id");
- let orders = json_success(&sandbox, &["--format", "json", "order", "list"]);
+ let orders = sandbox.json_success(&["--format", "json", "order", "list"]);
assert_eq!(orders["operation_id"], "order.list");
assert_eq!(orders["result"]["state"], "ready");
assert_eq!(orders["result"]["count"], 1);
@@ -345,10 +305,8 @@ fn buyer_target_flow_acceptance_uses_target_operations() {
"buyer_account_id"
);
- let submit = json_success(
- &sandbox,
- &["--format", "json", "--dry-run", "order", "submit", order_id],
- );
+ let submit =
+ sandbox.json_success(&["--format", "json", "--dry-run", "order", "submit", order_id]);
assert_eq!(submit["operation_id"], "order.submit");
assert_eq!(submit["dry_run"], true);
assert_eq!(submit["result"]["state"], "unconfigured");
@@ -368,106 +326,91 @@ fn seller_target_flow_acceptance_uses_target_operations() {
let listing_file = sandbox.root().join("listing.toml");
let listing_file = listing_file.to_string_lossy().into_owned();
- let account = json_success(&sandbox, &["--format", "json", "account", "create"]);
+ let account = sandbox.json_success(&["--format", "json", "account", "create"]);
assert_eq!(account["operation_id"], "account.create");
assert_eq!(account["result"]["account"]["signer"], "local");
- let farm = json_success(
- &sandbox,
- &[
- "--format",
- "json",
- "farm",
- "create",
- "--name",
- "Green Farm",
- "--location",
- "farmstand",
- "--delivery-method",
- "pickup",
- ],
- );
+ let farm = sandbox.json_success(&[
+ "--format",
+ "json",
+ "farm",
+ "create",
+ "--name",
+ "Green Farm",
+ "--location",
+ "farmstand",
+ "--delivery-method",
+ "pickup",
+ ]);
assert_eq!(farm["operation_id"], "farm.create");
assert_eq!(farm["result"]["state"], "saved");
- let create = json_success(
- &sandbox,
- &[
- "--format",
- "json",
- "listing",
- "create",
- "--output",
- listing_file.as_str(),
- "--key",
- "eggs",
- "--title",
- "Eggs",
- "--category",
- "eggs",
- "--summary",
- "Fresh eggs",
- "--bin-id",
- "bin-1",
- "--quantity-amount",
- "1",
- "--quantity-unit",
- "each",
- "--price-amount",
- "6",
- "--price-currency",
- "USD",
- "--price-per-amount",
- "1",
- "--price-per-unit",
- "each",
- "--available",
- "10",
- ],
- );
+ let create = sandbox.json_success(&[
+ "--format",
+ "json",
+ "listing",
+ "create",
+ "--output",
+ listing_file.as_str(),
+ "--key",
+ "eggs",
+ "--title",
+ "Eggs",
+ "--category",
+ "eggs",
+ "--summary",
+ "Fresh eggs",
+ "--bin-id",
+ "bin-1",
+ "--quantity-amount",
+ "1",
+ "--quantity-unit",
+ "each",
+ "--price-amount",
+ "6",
+ "--price-currency",
+ "USD",
+ "--price-per-amount",
+ "1",
+ "--price-per-unit",
+ "each",
+ "--available",
+ "10",
+ ]);
assert_eq!(create["operation_id"], "listing.create");
assert_eq!(create["result"]["file"], listing_file);
- let validate = json_success(
- &sandbox,
- &[
- "--format",
- "json",
- "listing",
- "validate",
- listing_file.as_str(),
- ],
- );
+ let validate = sandbox.json_success(&[
+ "--format",
+ "json",
+ "listing",
+ "validate",
+ listing_file.as_str(),
+ ]);
assert_eq!(validate["operation_id"], "listing.validate");
assert_eq!(validate["result"]["valid"], true);
assert_eq!(validate["result"]["issues"], Value::Null);
- let publish = json_success(
- &sandbox,
- &[
- "--format",
- "json",
- "--dry-run",
- "listing",
- "publish",
- listing_file.as_str(),
- ],
- );
+ let publish = sandbox.json_success(&[
+ "--format",
+ "json",
+ "--dry-run",
+ "listing",
+ "publish",
+ listing_file.as_str(),
+ ]);
assert_eq!(publish["operation_id"], "listing.publish");
assert_eq!(publish["result"]["state"], "dry_run");
- let signed = json_success(
- &sandbox,
- &[
- "--format",
- "json",
- "--approval-token",
- "approve",
- "listing",
- "publish",
- listing_file.as_str(),
- ],
- );
+ let signed = sandbox.json_success(&[
+ "--format",
+ "json",
+ "--approval-token",
+ "approve",
+ "listing",
+ "publish",
+ listing_file.as_str(),
+ ]);
assert_eq!(signed["operation_id"], "listing.publish");
assert_eq!(signed["result"]["state"], "signed");
assert_eq!(signed["result"]["signer_mode"], "local");