use rocketmq_error::fields;
use rocketmq_error::CliErrorView;
use rocketmq_error::CliVerbosity;
use rocketmq_error::ContextVisibility;
use rocketmq_error::Error;
use rocketmq_error::ErrorContext;
use rocketmq_error::FieldValueKind;
use rocketmq_error::FieldValueRef;
use rocketmq_error::Sensitive;
use rocketmq_error::CORE_INTERNAL_FAILURE;
const SENTINEL: &str = "Bearer token-secret secret_key=sk signature=sig password=pw\r\nsource-message";
#[test]
fn schemas_cover_all_value_kinds_and_visibility_classes() {
let cases = [
(
fields::TOPIC.schema(),
FieldValueKind::Text,
ContextVisibility::Public,
Some(127),
),
(
fields::BROKER_CODE.schema(),
FieldValueKind::I64,
ContextVisibility::Diagnostic,
None,
),
(
fields::TIMEOUT_MS.schema(),
FieldValueKind::U64,
ContextVisibility::Public,
None,
),
(
fields::ATTEMPTED.schema(),
FieldValueKind::Bool,
ContextVisibility::Diagnostic,
None,
),
(
fields::CREDENTIALS_PRESENT.schema(),
FieldValueKind::Presence,
ContextVisibility::SecretPresenceOnly,
None,
),
];
for (schema, kind, visibility, limit) in cases {
assert_eq!(schema.value_kind(), kind, "{}", schema.name());
assert_eq!(schema.visibility(), visibility, "{}", schema.name());
assert_eq!(schema.text_byte_limit(), limit, "{}", schema.name());
}
}
#[test]
fn public_iteration_excludes_diagnostics_and_secret_presence() {
let context = ErrorContext::new()
.with_text(fields::TOPIC, "TopicA")
.with_text(fields::ADDR, "10.0.0.1:10911")
.with_secret_presence(fields::CREDENTIALS_PRESENT)
.with_i64(fields::QUEUE_ID, -1)
.with_u64(fields::TIMEOUT_MS, 500)
.with_bool(fields::ATTEMPTED, true);
let fields = context.public_fields().collect::<Vec<_>>();
assert_eq!(fields.len(), 3);
assert_eq!(fields[0].name(), "topic");
assert_eq!(fields[0].value(), FieldValueRef::Text("TopicA"));
assert_eq!(fields[1].value(), FieldValueRef::I64(-1));
assert_eq!(fields[2].value(), FieldValueRef::U64(500));
let display = context.to_string();
assert!(display.contains("addr=<redacted>"));
assert!(display.contains("credentials_present=<redacted>"));
assert!(display.contains("attempted=<redacted>"));
assert!(!display.contains("10.0.0.1:10911"));
assert!(!format!("{context:?}").contains("10.0.0.1:10911"));
}
#[test]
fn text_is_control_safe_bounded_and_utf8_valid() {
let value = format!("prefix\r\n\u{0085}{}界", "x".repeat(120));
let context = ErrorContext::new().with_text(fields::TOPIC, &value);
let field = context.public_fields().next().expect("public topic");
let FieldValueRef::Text(text) = field.value() else {
panic!("topic must remain text");
};
assert!(text.len() <= 127);
assert!(!text.chars().any(char::is_control));
assert!(text.starts_with("prefix "));
assert!(context.is_truncated());
assert!(std::str::from_utf8(text.as_bytes()).is_ok());
}
#[test]
fn duplicate_names_preserve_first_value_and_mark_truncation() {
let context = ErrorContext::new()
.with_text(fields::OPERATION, "publish")
.with_text(fields::OPERATION_DIAGNOSTIC, "secret diagnostic operation");
assert_eq!(context.len(), 1);
assert!(context.is_truncated());
let field = context.public_fields().next().expect("first public operation");
assert_eq!(field.value(), FieldValueRef::Text("publish"));
assert!(!context.to_string().contains("secret diagnostic operation"));
}
#[test]
fn context_capacity_is_sixteen_unique_fields() {
let context = context_at_capacity().with_bool(fields::INSTALLED, true);
assert_eq!(context.len(), 16);
assert!(context.is_truncated());
assert!(!context.to_string().contains("installed="));
}
fn context_at_capacity() -> ErrorContext {
ErrorContext::new()
.with_text(fields::TOPIC, "topic")
.with_text(fields::CLUSTER, "cluster")
.with_text(fields::GROUP, "group")
.with_text(fields::PROPERTY, "property")
.with_text(fields::RESOURCE, "resource")
.with_text(fields::FIELD, "field")
.with_text(fields::FEATURE, "feature")
.with_text(fields::OPERATION, "operation")
.with_u64(fields::ACTUAL_BYTES, 1)
.with_u64(fields::LIMIT_BYTES, 2)
.with_i64(fields::QUEUE_ID, 3)
.with_i64(fields::MAX_QUEUE_ID, 4)
.with_u64(fields::TIMEOUT_MS, 5)
.with_u64(fields::DURATION_MS, 6)
.with_i64(fields::PERMISSION_VALUE, 7)
.with_bool(fields::ATTEMPTED, true)
}
#[test]
fn sentinel_never_enters_context_or_safe_boundary_output() {
let secret = Sensitive::new(SENTINEL.to_owned());
assert_eq!(secret.expose_secret(), SENTINEL);
let context = ErrorContext::new()
.with_secret_presence(fields::CREDENTIALS_PRESENT)
.with_secret_presence(fields::SOURCE_PRESENT);
let duplicate = ErrorContext::new()
.with_secret_presence(fields::CREDENTIALS_PRESENT)
.with_secret_presence(fields::CREDENTIALS_PRESENT);
let over_capacity = context_at_capacity().with_secret_presence(fields::SOURCE_PRESENT);
assert!(duplicate.is_truncated());
assert!(over_capacity.is_truncated());
for context in [&context, &duplicate, &over_capacity] {
assert!(context.public_fields().all(|field| match field.value() {
FieldValueRef::Text(value) => !value.contains(SENTINEL),
_ => true,
}));
assert!(!context.to_string().contains(SENTINEL));
assert!(!format!("{context:?}").contains(SENTINEL));
}
let canonical = Error::caused_by(&CORE_INTERNAL_FAILURE, std::io::Error::other(SENTINEL)).with_context(
ErrorContext::new()
.with_text(fields::OPERATION_DIAGNOSTIC, "sentinel operation")
.with_secret_presence(fields::SOURCE_PRESENT),
);
let public = canonical.public_view().expect("valid public view");
let cli = CliErrorView::from_error(&canonical);
assert!(!format!("{public:?}").contains(SENTINEL));
assert!(!cli.output(CliVerbosity::Default).stderr().contains(SENTINEL));
assert!(!cli.output(CliVerbosity::Verbose).stderr().contains(SENTINEL));
}