#[cfg(feature = "dashboard-host")]
use ic_query::HostCacheError;
use ic_query::ic::{
DEFAULT_IC_BOUNDARY_NODE_DATA_CENTERS_SOURCE_ENDPOINT, DEFAULT_IC_CANISTER_PAGE_LIMIT,
DEFAULT_IC_DASHBOARD_CANISTER_COLLECTION_SOURCE_ENDPOINT,
DEFAULT_IC_DASHBOARD_METRICS_SOURCE_ENDPOINT, DEFAULT_IC_DASHBOARD_SOURCE_ENDPOINT,
DEFAULT_IC_METRIC_STEP_SECS, DEFAULT_IC_NODE_PROVIDER_REWARD_HISTORY_STEP_SECS,
DEFAULT_IC_NODE_PROVIDER_REWARD_PAGE_LIMIT, DEFAULT_IC_REPLICA_VERSION_PAGE_LIMIT,
DEFAULT_IC_STATE_SOURCE_ENDPOINT, DEFAULT_ICRC_ACCOUNT_INFO_SOURCE_ENDPOINT,
DEFAULT_ICRC_ANALYTICS_SOURCE_ENDPOINT, IC_API_BOUNDARY_NODE_REPORT_SCHEMA_VERSION,
IcApiBoundaryNodeReport, IcApiBoundaryNodeRequest, IcApiBoundaryNodeRow,
IcBoundaryNodeDataCenterRow, IcBoundaryNodeDataCentersReport, IcBoundaryNodeDataCentersRequest,
IcCanisterCountReport, IcCanisterCountRequest, IcCanisterFilters, IcCanisterPageController,
IcCanisterPageReport, IcCanisterPageRequest, IcCanisterPageRow, IcCanisterReport,
IcCanisterRequest, IcCanisterUpgrade, IcCertifiedStateProvenance, IcDailyStatsQuery,
IcDailyStatsReport, IcDailyStatsRequest, IcDailyStatsRow, IcDashboardReportProvenance,
IcIcrcAccountInfoReport, IcIcrcAccountInfoRequest, IcIcrcAccountListQuery,
IcIcrcAccountListReport, IcIcrcAccountListRequest, IcIcrcAccountRow, IcIcrcAccountSort,
IcIcrcHolderListQuery, IcIcrcHolderListReport, IcIcrcHolderListRequest, IcIcrcHolderRow,
IcIcrcHolderSort, IcIcrcIndexedCountKind, IcIcrcIndexedCountReport, IcIcrcIndexedCountRequest,
IcIcrcTokenValueQuery, IcIcrcTokenValueReport, IcIcrcTokenValueRequest, IcIcrcTokenValueRow,
IcIcrcTotalSupplyObservation, IcIcrcTotalSupplyQuery, IcIcrcTotalSupplyReport,
IcIcrcTotalSupplyRequest, IcMetricKind, IcMetricObservation, IcMetricQuery, IcMetricReport,
IcMetricRequest, IcMetricSeries, IcNodeAssignmentStatusCounts, IcNodeCountComparison,
IcNodeProviderRewardHistoryObservation, IcNodeProviderRewardHistoryQuery,
IcNodeProviderRewardHistoryReport, IcNodeProviderRewardHistoryRequest,
IcNodeProviderRewardInfoReport, IcNodeProviderRewardInfoRequest, IcNodeProviderRewardListQuery,
IcNodeProviderRewardListReport, IcNodeProviderRewardListRequest, IcNodeProviderRewardRow,
IcNodeProviderRewardXdrConversionRate, IcNodeProviderStatusReport, IcNodeStatusCounts,
IcNodeStatusGroupCounts, IcNodeStatusObservation, IcNodeStatusReport, IcNodeStatusRow,
IcNodeStatusScope, IcNodeStatusSnapshot, IcNodeStatusView, IcReplicaVersionInfoReport,
IcReplicaVersionInfoRequest, IcReplicaVersionListQuery, IcReplicaVersionListReport,
IcReplicaVersionListRequest, IcReplicaVersionListRow, IcReplicaVersionStatus,
IcReplicaVersionSubnetRollout, IcSubnetStatusReport, MAX_IC_CANISTER_PAGE_LIMIT,
MAX_IC_DASHBOARD_RESPONSE_BYTES, MAX_IC_NODE_PROVIDER_REWARD_PAGE_LIMIT,
MAX_IC_REPLICA_VERSION_PAGE_LIMIT, ic_api_boundary_node_report_text,
ic_boundary_node_data_centers_report_text, ic_canister_count_report_text,
ic_canister_page_report_text, ic_canister_report_text, ic_daily_stats_report_text,
ic_metric_report_text, ic_node_provider_reward_history_report_text,
ic_node_provider_reward_info_report_text, ic_node_provider_reward_list_report_text,
ic_node_provider_status_report_from_snapshot, ic_node_provider_status_report_text,
ic_node_status_report_from_snapshot, ic_node_status_report_text,
ic_replica_version_info_report_text, ic_replica_version_list_report_text,
ic_subnet_status_report_from_snapshot, ic_subnet_status_report_text,
icrc_account_info_report_text, icrc_account_list_report_text, icrc_holder_list_report_text,
icrc_indexed_count_report_text, icrc_token_value_report_text, icrc_total_supply_report_text,
};
#[cfg(feature = "ic-state-host")]
use ic_query::ic::{
IcApiBoundaryNodeHostError, IcApiBoundaryNodeSource, IcApiBoundaryNodeSourceData,
IcApiBoundaryNodeSourceRequest, LiveIcStateSource, build_ic_api_boundary_node_report,
build_ic_api_boundary_node_report_with_source,
};
#[cfg(feature = "dashboard-host")]
use ic_query::ic::{
IcBoundaryNodeDataCentersSourceData, IcCanisterCollectionSource, IcCanisterCountSourceData,
IcCanisterPageSourceData, IcCanisterSource, IcCanisterSourceData, IcDailyStatsSourceData,
IcHostError, IcIcrcAccountInfoSourceData, IcIcrcAccountListSourceData, IcIcrcAccountSourceRow,
IcIcrcAnalyticsSource, IcIcrcHolderListSourceData, IcIcrcHolderSourceRow, IcIcrcIndexSource,
IcIcrcIndexedCountSourceData, IcIcrcTokenValueSourceData, IcIcrcTokenValueSourceRow,
IcIcrcTotalSupplySourceData, IcMetricSource, IcMetricSourceData, IcNetworkSource,
IcNodeProviderRewardSource, IcNodeStatusHostError, IcNodeStatusSnapshotRequest,
IcNodeStatusSource, IcNodeStatusSourceData, IcReplicaVersionSource, IcSourceRequest,
LiveIcSource, build_ic_boundary_node_data_centers_report,
build_ic_boundary_node_data_centers_report_with_source, build_ic_canister_count_report,
build_ic_canister_count_report_with_source, build_ic_canister_page_report,
build_ic_canister_page_report_with_source, build_ic_canister_report,
build_ic_canister_report_with_source, build_ic_daily_stats_report,
build_ic_daily_stats_report_with_source, build_ic_metric_report,
build_ic_metric_report_with_source, build_ic_node_provider_reward_history_report,
build_ic_node_provider_reward_history_report_with_source,
build_ic_node_provider_reward_info_report,
build_ic_node_provider_reward_info_report_with_source,
build_ic_node_provider_reward_list_report,
build_ic_node_provider_reward_list_report_with_source, build_ic_node_status_snapshot,
build_ic_node_status_snapshot_with_source, build_ic_replica_version_info_report,
build_ic_replica_version_info_report_with_source, build_ic_replica_version_list_report,
build_ic_replica_version_list_report_with_source, build_icrc_account_info_report,
build_icrc_account_info_report_with_source, build_icrc_account_list_report,
build_icrc_account_list_report_with_source, build_icrc_holder_list_report,
build_icrc_holder_list_report_with_source, build_icrc_indexed_count_report,
build_icrc_indexed_count_report_with_source, build_icrc_token_value_report,
build_icrc_token_value_report_with_source, build_icrc_total_supply_report,
build_icrc_total_supply_report_with_source,
};
const CANISTER_ID: &str = "ryjl3-tyaaa-aaaaa-aaaba-cai";
const ICRC_LEDGER_ID: &str = "mxzaz-hqaaa-aaaar-qaada-cai";
const SUBNET_ID: &str = "tdb26-jop6k-aogll-7ltgs-eruif-6kk7m-qpktf-gdiqx-mxtrf-vb5e6-eqe";
const REPLICA_VERSION_ID: &str = "e3d101b22ae3fa02aca737f9fb96cc6c4ca83ac3";
#[test]
fn public_dashboard_transport_limit_is_available_without_host() {
assert_eq!(MAX_IC_DASHBOARD_RESPONSE_BYTES, 8 * 1024 * 1024);
}
#[test]
fn public_certified_api_boundary_node_api_is_constructible_without_host() {
let request = IcApiBoundaryNodeRequest::new(DEFAULT_IC_STATE_SOURCE_ENDPOINT, 1_800_000_000);
let report = IcApiBoundaryNodeReport {
provenance: IcCertifiedStateProvenance {
schema_version: IC_API_BOUNDARY_NODE_REPORT_SCHEMA_VERSION,
network: "ic".to_string(),
authority: "certified_ic_state_tree".to_string(),
source_endpoint: request.source_endpoint,
effective_canister_id: "rwlgt-iiaaa-aaaaa-aaaaa-cai".to_string(),
fetched_at_unix_seconds: request.now_unix_secs,
fetched_at: "2027-01-15T08:00:00Z".to_string(),
fetched_by: "ic-query".to_string(),
certificate_time_unix_nanos: request.now_unix_secs * 1_000_000_000,
certificate_time_unix_seconds: request.now_unix_secs,
certificate_time: "2027-01-15T08:00:00Z".to_string(),
certified: true,
point_in_time_guaranteed: true,
},
node_count: 1,
rows: vec![IcApiBoundaryNodeRow {
node_id: "rrkah-fqaaa-aaaaa-aaaaq-cai".to_string(),
domain: "api1.example.com".to_string(),
ipv4_address: Some("192.0.2.1".to_string()),
ipv6_address: "2001:db8::1".to_string(),
}],
};
let text = ic_api_boundary_node_report_text(&report);
let json = serde_json::to_value(report).expect("serializable certified state report");
assert!(text.contains("authority: certified_ic_state_tree"));
assert_eq!(json["certified"], true);
assert_eq!(json["point_in_time_guaranteed"], true);
assert_eq!(json["rows"][0]["domain"], "api1.example.com");
}
#[cfg(feature = "ic-state-host")]
#[test]
fn public_certified_state_host_api_exposes_live_and_custom_builders() {
let _: fn(
&IcApiBoundaryNodeRequest,
) -> Result<IcApiBoundaryNodeReport, IcApiBoundaryNodeHostError> =
build_ic_api_boundary_node_report;
let _: fn(
&IcApiBoundaryNodeRequest,
&dyn IcApiBoundaryNodeSource,
) -> Result<IcApiBoundaryNodeReport, IcApiBoundaryNodeHostError> =
build_ic_api_boundary_node_report_with_source;
let _: LiveIcStateSource = LiveIcStateSource;
let request = IcApiBoundaryNodeRequest::new(DEFAULT_IC_STATE_SOURCE_ENDPOINT, 1_800_000_000);
let report = build_ic_api_boundary_node_report_with_source(&request, &StateFixtureSource)
.expect("custom certified state source");
assert_eq!(report.node_count, 1);
assert_eq!(report.rows[0].domain, "api1.example.com");
}
#[cfg(feature = "ic-state-host")]
struct StateFixtureSource;
#[cfg(feature = "ic-state-host")]
impl IcApiBoundaryNodeSource for StateFixtureSource {
fn fetch_api_boundary_nodes(
&self,
request: &IcApiBoundaryNodeSourceRequest,
) -> Result<IcApiBoundaryNodeSourceData, IcApiBoundaryNodeHostError> {
Ok(IcApiBoundaryNodeSourceData {
source: request.clone(),
certificate_time_unix_nanos: request.observed_at_unix_seconds * 1_000_000_000,
rows: vec![IcApiBoundaryNodeRow {
node_id: "rrkah-fqaaa-aaaaa-aaaaq-cai".to_string(),
domain: "api1.example.com".to_string(),
ipv4_address: None,
ipv6_address: "2001:db8::1".to_string(),
}],
})
}
}
#[test]
fn public_node_status_api_is_constructible_serializable_and_renderable_without_host() {
let snapshot = public_node_status_snapshot();
let view = IcNodeStatusView::attention();
let node_report: IcNodeStatusReport =
ic_node_status_report_from_snapshot(&snapshot, &view).expect("node projection");
let subnet_report: IcSubnetStatusReport =
ic_subnet_status_report_from_snapshot(&snapshot, &view).expect("Subnet projection");
let provider_report: IcNodeProviderStatusReport =
ic_node_provider_status_report_from_snapshot(&snapshot, &view)
.expect("provider projection");
assert!(ic_node_status_report_text(&node_report).contains("DOWN"));
assert!(ic_subnet_status_report_text(&subnet_report).contains("+NON-UP >F"));
assert!(ic_node_provider_status_report_text(&provider_report).contains("Provider"));
assert_eq!(
provider_report.providers[0].unassigned_up_vs_assigned_up,
IcNodeCountComparison::Equal
);
let node_json = serde_json::to_value(node_report).expect("serializable node status report");
let json = serde_json::to_value(subnet_report).expect("serializable Subnet status report");
let provider_json =
serde_json::to_value(provider_report).expect("serializable provider status report");
assert_eq!(json["authority"], "official_ic_dashboard_api");
assert_eq!(json["cloud_engine_nodes_included"], false);
assert_eq!(
node_json["counts"]["assignment_statuses"]["assigned"]["down"],
1
);
assert_eq!(json["subnets"][0]["statuses"]["down"], 1);
assert_eq!(
provider_json["providers"][0]["unassigned_non_up_vs_assigned_non_up"],
"less"
);
}
#[test]
fn public_ic_boundary_node_api_is_constructible_serializable_and_renderable() {
let request = IcBoundaryNodeDataCentersRequest::new(
DEFAULT_IC_BOUNDARY_NODE_DATA_CENTERS_SOURCE_ENDPOINT,
1_700_000_000,
);
let report = IcBoundaryNodeDataCentersReport {
provenance: public_provenance(request.source_endpoint),
data_center_count: 1,
total_node_count: 2,
rows: vec![public_boundary_node_data_center()],
};
let text = ic_boundary_node_data_centers_report_text(&report);
let json = serde_json::to_value(&report).expect("serializable boundary-node report");
assert!(text.contains("data_center_count: 1"));
assert_eq!(json["rows"][0]["dc_id"], "da11");
assert_eq!(json["rows"][0]["total_nodes"], "2");
assert_eq!(json["certified"], false);
assert!(json.get("provenance").is_none());
}
#[test]
fn public_ic_daily_stats_api_is_constructible_serializable_and_renderable() {
let query = IcDailyStatsQuery::new(1_785_456_000, 1_785_542_400);
let request = IcDailyStatsRequest::new(
DEFAULT_IC_DASHBOARD_SOURCE_ENDPOINT,
1_785_542_400,
query.clone(),
);
let report = IcDailyStatsReport {
provenance: public_provenance(request.source_endpoint),
query,
returned_day_count: 1,
rows: vec![public_daily_stats_row()],
};
let text = ic_daily_stats_report_text(&report);
let json = serde_json::to_value(&report).expect("serializable daily-statistics report");
assert!(text.contains("returned_day_count: 1"));
assert_eq!(json["rows"][0]["day"], "2026-07-31");
assert_eq!(
json["rows"][0]["average_transactions_per_second"],
"4378.980149999999"
);
assert_eq!(json["certified"], false);
assert!(json.get("provenance").is_none());
}
#[test]
fn public_ic_replica_version_api_is_constructible_serializable_and_renderable() {
let query = IcReplicaVersionListQuery::new(25, 0, None);
let request = IcReplicaVersionListRequest::new(
DEFAULT_IC_DASHBOARD_SOURCE_ENDPOINT,
1_800_000_000,
query.clone(),
);
let row = IcReplicaVersionListRow {
replica_version_id: REPLICA_VERSION_ID.to_string(),
proposal_id: 143_250,
executed_timestamp_seconds: 1_785_759_673,
status: IcReplicaVersionStatus::Executed,
title: "Elect new IC/GuestOS revision".to_string(),
url: "https://forum.dfinity.org/t/release/1".to_string(),
subnet_count: 1,
subnets: vec![public_replica_version_rollout()],
};
let list = IcReplicaVersionListReport {
provenance: public_provenance(request.source_endpoint),
query,
resolved_max_proposal_index: 438,
total_proposals: 438,
returned_count: 1,
next_offset: Some(1),
rows: vec![row],
};
let info_request = IcReplicaVersionInfoRequest::new(
DEFAULT_IC_DASHBOARD_SOURCE_ENDPOINT,
1_800_000_000,
REPLICA_VERSION_ID,
);
let info = IcReplicaVersionInfoReport {
provenance: public_provenance(info_request.source_endpoint),
replica_version_id: info_request.replica_version_id,
proposal_id: 143_250,
executed_timestamp_seconds: 1_785_759_673,
title: "Elect new IC/GuestOS revision".to_string(),
url: "https://forum.dfinity.org/t/release/1".to_string(),
summary: "Raw release notes".to_string(),
subnet_count: 1,
subnets: vec![public_replica_version_rollout()],
};
let list_text = ic_replica_version_list_report_text(&list);
let info_text = ic_replica_version_info_report_text(&info);
let list_json = serde_json::to_value(&list).expect("serializable replica-version list");
let info_json = serde_json::to_value(&info).expect("serializable replica-version info");
assert_eq!(DEFAULT_IC_REPLICA_VERSION_PAGE_LIMIT, 50);
assert_eq!(MAX_IC_REPLICA_VERSION_PAGE_LIMIT, 100);
assert!(list_text.contains("EXECUTED"));
assert!(info_text.contains("summary: Raw release notes"));
assert_eq!(list_json["rows"][0]["status"], "EXECUTED");
assert_eq!(info_json["summary"], "Raw release notes");
assert!(list_json.get("provenance").is_none());
}
#[test]
fn public_ic_node_provider_reward_api_is_constructible_serializable_and_renderable() {
let list_query = IcNodeProviderRewardListQuery::new(25, 0, Some(6_470));
let list_request = IcNodeProviderRewardListRequest::new(
DEFAULT_IC_DASHBOARD_SOURCE_ENDPOINT,
1_800_000_000,
list_query.clone(),
);
let reward = IcNodeProviderRewardRow {
reward_id: 7_562,
amount_e8s: 1_583_574_085_000,
details: std::collections::BTreeMap::from([(
"to_account".to_string(),
serde_json::Value::String("00".repeat(32)),
)]),
maximum_node_provider_rewards_e8s: Some(10_000_000_000_000),
minimum_xdr_permyriad_per_icp: Some(20_000),
node_provider_id: "rrkah-fqaaa-aaaaa-aaaaq-cai".to_string(),
proposal_id: None,
registry_version: None,
reward_mode: "RewardToAccount".to_string(),
reward_timestamp_unix_secs: 1_784_081_341,
dashboard_updated_at: "2026-07-15T04:30:01.558435".to_string(),
xdr_conversion_rate: IcNodeProviderRewardXdrConversionRate {
timestamp_unix_secs: Some(1_784_073_600),
xdr_permyriad_per_icp: Some(16_379),
},
};
let list = IcNodeProviderRewardListReport {
provenance: public_provenance(list_request.source_endpoint),
query: list_query,
resolved_max_reward_index: 6_470,
total_reward_records: 6_470,
returned_count: 1,
next_offset_hint: Some(1),
pages_may_overlap: true,
rows: vec![reward.clone()],
};
let info_request = IcNodeProviderRewardInfoRequest::new(
DEFAULT_IC_DASHBOARD_SOURCE_ENDPOINT,
1_800_000_000,
reward.reward_id,
);
let info = IcNodeProviderRewardInfoReport {
provenance: public_provenance(info_request.source_endpoint),
reward,
};
let history_query = IcNodeProviderRewardHistoryQuery::new(1_783_900_000, 1_784_300_000, 86_400);
let history_request = IcNodeProviderRewardHistoryRequest::new(
DEFAULT_IC_DASHBOARD_SOURCE_ENDPOINT,
1_800_000_000,
history_query.clone(),
);
let history = IcNodeProviderRewardHistoryReport {
provenance: public_provenance(history_request.source_endpoint),
query: history_query,
requested_observation_limit: 5,
returned_observation_count: 1,
observations: vec![IcNodeProviderRewardHistoryObservation {
timestamp_unix_secs: 1_784_073_600,
amount_e8s: 66_852_931_445_000,
}],
};
let list_json = serde_json::to_value(&list).expect("serializable reward list");
let info_json = serde_json::to_value(&info).expect("serializable reward info");
let history_json = serde_json::to_value(&history).expect("serializable reward history");
assert_eq!(DEFAULT_IC_NODE_PROVIDER_REWARD_PAGE_LIMIT, 50);
assert_eq!(MAX_IC_NODE_PROVIDER_REWARD_PAGE_LIMIT, 100);
assert_eq!(DEFAULT_IC_NODE_PROVIDER_REWARD_HISTORY_STEP_SECS, 86_400);
assert!(ic_node_provider_reward_list_report_text(&list).contains("pages_may_overlap: yes"));
assert!(ic_node_provider_reward_info_report_text(&info).contains("amount_e8s:"));
assert!(ic_node_provider_reward_history_report_text(&history).contains("AMOUNT_E8S"));
assert_eq!(list_json["rows"][0]["amount_e8s"], 1_583_574_085_000_u64);
assert_eq!(info_json["reward_timestamp_unix_secs"], 1_784_081_341_u64);
assert_eq!(
history_json["observations"][0]["amount_e8s"],
66_852_931_445_000_u64
);
assert!(list_json.get("provenance").is_none());
}
#[test]
fn public_ic_metric_api_is_constructible_serializable_and_renderable() {
let query = IcMetricQuery::new(
IcMetricKind::InstructionRate,
1_699_996_400,
1_700_000_000,
DEFAULT_IC_METRIC_STEP_SECS,
);
let request = IcMetricRequest::new(
DEFAULT_IC_DASHBOARD_METRICS_SOURCE_ENDPOINT,
1_700_000_000,
query.clone(),
);
let report = IcMetricReport {
provenance: public_provenance(request.source_endpoint),
query,
returned_series_count: 1,
returned_observation_count: 1,
series: vec![IcMetricSeries {
name: "instruction_rate".to_string(),
observations: vec![IcMetricObservation {
timestamp_unix_secs: 1_700_000_000,
value: "21089992048.10834".to_string(),
}],
}],
};
let text = ic_metric_report_text(&report);
let json = serde_json::to_value(&report).expect("serializable metric report");
assert!(text.contains("metric: instruction-rate"));
assert_eq!(json["metric"], "instruction-rate");
assert_eq!(
json["series"][0]["observations"][0]["value"],
"21089992048.10834"
);
assert!(json.get("provenance").is_none());
assert!(json.get("query").is_none());
}
#[test]
fn public_icrc_analytics_api_is_constructible_serializable_and_renderable() {
let query = IcIcrcTotalSupplyQuery::new(1_785_542_400, 1_785_628_800, 86_400);
let request = IcIcrcTotalSupplyRequest::new(
"https://icrc-api.internetcomputer.org/api/v2",
1_785_628_800,
ICRC_LEDGER_ID,
query.clone(),
);
let report = IcIcrcTotalSupplyReport {
provenance: public_provenance(request.analytics.source_endpoint),
ledger_canister_id: request.analytics.ledger_canister_id,
query,
requested_observation_limit: 2,
returned_observation_count: 2,
observations: vec![
IcIcrcTotalSupplyObservation {
timestamp_unix_secs: 1_785_542_400,
total_supply_base_units: "23309479199".to_string(),
},
IcIcrcTotalSupplyObservation {
timestamp_unix_secs: 1_785_628_800,
total_supply_base_units: "23680995300".to_string(),
},
],
};
let text = icrc_total_supply_report_text(&report);
let json = serde_json::to_value(&report).expect("serializable ICRC analytics report");
assert!(text.contains("ledger_canister_id: mxzaz-hqaaa-aaaar-qaada-cai"));
assert!(text.contains("23309479199"));
assert_eq!(json["step_secs"], 86_400);
assert_eq!(
json["observations"][1]["total_supply_base_units"],
"23680995300"
);
assert_eq!(json["certified"], false);
assert!(json.get("query").is_none());
}
#[test]
fn public_icrc_indexed_count_api_is_constructible_serializable_and_renderable() {
for (kind, label) in [
(IcIcrcIndexedCountKind::Account, "account"),
(IcIcrcIndexedCountKind::Holder, "holder"),
(IcIcrcIndexedCountKind::Transaction, "transaction"),
] {
let request = IcIcrcIndexedCountRequest::new(
"https://icrc-api.internetcomputer.org/api/v2",
1_785_628_800,
ICRC_LEDGER_ID,
kind,
);
let report = IcIcrcIndexedCountReport {
provenance: public_provenance(request.analytics.source_endpoint),
ledger_canister_id: request.analytics.ledger_canister_id,
kind: request.kind,
total: 78_272,
};
let text = icrc_indexed_count_report_text(&report);
let json = serde_json::to_value(&report).expect("serializable ICRC indexed-count report");
assert!(text.contains("ledger_canister_id: mxzaz-hqaaa-aaaar-qaada-cai"));
assert!(text.contains(&format!("kind: {label}")));
assert!(text.contains("total: 78272"));
assert_eq!(json["kind"], label);
assert_eq!(json["total"], 78_272);
assert_eq!(json["certified"], false);
}
}
#[test]
fn public_icrc_account_and_holder_index_api_is_constructible_serializable_and_renderable() {
let account_row = IcIcrcAccountRow {
account_id: "aaaaa-aa".to_string(),
owner: "aaaaa-aa".to_string(),
subaccount: String::new(),
balance_base_units: "1000".to_string(),
total_transactions: 3,
created_at_unix_secs: 1_731_833_720,
created_at_subsec_nanos: 56_622_711,
ledger_canister_id: ICRC_LEDGER_ID.to_string(),
latest_transaction_index: 1_898_152,
dashboard_updated_at: "2026-04-14T18:54:04.330496".to_string(),
active_fee_collector: false,
fee_collector_block_ranges: Vec::new(),
};
let account_query = IcIcrcAccountListQuery::new(20, IcIcrcAccountSort::Id)
.with_owner("aaaaa-aa")
.with_after("cursor");
let account_request = IcIcrcAccountListRequest::new(
DEFAULT_ICRC_ANALYTICS_SOURCE_ENDPOINT,
1_800_000_000,
ICRC_LEDGER_ID,
account_query.clone(),
);
let account_report = IcIcrcAccountListReport {
provenance: public_provenance(account_request.analytics.source_endpoint),
ledger_canister_id: ICRC_LEDGER_ID.to_string(),
query: account_query,
returned_count: 1,
previous_cursor: Some("previous".to_string()),
next_cursor: Some("next".to_string()),
rows: vec![account_row.clone()],
};
let account_json = serde_json::to_value(&account_report).expect("serializable account page");
assert!(icrc_account_list_report_text(&account_report).contains("accounts:"));
assert_eq!(
account_json["rows"][0]["created_at_unix_secs"],
1_731_833_720_u64
);
let info_request = IcIcrcAccountInfoRequest::new(
DEFAULT_ICRC_ACCOUNT_INFO_SOURCE_ENDPOINT,
1_800_000_000,
ICRC_LEDGER_ID,
"aaaaa-aa",
);
let info_report = IcIcrcAccountInfoReport {
provenance: public_provenance(info_request.analytics.source_endpoint),
account: account_row,
};
assert!(icrc_account_info_report_text(&info_report).contains("account_id: aaaaa-aa"));
let holder_query = IcIcrcHolderListQuery::new(20, IcIcrcHolderSort::BalanceDescending);
let holder_request = IcIcrcHolderListRequest::new(
DEFAULT_ICRC_ANALYTICS_SOURCE_ENDPOINT,
1_800_000_000,
ICRC_LEDGER_ID,
holder_query.clone(),
);
let holder_report = IcIcrcHolderListReport {
provenance: public_provenance(holder_request.analytics.source_endpoint),
ledger_canister_id: ICRC_LEDGER_ID.to_string(),
query: holder_query,
returned_count: 1,
previous_cursor: None,
next_cursor: Some("1000,aaaaa-aa".to_string()),
rows: vec![IcIcrcHolderRow {
principal: "aaaaa-aa".to_string(),
balance_base_units: "1000".to_string(),
total_transactions: 3,
created_at_unix_secs: 1_731_833_720,
created_at_subsec_nanos: 56_622_711,
ledger_canister_id: ICRC_LEDGER_ID.to_string(),
latest_transaction_index: 1_898_152,
percentage: serde_json::json!(0.25),
value_usd: serde_json::Value::Null,
dashboard_updated_at: "2026-04-14T18:55:55.827947".to_string(),
}],
};
assert!(icrc_holder_list_report_text(&holder_report).contains("holders:"));
assert_eq!(
serde_json::to_value(holder_report).expect("serializable holder page")["rows"][0]["percentage"],
serde_json::json!(0.25)
);
}
#[test]
fn public_icrc_token_value_api_is_constructible_serializable_and_renderable() {
let request = IcIcrcTokenValueRequest::new(
"https://icrc-api.internetcomputer.org/api/v2",
1_785_628_800,
ICRC_LEDGER_ID,
IcIcrcTokenValueQuery::new(1_785_542_400, 1_785_628_800, 100),
);
let report = IcIcrcTokenValueReport {
provenance: public_provenance(request.analytics.source_endpoint),
ledger_canister_id: request.analytics.ledger_canister_id,
query: request.query,
returned_row_count: 1,
limit_reached: false,
rows: vec![IcIcrcTokenValueRow {
price: Some("63710.86993032754".to_string()),
volume_24h: None,
price_usd: Some("63710.86993032754".to_string()),
volume_24h_usd: Some("23337.881075287027".to_string()),
source: Some("ICPSwap-API".to_string()),
source_url: Some("https://app.icpswap.com/".to_string()),
timestamp_unix_secs: 1_785_542_517,
}],
};
let text = icrc_token_value_report_text(&report);
let json = serde_json::to_value(&report).expect("serializable ICRC token-value report");
assert!(text.contains("source=ICPSwap-API"));
assert!(text.contains("limit_reached: no"));
assert_eq!(json["limit"], 100);
assert_eq!(json["rows"][0]["volume_24h"], serde_json::Value::Null);
assert_eq!(json["rows"][0]["timestamp_unix_secs"], 1_785_542_517_u64);
}
#[test]
fn public_ic_canister_api_is_constructible_and_renderable() {
let request = IcCanisterRequest::new(
"https://ic-api.internetcomputer.org/api/v3",
1_700_000_000,
CANISTER_ID,
);
let report = IcCanisterReport {
provenance: public_provenance(request.source_endpoint.clone()),
canister_id: request.canister_id.clone(),
dashboard_id: 226_973,
canister_type: Some("ledger".to_string()),
name: "ICP Ledger".to_string(),
subnet_id: SUBNET_ID.to_string(),
controllers: vec!["r7inp-6aaaa-aaaaa-aaabq-cai".to_string()],
language: String::new(),
module_hash: String::new(),
dashboard_updated_at: "2026-07-30T17:47:41.745647".to_string(),
upgrade_count: Some(1),
upgrades: Some(vec![IcCanisterUpgrade {
executed_timestamp_seconds: 1_756_730_623,
module_hash: "51f4be010f23064137defacd627ffbec024c5133210c68ca3b80ab8f257101d6"
.to_string(),
proposal_id: 138_271,
}]),
};
let text = ic_canister_report_text(&report);
let json = serde_json::to_value(&report).expect("serializable canister report");
assert_eq!(request.now_unix_secs, 1_700_000_000);
assert!(text.contains("canister_id: ryjl3-tyaaa-aaaaa-aaaba-cai"));
assert!(text.contains("authority: official_ic_dashboard_api"));
assert_eq!(json["schema_version"], 1);
assert_eq!(json["source_endpoint"], request.source_endpoint);
assert!(json.get("provenance").is_none());
}
#[test]
fn public_ic_canister_collection_api_is_constructible_and_renderable() {
let filters = IcCanisterFilters {
has_name: Some(true),
query: Some("ICP Ledger".to_string()),
..IcCanisterFilters::default()
};
let count_request = IcCanisterCountRequest::new(
DEFAULT_IC_DASHBOARD_CANISTER_COLLECTION_SOURCE_ENDPOINT,
1_700_000_000,
)
.with_filters(filters.clone());
let page_request = IcCanisterPageRequest::new(
DEFAULT_IC_DASHBOARD_CANISTER_COLLECTION_SOURCE_ENDPOINT,
1_700_000_000,
)
.with_filters(filters.clone())
.with_limit(25)
.with_after(CANISTER_ID);
let count = IcCanisterCountReport {
provenance: public_provenance(count_request.source_endpoint),
filters: filters.clone(),
total: 649,
};
let page = IcCanisterPageReport {
provenance: public_provenance(page_request.source_endpoint),
filters,
requested_limit: page_request.limit,
returned_count: 1,
after: page_request.after,
before: None,
previous_cursor: Some(CANISTER_ID.to_string()),
next_cursor: Some(CANISTER_ID.to_string()),
rows: vec![public_page_row()],
};
assert_eq!(DEFAULT_IC_CANISTER_PAGE_LIMIT, 50);
assert_eq!(MAX_IC_CANISTER_PAGE_LIMIT, 100);
assert!(ic_canister_count_report_text(&count).contains("total: 649"));
assert!(ic_canister_page_report_text(&page).contains("returned_count: 1"));
let page_json = serde_json::to_value(page).expect("serializable page report");
assert_eq!(page_json["authority"], "official_ic_dashboard_api");
assert!(page_json.get("provenance").is_none());
}
#[cfg(feature = "dashboard-host")]
#[test]
fn public_dashboard_host_api_exposes_live_and_custom_source_builders() {
type Builder = fn(&IcCanisterRequest) -> Result<IcCanisterReport, IcHostError>;
type CustomBuilder =
fn(&IcCanisterRequest, &dyn IcCanisterSource) -> Result<IcCanisterReport, IcHostError>;
let _: Builder = build_ic_canister_report;
let _: CustomBuilder = build_ic_canister_report_with_source;
let _: fn(&IcCanisterCountRequest) -> Result<IcCanisterCountReport, IcHostError> =
build_ic_canister_count_report;
let _: fn(
&IcCanisterCountRequest,
&dyn IcCanisterCollectionSource,
) -> Result<IcCanisterCountReport, IcHostError> = build_ic_canister_count_report_with_source;
let _: fn(&IcCanisterPageRequest) -> Result<IcCanisterPageReport, IcHostError> =
build_ic_canister_page_report;
let _: fn(
&IcCanisterPageRequest,
&dyn IcCanisterCollectionSource,
) -> Result<IcCanisterPageReport, IcHostError> = build_ic_canister_page_report_with_source;
let _: fn(&IcMetricRequest) -> Result<IcMetricReport, IcHostError> = build_ic_metric_report;
let _: fn(&IcMetricRequest, &dyn IcMetricSource) -> Result<IcMetricReport, IcHostError> =
build_ic_metric_report_with_source;
let _: fn(
&IcBoundaryNodeDataCentersRequest,
) -> Result<IcBoundaryNodeDataCentersReport, IcHostError> =
build_ic_boundary_node_data_centers_report;
let _: fn(
&IcBoundaryNodeDataCentersRequest,
&dyn IcNetworkSource,
) -> Result<IcBoundaryNodeDataCentersReport, IcHostError> =
build_ic_boundary_node_data_centers_report_with_source;
let _: fn(&IcDailyStatsRequest) -> Result<IcDailyStatsReport, IcHostError> =
build_ic_daily_stats_report;
let _: fn(
&IcDailyStatsRequest,
&dyn IcNetworkSource,
) -> Result<IcDailyStatsReport, IcHostError> = build_ic_daily_stats_report_with_source;
let _: LiveIcSource = LiveIcSource;
assert_eq!(
DEFAULT_IC_DASHBOARD_SOURCE_ENDPOINT,
"https://ic-api.internetcomputer.org/api/v3"
);
let source = FixtureSource;
let request = IcCanisterRequest::new(
DEFAULT_IC_DASHBOARD_SOURCE_ENDPOINT,
1_700_000_000,
CANISTER_ID,
);
let report =
build_ic_canister_report_with_source(&request, &source).expect("custom source report");
assert_eq!(report.canister_id, CANISTER_ID);
let count_request = IcCanisterCountRequest::new(
DEFAULT_IC_DASHBOARD_CANISTER_COLLECTION_SOURCE_ENDPOINT,
1_700_000_000,
);
let count = build_ic_canister_count_report_with_source(&count_request, &source)
.expect("custom count source report");
assert_eq!(count.total, 1);
let page_request = IcCanisterPageRequest::new(
DEFAULT_IC_DASHBOARD_CANISTER_COLLECTION_SOURCE_ENDPOINT,
1_700_000_000,
)
.with_limit(1);
let page = build_ic_canister_page_report_with_source(&page_request, &source)
.expect("custom page source report");
assert_eq!(page.returned_count, 1);
let metric_request = IcMetricRequest::new(
DEFAULT_IC_DASHBOARD_METRICS_SOURCE_ENDPOINT,
1_700_000_000,
IcMetricQuery::new(
IcMetricKind::InstructionRate,
1_699_996_400,
1_700_000_000,
DEFAULT_IC_METRIC_STEP_SECS,
),
);
let metric = build_ic_metric_report_with_source(&metric_request, &source)
.expect("custom metric source report");
assert_eq!(metric.returned_observation_count, 1);
let network_request = IcBoundaryNodeDataCentersRequest::new(
DEFAULT_IC_BOUNDARY_NODE_DATA_CENTERS_SOURCE_ENDPOINT,
1_700_000_000,
);
let network = build_ic_boundary_node_data_centers_report_with_source(&network_request, &source)
.expect("custom network source report");
assert_eq!(network.data_center_count, 1);
let daily_stats_request = IcDailyStatsRequest::new(
DEFAULT_IC_DASHBOARD_SOURCE_ENDPOINT,
1_785_542_400,
IcDailyStatsQuery::new(1_785_456_000, 1_785_542_400),
);
let daily_stats = build_ic_daily_stats_report_with_source(&daily_stats_request, &source)
.expect("custom daily-statistics source report");
assert_eq!(daily_stats.returned_day_count, 1);
}
#[cfg(feature = "dashboard-host")]
#[test]
fn public_dashboard_replica_version_host_api_exposes_builders() {
let _: fn(&IcReplicaVersionListRequest) -> Result<IcReplicaVersionListReport, IcHostError> =
build_ic_replica_version_list_report;
let _: fn(
&IcReplicaVersionListRequest,
&dyn IcReplicaVersionSource,
) -> Result<IcReplicaVersionListReport, IcHostError> =
build_ic_replica_version_list_report_with_source;
let _: fn(&IcReplicaVersionInfoRequest) -> Result<IcReplicaVersionInfoReport, IcHostError> =
build_ic_replica_version_info_report;
let _: fn(
&IcReplicaVersionInfoRequest,
&dyn IcReplicaVersionSource,
) -> Result<IcReplicaVersionInfoReport, IcHostError> =
build_ic_replica_version_info_report_with_source;
}
#[cfg(feature = "dashboard-host")]
#[test]
fn public_dashboard_node_provider_reward_host_api_exposes_builders() {
let _: fn(
&IcNodeProviderRewardListRequest,
) -> Result<IcNodeProviderRewardListReport, IcHostError> =
build_ic_node_provider_reward_list_report;
let _: fn(
&IcNodeProviderRewardListRequest,
&dyn IcNodeProviderRewardSource,
) -> Result<IcNodeProviderRewardListReport, IcHostError> =
build_ic_node_provider_reward_list_report_with_source;
let _: fn(
&IcNodeProviderRewardInfoRequest,
) -> Result<IcNodeProviderRewardInfoReport, IcHostError> =
build_ic_node_provider_reward_info_report;
let _: fn(
&IcNodeProviderRewardInfoRequest,
&dyn IcNodeProviderRewardSource,
) -> Result<IcNodeProviderRewardInfoReport, IcHostError> =
build_ic_node_provider_reward_info_report_with_source;
let _: fn(
&IcNodeProviderRewardHistoryRequest,
) -> Result<IcNodeProviderRewardHistoryReport, IcHostError> =
build_ic_node_provider_reward_history_report;
let _: fn(
&IcNodeProviderRewardHistoryRequest,
&dyn IcNodeProviderRewardSource,
) -> Result<IcNodeProviderRewardHistoryReport, IcHostError> =
build_ic_node_provider_reward_history_report_with_source;
}
#[cfg(feature = "dashboard-host")]
#[test]
fn public_dashboard_host_api_exposes_live_and_custom_node_status_builders() {
let _: fn(&IcNodeStatusSnapshotRequest) -> Result<IcNodeStatusSnapshot, IcHostError> =
build_ic_node_status_snapshot;
let _: fn(
&IcNodeStatusSnapshotRequest,
&dyn IcNodeStatusSource,
) -> Result<IcNodeStatusSnapshot, IcHostError> = build_ic_node_status_snapshot_with_source;
let request =
IcNodeStatusSnapshotRequest::new(DEFAULT_IC_DASHBOARD_SOURCE_ENDPOINT, 1_700_000_000);
let snapshot = build_ic_node_status_snapshot_with_source(&request, &FixtureSource)
.expect("custom node-status source snapshot");
assert_eq!(snapshot.node_count, 1);
assert_eq!(snapshot.counts.statuses.down, 1);
let parse_error =
serde_json::from_str::<serde_json::Value>("{").expect_err("malformed public cache fixture");
let error = IcNodeStatusHostError::from(HostCacheError::parse_cache(
"IC node status",
"cache.json".into(),
parse_error,
));
assert!(matches!(
error,
IcNodeStatusHostError::Cache(HostCacheError::ParseCache { .. })
));
}
#[cfg(feature = "dashboard-host")]
#[test]
fn public_dashboard_host_api_exposes_live_and_custom_icrc_analytics_builders() {
let _: fn(&IcIcrcAccountListRequest) -> Result<IcIcrcAccountListReport, IcHostError> =
build_icrc_account_list_report;
let _: fn(
&IcIcrcAccountListRequest,
&dyn IcIcrcIndexSource,
) -> Result<IcIcrcAccountListReport, IcHostError> = build_icrc_account_list_report_with_source;
let _: fn(&IcIcrcAccountInfoRequest) -> Result<IcIcrcAccountInfoReport, IcHostError> =
build_icrc_account_info_report;
let _: fn(
&IcIcrcAccountInfoRequest,
&dyn IcIcrcIndexSource,
) -> Result<IcIcrcAccountInfoReport, IcHostError> = build_icrc_account_info_report_with_source;
let _: fn(&IcIcrcHolderListRequest) -> Result<IcIcrcHolderListReport, IcHostError> =
build_icrc_holder_list_report;
let _: fn(
&IcIcrcHolderListRequest,
&dyn IcIcrcIndexSource,
) -> Result<IcIcrcHolderListReport, IcHostError> = build_icrc_holder_list_report_with_source;
let _: fn(&IcIcrcIndexedCountRequest) -> Result<IcIcrcIndexedCountReport, IcHostError> =
build_icrc_indexed_count_report;
let _: fn(
&IcIcrcIndexedCountRequest,
&dyn IcIcrcAnalyticsSource,
) -> Result<IcIcrcIndexedCountReport, IcHostError> =
build_icrc_indexed_count_report_with_source;
let _: fn(&IcIcrcTokenValueRequest) -> Result<IcIcrcTokenValueReport, IcHostError> =
build_icrc_token_value_report;
let _: fn(
&IcIcrcTokenValueRequest,
&dyn IcIcrcAnalyticsSource,
) -> Result<IcIcrcTokenValueReport, IcHostError> = build_icrc_token_value_report_with_source;
let _: fn(&IcIcrcTotalSupplyRequest) -> Result<IcIcrcTotalSupplyReport, IcHostError> =
build_icrc_total_supply_report;
let _: fn(
&IcIcrcTotalSupplyRequest,
&dyn IcIcrcAnalyticsSource,
) -> Result<IcIcrcTotalSupplyReport, IcHostError> = build_icrc_total_supply_report_with_source;
let source = FixtureSource;
let count_request = IcIcrcIndexedCountRequest::new(
"https://icrc-api.internetcomputer.org/api/v2",
1_785_628_800,
ICRC_LEDGER_ID,
IcIcrcIndexedCountKind::Transaction,
);
let count = build_icrc_indexed_count_report_with_source(&count_request, &source)
.expect("custom ICRC indexed-count source report");
assert_eq!(count.kind, IcIcrcIndexedCountKind::Transaction);
assert_eq!(count.total, 78_272);
let account_request = IcIcrcAccountListRequest::new(
DEFAULT_ICRC_ANALYTICS_SOURCE_ENDPOINT,
1_785_628_800,
ICRC_LEDGER_ID,
IcIcrcAccountListQuery::new(20, IcIcrcAccountSort::Id),
);
let accounts = build_icrc_account_list_report_with_source(&account_request, &source)
.expect("custom ICRC account source report");
assert_eq!(accounts.returned_count, 1);
let holder_request = IcIcrcHolderListRequest::new(
DEFAULT_ICRC_ANALYTICS_SOURCE_ENDPOINT,
1_785_628_800,
ICRC_LEDGER_ID,
IcIcrcHolderListQuery::new(20, IcIcrcHolderSort::Principal),
);
let holders = build_icrc_holder_list_report_with_source(&holder_request, &source)
.expect("custom ICRC holder source report");
assert_eq!(holders.returned_count, 1);
let token_value_request = IcIcrcTokenValueRequest::new(
"https://icrc-api.internetcomputer.org/api/v2",
1_785_628_800,
ICRC_LEDGER_ID,
IcIcrcTokenValueQuery::new(1_785_542_400, 1_785_628_800, 100),
);
let token_values = build_icrc_token_value_report_with_source(&token_value_request, &source)
.expect("custom ICRC token-value source report");
assert_eq!(token_values.returned_row_count, 1);
let request = IcIcrcTotalSupplyRequest::new(
"https://icrc-api.internetcomputer.org/api/v2",
1_785_628_800,
ICRC_LEDGER_ID,
IcIcrcTotalSupplyQuery::new(1_785_542_400, 1_785_628_800, 86_400),
);
let report = build_icrc_total_supply_report_with_source(&request, &source)
.expect("custom ICRC analytics source report");
assert_eq!(report.returned_observation_count, 2);
}
#[cfg(feature = "dashboard-host")]
#[test]
fn public_dashboard_host_error_preserves_oversized_response_evidence() {
let error = IcHostError::HttpResponseTooLarge {
url: "https://ic-api.internetcomputer.org/api/v3/canisters".to_string(),
max_bytes: MAX_IC_DASHBOARD_RESPONSE_BYTES,
observed_bytes: MAX_IC_DASHBOARD_RESPONSE_BYTES + 1,
};
assert!(matches!(
error,
IcHostError::HttpResponseTooLarge {
max_bytes: MAX_IC_DASHBOARD_RESPONSE_BYTES,
observed_bytes,
..
} if observed_bytes == MAX_IC_DASHBOARD_RESPONSE_BYTES + 1
));
}
#[cfg(feature = "dashboard-host")]
struct FixtureSource;
#[cfg(feature = "dashboard-host")]
impl IcCanisterSource for FixtureSource {
fn fetch_canister(
&self,
request: &IcSourceRequest,
canister_id: &str,
) -> Result<IcCanisterSourceData, IcHostError> {
Ok(IcCanisterSourceData {
source: request.clone(),
canister_id: canister_id.to_string(),
dashboard_id: 1,
canister_type: None,
name: String::new(),
subnet_id: SUBNET_ID.to_string(),
controllers: Vec::new(),
language: String::new(),
module_hash: String::new(),
dashboard_updated_at: "2026-07-30T17:47:41.745647".to_string(),
upgrades: None,
})
}
}
#[cfg(feature = "dashboard-host")]
impl IcCanisterCollectionSource for FixtureSource {
fn fetch_canister_count(
&self,
request: &IcSourceRequest,
filters: &IcCanisterFilters,
) -> Result<IcCanisterCountSourceData, IcHostError> {
Ok(IcCanisterCountSourceData {
source: request.clone(),
filters: filters.clone(),
total: 1,
})
}
fn fetch_canister_page(
&self,
request: &IcSourceRequest,
filters: &IcCanisterFilters,
limit: u16,
after: Option<&str>,
before: Option<&str>,
) -> Result<IcCanisterPageSourceData, IcHostError> {
Ok(IcCanisterPageSourceData {
source: request.clone(),
filters: filters.clone(),
requested_limit: limit,
after: after.map(str::to_string),
before: before.map(str::to_string),
previous_cursor: None,
next_cursor: Some(CANISTER_ID.to_string()),
rows: vec![public_page_row()],
})
}
}
#[cfg(feature = "dashboard-host")]
impl IcMetricSource for FixtureSource {
fn fetch_metric(
&self,
request: &IcSourceRequest,
query: &IcMetricQuery,
) -> Result<IcMetricSourceData, IcHostError> {
Ok(IcMetricSourceData {
source: request.clone(),
query: query.clone(),
series: vec![IcMetricSeries {
name: "instruction_rate".to_string(),
observations: vec![IcMetricObservation {
timestamp_unix_secs: query.end_unix_secs,
value: "21089992048.10834".to_string(),
}],
}],
})
}
}
#[cfg(feature = "dashboard-host")]
impl IcIcrcAnalyticsSource for FixtureSource {
fn fetch_indexed_count(
&self,
request: &IcSourceRequest,
ledger_canister_id: &str,
kind: IcIcrcIndexedCountKind,
) -> Result<IcIcrcIndexedCountSourceData, IcHostError> {
Ok(IcIcrcIndexedCountSourceData {
source: request.clone(),
ledger_canister_id: ledger_canister_id.to_string(),
kind,
total: 78_272,
})
}
fn fetch_token_value_series(
&self,
request: &IcSourceRequest,
ledger_canister_id: &str,
query: &IcIcrcTokenValueQuery,
) -> Result<IcIcrcTokenValueSourceData, IcHostError> {
Ok(IcIcrcTokenValueSourceData {
source: request.clone(),
ledger_canister_id: ledger_canister_id.to_string(),
query: query.clone(),
rows: vec![IcIcrcTokenValueSourceRow {
price: Some("63710.86993032754".to_string()),
volume_24h: None,
price_usd: Some("63710.86993032754".to_string()),
volume_24h_usd: Some("23337.881075287027".to_string()),
source: Some("ICPSwap-API".to_string()),
source_url: Some("https://app.icpswap.com/".to_string()),
timestamp_unix_secs: Some(query.start_unix_secs),
}],
})
}
fn fetch_total_supply_series(
&self,
request: &IcSourceRequest,
ledger_canister_id: &str,
query: &IcIcrcTotalSupplyQuery,
) -> Result<IcIcrcTotalSupplySourceData, IcHostError> {
Ok(IcIcrcTotalSupplySourceData {
source: request.clone(),
ledger_canister_id: ledger_canister_id.to_string(),
query: query.clone(),
observations: vec![
IcIcrcTotalSupplyObservation {
timestamp_unix_secs: query.start_unix_secs,
total_supply_base_units: "23309479199".to_string(),
},
IcIcrcTotalSupplyObservation {
timestamp_unix_secs: query.end_unix_secs,
total_supply_base_units: "23680995300".to_string(),
},
],
})
}
}
#[cfg(feature = "dashboard-host")]
impl IcIcrcIndexSource for FixtureSource {
fn fetch_account_list(
&self,
request: &IcSourceRequest,
ledger_canister_id: &str,
query: &IcIcrcAccountListQuery,
) -> Result<IcIcrcAccountListSourceData, IcHostError> {
Ok(IcIcrcAccountListSourceData {
source: request.clone(),
ledger_canister_id: ledger_canister_id.to_string(),
query: query.clone(),
previous_cursor: None,
next_cursor: Some("aaaaa-aa".to_string()),
rows: vec![public_icrc_account_source_row()],
})
}
fn fetch_account_info(
&self,
request: &IcSourceRequest,
_ledger_canister_id: &str,
_account_id: &str,
) -> Result<IcIcrcAccountInfoSourceData, IcHostError> {
Ok(IcIcrcAccountInfoSourceData {
source: request.clone(),
account: public_icrc_account_source_row(),
})
}
fn fetch_holder_list(
&self,
request: &IcSourceRequest,
ledger_canister_id: &str,
query: &IcIcrcHolderListQuery,
) -> Result<IcIcrcHolderListSourceData, IcHostError> {
Ok(IcIcrcHolderListSourceData {
source: request.clone(),
ledger_canister_id: ledger_canister_id.to_string(),
query: query.clone(),
previous_cursor: None,
next_cursor: Some("aaaaa-aa".to_string()),
rows: vec![IcIcrcHolderSourceRow {
principal: "aaaaa-aa".to_string(),
balance_base_units: "1000".to_string(),
total_transactions: 3,
created_at_unix_nanos: 1_731_833_720_056_622_711,
ledger_canister_id: ledger_canister_id.to_string(),
latest_transaction_index: 1_898_152,
percentage: serde_json::json!(0.25),
value_usd: serde_json::Value::Null,
dashboard_updated_at: "2026-04-14T18:55:55.827947".to_string(),
}],
})
}
}
#[cfg(feature = "dashboard-host")]
fn public_icrc_account_source_row() -> IcIcrcAccountSourceRow {
IcIcrcAccountSourceRow {
account_id: "aaaaa-aa".to_string(),
owner: "aaaaa-aa".to_string(),
subaccount: String::new(),
balance_base_units: "1000".to_string(),
total_transactions: 3,
created_at_unix_nanos: 1_731_833_720_056_622_711,
ledger_canister_id: ICRC_LEDGER_ID.to_string(),
latest_transaction_index: 1_898_152,
dashboard_updated_at: "2026-04-14T18:54:04.330496".to_string(),
active_fee_collector: false,
fee_collector_block_ranges: Vec::new(),
}
}
#[cfg(feature = "dashboard-host")]
impl IcNetworkSource for FixtureSource {
fn fetch_boundary_node_data_centers(
&self,
request: &IcSourceRequest,
) -> Result<IcBoundaryNodeDataCentersSourceData, IcHostError> {
Ok(IcBoundaryNodeDataCentersSourceData {
source: request.clone(),
rows: vec![public_boundary_node_data_center()],
})
}
fn fetch_daily_stats(
&self,
request: &IcSourceRequest,
query: &IcDailyStatsQuery,
) -> Result<IcDailyStatsSourceData, IcHostError> {
Ok(IcDailyStatsSourceData {
source: request.clone(),
query: query.clone(),
rows: vec![public_daily_stats_row()],
})
}
}
#[cfg(feature = "dashboard-host")]
impl IcNodeStatusSource for FixtureSource {
fn fetch_node_status_snapshot(
&self,
request: &IcSourceRequest,
) -> Result<IcNodeStatusSourceData, IcHostError> {
Ok(IcNodeStatusSourceData {
source: request.clone(),
scope: IcNodeStatusScope::DashboardMainnetDefault,
cloud_engine_nodes_included: false,
nodes: vec![public_node_status_row()],
})
}
}
fn public_replica_version_rollout() -> IcReplicaVersionSubnetRollout {
IcReplicaVersionSubnetRollout {
subnet_id: SUBNET_ID.to_string(),
proposal_id: 143_297,
executed_timestamp_seconds: 1_785_759_892,
}
}
fn public_provenance(source_endpoint: impl Into<String>) -> IcDashboardReportProvenance {
IcDashboardReportProvenance {
schema_version: 1,
network: "ic".to_string(),
authority: "official_ic_dashboard_api".to_string(),
source_endpoint: source_endpoint.into(),
fetched_at: "2023-11-14T22:13:20Z".to_string(),
fetched_by: "ic-query".to_string(),
certified: false,
point_in_time_guaranteed: false,
}
}
fn public_node_status_snapshot() -> IcNodeStatusSnapshot {
IcNodeStatusSnapshot {
observation: IcNodeStatusObservation {
source: public_provenance(DEFAULT_IC_DASHBOARD_SOURCE_ENDPOINT),
scope: IcNodeStatusScope::DashboardMainnetDefault,
cloud_engine_nodes_included: false,
cache: None,
},
node_count: 1,
counts: IcNodeStatusGroupCounts {
statuses: IcNodeStatusCounts {
total: 1,
up: 0,
down: 1,
disabled: 0,
degraded: 0,
unknown: 0,
},
assignment_statuses: IcNodeAssignmentStatusCounts {
assigned: IcNodeStatusCounts {
total: 1,
up: 0,
down: 1,
disabled: 0,
degraded: 0,
unknown: 0,
},
..IcNodeAssignmentStatusCounts::default()
},
},
nodes: vec![public_node_status_row()],
}
}
fn public_node_status_row() -> IcNodeStatusRow {
IcNodeStatusRow {
node_id: "aaaaa-aa".to_string(),
node_operator_id: "2vxsx-fae".to_string(),
node_provider_id: "rrkah-fqaaa-aaaaa-aaaaq-cai".to_string(),
node_provider_name: "Provider".to_string(),
node_type: "REPLICA".to_string(),
node_reward_type: "Type3dot1".to_string(),
status: "DOWN".to_string(),
alert_name: Some("IC_Node_Offline".to_string()),
subnet_id: Some(SUBNET_ID.to_string()),
cloud_engine_subnet_id: None,
data_center_id: "da11".to_string(),
data_center_name: "Dallas".to_string(),
owner: "Owner".to_string(),
region: "North America,US,Texas".to_string(),
guestos_version: Some("version".to_string()),
guestos_tee_active: Some(false),
ip_address: Some("2001:db8::1".to_string()),
ipv4_connectivity_status: Some(true),
node_hardware_generation: Some("Gen2".to_string()),
}
}
fn public_page_row() -> IcCanisterPageRow {
IcCanisterPageRow {
canister_id: CANISTER_ID.to_string(),
dashboard_id: 226_973,
canister_type: Some("ledger".to_string()),
name: "ICP Ledger".to_string(),
subnet_id: SUBNET_ID.to_string(),
controllers: vec![IcCanisterPageController {
principal_id: "r7inp-6aaaa-aaaaa-aaabq-cai".to_string(),
raw_metadata: Some("NNS".to_string()),
}],
language: String::new(),
module_hash: String::new(),
dashboard_updated_at: "2026-07-30T17:47:41.745647".to_string(),
}
}
fn public_boundary_node_data_center() -> IcBoundaryNodeDataCenterRow {
IcBoundaryNodeDataCenterRow {
dc_id: "da11".to_string(),
name: "Dallas".to_string(),
owner: "Equinix Metal".to_string(),
region: "North America,US,Texas".to_string(),
latitude: "32.7767".to_string(),
longitude: "-96.797".to_string(),
total_nodes: "2".to_string(),
}
}
fn public_daily_stats_row() -> IcDailyStatsRow {
IcDailyStatsRow {
day: "2026-07-31".to_string(),
timestamp_unix_secs: 1_785_542_399,
average_query_transactions_per_second: "3057.0771".to_string(),
average_update_transactions_per_second: "1321.9030499999997".to_string(),
average_transactions_per_second: "4378.980149999999".to_string(),
max_query_transactions_per_second: "3635.62381".to_string(),
max_update_transactions_per_second: "1688.4959999999999".to_string(),
max_total_transactions_per_second: "5062.08807".to_string(),
blocks_per_second_average: "193.50055560014323".to_string(),
}
}