Skip to content

Commit

Permalink
feat: add PocketIC library function to fetch canister logs (#2533)
Browse files Browse the repository at this point in the history
This PR adds the function `PocketIc::fetch_canister_logs` to fetch
canister logs via a query call to the management canister.
  • Loading branch information
mraszyk authored Nov 11, 2024
1 parent fd4bbd4 commit 0c69c97
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 8 deletions.
1 change: 1 addition & 0 deletions packages/pocket-ic/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Functions `PocketIc::take_canister_snapshot`, `PocketIc::load_canister_snapshot`, `PocketIc::list_canister_snapshots`, and `PocketIc::delete_canister_snapshot` to manage canister snapshots.
- Functions `PocketIc::upload_chunk`, `PocketIc::stored_chunks`, and `PocketIc::clear_chunk_store` to manage the WASM chunk store of a canister.
- The function `PocketIc::install_chunked_canister` to install a canister from WASM chunks in the WASM chunk store of a canister.
- The function `PocketIc::fetch_canister_logs` to fetch canister logs via a query call to the management canister.

### Removed
- Functions `PocketIc::from_config`, `PocketIc::from_config_and_max_request_time`, and `PocketIc::from_config_and_server_url`.
Expand Down
18 changes: 17 additions & 1 deletion packages/pocket-ic/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ use crate::{
InstanceId, MockCanisterHttpResponse, RawEffectivePrincipal, RawMessageId, SubnetId,
SubnetKind, SubnetSpec, Topology,
},
management_canister::{CanisterId, CanisterInstallMode, CanisterStatusResult, Snapshot},
management_canister::{
CanisterId, CanisterInstallMode, CanisterLogRecord, CanisterStatusResult, Snapshot,
},
nonblocking::PocketIc as PocketIcAsync,
};
use candid::{
Expand Down Expand Up @@ -668,6 +670,20 @@ impl PocketIc {
})
}

/// Fetch canister logs via a query call to the management canister.
pub fn fetch_canister_logs(
&self,
canister_id: CanisterId,
sender: Principal,
) -> Result<Vec<CanisterLogRecord>, CallError> {
let runtime = self.runtime.clone();
runtime.block_on(async {
self.pocket_ic
.fetch_canister_logs(canister_id, sender)
.await
})
}

