Skip to content

test(cketh): migrate integration tests to PocketIC - #10955

Merged
gregorydemay merged 45 commits into
masterfrom
ic_DEFI-2262_3_cketh-pocket-ic
Aug 13, 2026
Merged

test(cketh): migrate integration tests to PocketIC#10955
gregorydemay merged 45 commits into
masterfrom
ic_DEFI-2262_3_cketh-pocket-ic

Conversation

@gregorydemay

@gregorydemay gregorydemay commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

📚 PR stack

  1. test(ledger-suite-orchestrator): migrate integration tests to PocketIC #10949 — test(ledger-suite-orchestrator): migrate integration tests to PocketIC (merged)
  2. test(cketh): deduplicate signed-transaction and transaction-hash literals in integration tests #10950 — test(cketh): deduplicate signed-transaction and transaction-hash literals (merged)
  3. test(cketh): migrate integration tests to PocketIC #10955 — test(cketh): migrate integration tests to PocketIC 👈 you are here
  4. test(ledger-suite-orchestrator): drop the StateMachine test fixture #10956 — test(ledger-suite-orchestrator): drop the StateMachine test fixture — its content is part of this PR, see below

Follow-up, based on this PR: #11123 (PocketIC server lifetime).

Summary

Flips rs/ethereum/cketh/test_utils and the minter's cketh.rs/ckerc20.rs integration tests from ic-state-machine-tests onto the sync pocket_ic client, following the pattern established for the ledger-suite-orchestrator in #10949.

The minter is now initialized against the fiduciary subnet's "key_1" ECDSA key (StateMachine's "master_ecdsa_public_key" isn't available on PocketIC's fiduciary subnet), which changes the minter's derived Ethereum address. Every signature-derived constant is regenerated as a consequence: MINTER_ADDRESS, DEFAULT_WITHDRAWAL_TRANSACTION(_HASH), DEFAULT_CKERC20_WITHDRAWAL_TRANSACTION(_HASH), the r/s of the default ERC20 signed transaction, the per-account deposit address in should_record_address_to_deposit, and the resubmitted-transaction literal in should_resubmit_new_transaction_with_same_max_fee_per_gas_when_price_increased.

Canister-http mocking is rebuilt around PocketIC's get_canister_http() / mock_canister_http_response(), preserving the legacy "Http body exceeds size limit of {n} bytes." reject that the minter's response-size check matches on.

tests/dump_stable_memory.rs and tests/deposit_from_cex_demo.rs are untouched.

Also included: dropping the StateMachine fixture from the orchestrator

