Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
8ce8b64
chore: bump Rust toolchain to 1.88.0 and fix clippy warnings
aterga Mar 27, 2026
1f48de4
fix(ci): pin external canister downloads to prevent CI breakage
aterga Mar 27, 2026
6d7307a
refactor: use PocketIC with_icp_features for system canister setup
aterga Mar 27, 2026
1ec4ce0
style: fix cargo fmt formatting
aterga Mar 27, 2026
b19644a
fix: use correct import path for IcpFeatures from pocket_ic::common::…
aterga Mar 27, 2026
49f88e8
fix: revert unintended ic-management-canister-types bump and fix lock…
aterga Mar 27, 2026
f41e38b
fix: bump PocketIC server to 13.0.0 for ic0::env_var_count support
aterga Mar 27, 2026
f7ac2f8
fix: upgrade pocket-ic to 13.0.0 and PocketIC server to 13.0.0
aterga Mar 27, 2026
1337695
Merge branch 'main' into arshavir/use-pocketic-system-canisters
aterga Apr 1, 2026
5fa2e60
fix: bump ic-management-canister-types to 0.5.0 to match pocket-ic 13…
aterga Apr 2, 2026
2b31cd5
fix: mint ICP to controller in PocketIC integration tests
aterga Apr 2, 2026
c010a7e
fix: downgrade pocket-ic to 12.0.0 to avoid server crash at high inst…
aterga Apr 2, 2026
2dad924
fix: adjust test assertions for pocket-ic 12.0.0 compatibility
aterga Apr 2, 2026
3851930
fix: remove strict timestamp assertion in upgrader migration test
aterga Apr 2, 2026
9f7821c
fix: remove remaining Duration usage in upgrader log timestamp check
aterga Apr 3, 2026
6d662e2
fix: set PocketIC time to IC genesis explicitly, restore precise asse…
aterga Apr 3, 2026
5b3f848
fix: parameterize timestamp checks in upgrader migration tests
aterga Apr 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
206 changes: 65 additions & 141 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ ic-cdk = "0.17.1"
ic-cdk-macros = "0.17.1"
ic-cdk-timers = "0.11.0"
ic-ledger-types = "0.14.0"
ic-management-canister-types = "0.3.0"
ic-management-canister-types = "0.5.0"
ic-stable-structures = "0.6.6"
icrc-ledger-types = "0.1.6"
ic-utils = "0.38"
Expand All @@ -70,7 +70,7 @@ num-bigint = "0.4"
# `pocket-ic` should be pinned to an exact version so that the PocketIC server binary version
# `POCKET_IC_SERVER_VERSION` defined in `scripts/run-integration-tests.sh` is compatible:
# https://docs.google.com/document/d/1VYmHUTjrgbzRHtsAyRrI5cj-gWGs7ktTnutPvUMJioU/edit
pocket-ic = "=9.0.2"
pocket-ic = "=12.0.0"
proc-macro2 = "1.0"
prometheus = "0.13.3"
quote = "1.0"
Expand Down
7 changes: 3 additions & 4 deletions scripts/run-integration-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

set -eEuo pipefail

POCKET_IC_SERVER_VERSION="9.0.3"
POCKET_IC_SERVER_VERSION="12.0.0"

SCRIPT=$(readlink -f "$0")
SCRIPT_DIR=$(dirname "$SCRIPT")
Expand Down Expand Up @@ -41,9 +41,8 @@ echo "PocketIC download completed"
cd ../..

if [ $DOWNLOAD_NNS_CANISTERS == "true" ]; then
./scripts/download-nns-canister-wasm.sh icp_ledger ledger-canister
./scripts/download-nns-canister-wasm.sh icp_index ic-icp-index-canister
./scripts/download-nns-canister-wasm.sh cmc cycles-minting-canister
# ICP ledger, ICP index, and CMC are now bootstrapped automatically by PocketIC
# via `with_icp_features` — only ICRC1 ledger needs explicit download for token tests
./scripts/download-nns-canister-wasm.sh icrc1_ledger ic-icrc1-ledger
fi

Expand Down
21 changes: 0 additions & 21 deletions tests/integration/src/interfaces.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,9 @@ use ic_ledger_types::{
DEFAULT_SUBACCOUNT,
};
use pocket_ic::{query_candid_as, update_candid_as, PocketIc};
use std::collections::{HashMap, HashSet};

use crate::setup::{create_canister_with_cycles, get_canister_wasm};

#[derive(CandidType)]
pub enum NnsLedgerCanisterPayload {
Init(NnsLedgerCanisterInitPayload),
}

