From a23113f399a2895e167cf5f96ad5203e1d5f5957 Mon Sep 17 00:00:00 2001 From: Dimitris Sarlis Date: Mon, 13 Jan 2025 14:37:18 +0200 Subject: [PATCH] test: Add test for taking a snapshot that triggers storage reservation (#3395) This is a redo of #3360. Matching on the exact number of bytes requested can be flaky apparently so instead focus on matching only on the required amount of cycles in the error message which is the focus of the test anyway. --- .../tests/storage_reservation.rs | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/rs/execution_environment/tests/storage_reservation.rs b/rs/execution_environment/tests/storage_reservation.rs index 8c9e4598235..d60bbc77211 100644 --- a/rs/execution_environment/tests/storage_reservation.rs +++ b/rs/execution_environment/tests/storage_reservation.rs @@ -1,5 +1,6 @@ use ic_config::execution_environment::Config as ExecutionConfig; use ic_config::subnet_config::SubnetConfig; +use ic_error_types::ErrorCode; use ic_management_canister_types::TakeCanisterSnapshotArgs; use ic_management_canister_types::{self as ic00, CanisterInstallMode, EmptyBlob, Payload}; use ic_registry_subnet_type::SubnetType; @@ -168,3 +169,43 @@ fn test_storage_reservation_triggered_in_canister_snapshot_with_enough_cycles_av reserved_balance_before_snapshot ); } + +#[test] +fn test_storage_reservation_triggered_in_canister_snapshot_without_enough_cycles_available() { + // This test verifies that a canister cannot take a snapshot if it does not have enough + // cycles to cover the storage reservation triggered by the snapshot operation. The main + // point of the test is to verify that the error message is informative and includes the + // amount of cycles required to cover the storage reservation. + // + // The error message is produced by running the test once and checking the output. Calculating + // the exact amounts is hard to do in advance. Note that any changes to cycles cost or how + // the reservation mechanism works may require updating the error message in the test. + + let (env, canister_id) = setup( + SUBNET_MEMORY_THRESHOLD, + SUBNET_MEMORY_CAPACITY, + Some(300_400_000_000), + ); + assert_eq!(reserved_balance(&env, canister_id), 0); + + // Grow memory in update call, should trigger storage reservation. + let _ = env.execute_ingress(canister_id, "update", wasm().stable_grow(3000).build()); + let reserved_balance_before_snapshot = reserved_balance(&env, canister_id); + assert_gt!(reserved_balance_before_snapshot, 0); // Storage reservation is triggered. + + // Take a snapshot to trigger more storage reservation. The canister does not have + // enough cycles in its balance, so this should fail. + let res = env.take_canister_snapshot(TakeCanisterSnapshotArgs::new(canister_id, None)); + match res { + Ok(_) => panic!("Expected an error but got Ok(_)"), + Err(err) => { + assert_eq!(err.code(), ErrorCode::InsufficientCyclesInMemoryGrow); + // Match on a substring of the error message. Due to a difference in instructions consumed on + // Mac vs Linux, we cannot match on the exact number of cycles but we only need to verify it's + // a non-zero amount. + assert!(err + .description() + .contains("due to insufficient cycles. At least 339_603_")); + } + } +}