This was meant to be a separate PR (#10956) reviewed on top of this one. It was marked merged by mistake — see the note there — and since GitHub cannot reopen a merged PR, its content stays here rather than being shuffled again.

With the ckETH minter's tests on PocketIC, nothing in rs/ethereum uses the orchestrator's StateMachine-based fixture, so it is deleted along with the now-unused ic-state-machine-tests, ic-types, ic-management-canister-types-private and assert_matches dependencies. The environment-agnostic helpers the pocket_ic module and cketh still import (wasm loaders, init-arg builders, usdc()/usdt(), constants) stay at their existing paths. After this PR, no Rust target under rs/ethereum compiles against ic-state-machine-tests.

Behavioural differences PocketIC forces, and how they are handled

PocketIC differs from StateMachine in two ways the tests are sensitive to. Both are handled in the fixture, in one place each, rather than worked around per test.

Time advances kill the outcalls in flight. PocketIC fails canister-http requests once they cross CANISTER_HTTP_TIMEOUT_INTERVAL; StateMachine left them pending indefinitely. The requests also stay listed until a round processes the failures, so a stub could bind to a request that was already dead and see its response discarded. Worse, a scraping cycle awaiting such a request keeps holding its TimerGuard, so the firing due at the new time is dropped as AlreadyProcessing and the query the test expects is never issued. CkEthSetup::advance_time therefore fails those outcalls up front and deterministically, doing what the advance would have done anyway. It is a no-op when nothing is in flight, because a fixture that has executed no round is expected to trigger the first scraping cycle itself.

A stop request takes effect only in the following round. The outcalls the minter's timers issue in that last running round become visible after it, so stop_minter ticks once before draining them.

The block number stubs also now match on ["finalized", false], which is what the scraping cycle actually queries — unconstrained, they could be consumed by the block height refresh timer's query for the latest block.

Runtime

Measured against master with identical in-test instrumentation on both sides: +5% (ckETH) and +11% (ckERC20) of wall-clock at default parallelism (+15%/+20% single-threaded). Fixture-light tests are indistinguishable from master; the difference tracks the number of IC rounds a test drives, since PocketIC runs out of process. The dominant cost in both harnesses is tests that rebuild the whole fixture several times within one #[test].

🤖 Generated with Claude Code

gregorydemay and others added 10 commits July 29, 2026 11:42
Add a PocketIC-based test fixture (LedgerSuiteOrchestrator, AddErc20TokenFlow,
ManagedCanistersAssert, UniversalCanister, new_pocket_ic()) alongside the
existing StateMachine one in test_utils, and migrate tests/tests.rs to it.

The StateMachine fixture stays untouched at its current paths since
rs/ethereum/cketh still consumes it; it will be deleted once cketh migrates
to PocketIC in a later PR of this stack.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
LedgerSuiteOrchestrator and ManagedCanistersAssert each carried an
AsRef<PocketIc> impl left over from porting the StateMachine fixture, where
it satisfied `T: AsRef<StateMachine>` bounds on stop_canister/
out_of_band_upgrade and a manual CanisterHttpQuery impl. Those call sites now
take `&PocketIc` directly and go through PocketIcHttpQuery, so neither impl
has a caller left (G9/G12).

Addresses review comments:
#10949 (comment)
#10949 (comment)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- mod.rs: replace 13 inline `crate::` paths (default_init_arg, the wasm
  loaders, GIT_COMMIT_HASH, MAX_TICKS) with a single `use crate::{...}`
  block, matching the plain `use crate::MINTER_PRINCIPAL;` style already used
  in the sibling flow.rs. This also lets wait_for's panic message go back to
  inline-capture ("...{MAX_TICKS} ticks...") instead of a positional arg.
- mod.rs: alias `ic_cdk::management_canister::CanisterStatusResult` as
  `CdkCanisterStatusResult` to disambiguate it from the
  `ic_management_canister_types::CanisterStatusResult` imported in the same
  file, instead of spelling out the full path twice.
- flow.rs: import `ic_management_canister_types::CanisterId` instead of
  fully qualifying it once, for the same reason mod.rs already imports it.

Addresses review comments:
#10949 (comment)
#10949 (comment)
#10949 (comment)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ledger_canister_status() returns an owned CanisterStatusResult temporary, so
module_hash can be moved straight out of it instead of being cloned first
(R3).

Addresses review comment:
#10949 (comment)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This comment was carried over verbatim from the StateMachine fixture and
still referenced "10ms per transfer with state machine tests" inside the
module that specifically does not use StateMachine (C2). Dropping the
parenthetical rather than guessing a PocketIC-specific number.