/// Request a canister's status.
#[instrument(skip(self), fields(instance_id=self.pocket_ic.instance_id, sender = %sender.unwrap_or(Principal::anonymous()).to_string()))]
pub fn canister_status(
Expand Down
55 changes: 48 additions & 7 deletions packages/pocket-ic/src/nonblocking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ use crate::common::rest::{
};
use crate::management_canister::{
CanisterId, CanisterIdRecord, CanisterInstallMode, CanisterInstallModeUpgradeInner,
CanisterInstallModeUpgradeInnerWasmMemoryPersistenceInner, CanisterSettings,
CanisterStatusResult, ChunkHash, DeleteCanisterSnapshotArgs, InstallChunkedCodeArgs,
InstallCodeArgs, LoadCanisterSnapshotArgs, ProvisionalCreateCanisterWithCyclesArgs, Snapshot,
StoredChunksResult, TakeCanisterSnapshotArgs, UpdateSettingsArgs, UploadChunkArgs,
UploadChunkResult,
CanisterInstallModeUpgradeInnerWasmMemoryPersistenceInner, CanisterLogRecord, CanisterSettings,
CanisterStatusResult, ChunkHash, DeleteCanisterSnapshotArgs, FetchCanisterLogsResult,
InstallChunkedCodeArgs, InstallCodeArgs, LoadCanisterSnapshotArgs,
ProvisionalCreateCanisterWithCyclesArgs, Snapshot, StoredChunksResult,
TakeCanisterSnapshotArgs, UpdateSettingsArgs, UploadChunkArgs, UploadChunkResult,
};
pub use crate::DefaultEffectiveCanisterIdError;
use crate::{CallError, PocketIcBuilder, UserError, WasmResult};
Expand Down Expand Up @@ -515,7 +515,7 @@ impl PocketIc {
) -> Result<RawMessageId, UserError> {
self.submit_call_with_effective_principal(
canister_id,
RawEffectivePrincipal::None,
RawEffectivePrincipal::CanisterId(canister_id.as_slice().to_vec()),
sender,
method,
payload,
Expand Down Expand Up @@ -587,11 +587,29 @@ impl PocketIc {
sender: Principal,
method: &str,
payload: Vec<u8>,
) -> Result<WasmResult, UserError> {
self.query_call_with_effective_principal(
canister_id,
RawEffectivePrincipal::CanisterId(canister_id.as_slice().to_vec()),
sender,
method,
payload,
)
.await
}

pub(crate) async fn query_call_with_effective_principal(
&self,
canister_id: CanisterId,
effective_principal: RawEffectivePrincipal,
sender: Principal,
method: &str,
payload: Vec<u8>,
) -> Result<WasmResult, UserError> {
let endpoint = "read/query";
self.canister_call(
endpoint,
RawEffectivePrincipal::None,
effective_principal,
canister_id,
sender,
method,
Expand All @@ -600,6 +618,29 @@ impl PocketIc {
.await
}

/// Fetch canister logs via a query call to the management canister.
pub async fn fetch_canister_logs(
&self,
canister_id: CanisterId,
sender: Principal,
) -> Result<Vec<CanisterLogRecord>, CallError> {
with_candid::<_, (FetchCanisterLogsResult,), _>(
(CanisterIdRecord { canister_id },),
|payload| async {
self.query_call_with_effective_principal(
Principal::management_canister(),
RawEffectivePrincipal::CanisterId(canister_id.as_slice().to_vec()),
sender,
"fetch_canister_logs",
payload,
)
.await
},
)
.await
.map(|responses| responses.0.canister_log_records)
}

/// Request a canister's status.
#[instrument(skip(self), fields(instance_id=self.instance_id, sender = %sender.unwrap_or(Principal::anonymous()).to_string()))]
pub async fn canister_status(
Expand Down
1 change: 1 addition & 0 deletions packages/pocket-ic/test_canister/canister.did
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,5 @@ service : {
blob_len : (blob) -> (nat64);
call_with_large_blob : (principal, nat64) -> (nat64);
execute_many_instructions : (nat64) -> ();
canister_log : (text) -> ();
}
7 changes: 7 additions & 0 deletions packages/pocket-ic/test_canister/src/canister.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,4 +286,11 @@ async fn execute_many_instructions(n: u64) {
while instruction_counter() < n {}
}

// canister logs

#[update]
async fn canister_log(msg: String) {
ic_cdk::print(msg);
}

fn main() {}
45 changes: 45 additions & 0 deletions packages/pocket-ic/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1766,3 +1766,48 @@ fn test_wasm_chunk_store() {
let stored_chunks = pic.stored_chunks(canister_id, None).unwrap();
assert!(stored_chunks.is_empty());
}

#[test]
fn canister_logs() {
let pic = PocketIc::new();

// We deploy the test canister.
let canister = pic.create_canister();
pic.add_cycles(canister, INIT_CYCLES);
pic.install_canister(canister, test_canister_wasm(), vec![], None);

let logs = pic
.fetch_canister_logs(canister, Principal::anonymous())
.unwrap();
assert!(logs.is_empty());

let log_msg_works = "Logging works!";
pic.update_call(
canister,
Principal::anonymous(),
"canister_log",
encode_one(log_msg_works).unwrap(),
)
.unwrap();
let log_msg_multiple = "Multiple logs are stored!";
pic.update_call(
canister,
Principal::anonymous(),
"canister_log",
encode_one(log_msg_multiple).unwrap(),
)
.unwrap();

let logs = pic
.fetch_canister_logs(canister, Principal::anonymous())
.unwrap();
assert_eq!(logs.len(), 2);
assert_eq!(
String::from_utf8(logs[0].content.clone()).unwrap(),
log_msg_works
);
assert_eq!(
String::from_utf8(logs[1].content.clone()).unwrap(),
log_msg_multiple
);
}

0 comments on commit 0c69c97

Please sign in to comment.