use crate::report::CollectionSourceRequest;
use crate::sns::report::SnsNeuronRow;
use crate::sns::report::tests::fixtures::*;
use crate::sns::report::tests::*;
struct InvalidBoundedSnsNeuronsSource;
delegate_sns_discovery!(InvalidBoundedSnsNeuronsSource);
impl SnsNeuronsSource for InvalidBoundedSnsNeuronsSource {
fn fetch_sns_neurons<'a>(
&'a self,
request: &'a CollectionSourceRequest,
sns: &'a MainnetSns,
limit: u32,
owner_principal_id: Option<&'a str>,
) -> crate::report::SourceFuture<'a, Result<Vec<SnsNeuronRow>, SnsHostError>> {
Box::pin(async move {
let mut neurons = FixtureSnsNeuronsSource
.fetch_sns_neurons(request, sns, limit, owner_principal_id)
.await?;
neurons[0].neuron_id = "0A".to_string();
Ok(neurons)
})
}
fn fetch_sns_neuron_page<'a>(
&'a self,
_request: &'a CollectionSourceRequest,
_sns: &'a MainnetSns,
_limit: u32,
_start_page_at: Option<&'a SnsNeuronId>,
_owner_principal_id: Option<&'a str>,
) -> crate::report::SourceFuture<'a, Result<MainnetSnsNeuronPage, SnsHostError>> {
Box::pin(async move { unreachable!("bounded validation fixture does not refresh") })
}
}
#[tokio::test(flavor = "current_thread")]
async fn sns_neurons_resolves_list_id_and_renders_governance_neurons() {
let mut request = neurons_request("1");
request.collection = crate::sns::SnsNeuronCollection::Live {
owner_principal_id: Some(GOVERNANCE_A.to_string()),
};
let report = build_sns_neurons_report_with_source(&request, &FixtureSnsNeuronsSource)
.await
.expect("sns neurons report");
assert_eq!(report.schema_version, SNS_NEURONS_REPORT_SCHEMA_VERSION);
assert_eq!(report.context.instance.id, 1);
assert_eq!(report.context.instance.name, "Fixture SNS");
assert_eq!(report.context.instance.root_canister_id, ROOT_A);
assert_eq!(report.context.governance_canister_id, GOVERNANCE_A);
assert_eq!(report.requested_limit, 10);
assert_eq!(report.owner_principal_id.as_deref(), Some(GOVERNANCE_A));
assert_eq!(report.total_neuron_count, 1);
assert_eq!(report.neuron_count, 1);
assert_eq!(report.provenance.data_source().as_str(), "live");
assert!(report.provenance.cache_path().is_none());
assert!(report.provenance.cache_complete().is_none());
assert_eq!(report.neurons[0].neuron_id, NEURON_A);
assert_eq!(report.neurons[0].cached_neuron_stake_e8s, 123);
assert_eq!(report.neurons[0].maturity_e8s_equivalent, 456);
assert_eq!(report.neurons[0].staked_maturity_e8s_equivalent, Some(789));
assert_eq!(report.neurons[0].created_timestamp_seconds, 1_780_272_000);
assert_eq!(report.neurons[0].source_nns_neuron_id, Some(42));
assert_eq!(report.neurons[0].auto_stake_maturity, Some(true));
assert_eq!(
report.neurons[0].aging_since_timestamp_seconds,
1_780_272_100
);
assert_eq!(
report.neurons[0].dissolve_state,
Some(SnsNeuronDissolveState::DissolveDelaySeconds(31_536_000))
);
assert_eq!(report.neurons[0].voting_power_percentage_multiplier, 100);
assert_eq!(report.neurons[0].vesting_period_seconds, Some(63_072_000));
assert_eq!(report.neurons[0].neuron_fees_e8s, 10);
}
#[tokio::test(flavor = "current_thread")]
async fn sns_neurons_builder_rejects_noncanonical_custom_source_rows() {
let mut request = neurons_request("1");
request.collection = crate::sns::SnsNeuronCollection::Live {
owner_principal_id: Some(GOVERNANCE_A.to_string()),
};
let error = build_sns_neurons_report_with_source(&request, &InvalidBoundedSnsNeuronsSource)
.await
.expect_err("invalid custom-source row rejected");
assert!(matches!(
error,
SnsHostError::InvalidSourceData {
capability: "SNS neurons",
reason,
} if reason.contains("canonical lowercase hexadecimal")
));
}
#[tokio::test(flavor = "current_thread")]
async fn sns_neurons_live_owner_filter_is_admitted() {
let mut request = neurons_request("1");
request.collection = crate::sns::SnsNeuronCollection::Live {
owner_principal_id: Some(GOVERNANCE_A.to_string()),
};
build_sns_neurons_report_with_source(&request, &FixtureSnsNeuronsSource)
.await
.expect("sns neurons report");
}