Addresses review comment:
#10949 (comment)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
//:pocket-ic-server was listed in both `data` (needed for the
$(rootpath //:pocket-ic-server) used by POCKET_IC_BIN) and `deps`. It's a
runtime binary reached only through that env var, not linked, so `data`
alone is the accurate declaration -- matching rs/ledger_suite/icp/BUILD.bazel
(data only), as opposed to rs/bitcoin/checker and rs/dogecoin/ckdoge/minter
(deps only, where the binary is invoked differently).

Verified bazel test //rs/ethereum/ledger-suite-orchestrator:integration_tests
still passes (POCKET_IC_BIN resolves fine from data alone).

Addresses review comment:
#10949 (comment)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Inline raw-transaction and transaction-hash string literals in
minter/tests/cketh.rs were byte-identical to the DEFAULT_WITHDRAWAL_TRANSACTION
and DEFAULT_WITHDRAWAL_TRANSACTION_HASH constants already exported by
cketh/test_utils. Replace them with the constants so the upcoming
PocketIC migration only needs to regenerate the signature in one place.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ocketIC

Swap ic-state-machine-tests (and its transitive management-canister-types
/ic-types/ic-cdk deps) for the sync pocket-ic client in
rs/ethereum/cketh/test_utils and the minter's integration_tests target, and
wire up the pocket-ic-server binary as test data.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Flip CkEthSetup and its helpers (mock.rs, flow.rs, ckerc20.rs) onto the
sync PocketIC client: builder uses with_fiduciary_subnet() with canister
execution rate limiting disabled, canisters are funded with
add_cycles(id, u128::MAX), and the minter is initialized with the
fiduciary subnet's "key_1" ECDSA key instead of the StateMachine-only
"master_ecdsa_public_key".

The ECDSA key change means the minter's Ethereum address changes, so
every signature-derived constant is regenerated: MINTER_ADDRESS,
DEFAULT_WITHDRAWAL_TRANSACTION(_HASH), DEFAULT_CKERC20_WITHDRAWAL_
TRANSACTION(_HASH), and the r/s of the default ERC20 signed transaction
in response.rs.

Canister-http mocking is rebuilt around PocketIC's get_canister_http() +
mock_canister_http_response() (replacing the StateMachine-specific manual
cleanup_response/transform dance), while preserving the legacy
"Http body exceeds size limit of {n} bytes." reject the minter's
is_response_too_large matches on. Polling for a specific outcall now also
tolerates ic-cdk-timers' per-timer concurrent-call cap: the block-height-
refresh timer and the log-scraping timer compete for it, and once hit, the
loser defers by a full REFRESH_LATEST_BLOCK_HEIGHT_INTERVAL that
nanosecond-granularity ticking alone can never cross.

Awaiting an update call now goes through a manual ingress_status polling
loop instead of PocketIC's own await_call, since the latter ticks up to
100 rounds with no opportunity to inject mock canister-http responses in
between.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adapt cketh.rs and ckerc20.rs to the PocketIC-backed test_utils API
(error_code/reject_message instead of code()/description(), get_canister_
http() instead of canister_http_request_contexts(), stop_canister(id,
None), Principal-based canister ids), and regenerate the few remaining
signature/key-derivation-dependent literals inline in these test files:
the per-account deposit-address in should_record_address_to_deposit and
the resubmitted-transaction literal in
should_resubmit_new_transaction_with_same_max_fee_per_gas_when_price_increased.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Migrates ckETH/ckERC20 integration test utilities and minter integration tests from ic-state-machine-tests to the synchronous pocket_ic client, aligning the cketh test harness with the PocketIC-based approach introduced earlier for the ledger-suite-orchestrator.

Changes:

  • Replaced StateMachine-based fixtures and ingress/query plumbing with PocketIC equivalents (including manual ingress-status polling where needed).
  • Rebuilt canister-http mocking around PocketIC’s get_canister_http() / mock_canister_http_response() and regenerated signature-derived test constants due to ECDSA key differences.
  • Updated Rust and Bazel dependency graphs to drop ic-state-machine-tests usage in these test crates and add PocketIC inputs/binaries for test execution.

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
rs/ethereum/cketh/test_utils/src/response.rs Regenerates signature components for default signed tx fixtures under PocketIC-derived minter identity.
rs/ethereum/cketh/test_utils/src/mock.rs Ports JSON-RPC outcall matching/mocking to PocketIC’s canister-http APIs and adds retry logic for timer-cap interactions.
rs/ethereum/cketh/test_utils/src/lib.rs Replaces StateMachine fixture with PocketIC fixture, updates canister lifecycle helpers, and adds manual await/drain helpers for outcalls.
rs/ethereum/cketh/test_utils/src/flow.rs Switches async call tracking from MessageId to PocketIC RawMessageId and uses the new await helper.
rs/ethereum/cketh/test_utils/src/ckerc20.rs Ports ckERC20 test setup/flows to PocketIC and updates orchestrator fixture usage to pocket-ic module.
rs/ethereum/cketh/test_utils/Cargo.toml Swaps StateMachine-related deps for PocketIC + public management-canister-types + metrics assert pocket_ic feature.
rs/ethereum/cketh/test_utils/BUILD.bazel Updates Bazel deps to PocketIC + ic-metrics-assert pocket_ic target; drops state-machine deps.
rs/ethereum/cketh/minter/tests/cketh.rs Updates tests to PocketIC APIs and new regenerated constants; adjusts assertions around outcall queues.
rs/ethereum/cketh/minter/tests/ckerc20.rs Updates ckERC20 integration tests to PocketIC fixtures/APIs and regenerated deposit address constant.
rs/ethereum/cketh/minter/Cargo.toml Drops ic-state-machine-tests and adds PocketIC + public management-canister-types for tests.
rs/ethereum/cketh/minter/BUILD.bazel Adds PocketIC server binary to test data and switches deps to PocketIC.
Cargo.lock Reflects dependency changes (adds pocket-ic, removes ic-state-machine-tests from these crates, adds ic-management-canister-types).
Comments suppressed due to low confidence (3)

rs/ethereum/cketh/test_utils/src/lib.rs:574

  • stop_ongoing_https_outcalls returns even if it never manages to drain pending canister-http requests. Callers (e.g. tests using PocketIc::await_call) can then hang/flap because outcalls remain open; it’s better to fail loudly with diagnostics when draining doesn’t succeed.
    pub fn stop_ongoing_https_outcalls(&self) {
        for _ in 0..MAX_TICKS {
            if self.drain_pending_https_outcalls() {
                return;
            }

rs/ethereum/cketh/test_utils/src/lib.rs:841

  • drain_startup_http_outcalls registers mocked responses but never ticks afterwards in the final loop iteration, so the last batch of startup outcalls may remain pending (defeating the purpose of draining them before long advance_time jumps). It should tick after mocking and stop once the queue is empty.
fn drain_startup_http_outcalls(env: &PocketIc) {
    for _ in 0..MAX_TICKS {
        env.tick();
        for request in env.get_canister_http() {
            // Reject rather than answer: these are the startup timers' one-shot log-scraping and

rs/ethereum/cketh/test_utils/src/lib.rs:857

  • drain_stray_latest_block_refresh_calls unconditionally unwrap()s JSON parsing of every pending outcall body; if any non-JSON/JSON-RPC request is present, tests will panic while trying to drain. Also, like the startup drain, it should tick after registering mocks to actually clear requests.
        env.tick();
        for request in env.get_canister_http() {
            let json_request: serde_json::Value = serde_json::from_slice(&request.body).unwrap();
            if json_request["method"] == "eth_getBlockByNumber"
                && json_request["params"] == serde_json::json!(["latest", false])

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread rs/ethereum/cketh/test_utils/src/lib.rs
Comment thread rs/ethereum/cketh/test_utils/src/mock.rs
Comment thread rs/ethereum/cketh/test_utils/src/mock.rs Outdated
Comment thread rs/ethereum/cketh/test_utils/src/lib.rs Outdated
Comment thread rs/ethereum/cketh/test_utils/src/lib.rs
Comment thread rs/ethereum/cketh/test_utils/src/lib.rs Outdated
Comment thread rs/ethereum/cketh/test_utils/src/lib.rs Outdated
Comment thread rs/ethereum/cketh/test_utils/src/mock.rs Outdated
Comment thread rs/ethereum/cketh/test_utils/src/lib.rs Outdated
Comment thread rs/ethereum/cketh/test_utils/src/lib.rs Outdated
Comment thread rs/ethereum/cketh/test_utils/src/lib.rs
Comment thread rs/ethereum/cketh/test_utils/src/mock.rs Outdated
Comment thread rs/ethereum/cketh/test_utils/src/lib.rs Outdated
Comment thread rs/ethereum/cketh/test_utils/src/lib.rs Outdated
Comment thread rs/ethereum/cketh/minter/tests/ckerc20.rs

@gregorydemay gregorydemay left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖🧐 VERDICT: CHANGES_REQUESTED — 0 blockers, 6 mediums, 7 nits; CI pending (24 pass / 4 pending / 0 fail). Local: 2 consecutive uncached green runs of //rs/ethereum/cketh/minter:integration_tests.

⚠️ Posted as a comment rather than a formal “Request changes” review: GitHub refuses --request-changes on a PR owned by the same account. The verdict below is still CHANGES_REQUESTED.

Review details

Bottom line

The port itself is excellent. The #[test] sets in tests/cketh.rs (26) and tests/ckerc20.rs (39) are identical to the PR-2 base — no test dropped, renamed, #[ignore]d or #[should_panic]ed — and every assertion change is accounted for by the documented type/constant mapping. No loosened asserts, no println!/dbg!/commented-out asserts survived the constant-regeneration pass. Senders are preserved exactly (execute_ingress/queryPrincipal::anonymous(), execute_ingress_as/query_aspid.0). mock.rs keeps the raw-body max_response_bytes check with the verbatim legacy message and never invokes cleanup_response manually. Component boundary is clean: the delta touches only rs/ethereum/cketh/** + Cargo.lock; dump_stable_memory.rs and deposit_from_cex_demo.rs are untouched. Everything I flag is in the new fixture plumbing, not in the tests.

The three declared deviations

  1. find_rpc_call_retrying (positive-only) — the asymmetry is sound. expect_no_matching_rpc_call / expect_no_refresh_gas_fee_estimate still go through find_rpc_callpoll_for_request, whose detection window (10 × tick() + 1ns) is byte-for-byte the StateMachine tick_until_next_http_request it replaces — it has not silently shrunk. The mechanism is deterministic, not a flakiness source. I found no simpler in-repo precedent: rs/bitcoin/checker's tick_until_next_request is the same plain 10-tick loop, but the checker has no competing periodic timer, so it doesn't face this. One concrete concern remains (see the inline note on mock.rs:152): the retry budget can burn 300s of simulated time, more than SCRAPING_ETH_LOGS_INTERVAL (180s), so it can cross a scrape boundary.
  2. Custom await_call — justified: pocket-ic's own await_call ticks internally with no injection point, and await_call_no_ticks would deadlock in a sync client that must drive the rounds itself. It is bounded (1000 ticks) and panics with the message id. Two issues inline: the 100× bound relaxation is written as 100 * MAX_TICKS, and the helper's implicit "500 every pending outcall" policy is only wanted by stop_minter, not by minter_response/expect_trap.
  3. new_pocket_ic() made pubsound and effectively forced. CkErc20Setup::new_without_ckerc20_active installs the minter, and CkEthSetup::new calls minter_address(), which needs the fiduciary key_1; LSO's system-subnet builder cannot serve it. The only semantic delta is that cycles are now charged, and neither should_trap_when_ckerc20_feature_not_active asserts a balance — they assert a trap. pub is the minimum surface needed by the two call sites.

Maintainability accounting

  • Duplicationfound. The latest-block-refresh predicate exists in three places, two of them different implementations (mock.rs::matches, mock.rs::is_latest_block_refresh, lib.rs::drain_stray_latest_block_refresh_calls). The 500-reply block and the transient-reject block are each duplicated across two sites. Inline on mock.rs:187, lib.rs:851, lib.rs:584.
  • Divergent invariant handlingfound, same predicate: a malformed request body is .expect(..) in one copy, Err(_) => false in the second, bare .unwrap() in the third.
  • Silent fallbacksfound: stop_ongoing_https_outcalls gives up after 10×10 attempts and returns as though it succeeded (lib.rs:570). The two drain_* helpers likewise never assert they drained anything.
  • Unused derives — none; the PR introduces no new types.
  • Primitive-obsession parameters — none of substance; the only primitives are the raw reject_code literals (nit, mock.rs:125).
  • Test-only code in production modules — none.
  • Redundant / derivable parameters, caller-owned decisions — one instance: await_call's drain policy is decided implicitly for all three call sites (lib.rs:885).

Coverage / test-pyramid

No new tests are warranted — this is a harness swap, and the existing suite is the regression oracle. The regenerated constants (MINTER_ADDRESS, both DEFAULT_*_WITHDRAWAL_TRANSACTION(_HASH), both response.rs r/s pairs) are cross-checked by test_utils:lib_tests::should_use_meaningful_constants (encoding + keccak hash) and, for MINTER_ADDRESS, by the assert_eq! in CkEthSetup::new. Note that the per-account deposit address at tests/ckerc20.rs:182 is a pure golden value with no independent oracle — unavoidable, just worth knowing.

Not blocking, FYI

drain_startup_http_outcalls in CkEthSetup::new has no StateMachine counterpart: it rejects the genesis scrape and block-refresh outcalls rather than leaving them pending. I traced this and it does not weaken anything (a rejection leaves last_observed_block_number None exactly as a pending call did, and the scrape timer retries on the next interval), but it is a behavioural delta the PR description does not mention — worth a sentence there. Also: the description says 40 ckerc20 tests; there are 39.

Verification run

  • bazel test //rs/ethereum/cketh/test_utils:lib_tests //rs/ethereum/cketh/minter:lib_tests //rs/ethereum/cketh/minter:integration_tests --nocache_test_results4/4 PASSED (ckerc20 143.9s, cketh 71.3s)
  • second uncached run of //rs/ethereum/cketh/minter:integration_tests2/2 PASSED (141.3s / 71.0s) — no timing drift between runs, consistent with the retry mechanism being deterministic
  • cargo check --all-targets --all-features -p ic-cketh-test-utils -p ic-cketh-minter → clean
  • timeout = "long" on the suite is still appropriate (bazel reports the declared size as too big, not too small).

CI is not yet green (4 jobs still pending, none failing), which is an independent gate on a READY verdict.

gregorydemay and others added 2 commits July 29, 2026 16:16
…odes

Addresses review comments on PR #10955:
- mock.rs:187 (medium, G5) - the fallback in `matches()` reimplemented
  `is_latest_block_refresh`, with a different malformed-body behaviour
  (`.expect(...)` vs. `Err(_) => false`). Collapse to
  `!is_latest_block_refresh(request)` and make the predicate `pub(crate)`
  so `lib.rs` can reuse the same implementation.
- mock.rs:152 (medium) - `find_rpc_call_retrying`'s retry budget was
  10 * REFRESH_LATEST_BLOCK_HEIGHT_INTERVAL (300s), more than
  SCRAPING_ETH_LOGS_INTERVAL (180s), so it could itself trigger an
  unrelated log scrape. Capped at 180s (6 attempts): 150s (5 attempts)
  was tried and left several tests unable to find their target call
  within budget, so the cap sits at the interval's exact boundary
  rather than strictly under it.
- mock.rs:125 (nit, G25) - named the SysFatal/SysTransient reject-code
  literals instead of repeating a comment at each site.
- poll_for_request's inline SysTransient reject block now calls the
  shared `reject_stray_http_outcall` (see next commit) instead of
  duplicating it (nit, G5).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Addresses review comments on PR #10955:
- lib.rs:885 (medium) - the single `await_call` bundled two policies:
  "await the message" and "answer every pending outcall with HTTP 500".
  `stop_minter` needs the draining; `WithdrawalFlow::minter_response`,
  `Erc20WithdrawalFlow::expect_trap` and `DepositErc20Flow::expect_trap`
  do not, since the drain could silently swallow an outcall one of
  those tests intended to stub itself. Split into `await_call` (which
  only clears stray latest-block-refresh outcalls - never a test-owned
  stub, see `mock::is_latest_block_refresh` - so it cannot swallow
  anything a test cares about) and `await_call_draining_outcalls` (the
  old blanket-500 behaviour), used only by `stop_minter`.
- lib.rs:570 (medium) - `stop_ongoing_https_outcalls` returned as if it
  had succeeded even when it never managed to drain the pending
  outcalls. Panic instead, with the still-pending requests attached.
- lib.rs:576 (medium, C2) - the comment on the same function pointed at
  `mock::tick_until_next_http_request`, which no longer exists (renamed
  to `JsonRpcRequestMatcher::poll_for_request` /
  `find_rpc_call_retrying`). Retargeted.
- lib.rs:851 (medium, G5) - `drain_stray_latest_block_refresh_calls` had
  its own copy of the latest-block-refresh predicate, parsed as a raw
  `serde_json::Value` and `.unwrap()`-ed. Reuse
  `mock::is_latest_block_refresh` instead.
- lib.rs:837 (nit) - `drain_startup_http_outcalls` and
  `drain_stray_latest_block_refresh_calls` burned their full tick budget
  even once the outcall queue was already empty. Added an early exit.
- lib.rs:584 (nit, G5) - the HTTP-500 reply block in
  `drain_pending_https_outcalls` was byte-for-byte the one inside
  `await_call`. Extracted a shared `reply_500` helper.
- lib.rs:881 (nit, C4) - the doc comment said "keeps rejecting" pending
  outcalls, but the code replies with an HTTP 500 (a different path
  through the minter than a canister-http reject). Reworded.
- lib.rs:889 (nit, G25/G16) - `100 * MAX_TICKS` expressed 1000 as a
  multiple of an unrelated constant. Wrote the literal with a one-line
  rationale (this is a 100x relaxation of the `await_ingress(id,
  MAX_TICKS)` bound it replaces, needed because DTS can slice a large
  log-response over many more rounds than that).
- lib.rs:538 (nit, G12) - `try_stop_minter_without_stopping_ongoing_
  https_outcalls`'s 10-iteration loop was dead on the passing path (the
  first check returns immediately) and re-read a terminal status 9 more
  times on the failing path. Collapsed to a single tick + assert.
- lib.rs:562 (Copilot) - `tick_until_minter_canister_status` re-read
  `minter_status()` without ever advancing a round itself, relying on
  `canister_status`'s own update-call processing to make progress.
  Added an explicit `tick()` so the loop doesn't depend on that
  incidental side effect.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@gregorydemay
gregorydemay marked this pull request as ready for review August 12, 2026 07:18
@gregorydemay
gregorydemay requested a review from a team as a code owner August 12, 2026 07:18
@zeropath-ai

zeropath-ai Bot commented Aug 12, 2026

Copy link
Copy Markdown

No security or compliance issues detected. Reviewed everything up to 2615f60.

Security Overview
Detected Code Changes

The diff is too large to display a summary of code changes.

Comment thread rs/ethereum/cketh/minter/BUILD.bazel
Rachit2323 pushed a commit to Rachit2323/ic that referenced this pull request Aug 12, 2026
dfinity#10949)

Migrates the ledger-suite-orchestrator (LSO) integration tests from the
internal `ic-state-machine-tests` harness to PocketIC, following the
pattern already used for the EVM-RPC canister
(dfinity/evm-rpc-canister#294). This is DEFI-2262: a pure refactor of
the test harness — same tests, same coverage, same semantics.

`CkErc20Setup` in `rs/ethereum/cketh` embeds LSO's fixture and shares a
`StateMachine` with it, so flipping LSO's fixture in place would break
cketh in the same commit. To keep this change single-component and the
tree green, this PR **adds** a PocketIC fixture in LSO's `test_utils`
alongside the existing StateMachine one (precedent:
`rs/ledger_suite/icp/test_utils`'s
`state_machine_helpers`/`pocket_ic_helpers` coexisting) and migrates
only `ledger-suite-orchestrator/tests/tests.rs` to it. The StateMachine
fixture stays untouched at its current paths and is still exercised by
`cketh`; it is deleted only in the last PR of this stack, once cketh no
longer needs it.

## Summary of changes
- New `pocket_ic` module in `ledger-suite-orchestrator/test_utils`
(`LedgerSuiteOrchestrator`, `AddErc20TokenFlow`,
`ManagedCanistersAssert`, `UniversalCanister`, `new_pocket_ic()`),
mirroring the existing StateMachine-based types and method names
one-for-one so later PRs in the stack only need to swap import paths.
- `new_pocket_ic()` builds a system subnet (LSO tests assert exact cycle
balances, which only hold on a non-charging subnet) with
canister-execution rate limiting disabled (LSO repeatedly
installs/upgrades ledger, index and archive canisters).
- Metrics assertions now go through `ic-metrics-assert`'s
`PocketIcHttpQuery` (feature `pocket_ic`) instead of a hand-rolled
`CanisterHttpQuery` impl.
- `tests/tests.rs` fully migrated to the new fixture.
- `Cargo.toml`/`BUILD.bazel` updated accordingly; `test_utils` keeps
`ic-state-machine-tests` for now (still needed by the old fixture).

## Coverage
- All spec requirements from the PR are covered: PocketIC fixture added
alongside the old one, `tests/tests.rs` fully migrated, old
fixture/paths untouched, `cketh` still builds against the untouched
fixture.

## 📚 PR stack

1. **dfinity#10949 — test(ledger-suite-orchestrator): migrate integration tests
to PocketIC** 👈 you are here
2. dfinity#10950 — test(cketh): deduplicate signed-transaction literals in
integration tests
3. dfinity#10955 — test(cketh): migrate integration tests to PocketIC
4. dfinity#10956 — test(ledger-suite-orchestrator): drop the StateMachine test
fixture

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…cket-ic

# Conflicts:
#	Cargo.lock
#	rs/ethereum/cketh/test_utils/Cargo.toml
#	rs/ethereum/cketh/test_utils/src/ckerc20.rs
Base automatically changed from ic_DEFI-2262_2_cketh-prep to master August 12, 2026 16:16
gregorydemay and others added 4 commits August 12, 2026 16:18
The server exits hard 10 minutes after it starts whatever the traffic,
and every fixture in a test binary shares one server, so the clock runs
against the whole suite rather than a single test. A single-threaded run
of the ckERC20 suite takes about 8 of those 10 minutes, and losing the
server mid-suite fails every test that follows instead of the one that
is actually slow.

PocketIcBuilder does not expose the lifetime, so start the server here
and hand the builder its URL.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pull Bot pushed a commit to mikeyhodl/ic that referenced this pull request Aug 12, 2026
…rals in integration tests (dfinity#10950)

## Summary
Small preparatory cleanup for the cketh integration tests ahead of the
PocketIC migration (DEFI-2262). No framework change here — tests still
run on `ic-state-machine-tests` and stay green.

- Deduplicated the inline raw-transaction and transaction-hash string
literals in `minter/tests/cketh.rs` that were byte-identical to the
`DEFAULT_WITHDRAWAL_TRANSACTION` / `DEFAULT_WITHDRAWAL_TRANSACTION_HASH`
constants already exported by `cketh/test_utils`.

Since PR 3 regenerates these signatures for the PocketIC fixture,
keeping every occurrence tied to the single constant means the
regeneration only needs to touch one place instead of risking a missed
or inconsistent literal.

## 📚 PR stack

1. dfinity#10949 — test(ledger-suite-orchestrator): migrate integration tests
to PocketIC
2. **dfinity#10950 — test(cketh): deduplicate signed-transaction literals in
integration tests** 👈 you are here
3. dfinity#10955 — test(cketh): migrate integration tests to PocketIC
4. dfinity#10956 — test(ledger-suite-orchestrator): drop the StateMachine test
fixture

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
gregorydemay and others added 2 commits August 13, 2026 05:59
The parameters state which query the stub answers, so a note repeating
it next to each one adds nothing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ic-http-types, proptest and ic-test-utilities-load-wasm are not
referenced anywhere in the crate, and were already unused before this
PR removed the StateMachine fixture.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@gregorydemay

Copy link
Copy Markdown
Contributor Author

🤖 Applied your review from #10956 here, since that PR's content ended up in this one — 2615f601d4 drops ic-http-types, proptest (both from Cargo.toml and BUILD.bazel) and ic-test-utilities-load-wasm (Cargo.toml only, it was never in BUILD.bazel). Confirmed none of the three is referenced anywhere in the crate.

Also removed the note repeating what the stub parameters already say (3b6dff3259).

ledger-suite-orchestrator:integration_tests and cketh_test pass, buildifier clean.

@gregorydemay
gregorydemay enabled auto-merge August 13, 2026 06:04
@gregorydemay
gregorydemay added this pull request to the merge queue Aug 13, 2026
Merged via the queue into master with commit 53efbe5 Aug 13, 2026
40 checks passed
@gregorydemay
gregorydemay deleted the ic_DEFI-2262_3_cketh-pocket-ic branch August 13, 2026 06:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants