#![allow(clippy::unwrap_used, clippy::expect_used)]
use canton_admin::{AdminClient, Config, Store, TopologyClient};
use canton_auth::{OidcConfig, TokenProvider};
fn endpoint() -> Option<String> {
std::env::var("CANTON_TEST_ENDPOINT").ok()
}
fn admin_endpoint() -> Option<String> {
std::env::var("CANTON_TEST_ADMIN_ENDPOINT").ok()
}
fn oidc() -> Option<OidcConfig> {
Some(OidcConfig::new(
std::env::var("CANTON_TEST_TOKEN_URL").ok()?,
std::env::var("CANTON_TEST_CLIENT_ID").ok()?,
std::env::var("CANTON_TEST_CLIENT_SECRET").ok()?,
))
}
fn admin_oidc() -> Option<OidcConfig> {
Some(OidcConfig::new(
std::env::var("CANTON_TEST_TOKEN_URL").ok()?,
std::env::var("CANTON_TEST_ADMIN_CLIENT_ID").ok()?,
std::env::var("CANTON_TEST_ADMIN_CLIENT_SECRET").ok()?,
))
}
fn admin_client(config: OidcConfig) -> Option<AdminClient> {
AdminClient::connect_lazy(Config::new(endpoint()?).with_oidc(TokenProvider::new(config))).ok()
}
#[tokio::test]
async fn user_self_inspect_reports_the_authenticated_user() {
let (Some(oidc_config), Some(client)) = (oidc(), oidc().and_then(admin_client)) else {
eprintln!(
"skipping user_self_inspect: set CANTON_TEST_ENDPOINT + \
CANTON_TEST_TOKEN_URL/CLIENT_ID/CLIENT_SECRET"
);
return;
};
let _ = oidc_config;
let user = client.current_user().await.expect("current_user");
assert!(!user.id.is_empty(), "authenticated user should have an id");
let rights = client.current_user_rights().await.expect("rights");
let acting = client.acting_parties().await.expect("acting parties");
println!(
"self-inspect — user={} rights={} acting_parties={:?}",
user.id,
rights.len(),
acting
);
}
#[tokio::test]
async fn participant_id_is_returned() {
let Some(client) = oidc().and_then(admin_client) else {
eprintln!("skipping participant_id_is_returned: set CANTON_TEST_ENDPOINT + token env");
return;
};
let id = client.participant_id().await.expect("participant_id");
assert!(id.starts_with("participant"), "got {id}");
println!("participant id: {id}");
}
#[tokio::test]
async fn party_admin_allocate_list_and_get() {
let Some(client) = admin_oidc().and_then(admin_client) else {
eprintln!(
"skipping party_admin_allocate_list_and_get: set CANTON_TEST_ENDPOINT + \
CANTON_TEST_ADMIN_CLIENT_ID/CANTON_TEST_ADMIN_CLIENT_SECRET (ParticipantAdmin)"
);
return;
};
let hint = format!("sdk-admin-test-{}", uuid_like());
let allocated = client
.allocate_party(Some(&hint))
.await
.expect("allocate_party");
assert!(
allocated.party.starts_with(&hint),
"got {}",
allocated.party
);
assert!(allocated.is_local, "allocated party should be local");
println!("allocated party: {}", allocated.party);
let all = client
.list_known_parties()
.await
.expect("list_known_parties");
assert!(
all.iter().any(|p| p.party == allocated.party),
"allocated party should be listed among {} parties",
all.len()
);
let (page, next) = client
.list_known_parties_page(1, None)
.await
.expect("list_known_parties_page");
assert_eq!(page.len(), 1, "page_size=1 yields one party");
assert!(
next.is_some(),
"more parties remain, so a token is returned"
);
let got = client
.get_parties(vec![allocated.party.clone(), "unknown::0".to_string()])
.await
.expect("get_parties");
assert!(got.iter().any(|p| p.party == allocated.party));
assert!(
!got.iter().any(|p| p.party == "unknown::0"),
"unknown parties are silently dropped"
);
println!("list={} page_next={:?} got={}", all.len(), next, got.len());
}
#[tokio::test]
async fn topology_reads_return_mappings() {
let Some(admin_ep) = admin_endpoint() else {
eprintln!(
"skipping topology_reads_return_mappings: set CANTON_TEST_ADMIN_ENDPOINT (:3902)"
);
return;
};
let client = TopologyClient::connect_lazy(Config::new(admin_ep)).expect("valid config");
let p2p = client
.list_party_to_participant(Store::Authorized, "", "")
.await
.expect("list_party_to_participant");
assert!(
!p2p.is_empty(),
"a provisioned participant should have party→participant mappings"
);
let delegations = client
.list_namespace_delegations(Store::Authorized, "", "")
.await
.expect("list_namespace_delegations");
assert!(
!delegations.is_empty(),
"the participant should have at least its own namespace delegation"
);
let vetted = client
.list_vetted_packages(Store::Authorized, "")
.await
.expect("list_vetted_packages");
println!(
"topology — party→participant={} namespace_delegations={} vetted(authorized)={}",
p2p.len(),
delegations.len(),
vetted.len()
);
if let Ok(sync_id) = std::env::var("CANTON_TEST_SYNC_ID") {
let vetted_sync = client
.list_vetted_packages(Store::Synchronizer(sync_id), "")
.await
.expect("list_vetted_packages(synchronizer)");
assert!(
!vetted_sync.is_empty(),
"the synchronizer store should list vetted packages"
);
println!("topology — vetted(synchronizer)={}", vetted_sync.len());
}
}
fn uuid_like() -> String {
use std::time::{SystemTime, UNIX_EPOCH};
let nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map_or(0, |d| d.as_nanos());
format!("{nanos:x}")
}
#[tokio::test]
async fn packages_read_lists_and_reports_status() {
use canton_admin::PackageStatus;
let Some(client) = oidc().and_then(admin_client) else {
eprintln!("skipping packages_read_lists_and_reports_status: set endpoint + token env");
return;
};
let packages = client.list_packages().await.expect("list_packages");
assert!(
!packages.is_empty(),
"a provisioned participant should know at least one package"
);
let probe = std::env::var("CANTON_TEST_LICENSING_PKG")
.ok()
.filter(|pkg| packages.contains(pkg))
.unwrap_or_else(|| packages[0].clone());
let status = client
.get_package_status(&probe)
.await
.expect("get_package_status");
assert_eq!(
status,
PackageStatus::Registered,
"known package is registered"
);
let unknown = client
.get_package_status("0000000000000000000000000000000000000000000000000000000000000000")
.await
.expect("get_package_status(unknown)");
assert_ne!(unknown, PackageStatus::Registered);
println!(
"packages — {} known, probe {} registered, unknown → {:?}",
packages.len(),
&probe[..12.min(probe.len())],
unknown
);
}