use crate::sns::report::tests::{fixtures::*, *};
struct NoCallSnsNeuronSource;
impl SnsDiscoverySource for NoCallSnsNeuronSource {
fn fetch_sns_inventory<'a>(
&'a self,
_request: &'a SnsSourceRequest,
) -> crate::report::SourceFuture<'a, Result<MainnetSnsInventory, SnsHostError>> {
Box::pin(async move { unreachable!("invalid neuron id must fail before discovery") })
}
fn fetch_sns_metadata<'a>(
&'a self,
_request: &'a SnsSourceRequest,
_targets: &'a [MainnetSnsCanisters],
) -> crate::report::SourceFuture<'a, Result<Vec<MainnetSnsMetadata>, SnsHostError>> {
Box::pin(async move { unreachable!("invalid neuron id must fail before metadata") })
}
}
impl SnsNeuronSource for NoCallSnsNeuronSource {
fn fetch_sns_neuron<'a>(
&'a self,
_request: &'a SnsSourceRequest,
_sns: &'a MainnetSns,
_neuron_id: &'a str,
) -> crate::report::SourceFuture<'a, Result<MainnetSnsNeuron, SnsHostError>> {
Box::pin(async move { unreachable!("invalid neuron id must fail before exact lookup") })
}
}
struct InvalidPolicySnsNeuronSource;
delegate_sns_discovery!(InvalidPolicySnsNeuronSource);
impl SnsNeuronSource for InvalidPolicySnsNeuronSource {
fn fetch_sns_neuron<'a>(
&'a self,
_request: &'a SnsSourceRequest,
_sns: &'a MainnetSns,
_neuron_id: &'a str,
) -> crate::report::SourceFuture<'a, Result<MainnetSnsNeuron, SnsHostError>> {
Box::pin(async move {
let mut neuron = fixture_sns_neuron();
neuron.detail.maturity_mint_conversion_observed_disabled =
SnsPolicyObservationStatus::ObservedSatisfied;
Ok(neuron)
})
}
}
#[tokio::test(flavor = "current_thread")]
async fn exact_neuron_detail_preserves_native_permission_and_disbursement_evidence() {
let report =
build_sns_neuron_detail_report_with_source(&neuron_request("1"), &FixtureSnsNeuronSource)
.await
.expect("exact neuron detail");
let text = sns_neuron_detail_report_text(&report);
assert_eq!(
report.schema_version,
SNS_NEURON_DETAIL_REPORT_SCHEMA_VERSION
);
assert_eq!(report.neuron_id, NEURON_A);
assert_eq!(report.root_canister_id, ROOT_A);
assert_eq!(report.governance_canister_id, GOVERNANCE_A);
assert_eq!(report.data_source.as_str(), "live");
assert_eq!(report.detail.neuron.neuron_id, NEURON_A);
assert_eq!(report.detail.permissions[0].permission_types[4].code, 9);
assert_eq!(
report.detail.permissions[0].permission_types[4].name,
"stake_maturity"
);
assert_eq!(
report.detail.maturity_mint_conversion_observed_disabled,
SnsPolicyObservationStatus::Violated
);
assert_eq!(
report.detail.manual_maturity_staking_observed_disabled,
SnsPolicyObservationStatus::Violated
);
let destination = report.detail.disburse_maturity_in_progress[0]
.account_to_disburse_to
.as_ref()
.expect("destination account");
assert_eq!(destination.owner.as_deref(), Some(ROOT_A));
assert_eq!(
destination.subaccount_hex.as_deref(),
Some(&*"ab".repeat(32))
);
assert_eq!(
report.detail.followees[0].followee_neuron_ids[0],
"11".repeat(32)
);
assert_eq!(
report.detail.topic_followees.as_ref().expect("topic rows")[0].followees[0]
.alias
.as_deref(),
Some("governance lead")
);
assert!(text.contains("maturity_mint_conversion_observed_disabled: violated"));
assert!(text.contains("9:stake_maturity"));
assert!(text.contains(ROOT_A));
}
#[tokio::test(flavor = "current_thread")]
async fn exact_neuron_detail_rejects_invalid_input_before_source_calls() {
for invalid in [
"AB".repeat(32),
"gg".repeat(32),
"+a".repeat(32),
format!("aƩ{}", "0".repeat(61)),
"00".repeat(31),
"00".repeat(33),
String::new(),
] {
let mut request = neuron_request("1");
request.neuron_id = invalid.clone();
assert!(matches!(
build_sns_neuron_detail_report_with_source(&request, &NoCallSnsNeuronSource).await,
Err(SnsHostError::InvalidNeuronIdText { neuron_id }) if neuron_id == invalid
));
}
}
#[tokio::test(flavor = "current_thread")]
async fn exact_neuron_detail_recomputes_custom_source_policy_observations() {
let error = build_sns_neuron_detail_report_with_source(
&neuron_request("1"),
&InvalidPolicySnsNeuronSource,
)
.await
.expect_err("tampered derived status rejected");
assert!(matches!(
error,
SnsHostError::InvalidSourceData {
capability: "SNS neuron detail",
reason,
} if reason.contains("does not match raw evidence")
));
}