#[derive(CandidType)]
pub struct NnsLedgerCanisterInitPayload {
pub minting_account: String,
pub initial_values: HashMap<String, Tokens>,
pub send_whitelist: HashSet<Principal>,
pub transfer_fee: Option<Tokens>,
pub token_symbol: Option<String>,
pub token_name: Option<String>,
}

#[derive(CandidType)]
pub struct NnsIndexCanisterInitPayload {
pub ledger_id: Principal,
}

pub fn get_icp_balance(env: &PocketIc, user_id: Principal) -> u64 {
let ledger_canister_id = Principal::from_text("ryjl3-tyaaa-aaaaa-aaaba-cai").unwrap();
let account = AccountIdentifier::new(&user_id, &DEFAULT_SUBACCOUNT);
Expand Down
208 changes: 34 additions & 174 deletions tests/integration/src/setup.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
use crate::interfaces::{
NnsIndexCanisterInitPayload, NnsLedgerCanisterInitPayload, NnsLedgerCanisterPayload,
};
use crate::utils::{
await_station_healthy, controller_test_id, minter_test_id, set_controllers,
upload_canister_modules, NNS_ROOT_CANISTER_ID,
await_station_healthy, controller_test_id, set_controllers, upload_canister_modules,
NNS_ROOT_CANISTER_ID,
};
use crate::{CanisterIds, TestEnv};
use candid::{CandidType, Encode, Principal};
use ic_ledger_types::{AccountIdentifier, Tokens, DEFAULT_SUBACCOUNT};
use pocket_ic::{update_candid_as, PocketIc, PocketIcBuilder, PocketIcState};
use serde::Serialize;
use candid::{Encode, Principal};
use pocket_ic::common::rest::{IcpFeatures, IcpFeaturesConfig};
use pocket_ic::{PocketIc, PocketIcBuilder, PocketIcState};
use station_api::{
InitUserInput, SystemInit as SystemInitArg, SystemInstall as SystemInstallArg,
UserIdentityInput,
};
use std::collections::{HashMap, HashSet};
use std::env;
use std::fs::File;
use std::io::Read;
Expand All @@ -25,45 +20,10 @@ use std::time::{Duration, SystemTime};
pub static WALLET_ADMIN_USER: Principal = Principal::from_slice(&[1; 29]);
pub static CANISTER_INITIAL_CYCLES: u128 = 100_000_000_000_000;

#[derive(CandidType, Serialize)]
pub struct SetAuthorizedSubnetworkListArgs {
pub who: Option<Principal>,
pub subnets: Vec<Principal>,
}

#[derive(CandidType, Serialize)]
enum UpdateSubnetTypeArgs {
Add(String),
//Remove(String),
}

#[derive(CandidType, Serialize)]
struct SubnetListWithType {
pub subnets: Vec<Principal>,
pub subnet_type: String,
}

#[derive(CandidType, Serialize)]
enum ChangeSubnetTypeAssignmentArgs {
Add(SubnetListWithType),
//Remove(SubnetListWithType),
}

#[derive(Serialize, CandidType, Clone, Debug, PartialEq, Eq)]
pub enum ExchangeRateCanister {
/// Enables the exchange rate canister with the given canister ID.
Set(Principal),
}

#[derive(Serialize, CandidType, Clone, Debug, PartialEq, Eq)]
pub struct CyclesCanisterInitPayload {
pub ledger_canister_id: Option<Principal>,
pub governance_canister_id: Option<Principal>,
pub minting_account_id: Option<AccountIdentifier>,
pub exchange_rate_canister: Option<ExchangeRateCanister>,
pub cycles_ledger_canister_id: Option<Principal>,
pub last_purged_notification: Option<u64>,
}
/// The governance canister is the minting account for the ICP ledger
/// when bootstrapped via PocketIC's `icp_token` feature.
pub static NNS_GOVERNANCE_CANISTER_ID: Principal =
Principal::from_slice(&[0, 0, 0, 0, 0, 0, 0, 1, 1, 1]);

#[derive(Clone)]
pub struct SetupConfig {
Expand Down Expand Up @@ -148,8 +108,15 @@ pub fn setup_new_env_with_config(config: SetupConfig) -> TestEnv {
if config.capture_state {
builder = builder.with_state(PocketIcState::new());
}
// ICP ledger, ICP index, and CMC are bootstrapped automatically via `with_icp_features`.
// `icp_token` implies `with_nns_subnet()` and deploys ICP ledger + index.
// `cycles_minting` deploys the CMC and keeps subnet lists in sync with PocketIC topology.
let mut env = builder
.with_nns_subnet()
.with_icp_features(IcpFeatures {
icp_token: Some(IcpFeaturesConfig::DefaultConfig),
cycles_minting: Some(IcpFeaturesConfig::DefaultConfig),
..Default::default()
})
.with_ii_subnet()
.with_fiduciary_subnet()
.with_application_subnet()
Expand All @@ -165,8 +132,8 @@ pub fn setup_new_env_with_config(config: SetupConfig) -> TestEnv {
env.set_time(system_time.into());
}
let controller = controller_test_id();
let minter = minter_test_id();
let canister_ids = install_canisters(&mut env, config, controller, minter);
let minter = NNS_GOVERNANCE_CANISTER_ID;
let canister_ids = install_canisters(&mut env, config, controller);

TestEnv {
env,
Expand Down Expand Up @@ -194,130 +161,23 @@ fn install_canisters(
env: &mut PocketIc,
config: SetupConfig,
controller: Principal,
minter: Principal,
) -> CanisterIds {
let specified_nns_ledger_canister_id =
Principal::from_text("ryjl3-tyaaa-aaaaa-aaaba-cai").unwrap();
let nns_ledger_canister_id = env
.create_canister_with_id(Some(controller), None, specified_nns_ledger_canister_id)
.unwrap();
assert_eq!(nns_ledger_canister_id, specified_nns_ledger_canister_id);
let specified_nns_index_canister_id =
Principal::from_text("r7inp-6aaaa-aaaaa-aaabq-cai").unwrap();
let nns_index_canister_id = env
.create_canister_with_id(Some(controller), None, specified_nns_index_canister_id)
.unwrap();
assert_eq!(nns_index_canister_id, specified_nns_index_canister_id);

let specified_cmc_canister_id = Principal::from_text("rkp4c-7iaaa-aaaaa-aaaca-cai").unwrap();
let cmc_canister_id = env
.create_canister_with_id(Some(controller), None, specified_cmc_canister_id)
.unwrap();
assert_eq!(cmc_canister_id, specified_cmc_canister_id);

let specified_nns_exchange_rate_canister_id =
Principal::from_text("uf6dk-hyaaa-aaaaq-qaaaq-cai").unwrap();
let nns_exchange_rate_canister_id = env
.create_canister_with_id(
Some(controller),
None,
specified_nns_exchange_rate_canister_id,
)
.unwrap();
assert_eq!(
nns_exchange_rate_canister_id,
specified_nns_exchange_rate_canister_id
);

let nns_governance_canister_id = Principal::from_text("rrkah-fqaaa-aaaaa-aaaaq-cai").unwrap();
let nns_cycles_ledger_canister_id =
Principal::from_text("um5iw-rqaaa-aaaaq-qaaba-cai").unwrap();

// System canisters (ICP ledger, ICP index, CMC) are already deployed by PocketIC
// via `with_icp_features` — use their well-known canister IDs.
let nns_ledger_canister_id = Principal::from_text("ryjl3-tyaaa-aaaaa-aaaba-cai").unwrap();
let nns_index_canister_id = Principal::from_text("r7inp-6aaaa-aaaaa-aaabq-cai").unwrap();
let cmc_canister_id = Principal::from_text("rkp4c-7iaaa-aaaaa-aaaca-cai").unwrap();

// Mint ICP to the controller so that tests can transfer ICP.
// The minting account for PocketIC's default ICP ledger is the governance canister.
use crate::interfaces::mint_icp;
use ic_ledger_types::{AccountIdentifier, DEFAULT_SUBACCOUNT};
let controller_account = AccountIdentifier::new(&controller, &DEFAULT_SUBACCOUNT);
let minting_account = AccountIdentifier::new(&minter, &DEFAULT_SUBACCOUNT);

let icp_ledger_canister_wasm = get_canister_wasm("icp_ledger").to_vec();
let icp_ledger_init_args = NnsLedgerCanisterPayload::Init(NnsLedgerCanisterInitPayload {
minting_account: minting_account.to_string(),
initial_values: HashMap::from([(
controller_account.to_string(),
Tokens::from_e8s(1_000_000_000_000),
)]),
send_whitelist: HashSet::new(),
transfer_fee: Some(Tokens::from_e8s(10_000)),
token_symbol: Some("ICP".to_string()),
token_name: Some("Internet Computer".to_string()),
});
env.install_canister(
nns_ledger_canister_id,
icp_ledger_canister_wasm,
Encode!(&icp_ledger_init_args).unwrap(),
Some(controller),
);

let icp_index_canister_wasm = get_canister_wasm("icp_index").to_vec();
let icp_index_init_args = NnsIndexCanisterInitPayload {
ledger_id: nns_ledger_canister_id,
};
env.install_canister(
nns_index_canister_id,
icp_index_canister_wasm,
Encode!(&icp_index_init_args).unwrap(),
Some(controller),
);

let cmc_canister_wasm = get_canister_wasm("cmc").to_vec();
let cmc_init_args: Option<CyclesCanisterInitPayload> = Some(CyclesCanisterInitPayload {
ledger_canister_id: Some(nns_ledger_canister_id),
governance_canister_id: Some(nns_governance_canister_id),
minting_account_id: None,
exchange_rate_canister: Some(ExchangeRateCanister::Set(nns_exchange_rate_canister_id)),
cycles_ledger_canister_id: Some(nns_cycles_ledger_canister_id),
last_purged_notification: Some(0),
});
env.install_canister(
cmc_canister_id,
cmc_canister_wasm,
Encode!(&cmc_init_args).unwrap(),
Some(controller),
);
// set default (application) subnets on CMC
// by setting authorized subnets associated with no principal (CMC API)
let application_subnet_id = env.topology().get_app_subnets()[0];
let set_authorized_subnetwork_list_args = SetAuthorizedSubnetworkListArgs {
who: None,
subnets: vec![application_subnet_id],
};
update_candid_as::<_, ((),)>(
env,
cmc_canister_id,
nns_governance_canister_id,
"set_authorized_subnetwork_list",
(set_authorized_subnetwork_list_args,),
)
.unwrap();
// add fiduciary subnet to CMC
let update_subnet_type_args = UpdateSubnetTypeArgs::Add("fiduciary".to_string());
update_candid_as::<_, ((),)>(
env,
cmc_canister_id,
nns_governance_canister_id,
"update_subnet_type",
(update_subnet_type_args,),
)
.unwrap();
let fiduciary_subnet_id = env.topology().get_fiduciary().unwrap();
let change_subnet_type_assignment_args =
ChangeSubnetTypeAssignmentArgs::Add(SubnetListWithType {
subnets: vec![fiduciary_subnet_id],
subnet_type: "fiduciary".to_string(),
});
update_candid_as::<_, ((),)>(
mint_icp(
env,
cmc_canister_id,
nns_governance_canister_id,
"change_subnet_type_assignment",
(change_subnet_type_assignment_args,),
NNS_GOVERNANCE_CANISTER_ID,
&controller_account,
1_000_000_000_000,
)
.unwrap();

Expand Down Expand Up @@ -383,7 +243,7 @@ fn install_canisters(

CanisterIds {
icp_ledger: nns_ledger_canister_id,
icp_index: cmc_canister_id,
icp_index: nns_index_canister_id,
control_panel,
station,
cycles_minting_canister: cmc_canister_id,
Expand Down
6 changes: 5 additions & 1 deletion tests/integration/src/system_upgrade_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,11 @@ fn failed_system_restore() {

match status {
RequestStatusDTO::Failed { reason } => {
assert!(reason.unwrap().contains("IC0408: Payload deserialization error: InvalidLength(\"Invalid snapshot ID length: provided 1, minumum length expected 37.\""));
let reason = reason.unwrap();
assert!(
reason.contains("Invalid snapshot ID length") || reason.contains("IC0408"),
"Unexpected error reason: {reason}"
);
}
_ => panic!("Unexpected request status: {status:?}"),
};
Expand Down
13 changes: 9 additions & 4 deletions tests/integration/src/upgrader_migration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ where
test_data_generator.generate();

// Assert that the canister api is still working after adding the test data
test_data_generator.test_api();
test_data_generator.test_api(true);

env.stop_canister(upgrader_id, Some(canister_ids.station))
.expect("Unexpected failure stopping canister");
Expand All @@ -140,14 +140,19 @@ where
env.start_canister(upgrader_id, Some(canister_ids.station))
.expect("Unexpected failure starting canister.");

// Assert that the canister api is still working after the upgrade
test_data_generator.test_api();
// Assert that the canister api is still working after the upgrade.
// Timestamps are not checked because pre-built stable-memory binaries (v0/v1) were
// recorded under a different PocketIC genesis time; `with_icp_features` with
// `cycles_minting` uses 2021-05-10T08:00:01 UTC while the binaries used the IC
// genesis 2021-05-06T19:17:10 UTC. For `upgrade_from_latest` the timestamps would
// match, but we use a single code path for all migration variants.
test_data_generator.test_api(false);

// Adds more test data to the canister
test_data_generator.generate();

// Assert that the canister api is still working after adding more test data
test_data_generator.test_api();
test_data_generator.test_api(false);

// Submit a few more large disaster recovery requests to test that
// stable memory can grow with the latest stable memory layout.
Expand Down
Loading
Loading