commit bd6661559deb7f88a647ecd308550fa68d0b4867
parent 4a7b84d31254d9b2b8c2de1659c2115b768e3587
Author: triesap <tyson@radroots.org>
Date: Wed, 8 Jul 2026 21:03:15 +0000
transport: split profile and status output
- separate profile metadata from canonical transport status rows
- remove profile-level transport kind and implementation state fields
- render transport rows with shared readiness and usability labels
- cover local, Nostr, hybrid, proxy, and Reticulum profile output
Diffstat:
4 files changed, 440 insertions(+), 76 deletions(-)
diff --git a/src/out/terminal/renderers/runtime.rs b/src/out/terminal/renderers/runtime.rs
@@ -98,15 +98,14 @@ fn transport_profile_document(envelope: &OutputEnvelope, result: &Value) -> Term
common::push_path_field(&mut document, "State", result, &["state"]);
common::push_path_field(&mut document, "Source", result, &["source"]);
common::push_path_field(&mut document, "Profile", result, &["profile_id"]);
- common::push_path_field(&mut document, "Kind", result, &["transport_kind"]);
+ common::push_path_field(&mut document, "Profile kind", result, &["profile_kind"]);
common::push_path_field(&mut document, "Configured", result, &["configured_state"]);
- common::push_path_field(
+ common::push_bool_field(
&mut document,
- "Implementation",
+ "Delivery usable",
result,
- &["implementation_state"],
+ &["profile_delivery_usable"],
);
- common::push_bool_field(&mut document, "Usable", result, &["usable_for_delivery"]);
common::push_path_field(&mut document, "Message", result, &["message"]);
common::push_path_field(
&mut document,
@@ -138,6 +137,7 @@ fn transport_profile_document(envelope: &OutputEnvelope, result: &Value) -> Term
"No Nostr relays configured",
));
}
+ push_transport_status_table(&mut document, result, &["transport_statuses"]);
document
}
@@ -146,34 +146,74 @@ fn transport_status_document(envelope: &OutputEnvelope, result: &Value) -> Termi
let mut document = common::document_with_title(envelope, title);
common::push_path_field(&mut document, "State", result, &["state"]);
common::push_path_field(&mut document, "Source", result, &["source"]);
- let rows = common::array(result, &["transports"])
+ common::push_path_field(
+ &mut document,
+ "Profile",
+ result,
+ &["active_profile", "profile_id"],
+ );
+ common::push_path_field(
+ &mut document,
+ "Profile kind",
+ result,
+ &["active_profile", "profile_kind"],
+ );
+ common::push_path_field(
+ &mut document,
+ "Configured",
+ result,
+ &["active_profile", "configured_state"],
+ );
+ common::push_bool_field(
+ &mut document,
+ "Delivery usable",
+ result,
+ &["active_profile", "profile_delivery_usable"],
+ );
+ common::push_path_field(
+ &mut document,
+ "Message",
+ result,
+ &["active_profile", "message"],
+ );
+ push_transport_status_table(&mut document, result, &["transports"]);
+ document
+}
+
+fn push_transport_status_table(document: &mut TerminalDocument, result: &Value, path: &[&str]) {
+ let rows = common::array(result, path)
.into_iter()
.flatten()
.map(|transport| {
TerminalTableRow::new(vec![
- common::string(transport, &["profile_id"]).unwrap_or_default(),
common::string(transport, &["transport_kind"]).unwrap_or_default(),
- common::string(transport, &["configured_state"]).unwrap_or_default(),
+ common::string(transport, &["profile_id"]).unwrap_or_default(),
common::string(transport, &["implementation_state"]).unwrap_or_default(),
- common::bool_path(transport, &["usable_for_delivery"])
+ common::string(transport, &["readiness"]).unwrap_or_default(),
+ common::bool_path(transport, &["publish_usable"])
+ .map(|value| if value { "yes" } else { "no" }.to_owned())
+ .unwrap_or_default(),
+ common::bool_path(transport, &["fetch_usable"])
.map(|value| if value { "yes" } else { "no" }.to_owned())
.unwrap_or_default(),
+ common::string(transport, &["endpoint_uri"]).unwrap_or_default(),
])
})
.collect::<Vec<_>>();
document.sections.push(common::table_section(
"Transports",
vec![
- TerminalTableColumn::new("Profile", 7, 18),
TerminalTableColumn::new("Kind", 4, 12),
- TerminalTableColumn::new("Configured", 10, 18),
+ TerminalTableColumn::new("Profile", 7, 18),
TerminalTableColumn::new("Implementation", 14, 24),
- TerminalTableColumn::new("Usable", 6, 6),
+ TerminalTableColumn::new("Readiness", 9, 20),
+ TerminalTableColumn::new("Publish", 7, 7),
+ TerminalTableColumn::new("Fetch", 5, 5),
+ TerminalTableColumn::new("Endpoint", 8, 32),
],
rows,
"No transports reported",
));
- document
}
fn transport_outbox_status_document(envelope: &OutputEnvelope, result: &Value) -> TerminalDocument {
diff --git a/src/runtime/transport.rs b/src/runtime/transport.rs
@@ -2,7 +2,11 @@ use std::fs;
use std::path::PathBuf;
use radroots_sdk::{PushOutboxRequest, SyncStatusRequest};
-use radroots_transport::RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE;
+use radroots_transport::{
+ RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI, RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE,
+ RadrootsTransportImplementationState, RadrootsTransportKind, RadrootsTransportReadinessState,
+ RadrootsTransportStatus,
+};
use serde_json::Value as JsonValue;
use toml::{Value, map::Map};
@@ -11,7 +15,8 @@ use crate::runtime::RuntimeError;
use crate::runtime::config::{RuntimeConfig, TransportProfileKind};
use crate::runtime::sdk::{CliSdkAdapterError, CliSdkSession, sdk_nostr_relay_url_policy};
use crate::view::runtime::{
- TransportOutboxPushView, TransportOutboxStatusView, TransportProfileView, TransportStatusView,
+ TransportOutboxPushView, TransportOutboxStatusView, TransportProfileSummaryView,
+ TransportProfileView, TransportRuntimeStatusView, TransportStatusView,
};
const TRANSPORT_SOURCE: &str = "transport profile config";
@@ -118,26 +123,13 @@ pub fn set_profile(
}
pub fn status(config: &RuntimeConfig) -> TransportStatusView {
- let mut transports = vec![active_profile_view(config)];
- if !matches!(
- config.transport.profile,
- TransportProfileKind::ReticulumPreview | TransportProfileKind::Hybrid
- ) {
- transports.push(profile_view_from_parts(
- "reticulum_preview",
- Vec::new(),
- Some("reject_delivery_attempts".to_owned()),
- None,
- None,
- None,
- "preview_unavailable",
- ));
- }
+ let profile = active_profile_view(config);
TransportStatusView {
state: "ready".to_owned(),
source: TRANSPORT_SOURCE.to_owned(),
- transports,
+ active_profile: profile.summary(),
+ transports: profile.transport_statuses,
}
}
@@ -314,31 +306,24 @@ fn profile_view_from_parts(
proxy_token_secret_id: Option<String>,
configured_state: &str,
) -> TransportProfileView {
- let transport_kind = match profile_id {
- "nostr" => "nostr",
- "reticulum_preview" => "reticulum",
- "hybrid" => "hybrid",
- "proxy" => "proxy",
- _ => "local",
- };
- let implementation_state = match profile_id {
- "nostr" => "available",
- "reticulum_preview" => "preview_unavailable",
- "hybrid" => "available_with_preview",
- "proxy" => "delegated",
- _ => "local_only",
- };
- let usable_for_delivery =
- matches!(profile_id, "nostr" | "hybrid" | "proxy") && configured_state == "configured";
+ let transport_statuses = transport_statuses_from_parts(
+ profile_id,
+ nostr_relays.as_slice(),
+ proxy_url.as_deref(),
+ configured_state,
+ );
+ let profile_delivery_usable = transport_statuses
+ .iter()
+ .any(|status| status.publish_usable);
let message = match profile_id {
- "nostr" if usable_for_delivery => "Nostr relay transport is configured for delivery",
+ "nostr" if profile_delivery_usable => "Nostr relay transport is configured for delivery",
"nostr" => "Nostr transport requires configured Nostr relay targets",
"reticulum_preview" => RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE,
- "hybrid" if usable_for_delivery => {
+ "hybrid" if profile_delivery_usable => {
"Hybrid transport publishes through configured Nostr relays and reports Reticulum preview status"
}
"hybrid" => "Hybrid transport requires configured Nostr relay targets",
- "proxy" if usable_for_delivery => {
+ "proxy" if profile_delivery_usable => {
"Proxy transport delegates delivery to the configured endpoint"
}
"proxy" => "Proxy transport requires a configured token file or token secret id",
@@ -358,10 +343,9 @@ fn profile_view_from_parts(
state: configured_state.to_owned(),
source: TRANSPORT_SOURCE.to_owned(),
profile_id: profile_id.to_owned(),
- transport_kind: transport_kind.to_owned(),
+ profile_kind: profile_id.to_owned(),
configured_state: configured_state.to_owned(),
- implementation_state: implementation_state.to_owned(),
- usable_for_delivery,
+ profile_delivery_usable,
message: message.to_owned(),
nostr_relays,
reticulum_preview_behavior,
@@ -369,12 +353,147 @@ fn profile_view_from_parts(
proxy_token_source,
proxy_token_file,
proxy_token_secret_id,
- actions: profile_actions(profile_id, usable_for_delivery),
+ transport_statuses,
+ actions: profile_actions(profile_id, profile_delivery_usable),
+ }
+}
+
+fn transport_statuses_from_parts(
+ profile_id: &str,
+ nostr_relays: &[String],
+ proxy_url: Option<&str>,
+ configured_state: &str,
+) -> Vec<TransportRuntimeStatusView> {
+ match profile_id {
+ "nostr" => vec![transport_runtime_status_view(nostr_transport_status(
+ profile_id,
+ !nostr_relays.is_empty(),
+ ))],
+ "reticulum_preview" => {
+ vec![transport_runtime_status_view(
+ reticulum_preview_transport_status(profile_id),
+ )]
+ }
+ "hybrid" => vec![
+ transport_runtime_status_view(nostr_transport_status(
+ profile_id,
+ !nostr_relays.is_empty(),
+ )),
+ transport_runtime_status_view(reticulum_preview_transport_status(profile_id)),
+ ],
+ "proxy" => vec![transport_runtime_status_view(proxy_transport_status(
+ profile_id,
+ proxy_url,
+ configured_state == "configured",
+ ))],
+ _ => vec![transport_runtime_status_view(local_transport_status(
+ profile_id,
+ ))],
}
}
-fn profile_actions(profile_id: &str, usable_for_delivery: bool) -> Vec<String> {
- if usable_for_delivery {
+fn local_transport_status(profile_id: &str) -> RadrootsTransportStatus {
+ RadrootsTransportStatus::new(
+ RadrootsTransportKind::Local,
+ RadrootsTransportImplementationState::Available,
+ RadrootsTransportReadinessState::Ready,
+ )
+ .with_profile_id(profile_id)
+ .with_redacted_message("Local-only profile writes only to local state")
+}
+
+fn nostr_transport_status(profile_id: &str, targets_configured: bool) -> RadrootsTransportStatus {
+ RadrootsTransportStatus::new(
+ RadrootsTransportKind::Nostr,
+ if targets_configured {
+ RadrootsTransportImplementationState::Available
+ } else {
+ RadrootsTransportImplementationState::Misconfigured
+ },
+ if targets_configured {
+ RadrootsTransportReadinessState::Ready
+ } else {
+ RadrootsTransportReadinessState::Misconfigured
+ },
+ )
+ .with_profile_id(profile_id)
+ .with_publish_usable(targets_configured)
+ .with_fetch_usable(targets_configured)
+}
+
+fn reticulum_preview_transport_status(profile_id: &str) -> RadrootsTransportStatus {
+ RadrootsTransportStatus::new(
+ RadrootsTransportKind::Reticulum,
+ RadrootsTransportImplementationState::PreviewUnavailable,
+ RadrootsTransportReadinessState::PreviewUnavailable,
+ )
+ .with_profile_id(profile_id)
+ .with_endpoint_uri(RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI)
+ .with_redacted_message(RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE)
+}
+
+fn proxy_transport_status(
+ profile_id: &str,
+ proxy_url: Option<&str>,
+ token_ready: bool,
+) -> RadrootsTransportStatus {
+ let mut status = RadrootsTransportStatus::new(
+ RadrootsTransportKind::Proxy,
+ if token_ready {
+ RadrootsTransportImplementationState::Available
+ } else {
+ RadrootsTransportImplementationState::Misconfigured
+ },
+ if token_ready {
+ RadrootsTransportReadinessState::Ready
+ } else {
+ RadrootsTransportReadinessState::Misconfigured
+ },
+ )
+ .with_profile_id(profile_id)
+ .with_publish_usable(token_ready);
+ if let Some(proxy_url) = proxy_url {
+ status = status.with_endpoint_uri(proxy_url);
+ }
+ status
+}
+
+fn transport_runtime_status_view(status: RadrootsTransportStatus) -> TransportRuntimeStatusView {
+ TransportRuntimeStatusView {
+ transport_kind: status.kind.canonical_label(),
+ profile_id: status.profile_id,
+ endpoint_uri: status.endpoint_uri,
+ implementation_state: transport_implementation_state_label(status.implementation_state)
+ .to_owned(),
+ readiness: transport_readiness_state_label(status.readiness).to_owned(),
+ publish_usable: status.publish_usable,
+ fetch_usable: status.fetch_usable,
+ redacted_message: status.redacted_message,
+ }
+}
+
+fn transport_implementation_state_label(
+ state: RadrootsTransportImplementationState,
+) -> &'static str {
+ match state {
+ RadrootsTransportImplementationState::Available => "available",
+ RadrootsTransportImplementationState::Disabled => "disabled",
+ RadrootsTransportImplementationState::Misconfigured => "misconfigured",
+ RadrootsTransportImplementationState::PreviewUnavailable => "preview_unavailable",
+ }
+}
+
+fn transport_readiness_state_label(state: RadrootsTransportReadinessState) -> &'static str {
+ match state {
+ RadrootsTransportReadinessState::Ready => "ready",
+ RadrootsTransportReadinessState::Disabled => "disabled",
+ RadrootsTransportReadinessState::Misconfigured => "misconfigured",
+ RadrootsTransportReadinessState::PreviewUnavailable => "preview_unavailable",
+ }
+}
+
+fn profile_actions(profile_id: &str, profile_delivery_usable: bool) -> Vec<String> {
+ if profile_delivery_usable {
return Vec::new();
}
match profile_id {
@@ -445,6 +564,7 @@ fn validate_proxy_token_material(
trait TransportProfileViewMessage {
fn with_message(self, message: String) -> Self;
+ fn summary(&self) -> TransportProfileSummaryView;
}
impl TransportProfileViewMessage for TransportProfileView {
@@ -452,6 +572,16 @@ impl TransportProfileViewMessage for TransportProfileView {
self.message = message;
self
}
+
+ fn summary(&self) -> TransportProfileSummaryView {
+ TransportProfileSummaryView {
+ profile_id: self.profile_id.clone(),
+ profile_kind: self.profile_kind.clone(),
+ configured_state: self.configured_state.clone(),
+ profile_delivery_usable: self.profile_delivery_usable,
+ message: self.message.clone(),
+ }
+ }
}
pub(crate) fn update_app_config_table(
diff --git a/src/view/runtime.rs b/src/view/runtime.rs
@@ -3272,10 +3272,9 @@ pub struct TransportProfileView {
pub state: String,
pub source: String,
pub profile_id: String,
- pub transport_kind: String,
+ pub profile_kind: String,
pub configured_state: String,
- pub implementation_state: String,
- pub usable_for_delivery: bool,
+ pub profile_delivery_usable: bool,
pub message: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub nostr_relays: Vec<String>,
@@ -3289,6 +3288,7 @@ pub struct TransportProfileView {
pub proxy_token_file: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub proxy_token_secret_id: Option<String>,
+ pub transport_statuses: Vec<TransportRuntimeStatusView>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub actions: Vec<String>,
}
@@ -3297,7 +3297,32 @@ pub struct TransportProfileView {
pub struct TransportStatusView {
pub state: String,
pub source: String,
- pub transports: Vec<TransportProfileView>,
+ pub active_profile: TransportProfileSummaryView,
+ pub transports: Vec<TransportRuntimeStatusView>,
+}
+
+#[derive(Debug, Clone, Serialize)]
+pub struct TransportProfileSummaryView {
+ pub profile_id: String,
+ pub profile_kind: String,
+ pub configured_state: String,
+ pub profile_delivery_usable: bool,
+ pub message: String,
+}
+
+#[derive(Debug, Clone, Serialize)]
+pub struct TransportRuntimeStatusView {
+ pub transport_kind: String,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub profile_id: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub endpoint_uri: Option<String>,
+ pub implementation_state: String,
+ pub readiness: String,
+ pub publish_usable: bool,
+ pub fetch_usable: bool,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub redacted_message: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
diff --git a/tests/target_cli.rs b/tests/target_cli.rs
@@ -1297,9 +1297,24 @@ fn transport_profile_reticulum_preview_output_is_transport_specific() {
assert_eq!(value["operation_id"], "transport.profile.get");
assert_eq!(result["profile_id"], "reticulum_preview");
- assert_eq!(result["transport_kind"], "reticulum");
- assert_eq!(result["implementation_state"], "preview_unavailable");
- assert_eq!(result["usable_for_delivery"], false);
+ assert_eq!(result["profile_kind"], "reticulum_preview");
+ assert_eq!(result["profile_delivery_usable"], false);
+ assert!(result.get("transport_kind").is_none());
+ assert!(result.get("implementation_state").is_none());
+ assert_eq!(
+ result["transport_statuses"][0]["transport_kind"],
+ "reticulum"
+ );
+ assert_eq!(
+ result["transport_statuses"][0]["implementation_state"],
+ "preview_unavailable"
+ );
+ assert_eq!(
+ result["transport_statuses"][0]["readiness"],
+ "preview_unavailable"
+ );
+ assert_eq!(result["transport_statuses"][0]["publish_usable"], false);
+ assert_eq!(result["transport_statuses"][0]["fetch_usable"], false);
assert_eq!(result["reticulum_preview_behavior"], "defer_delivery_plans");
assert_eq!(result["message"], RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE);
assert!(
@@ -1319,6 +1334,32 @@ fn transport_profile_reticulum_preview_output_is_transport_specific() {
}
#[test]
+fn transport_profile_local_only_output_uses_profile_and_local_status_fields() {
+ let sandbox = RadrootsCliSandbox::new();
+
+ let value = sandbox.json_success(&["--format", "json", "transport", "profile", "get"]);
+ let result = &value["result"];
+ let statuses = result["transport_statuses"]
+ .as_array()
+ .expect("transport statuses");
+ let local = &statuses[0];
+
+ assert_eq!(value["operation_id"], "transport.profile.get");
+ assert_eq!(result["profile_id"], "local_only");
+ assert_eq!(result["profile_kind"], "local_only");
+ assert_eq!(result["profile_delivery_usable"], false);
+ assert!(result.get("transport_kind").is_none());
+ assert!(result.get("implementation_state").is_none());
+ assert_eq!(statuses.len(), 1);
+ assert_eq!(local["transport_kind"], "local");
+ assert_eq!(local["profile_id"], "local_only");
+ assert_eq!(local["implementation_state"], "available");
+ assert_eq!(local["readiness"], "ready");
+ assert_eq!(local["publish_usable"], false);
+ assert_eq!(local["fetch_usable"], false);
+}
+
+#[test]
fn transport_profile_set_hybrid_persists_nostr_and_reticulum_preview_config() {
let sandbox = RadrootsCliSandbox::new();
@@ -1338,13 +1379,37 @@ fn transport_profile_set_hybrid_persists_nostr_and_reticulum_preview_config() {
assert_eq!(value["operation_id"], "transport.profile.set");
assert_eq!(value["result"]["profile_id"], "hybrid");
- assert_eq!(value["result"]["transport_kind"], "hybrid");
+ assert_eq!(value["result"]["profile_kind"], "hybrid");
+ assert!(value["result"].get("transport_kind").is_none());
assert_eq!(value["result"]["state"], "configured");
assert_eq!(
value["result"]["nostr_relays"][0],
"wss://relay.example.com"
);
assert_eq!(
+ value["result"]["transport_statuses"]
+ .as_array()
+ .unwrap()
+ .len(),
+ 2
+ );
+ assert_eq!(
+ value["result"]["transport_statuses"][0]["transport_kind"],
+ "nostr"
+ );
+ assert_eq!(
+ value["result"]["transport_statuses"][0]["implementation_state"],
+ "available"
+ );
+ assert_eq!(
+ value["result"]["transport_statuses"][1]["transport_kind"],
+ "reticulum"
+ );
+ assert_eq!(
+ value["result"]["transport_statuses"][1]["readiness"],
+ "preview_unavailable"
+ );
+ assert_eq!(
value["result"]["reticulum_preview_behavior"],
"defer_delivery_plans"
);
@@ -1369,6 +1434,59 @@ fn transport_profile_set_hybrid_persists_nostr_and_reticulum_preview_config() {
}
#[test]
+fn transport_status_hybrid_output_reports_nostr_and_reticulum_rows() {
+ let sandbox = RadrootsCliSandbox::new();
+ sandbox.write_app_config(
+ "[transport]\nprofile = \"hybrid\"\n\n[transport.nostr]\nrelay_urls = [\"wss://relay.example.com\"]\n\n[transport.reticulum_preview]\nbehavior = \"defer_delivery_plans\"\n",
+ );
+
+ let value = sandbox.json_success(&["--format", "json", "transport", "status"]);
+ let result = &value["result"];
+ let transports = result["transports"].as_array().expect("transports");
+ let nostr = &transports[0];
+ let reticulum = &transports[1];
+
+ assert_eq!(value["operation_id"], "transport.status");
+ assert_eq!(result["active_profile"]["profile_id"], "hybrid");
+ assert_eq!(result["active_profile"]["profile_kind"], "hybrid");
+ assert_eq!(result["active_profile"]["profile_delivery_usable"], true);
+ assert_eq!(transports.len(), 2);
+ assert_eq!(nostr["transport_kind"], "nostr");
+ assert_eq!(nostr["implementation_state"], "available");
+ assert_eq!(nostr["readiness"], "ready");
+ assert_eq!(nostr["publish_usable"], true);
+ assert_eq!(nostr["fetch_usable"], true);
+ assert_eq!(reticulum["transport_kind"], "reticulum");
+ assert_eq!(reticulum["implementation_state"], "preview_unavailable");
+ assert_eq!(reticulum["readiness"], "preview_unavailable");
+ assert_eq!(reticulum["publish_usable"], false);
+ assert_eq!(reticulum["fetch_usable"], false);
+}
+
+#[test]
+fn transport_status_nostr_output_uses_canonical_transport_row() {
+ let sandbox = RadrootsCliSandbox::new();
+ configure_nostr_transport(&sandbox, "wss://relay.example.com");
+
+ let value = sandbox.json_success(&["--format", "json", "transport", "status"]);
+ let result = &value["result"];
+ let transports = result["transports"].as_array().expect("transports");
+ let nostr = &transports[0];
+
+ assert_eq!(value["operation_id"], "transport.status");
+ assert_eq!(result["active_profile"]["profile_id"], "nostr");
+ assert_eq!(result["active_profile"]["profile_kind"], "nostr");
+ assert_eq!(result["active_profile"]["profile_delivery_usable"], true);
+ assert_eq!(transports.len(), 1);
+ assert_eq!(nostr["transport_kind"], "nostr");
+ assert_eq!(nostr["profile_id"], "nostr");
+ assert_eq!(nostr["implementation_state"], "available");
+ assert_eq!(nostr["readiness"], "ready");
+ assert_eq!(nostr["publish_usable"], true);
+ assert_eq!(nostr["fetch_usable"], true);
+}
+
+#[test]
fn transport_status_reticulum_preview_output_reports_unusable_preview_state() {
let sandbox = RadrootsCliSandbox::new();
sandbox.write_app_config(
@@ -1383,14 +1501,23 @@ fn transport_status_reticulum_preview_output_reports_unusable_preview_state() {
assert_eq!(value["operation_id"], "transport.status");
assert_eq!(result["state"], "ready");
assert_eq!(transports.len(), 1);
- assert_eq!(active["profile_id"], "reticulum_preview");
+ assert_eq!(result["active_profile"]["profile_id"], "reticulum_preview");
+ assert_eq!(
+ result["active_profile"]["profile_kind"],
+ "reticulum_preview"
+ );
+ assert_eq!(result["active_profile"]["profile_delivery_usable"], false);
assert_eq!(active["transport_kind"], "reticulum");
assert_eq!(active["implementation_state"], "preview_unavailable");
- assert_eq!(active["usable_for_delivery"], false);
- assert_eq!(active["reticulum_preview_behavior"], "defer_delivery_plans");
- assert_eq!(active["message"], RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE);
+ assert_eq!(active["readiness"], "preview_unavailable");
+ assert_eq!(active["publish_usable"], false);
+ assert_eq!(active["fetch_usable"], false);
+ assert_eq!(
+ active["redacted_message"],
+ RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE
+ );
assert!(
- !active["message"]
+ !active["redacted_message"]
.as_str()
.expect("message")
.contains("Nostr")
@@ -1503,8 +1630,9 @@ fn transport_source_boundary_rejects_removed_relay_and_publish_proxy_surfaces()
fs::read_to_string(manifest_dir.join("src/runtime/transport.rs")).expect("read source");
for required in [
"RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE",
- "if !matches!(",
- "TransportProfileKind::ReticulumPreview | TransportProfileKind::Hybrid",
+ "RadrootsTransportStatus::new",
+ "RadrootsTransportKind::Reticulum",
+ "transport_runtime_status_view",
"validate_proxy_token_material",
"proxy_token_ready(&validation_config)",
"crate::runtime::sdk::validate_proxy_bearer_token(config)",
@@ -1714,7 +1842,10 @@ fn config_get_exposes_proxy_missing_token_state() {
assert_eq!(value["result"]["write_plane"]["state"], "unconfigured");
assert_eq!(value["result"]["transport"]["profile_id"], "proxy");
assert_eq!(value["result"]["transport"]["state"], "unconfigured");
- assert_eq!(value["result"]["transport"]["usable_for_delivery"], false);
+ assert_eq!(
+ value["result"]["transport"]["profile_delivery_usable"],
+ false
+ );
assert_eq!(
value["result"]["transport"]["proxy_url"],
"http://127.0.0.1:7070"
@@ -1753,7 +1884,10 @@ fn config_get_proxy_with_token_file_reports_ready_transport() {
assert_eq!(value["result"]["publish"]["reason"], Value::Null);
assert_eq!(value["result"]["transport"]["profile_id"], "proxy");
assert_eq!(value["result"]["transport"]["state"], "configured");
- assert_eq!(value["result"]["transport"]["usable_for_delivery"], true);
+ assert_eq!(
+ value["result"]["transport"]["profile_delivery_usable"],
+ true
+ );
assert_eq!(
value["result"]["transport"]["proxy_token_source"],
"token_file"
@@ -1791,7 +1925,10 @@ fn config_get_proxy_with_token_secret_id_reports_ready_transport() {
assert_eq!(value["result"]["publish"]["executable"], true);
assert_eq!(value["result"]["transport"]["profile_id"], "proxy");
assert_eq!(value["result"]["transport"]["state"], "configured");
- assert_eq!(value["result"]["transport"]["usable_for_delivery"], true);
+ assert_eq!(
+ value["result"]["transport"]["profile_delivery_usable"],
+ true
+ );
assert_eq!(
value["result"]["transport"]["proxy_token_source"],
"token_secret_id"
@@ -1913,7 +2050,23 @@ fn transport_profile_set_proxy_persists_token_file_without_printing_token_materi
assert_eq!(value["operation_id"], "transport.profile.set");
assert_eq!(value["result"]["profile_id"], "proxy");
assert_eq!(value["result"]["state"], "configured");
- assert_eq!(value["result"]["usable_for_delivery"], true);
+ assert_eq!(value["result"]["profile_delivery_usable"], true);
+ assert_eq!(
+ value["result"]["transport_statuses"][0]["transport_kind"],
+ "proxy"
+ );
+ assert_eq!(
+ value["result"]["transport_statuses"][0]["implementation_state"],
+ "available"
+ );
+ assert_eq!(
+ value["result"]["transport_statuses"][0]["readiness"],
+ "ready"
+ );
+ assert_eq!(
+ value["result"]["transport_statuses"][0]["publish_usable"],
+ true
+ );
assert_eq!(value["result"]["proxy_token_source"], "token_file");
assert_eq!(
value["result"]["proxy_token_file"],
@@ -1962,7 +2115,23 @@ fn transport_profile_set_proxy_persists_token_secret_id_without_printing_token_m
assert_eq!(value["operation_id"], "transport.profile.set");
assert_eq!(value["result"]["profile_id"], "proxy");
assert_eq!(value["result"]["state"], "configured");
- assert_eq!(value["result"]["usable_for_delivery"], true);
+ assert_eq!(value["result"]["profile_delivery_usable"], true);
+ assert_eq!(
+ value["result"]["transport_statuses"][0]["transport_kind"],
+ "proxy"
+ );
+ assert_eq!(
+ value["result"]["transport_statuses"][0]["implementation_state"],
+ "available"
+ );
+ assert_eq!(
+ value["result"]["transport_statuses"][0]["readiness"],
+ "ready"
+ );
+ assert_eq!(
+ value["result"]["transport_statuses"][0]["publish_usable"],
+ true
+ );
assert_eq!(value["result"]["proxy_token_source"], "token_secret_id");
assert!(value["result"]["proxy_token_file"].is_null());
assert_eq!(