-
Notifications
You must be signed in to change notification settings - Fork 76
Custom assertion methods return Result #848
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
…act_balance_by_block_id
WalkthroughRefactors many test helpers in tests/integration/common/utils.rs and related test files to return Result<(), anyhow::Error> (replacing panic-based handling). Helpers updated include assert_tx_succeeded_accepted, get_contract_balance(_by_block_id), assert_tx_reverted, assert_equal_elements, extract_message_error, extract_nested_error, assert_no_notifications, assert_contains, and several block/receipt/balance helpers. TransactionReceipt is used for typed receipt handling. Numerous integration tests across transactions, receipts, block generation, subscriptions (blocks/events/txs/reorg/tx status), fee estimation, advancing time, forking, messaging, deployments, v3 transactions, tracing, minting, dump/load, and websockets updated to handle the new Result returns (mostly by appending .await.unwrap() or unwrapping nested extraction helpers). RpcError gained an impl of std::error::Error. Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Pre-merge checks and finishing touches✅ Passed checks (5 passed)
✨ Finishing touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
I changed the PR description because there are no usage related changes (from the perspective of those parties that just use and don't develop Devnet). @coderabbitai review |
@FabijanC Thank you for the clarification on the PR description. You're absolutely right that these changes are purely development-focused and don't impact users who simply consume Devnet. The distinction between usage-related and development-related changes is important for clarity. I'll review the changes where custom assertion methods now return ✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
tests/integration/common/utils.rs (1)
146-163
: Avoid panic inside Result-returning helperassert_eq!(res.len(), 1) at tests/integration/common/utils.rs:158 still panics; return an anyhow::Error with context instead.
- match devnet.json_rpc_client.call(contract_call, block_id).await { - Ok(res) => { - assert_eq!(res.len(), 1); - Ok(res[0]) - } - Err(e) => anyhow::bail!("Call failed: {e}"), - } + match devnet.json_rpc_client.call(contract_call, block_id).await { + Ok(res) => { + if res.len() != 1 { + anyhow::bail!( + "Expected single return from get_balance at {contract_address:#x}; got len={} (resp={res:?})", + res.len() + ); + } + Ok(res[0]) + } + Err(e) => anyhow::bail!("Call failed (block_id={block_id:?}): {e}"), + }
🧹 Nitpick comments (31)
tests/integration/test_abort_blocks.rs (1)
36-47
: Include tx hash in error for easier debuggingIf this ever fails, the tx hash in the error is handy.
- match devnet.json_rpc_client.get_transaction_by_hash(tx_hash).await { + match devnet.json_rpc_client.get_transaction_by_hash(tx_hash).await { Err(ProviderError::StarknetError(StarknetError::TransactionHashNotFound)) => (), - other => anyhow::bail!("Unexpected tx response: {other:?}"), + other => anyhow::bail!("Unexpected tx response for {tx_hash:#x}: {other:?}"), }tests/integration/test_fork.rs (1)
309-312
: Minor: simplify the match into unwrap_or_elseFunctionally identical, but a tad cleaner.
-match assert_tx_succeeded_accepted(&result.transaction_hash, &devnet.json_rpc_client).await { - Ok(_) => (), - Err(e) => panic!("Transaction failed: {}", e), -}; +assert_tx_succeeded_accepted(&result.transaction_hash, &devnet.json_rpc_client) + .await + .unwrap_or_else(|e| panic!("Transaction failed: {e}"));tests/integration/test_v3_transactions.rs (1)
309-312
: Minor: prefer unwrap_or_else over manual matchSame behavior, slightly tighter.
-match assert_tx_succeeded_accepted(&result.transaction_hash, &devnet.json_rpc_client).await { - Ok(_) => (), - Err(e) => panic!("Transaction failed: {}", e), -}; +assert_tx_succeeded_accepted(&result.transaction_hash, &devnet.json_rpc_client) + .await + .unwrap_or_else(|e| panic!("Transaction failed: {e}"));tests/integration/common/utils.rs (4)
105-123
: Add tx-hash context to errors for faster triageBubbling provider errors is fine, but including the tx hash in messages makes debugging much faster. Also include the tx hash in assertion failures.
pub async fn assert_tx_succeeded_accepted<T: Provider>( tx_hash: &Felt, client: &T, ) -> Result<(), anyhow::Error> { - let receipt = match client.get_transaction_receipt(tx_hash).await { - Ok(receipt) => receipt.receipt, - Err(err) => return Err(err.into()), - }; + let receipt = match client.get_transaction_receipt(tx_hash).await { + Ok(receipt) => receipt.receipt, + Err(err) => return Err(anyhow::anyhow!("Failed to fetch receipt for {tx_hash:#x}: {err}")), + }; match receipt.execution_result() { ExecutionResult::Succeeded => (), - other => anyhow::bail!("Should have succeeded; got: {other:?}"), + other => anyhow::bail!("Tx {tx_hash:#x} should have succeeded; got: {other:?}"), } match receipt.finality_status() { - starknet_rs_core::types::TransactionFinalityStatus::AcceptedOnL2 => Ok(()), - other => anyhow::bail!("Should have been accepted on L2; got: {other:?}"), + starknet_rs_core::types::TransactionFinalityStatus::AcceptedOnL2 => Ok(()), + other => anyhow::bail!("Tx {tx_hash:#x} should have been accepted on L2; got: {other:?}"), } }
165-187
: Include tx hash in revert-path errorsHelpful when reading CI logs.
- other => anyhow::bail!("Should have reverted; got: {other:?}; receipt: {receipt:?}"), + other => anyhow::bail!( + "Tx {tx_hash:#x} should have reverted; got: {other:?}; receipt: {receipt:?}" + ),
457-473
: Remove panic and redundant length checkYou both assert and then check lengths; keep a single non-panicking path that returns an error.
-pub fn assert_equal_elements<T>(iterable1: &[T], iterable2: &[T]) -> Result<(), anyhow::Error> +pub fn assert_equal_elements<T>(iterable1: &[T], iterable2: &[T]) -> Result<(), anyhow::Error> where T: PartialEq, { - assert_eq!(iterable1.len(), iterable2.len()); - match iterable1.len() == iterable2.len() { - true => (), - false => return Err(anyhow::anyhow!("Length mismatch")), - } + if iterable1.len() != iterable2.len() { + return Err(anyhow::anyhow!( + "Length mismatch: left={}, right={}", + iterable1.len(), + iterable2.len() + )); + } for e in iterable1 { - match iterable2.contains(e) { - true => (), - false => return Err(anyhow::anyhow!("Element mismatch")), - } + if !iterable2.contains(e) { + return Err(anyhow::anyhow!("Element mismatch")); + } } Ok(()) }
612-622
: Don’t rely on error strings for timeout detectionString matching on “deadline has elapsed” is brittle. Downcast to tokio’s Elapsed instead.
- match receive_rpc_via_ws(ws).await { - Ok(resp) => anyhow::bail!("Expected no notifications; found: {resp}"), - Err(e) if e.to_string().contains("deadline has elapsed") => { - /* expected */ - Ok(()) - } - Err(e) => anyhow::bail!("Expected to error out due to empty channel; found: {e}"), - } + match receive_rpc_via_ws(ws).await { + Ok(resp) => anyhow::bail!("Expected no notifications; found: {resp}"), + Err(e) if e.downcast_ref::<tokio::time::error::Elapsed>().is_some() => Ok(()), // expected + Err(e) => anyhow::bail!("Expected timeout due to empty channel; found: {e}"), + }tests/integration/test_subscription_with_invalid_block_id.rs (1)
37-37
: LGTM: unwrap on assert_no_notifications matches new helper contract.Consider
.expect("no notifications expected")
for slightly clearer failures, but optional.Also applies to: 61-61
tests/integration/test_subscription_to_new_txs.rs (1)
27-27
: DRY option: wrap repeated.await.unwrap()
in a tiny macro.Many identical calls to
assert_no_notifications(&mut ws).await.unwrap();
. Optional macro reduces noise:+// in a common test module (e.g., tests/integration/common/mod.rs) or at file top: +macro_rules! assert_no_notifs { + ($ws:expr) => {{ + crate::common::utils::assert_no_notifications($ws) + .await + .expect("Unexpected WS notifications"); + }}; +}Then replace lines like:
-assert_no_notifications(&mut ws).await.unwrap(); +assert_no_notifs!(&mut ws);Also applies to: 47-47, 75-75, 98-98, 123-123, 161-161, 175-175, 195-195, 217-217, 237-237, 252-252, 269-269, 289-289, 311-311, 315-315, 334-334
tests/integration/test_subscription_to_events.rs (1)
107-107
: LGTM; optional helper to reduce repetition.Same macro pattern as suggested in new_txs can shorten these
.await.unwrap()
repetitions and standardize the failure message.Also applies to: 112-112, 142-142, 173-173, 203-203, 249-250, 315-316, 339-340, 392-392, 425-425, 444-444, 495-495, 510-510, 553-553
tests/integration/test_subscription_to_tx_status.rs (1)
85-85
: LGTM; consider a shared macro for clearer, consistent failures.Adopt the
assert_no_notifs!
macro (see earlier comment) to reduce boilerplate and give uniform panic text.Also applies to: 94-94, 109-109, 125-125, 136-136, 162-162, 175-175, 211-211, 229-229, 234-234, 256-256, 257-257, 275-275, 299-299, 321-321
tests/integration/test_subscription_to_reorg.rs (1)
62-62
: LGTM; optional macro for the no-notifications checks.Switching to a shared
assert_no_notifs!
macro would keep these concise across tests.Also applies to: 125-125
tests/integration/test_account_impersonation.rs (3)
203-203
: Good: helpers now return Result; unwrapping at call sites matches PR intent.Using
.unwrap()
onassert_contains(...)
centralizes failures at the call site and improves trace quality. Consider.expect("context")
where extra context helps future debugging.Also applies to: 271-272
269-272
: Make contains-check robust to caller casing.Lowercase both haystack and needle to avoid case fragility.
fn assert_anyhow_error_contains_message(error: anyhow::Error, message: &str) { let error_string = format!("{:?}", error.root_cause()).to_lowercase(); - assert_contains(&error_string, message).unwrap(); + let message = message.to_lowercase(); + assert_contains(&error_string, &message).unwrap(); }
245-265
: Balance assertion is directionally correct; consider clarifying intent.
initial >= amount + final
implies ≥ amount spent (plus fees). A short comment can prevent misreads (amount vs fee vs token).tests/integration/test_call.rs (1)
142-154
: LGTM: Result-returning extractors now unwrapped at the assertion site.This preserves precise failure locations. If failures are noisy, switch to
.expect("failed to extract nested error")
for clarity.tests/integration/test_subscription_to_new_tx_receipts.rs (1)
52-52
: LGTM: assert_no_notifications unwrapped across scenarios.Pattern is consistent and improves failure locality. If this becomes repetitive, consider a tiny helper
assert_no_notifications_ok(ws).await?
in utils and let tests returnanyhow::Result<()>
.Also applies to: 72-72, 104-104, 129-129, 154-154, 177-177, 216-216, 231-231, 250-250, 271-271, 293-293, 309-309, 328-328, 350-350, 377-377, 381-381, 403-403
tests/integration/test_advancing_time_on_fork.rs (2)
67-79
: Explicit unwraps match the new Result-returning helpers — OK; considerexpect(...)
for context.Current approach satisfies PR goals. Optionally add short
expect("extraction failed at <step>")
messages to speed up triage when this fails.
142-142
: Guard against missing revert_reason to avoid a panic.
revert_reason()
returns anOption
; useexpect
to provide context if it’sNone
.- assert_contains(tx_details.revert_reason().unwrap(), "Wait a bit more").unwrap(); + assert_contains( + tx_details.revert_reason().as_deref().expect("missing revert_reason"), + "Wait a bit more", + ).unwrap();tests/integration/test_deploy.rs (1)
71-82
: Result-based error extraction and assertions look good.Unwraps are consistent with the helpers now returning
Result
. If you want slightly clearer failures, swap a fewunwrap()
s forexpect("reason")
(optional).Also applies to: 111-111, 140-141, 166-167
tests/integration/test_advancing_time.rs (2)
631-643
: Good: explicit unwraps on nested/message extraction align with the new signatures.No functional issues spotted. Optional: replace some
unwrap()
s withexpect("context")
to make failures self-descriptive.
689-689
: Minor: provide context when unwrapping revert reason.Same nit as elsewhere;
expect("missing revert_reason")
would clarify failures.tests/integration/test_estimate_fee.rs (2)
279-289
: Simplify revert assertion control flow.Use
expect
to shorten and keep intent crisp.- match assert_tx_reverted( - &unsuccessful_invoke_tx.transaction_hash, - &devnet.json_rpc_client, - &["Insufficient max L2Gas"], - ) - .await - { - Ok(_) => {} - Err(e) => panic!("Transaction not reverted: {:?}", e), - }; + assert_tx_reverted( + &unsuccessful_invoke_tx.transaction_hash, + &devnet.json_rpc_client, + &["Insufficient max L2Gas"], + ) + .await + .expect("Transaction not reverted");
367-372
: Optional: factor repetitive nested-error unwrap chain into a tiny helper.A small test-only utility like
fn deepest_msg(err: &ExecutionError) -> String
would remove repetition across tests.tests/integration/test_messaging.rs (1)
1041-1041
: Minor: stabilize revert reason unwrapping.Consider
expect("missing revert_reason")
for clearer panic if provider omits it.tests/integration/test_accepting_blocks_on_l1.rs (2)
21-37
: Make helper fully Result-based (avoid assert!/unwrap! inside).Propagate errors via
?
/ensure!
to align with the PR goal and improve failure locality.Apply:
async fn assert_accepted_on_l1( devnet: &BackgroundDevnet, block_hashes: &[Felt], tx_hashes: &[Felt], ) -> Result<(), anyhow::Error> { for block_hash in block_hashes { match devnet.json_rpc_client.get_block_with_tx_hashes(BlockId::Hash(*block_hash)).await { - Ok(MaybePreConfirmedBlockWithTxHashes::Block(block)) => { - assert_eq!(block.status, BlockStatus::AcceptedOnL1) - } + Ok(MaybePreConfirmedBlockWithTxHashes::Block(block)) => { + anyhow::ensure!( + block.status == BlockStatus::AcceptedOnL1, + "expected block {block_hash:?} to be ACCEPTED_ON_L1, got {:?}", + block.status + ); + } other => anyhow::bail!("Unexpected block: {other:?}"), } } for tx_hash in tx_hashes { - let tx_status = devnet.json_rpc_client.get_transaction_status(tx_hash).await.unwrap(); - assert_eq!(tx_status.finality_status(), SequencerTransactionStatus::AcceptedOnL1); + let tx_status = devnet.json_rpc_client.get_transaction_status(tx_hash).await?; + anyhow::ensure!( + tx_status.finality_status() == SequencerTransactionStatus::AcceptedOnL1, + "expected tx {tx_hash:?} to be ACCEPTED_ON_L1, got {:?}", + tx_status.finality_status() + ); } Ok(()) }
68-69
: Consistent unwrap messaging at call sites.Optional: use
.expect("assert_accepted_on_l1 failed")
for clearer failure context.Example:
-assert_accepted_on_l1(&devnet, &block_hashes, &tx_hashes).await.unwrap(); +assert_accepted_on_l1(&devnet, &block_hashes, &tx_hashes) + .await + .expect("assert_accepted_on_l1 failed");Also applies to: 90-91, 112-114, 135-136
tests/integration/test_account_selection.rs (2)
152-160
: Simplify to expect/unwrap for consistency.Replace verbose match + panic with
.await.expect(...)
to match surrounding style.- match assert_tx_succeeded_accepted( - &account_deployment.transaction_hash, - &devnet.json_rpc_client, - ) - .await - { - Ok(_) => (), - Err(e) => panic!("Transaction failed: {}", e), - }; + assert_tx_succeeded_accepted( + &account_deployment.transaction_hash, + &devnet.json_rpc_client, + ) + .await + .expect("Transaction failed");
81-83
: Optional: add context to unwraps.Use
.expect("tx should be ACCEPTED_ON_L2")
to improve failure messages.Also applies to: 95-97, 109-111, 138-140, 214-216
tests/integration/test_blocks_generation.rs (2)
100-127
: Make assert_latest_block_with_receipts fully Result-driven (no panics).Use
ensure!
and?
to avoidassert_eq!
panics and manual matches.async fn assert_latest_block_with_receipts( devnet: &BackgroundDevnet, block_number: u64, tx_count: usize, ) -> Result<(), anyhow::Error> { let latest_block = &devnet .send_custom_rpc("starknet_getBlockWithReceipts", json!({ "block_id": "latest" })) .await .unwrap(); - assert_eq!(latest_block["transactions"].as_array().unwrap().len(), tx_count); - assert_eq!(latest_block["block_number"], block_number); - assert_eq!(latest_block["status"], "ACCEPTED_ON_L2"); + anyhow::ensure!( + latest_block["transactions"].as_array().unwrap().len() == tx_count, + "unexpected tx count in latest block" + ); + anyhow::ensure!(latest_block["block_number"] == block_number, "unexpected block number"); + anyhow::ensure!(latest_block["status"] == "ACCEPTED_ON_L2", "unexpected block status"); for tx in latest_block["transactions"].as_array().unwrap() { - match assert_tx_succeeded_accepted( - &Felt::from_hex_unchecked(tx["receipt"]["transaction_hash"].as_str().unwrap()), - &devnet.json_rpc_client, - ) - .await - { - Ok(_) => {} - Err(e) => anyhow::bail!("Transaction failed: {}", e), - }; + assert_tx_succeeded_accepted( + &Felt::from_hex_unchecked(tx["receipt"]["transaction_hash"].as_str().unwrap()), + &devnet.json_rpc_client, + ) + .await?; } Ok(()) }
249-252
: Avoid usize math/casts when building Felt.Prefer u128 to prevent platform-dependent
usize
casts.- assert_balance(&devnet, Felt::from(tx_count * DUMMY_AMOUNT as usize), BlockTag::PreConfirmed) + assert_balance( + &devnet, + Felt::from((tx_count as u128) * DUMMY_AMOUNT), + BlockTag::PreConfirmed, + ) .await; - assert_balance(&devnet, Felt::ZERO, BlockTag::Latest).await; + assert_balance(&devnet, Felt::ZERO, BlockTag::Latest).await;- assert_balance(&devnet, Felt::from(tx_count * DUMMY_AMOUNT as usize), BlockTag::PreConfirmed) + assert_balance( + &devnet, + Felt::from((tx_count as u128) * DUMMY_AMOUNT), + BlockTag::PreConfirmed, + ) .await; - assert_balance(&devnet, Felt::from(tx_count * DUMMY_AMOUNT as usize), BlockTag::Latest).await; + assert_balance( + &devnet, + Felt::from((tx_count as u128) * DUMMY_AMOUNT), + BlockTag::Latest, + ) + .await;Also applies to: 264-267
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (29)
tests/integration/common/utils.rs
(8 hunks)tests/integration/get_transaction_by_hash.rs
(3 hunks)tests/integration/get_transaction_receipt_by_hash.rs
(1 hunks)tests/integration/test_abort_blocks.rs
(3 hunks)tests/integration/test_accepting_blocks_on_l1.rs
(5 hunks)tests/integration/test_account_impersonation.rs
(2 hunks)tests/integration/test_account_selection.rs
(7 hunks)tests/integration/test_advancing_time.rs
(2 hunks)tests/integration/test_advancing_time_on_fork.rs
(2 hunks)tests/integration/test_blocks_generation.rs
(10 hunks)tests/integration/test_call.rs
(1 hunks)tests/integration/test_deploy.rs
(4 hunks)tests/integration/test_estimate_fee.rs
(7 hunks)tests/integration/test_fork.rs
(5 hunks)tests/integration/test_gas_modification.rs
(1 hunks)tests/integration/test_messaging.rs
(6 hunks)tests/integration/test_old_state.rs
(2 hunks)tests/integration/test_restart.rs
(1 hunks)tests/integration/test_simulate_transactions.rs
(5 hunks)tests/integration/test_subscription_to_blocks.rs
(7 hunks)tests/integration/test_subscription_to_events.rs
(14 hunks)tests/integration/test_subscription_to_new_tx_receipts.rs
(16 hunks)tests/integration/test_subscription_to_new_txs.rs
(15 hunks)tests/integration/test_subscription_to_reorg.rs
(2 hunks)tests/integration/test_subscription_to_tx_status.rs
(13 hunks)tests/integration/test_subscription_with_invalid_block_id.rs
(2 hunks)tests/integration/test_transaction_handling.rs
(2 hunks)tests/integration/test_v3_transactions.rs
(5 hunks)tests/integration/test_websocket.rs
(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-06-16T07:35:24.725Z
Learnt from: FabijanC
PR: 0xSpaceShard/starknet-devnet#799
File: crates/starknet-devnet-core/src/starknet/mod.rs:240-246
Timestamp: 2025-06-16T07:35:24.725Z
Learning: In the Starknet devnet codebase, `get_class_hash_at` has different behaviors at different layers: at the internal state level (blockifier), it returns `Ok(ClassHash(Felt::ZERO))` for undeployed addresses, while at the RPC layer, this gets converted to `Err(Error::ContractNotFound)`. When checking if a contract is deployed at the internal state level, use `is_ok_and(|h| h.0 == Felt::ZERO)` to detect undeployed addresses.
Applied to files:
tests/integration/test_old_state.rs
tests/integration/test_restart.rs
tests/integration/test_v3_transactions.rs
tests/integration/test_account_selection.rs
tests/integration/test_deploy.rs
🔇 Additional comments (30)
tests/integration/test_websocket.rs (1)
109-109
: Unwrapping the Result is correct hereSwitching to
.await.unwrap()
onassert_no_notifications
aligns with the new Result-returning helper and will fail the test with a clear message if notifications are present.tests/integration/test_abort_blocks.rs (2)
21-21
: Result-based assertion use looks goodUnwrapping
assert_contains
propagates failures cleanly and fits the new pattern.
115-116
: Correct: assert aborted txs via Result-returning helperUsing
.await.unwrap()
matches the new API and surfaces assertion failures precisely.tests/integration/test_fork.rs (4)
264-269
: Good migration to Result-returning balance helperUnwrapping
get_contract_balance
is consistent with the refactor.
295-298
: Explicit success assertion now propagates errors properly
assert_tx_succeeded_accepted(...).await.unwrap();
is the right replacement for panic-based assertions.
300-304
: Consistent use of Result-returning balance helperSame comment as above—LGTM.
576-581
: Good: propagate all substring checks via ResultThe chained
assert_contains(...).unwrap()
calls make failures explicit and keep messages informative.tests/integration/test_v3_transactions.rs (4)
75-77
: LGTM: explicit unwrap on Result-returning assertionThis matches the new helper semantics and improves failure locality.
105-107
: LGTM: deploy success assertion now returns ResultGood usage of
.await.unwrap()
to fail fast when deployment isn’t accepted.
158-160
: LGTM: unwrap after balance-affecting transfer assertionConsistent with the refactor.
406-407
: LGTM: substring check uses Result-returning helperClearer failure message on revert reason mismatch.
tests/integration/common/utils.rs (5)
20-21
: Importing TransactionReceipt is correctThe typed receipt usage below relies on this; good addition.
139-145
: Good API: balance helper returns ResultThis aligns with the PR goal and keeps call sites consistent.
509-513
: Good: convert Message error extraction to ResultClear, idiomatic use of
bail!
for unexpected variants.
516-523
: Good: convert Nested error extraction to ResultConsistent with the error-propagation approach.
708-718
: LGTM:assert_contains
now returns Result with a helpful diffGreat error message; keeps both pattern and text in context.
tests/integration/test_restart.rs (1)
96-99
: LGTM: unwrap adopted for Result-based assertion helper.Change improves error pinpointing without altering test intent.
tests/integration/get_transaction_receipt_by_hash.rs (1)
245-245
: LGTM: explicit unwrap on substring assertion.Keeps failure localized while retaining flexible substring matching.
tests/integration/test_gas_modification.rs (1)
410-412
: LGTM: explicit unwrap surfaces the failing line in stack traces.Switch assert_tx_succeeded_accepted to .await.unwrap() — aligned with the Result‑returning helpers goal. Automated verification couldn't confirm other helpers (ripgrep skipped files / PCRE2 errors); run the supplied check locally or confirm all listed async/sync helpers consistently use .await.unwrap()/.expect()/? or propagate Result.
tests/integration/test_transaction_handling.rs (1)
48-49
: LGTM: unwrap updated Result-based assertions.The
.unwrap()
additions align with the new helper signatures and keep failures localized.Also applies to: 126-127
tests/integration/test_old_state.rs (1)
216-239
: LGTM: explicit unwrap on nested-error/message extraction.Consistent with Result-based helpers and keeps stack traces actionable.
tests/integration/get_transaction_by_hash.rs (1)
41-44
: LGTM: assert_tx_succeeded_accepted now unwrapped.Matches the Result-based helper contract; keeps tests straightforward.
Also applies to: 71-74, 103-106
tests/integration/test_subscription_to_blocks.rs (1)
47-47
: LGTM: unwrapping no-notification assertions.Consistent with the suite-wide change. Keep the two commented flaky assertions as-is until CI stabilizes.
Also applies to: 92-92, 119-119, 141-141, 145-145, 180-180, 216-216, 231-231
tests/integration/test_simulate_transactions.rs (1)
489-489
: LGTM: assert_contains unwrapped in simulation negative-path assertionsThese negative-path assertions unwrap message text and are brittle; match on stable substrings or error codes where possible.
Verification: the ripgrep scan returned "No files were searched" (ripgrep skipped files) — cannot confirm no remaining helper uses. Re-run locally with:
rg -nP -S --hidden -g '!target/**' -C1 '\b(assert_contains|assert_no_notifications|assert_tx_succeeded_accepted|extract_(nested|message)_error)\s*\('Applies to tests/integration/test_simulate_transactions.rs lines 489, 581, 713, 940, 1121.
tests/integration/test_estimate_fee.rs (2)
102-104
: LGTM: asserting txs succeeded now properly propagates errors via.await.unwrap()
.Also applies to: 205-207
642-643
: Message substring checks now propagate failures — OK.These are fine; if messages become unstable across deps, consider constants or pattern-based checks.
Also applies to: 736-737
tests/integration/test_messaging.rs (2)
307-307
: LGTM: success assertion now fails fast via.await.unwrap()
.
441-441
: LGTM: error-message assertions now propagate via.unwrap()
.Also applies to: 453-453
tests/integration/test_blocks_generation.rs (2)
60-61
: LGTM on updated unwraps and Result-based helpers usage.Call-site adjustments are consistent with the PR direction.
If CI lints for
unused_must_use
, please re-run after applying thepre_confirmed
unwrap fix above.Also applies to: 86-87, 230-231, 259-260, 275-275, 458-468, 354-357
70-71
: No change required — helper returns () (no Result to unwrap).
assert_tx_succeeded_pre_confirmed is defined aspub async fn assert_tx_succeeded_pre_confirmed<T: Provider>(_tx_hash: &Felt, _client: &T)
in tests/integration/common/utils.rs:125 and returns unit, so awaiting it without unwrap/expect is correct.Likely an incorrect or invalid review comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are still many helper functions containing macro assertions. In case of error/failed assertion, this has the same effect as panicking.
Also, coderabbit left some useful nitpick comments.
tests/integration/common/utils.rs
Outdated
let receipt = match client.get_transaction_receipt(tx_hash).await { | ||
Ok(receipt) => receipt.receipt, | ||
Err(err) => return Err(err.into()), | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can it be replaced with this?
let receipt = match client.get_transaction_receipt(tx_hash).await { | |
Ok(receipt) => receipt.receipt, | |
Err(err) => return Err(err.into()), | |
}; | |
let receipt = client.get_transaction_receipt(tx_hash).await?.receipt; |
tests/integration/common/utils.rs
Outdated
let receipt: TransactionReceipt = match client.get_transaction_receipt(tx_hash).await { | ||
Ok(receipt) => receipt.receipt, | ||
Err(e) => return Err(e.into()), | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can it be replaced with this?
let receipt: TransactionReceipt = match client.get_transaction_receipt(tx_hash).await { | |
Ok(receipt) => receipt.receipt, | |
Err(e) => return Err(e.into()), | |
}; | |
let receipt = client.get_transaction_receipt(tx_hash).await?.receipt; |
tests/integration/common/utils.rs
Outdated
Ok(res) => { | ||
assert_eq!(res.len(), 1); | ||
res[0] | ||
Ok(res[0]) | ||
} | ||
Err(e) => panic!("Call failed: {e}"), | ||
Err(e) => anyhow::bail!("Call failed: {e}"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Keeping assertions in these helper functions does not completely address the issue targeted by this PR. Perhaps it can be restructured as:
Ok(res) if res.len() == 1 => Ok(res[0]),
other => anyhow::bail!("Expected a single-felt result; got: {other:?}"),
tests/integration/common/utils.rs
Outdated
match assert_contains(reason, expected_reason) { | ||
Ok(_) => (), | ||
Err(e) => return Err(e), | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can it be replaced with this?
match assert_contains(reason, expected_reason) { | |
Ok(_) => (), | |
Err(e) => return Err(e), | |
} | |
assert_contains(reason, expected_reason)?; |
tests/integration/common/utils.rs
Outdated
assert_eq!(iterable1.len(), iterable2.len()); | ||
match iterable1.len() == iterable2.len() { | ||
true => (), | ||
false => return Err(anyhow::anyhow!("Length mismatch")), | ||
} | ||
for e in iterable1 { | ||
assert!(iterable2.contains(e)); | ||
match iterable2.contains(e) { | ||
true => (), | ||
false => return Err(anyhow::anyhow!("Element mismatch")), | ||
} | ||
} | ||
Ok(()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO matching with cases true
and false
is rarely acceptable. And keeping the length assertion as the first line of the function makes no sense.
assert_eq!(iterable1.len(), iterable2.len()); | |
match iterable1.len() == iterable2.len() { | |
true => (), | |
false => return Err(anyhow::anyhow!("Length mismatch")), | |
} | |
for e in iterable1 { | |
assert!(iterable2.contains(e)); | |
match iterable2.contains(e) { | |
true => (), | |
false => return Err(anyhow::anyhow!("Element mismatch")), | |
} | |
} | |
Ok(()) | |
if iterable1.len() != iterable2.len() { | |
anyhow::bail!( | |
"Length mismatch: left = {}, right = {}", | |
iterable1.len(), | |
iterable2.len() | |
); | |
} | |
for e in iterable1 { | |
if !iterable2.contains(e) { | |
anyhow::bail!("Element {:?} from left not found in right", e); | |
} | |
} | |
Ok(()) |
tests/integration/common/utils.rs
Outdated
Err(e) if e.to_string().contains("deadline has elapsed") => { | ||
/* expected */ | ||
Ok(()) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Err(e) if e.to_string().contains("deadline has elapsed") => { | |
/* expected */ | |
Ok(()) | |
} | |
Err(e) if e.to_string().contains("deadline has elapsed") => Ok(()), // expected |
match assert_tx_succeeded_accepted( | ||
&account_deployment.transaction_hash, | ||
&devnet.json_rpc_client, | ||
) | ||
.await | ||
{ | ||
Ok(_) => (), | ||
Err(e) => panic!("Transaction failed: {}", e), | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not unwrap?
match assert_tx_succeeded_accepted( | ||
&Felt::from_hex_unchecked(tx["receipt"]["transaction_hash"].as_str().unwrap()), | ||
&devnet.json_rpc_client, | ||
) | ||
.await; | ||
.await | ||
{ | ||
Ok(_) => {} | ||
Err(e) => anyhow::bail!("Transaction failed: {}", e), | ||
}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use .map_err(...)?
@@ -112,12 +112,18 @@ async fn assert_latest_block_with_receipts( | |||
assert_eq!(latest_block["status"], "ACCEPTED_ON_L2"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The abundance of assertions remaining in helper functions might be addressed with anyhow::ensure
, as suggested by coderabbit in one of its nitpick comments.
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
tests/integration/test_trace.rs (1)
24-60
: Remove remaining unwraps to fully eliminate panics in the assertion helper.Both validate_invocation and fee_transfer_invocation are Options; unwrap can still panic.
- for invocation in [validate_invocation.as_ref().unwrap(), execute_invocation] { + let validate = validate_invocation + .as_ref() + .ok_or_else(|| anyhow::anyhow!("missing validate_invocation"))?; + for invocation in [validate, execute_invocation] { anyhow::ensure!( invocation.contract_address == Felt::from_hex_unchecked(CHARGEABLE_ACCOUNT_ADDRESS) ); anyhow::ensure!( invocation.calldata == vec![ // ... ] ); } - - anyhow::ensure!( - fee_transfer_invocation.as_ref().unwrap().contract_address - == STRK_ERC20_CONTRACT_ADDRESS - ); + let fee = fee_transfer_invocation + .as_ref() + .ok_or_else(|| anyhow::anyhow!("missing fee_transfer_invocation"))?; + anyhow::ensure!(fee.contract_address == STRK_ERC20_CONTRACT_ADDRESS);
♻️ Duplicate comments (2)
tests/integration/common/utils.rs (1)
443-458
: Consider set-based comparison for large inputs.Current O(n²) search is fine for small test slices; if inputs can grow, prefer HashSet to avoid quadratic cost. Requires T: Eq + Hash.
Example:
use std::collections::HashSet; anyhow::ensure!(HashSet::<&T>::from_iter(iterable1) == HashSet::<&T>::from_iter(iterable2));tests/integration/test_messaging.rs (1)
982-987
: Re: borrowing from temporary JSON — this pattern is validReferencing a field of a temporary
serde_json::Value
is OK here due to Rust’s temporary lifetime extension. Previous warning was incorrect.
🧹 Nitpick comments (20)
tests/integration/common/errors.rs (1)
52-59
: Nice: RpcError now implements Error; consider improving Display output.Current Display prints Debug, which is noisy in logs. Prefer a concise "code:message" format.
impl fmt::Display for RpcError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{self:?}") + match &self.data { + Some(data) => write!(f, "RPC error {}: {} | data={}", self.code, self.message, data), + None => write!(f, "RPC error {}: {}", self.code, self.message), + } } }tests/integration/test_dump_and_load.rs (1)
22-39
: Avoid unwrap in Result-returning helper; propagate with ?This aligns with the PR’s goal (no panics in helpers).
- devnet_dump.create_block().await.unwrap(); + devnet_dump.create_block().await?;tests/integration/test_advancing_time.rs (1)
33-38
: Prevent potential u64 overflow when computing upper_limit.Use checked_add (error) or saturating_add (clamp) instead of plain +.
pub fn assert_ge_with_buffer(val1: u64, val2: u64) -> Result<(), anyhow::Error> { anyhow::ensure!(val1 >= val2, "Failed inequation: {val1:?} >= {val2:?}"); - let upper_limit = val2 + BUFFER_TIME_SECONDS; + let upper_limit = val2 + .checked_add(BUFFER_TIME_SECONDS) + .ok_or_else(|| anyhow::anyhow!("timestamp overflow computing upper limit"))?; anyhow::ensure!(val1 <= upper_limit, "Failed inequation: {val1:?} <= {upper_limit:?}"); Ok(()) } pub fn assert_gt_with_buffer(val1: u64, val2: u64) -> Result<(), anyhow::Error> { anyhow::ensure!(val1 > val2, "Failed inequation: {val1:?} > {val2:?}"); - let upper_limit = val2 + BUFFER_TIME_SECONDS; + let upper_limit = val2 + .checked_add(BUFFER_TIME_SECONDS) + .ok_or_else(|| anyhow::anyhow!("timestamp overflow computing upper limit"))?; anyhow::ensure!(val1 <= upper_limit, "Failed inequation: {val1:?} <= {upper_limit:?}"); Ok(()) }Also applies to: 40-45
tests/integration/test_account_selection.rs (1)
44-65
: Add messages to ensure! checks for clearer failures.Current ensures are silent on mismatches; include expected vs. actual.
- anyhow::ensure!(retrieved_class_hash == expected_hash); + anyhow::ensure!( + retrieved_class_hash == expected_hash, + "unexpected class hash: got {retrieved_class_hash:#x}, expected {expected_hash:#x}" + ); @@ - anyhow::ensure!(Felt::from_hex_unchecked(config_class_hash_hex) == expected_hash); + anyhow::ensure!( + Felt::from_hex_unchecked(config_class_hash_hex) == expected_hash, + "config.account_contract_class_hash mismatch: got {config_class_hash_hex}, expected {expected_hash_hex}" + );tests/integration/common/utils.rs (5)
122-134
: Convert pre-confirmed helper to Result for consistency.Keep it feature‑gated or stubbed, but make the signature return Result to align with this PR’s goal and call‑sites.
Apply this minimal stub until starknet-rs 0.17 is adopted:
-pub async fn assert_tx_succeeded_pre_confirmed<T: Provider>(_tx_hash: &Felt, _client: &T) { - eprintln!("TODO: Currently suppressed; undo when starknet-rs updated to 0.17"); +pub async fn assert_tx_succeeded_pre_confirmed<T: Provider>( + _tx_hash: &Felt, + _client: &T, +) -> Result<(), anyhow::Error> { + // TODO: Re-enable real checks when starknet-rs is updated to 0.17. + Ok(())
222-226
: Avoid unwrap on ABI extraction.
as_str().unwrap()
can panic if ABI isn’t a string; return a structured error instead.Apply:
fn take_abi_from_json(value: &mut serde_json::Value) -> Result<serde_json::Value, anyhow::Error> { let abi_jsonified = value["abi"].take(); - anyhow::ensure!(abi_jsonified != serde_json::json!(null)); - Ok(serde_json::from_str(abi_jsonified.as_str().unwrap())?) + anyhow::ensure!(abi_jsonified != serde_json::json!(null), "Missing `abi` field"); + let abi_str = abi_jsonified + .as_str() + .ok_or_else(|| anyhow::anyhow!("`abi` field is not a string"))?; + Ok(serde_json::from_str(abi_str)?) }
470-486
: Remove downcast unwrap to avoid panic.Gracefully handle unexpected provider error types.
- let impl_specific_error: &JsonRpcClientError<HttpTransportError> = - provider_impl_error.as_any().downcast_ref().unwrap(); - match impl_specific_error { + if let Some(impl_specific_error) = + provider_impl_error.as_any().downcast_ref::<JsonRpcClientError<HttpTransportError>>() + { + match impl_specific_error { JsonRpcClientError::JsonRpcError(json_rpc_error) => Ok(json_rpc_error.clone()), - other => { - Err(anyhow::Error::msg(format!("Cannot extract RPC error from: {:?}", other))) - } - } + other => Err(anyhow::anyhow!("Cannot extract RPC error from: {other:?}")), + } + } else { + Err(anyhow::anyhow!("Provider error is not JsonRpcClientError; got: {provider_impl_error:?}")) + }
588-597
: Add context to ensures for easier diffs.Including expected/actual in error text speeds up debugging.
- anyhow::ensure!(notification["jsonrpc"] == "2.0"); - anyhow::ensure!(notification["method"] == method); + anyhow::ensure!(notification["jsonrpc"] == "2.0", "jsonrpc != 2.0: {notification}"); + anyhow::ensure!(notification["method"] == method, "method mismatch: {notification}");
601-609
: Match timeout by type, not substring.String-matching “deadline has elapsed” is brittle; downcast to tokio timeout error.
match receive_rpc_via_ws(ws).await { - Ok(resp) => anyhow::bail!("Expected no notifications; found: {resp}"), - Err(e) if e.to_string().contains("deadline has elapsed") => Ok(()), /* expected */ + Ok(resp) => anyhow::bail!("Expected no notifications; found: {resp}"), + Err(e) if e.downcast_ref::<tokio::time::error::Elapsed>().is_some() => Ok(()), /* expected */ Err(e) => anyhow::bail!("Expected to error out due to empty channel; found: {e}"), }tests/integration/test_subscription_to_tx_status.rs (2)
34-54
: Prefer field-wise checks over whole-JSON equality.Object order or unrelated fields could cause fragile failures; assert specific fields instead.
- anyhow::ensure!( - notification - == json!({ - "jsonrpc": "2.0", - "method": "starknet_subscriptionTransactionStatus", - "params": { - "result": { - "transaction_hash": tx_hash, - "status": { - "finality_status": expected_finality_status, - "failure_reason": null, - "execution_status": "SUCCEEDED", - }, - }, - "subscription_id": subscription_id, - } - }) - ); + anyhow::ensure!(notification["jsonrpc"] == "2.0", "bad jsonrpc: {notification}"); + anyhow::ensure!(notification["method"] == "starknet_subscriptionTransactionStatus", "bad method: {notification}"); + anyhow::ensure!(notification["params"]["subscription_id"] == subscription_id, "bad sub id: {notification}"); + let result = ¬ification["params"]["result"]; + anyhow::ensure!(result["transaction_hash"] == tx_hash, "bad tx hash: {notification}"); + anyhow::ensure!(result["status"]["execution_status"] == "SUCCEEDED", "bad exec status: {notification}"); + anyhow::ensure!(result["status"]["finality_status"] == expected_finality_status, "bad finality: {notification}"); + anyhow::ensure!(result["status"]["failure_reason"].is_null(), "unexpected failure_reason: {notification}");
141-179
: Use?
instead ofunwrap()
inside Result-returning helper.Keep propagation consistent and avoid test panics.
- let notification = receive_rpc_via_ws(ws_before_tx).await.unwrap(); + let notification = receive_rpc_via_ws(ws_before_tx).await?; @@ - let notification = receive_rpc_via_ws(ws_after_tx).await?; + let notification = receive_rpc_via_ws(ws_after_tx).await?;tests/integration/test_gas_modification.rs (2)
174-184
: Avoid float-to-integer casts for big constants.
8.5e18 as u128
loses precision; use integer literals.- let l1_fri_price = 8.5e18 as u128; - let l1_data_fri_price = 7.5e18 as u128; - let l2_fri_price = 6.5e18 as u128; + let l1_fri_price: u128 = 8_500_000_000_000_000_000; + let l1_data_fri_price: u128 = 7_500_000_000_000_000_000; + let l2_fri_price: u128 = 6_500_000_000_000_000_000; @@ - "gas_price_wei": 9e18 as u128, + "gas_price_wei": 9_000_000_000_000_000_000_u128, @@ - "data_gas_price_wei": 8e18 as u128, + "data_gas_price_wei": 8_000_000_000_000_000_000_u128, @@ - "l2_gas_price_wei": 7e18 as u128, + "l2_gas_price_wei": 7_000_000_000_000_000_000_u128, @@ - let new_l2_fri_price = 9e7 as u128; // approximate upper limit that will pass + let new_l2_fri_price: u128 = 90_000_000; // approximate upper limit that will pass @@ - "gas_price_wei": 9e18 as u128, - "data_gas_price_wei": 8e18 as u128, - "l2_gas_price_wei": 7.5e18 as u128, - "gas_price_fri": 7e18 as u128, - "data_gas_price_fri": 6e18 as u128, - "l2_gas_price_fri": 5.5e18 as u128, + "gas_price_wei": 9_000_000_000_000_000_000_u128, + "data_gas_price_wei": 8_000_000_000_000_000_000_u128, + "l2_gas_price_wei": 7_500_000_000_000_000_000_u128, + "gas_price_fri": 7_000_000_000_000_000_000_u128, + "data_gas_price_fri": 6_000_000_000_000_000_000_u128, + "l2_gas_price_fri": 5_500_000_000_000_000_000_u128,Also applies to: 189-201, 203-217, 448-455
193-201
: Optional: validate response shape before indexing [0].Safer to check array length and bail with a clear message.
let resp = devnet.send_custom_rpc("starknet_simulateTransactions", params).await?; let first = resp.get(0).ok_or_else(|| anyhow::anyhow!("empty simulateTransactions response"))?;Also applies to: 203-217
tests/integration/test_v3_transactions.rs (1)
321-343
: Eliminate unwraps inside Result-returning helper; propagate with?
.Prevents accidental panics if callers change Action/options.
- .await - .map_err(|e| anyhow::Error::msg(e.to_string()))?, + .await + .map_err(|e| anyhow::Error::msg(e.to_string()))?, @@ - DeclarationV3::new(sierra_class.clone(), *casm_hash, account.unwrap()) + DeclarationV3::new( + sierra_class.clone(), + *casm_hash, + account.ok_or(anyhow::anyhow!("Account not found"))?, + ) @@ - match declaration.send().await.unwrap_err() { + match declaration.send().await.unwrap_err() { @@ - let account_deployment = AccountDeploymentV3::new(*salt, account_factory.unwrap()) + let account_deployment = AccountDeploymentV3::new( + *salt, + account_factory.ok_or(anyhow::anyhow!("Account Factory is None"))?, + ) @@ - let execution = ExecutionV3::new(calls.clone(), account.unwrap()) + let execution = ExecutionV3::new( + calls.clone(), + account.ok_or(anyhow::anyhow!("Account not found"))?, + ) @@ - assert_contains(reason.as_str(), "Insufficient max L2Gas").unwrap(); + assert_contains(reason.as_str(), "Insufficient max L2Gas")?;Also applies to: 358-373, 375-390, 392-399, 401-423, 427-428
tests/integration/test_abort_blocks.rs (2)
24-38
: Make “block not found” assertion robust (avoid brittle full-equality on RpcError)Comparing whole RpcError (including exact message and data) is fragile. Match code and check substring on message instead.
- anyhow::ensure!(err == RpcError { code: 24, message: "Block not found".into(), data: None }); - Ok(()) + assert_contains(&err.message, "Block not found")?; + anyhow::ensure!(err.code == 24); + Ok(())
40-51
: Tighten match: handle Ok separately for clearer failuresCurrently Ok() and unexpected Err() are merged. Split them for sharper diagnostics.
- for tx_hash in tx_hashes { - match devnet.json_rpc_client.get_transaction_by_hash(tx_hash).await { - Err(ProviderError::StarknetError(StarknetError::TransactionHashNotFound)) => (), - other => anyhow::bail!("Unexpected tx response for {tx_hash:#x}: {other:?}"), - } - } + for tx_hash in tx_hashes { + match devnet.json_rpc_client.get_transaction_by_hash(tx_hash).await { + Err(ProviderError::StarknetError(StarknetError::TransactionHashNotFound)) => {} + Ok(_) => anyhow::bail!("Unexpectedly found tx {tx_hash:#x}"), + Err(e) => anyhow::bail!("Unexpected error for {tx_hash:#x}: {e:?}"), + } + }tests/integration/test_minting.rs (1)
11-48
: Avoid unwraps in Result-returning helper; harden tx_hash checkUse ? instead of unwrap and validate tx_hash presence before starts_with.
- let tx_hash_value = resp_body["tx_hash"].take(); - anyhow::ensure!(tx_hash_value.as_str().unwrap().starts_with("0x")); + let tx_hash_value = resp_body["tx_hash"].take(); + let tx_hash_str = tx_hash_value + .as_str() + .ok_or_else(|| anyhow::anyhow!("tx_hash missing or not a string"))?; + anyhow::ensure!(tx_hash_str.starts_with("0x")); @@ - let new_balance = - devnet.get_balance_latest(&Felt::from_hex_unchecked(address), unit).await.unwrap(); + let new_balance = + devnet.get_balance_latest(&Felt::from_hex_unchecked(address), unit).await?;tests/integration/test_accepting_blocks_on_l1.rs (1)
31-37
: Prefer?
overunwrap()
in helper to preserve error contextThis helper already returns Result; propagate provider errors directly.
- for tx_hash in tx_hashes { - let tx_status = devnet.json_rpc_client.get_transaction_status(tx_hash).await.unwrap(); - anyhow::ensure!(tx_status.finality_status() == SequencerTransactionStatus::AcceptedOnL1); - } + for tx_hash in tx_hashes { + let tx_status = devnet.json_rpc_client.get_transaction_status(tx_hash).await?; + anyhow::ensure!(tx_status.finality_status() == SequencerTransactionStatus::AcceptedOnL1); + }tests/integration/test_messaging.rs (1)
196-225
: Don’tunwrap()
inside a Result-returning helperPropagate the balance query error with
?
.- anyhow::ensure!(get_balance(devnet, from_address, user).await.unwrap() == [Felt::ZERO]); + anyhow::ensure!(get_balance(devnet, from_address, user).await? == [Felt::ZERO]);tests/integration/test_blocks_generation.rs (1)
271-275
: Avoid architecture-dependent arithmetic withusize
when creating FeltsUse explicit u128 to prevent surprises across platforms.
- assert_balance(&devnet, Felt::from(tx_count * DUMMY_AMOUNT as usize), BlockTag::PreConfirmed) + assert_balance(&devnet, Felt::from((tx_count as u128) * DUMMY_AMOUNT), BlockTag::PreConfirmed) @@ - assert_balance(&devnet, Felt::from(tx_count * DUMMY_AMOUNT as usize), BlockTag::PreConfirmed) + assert_balance(&devnet, Felt::from((tx_count as u128) * DUMMY_AMOUNT), BlockTag::PreConfirmed) @@ - assert_balance(&devnet, Felt::from(tx_count * DUMMY_AMOUNT as usize), BlockTag::Latest) + assert_balance(&devnet, Felt::from((tx_count as u128) * DUMMY_AMOUNT), BlockTag::Latest)Also applies to: 287-293
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
tests/integration/common/errors.rs
(1 hunks)tests/integration/common/utils.rs
(10 hunks)tests/integration/general_rpc_tests.rs
(1 hunks)tests/integration/test_abort_blocks.rs
(11 hunks)tests/integration/test_accepting_blocks_on_l1.rs
(5 hunks)tests/integration/test_account_selection.rs
(8 hunks)tests/integration/test_advancing_time.rs
(20 hunks)tests/integration/test_blocks_generation.rs
(12 hunks)tests/integration/test_dump_and_load.rs
(4 hunks)tests/integration/test_estimate_fee.rs
(7 hunks)tests/integration/test_gas_modification.rs
(7 hunks)tests/integration/test_messaging.rs
(37 hunks)tests/integration/test_minting.rs
(4 hunks)tests/integration/test_subscription_to_tx_status.rs
(15 hunks)tests/integration/test_trace.rs
(5 hunks)tests/integration/test_v3_transactions.rs
(11 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- tests/integration/test_estimate_fee.rs
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-06-16T07:35:24.725Z
Learnt from: FabijanC
PR: 0xSpaceShard/starknet-devnet#799
File: crates/starknet-devnet-core/src/starknet/mod.rs:240-246
Timestamp: 2025-06-16T07:35:24.725Z
Learning: In the Starknet devnet codebase, `get_class_hash_at` has different behaviors at different layers: at the internal state level (blockifier), it returns `Ok(ClassHash(Felt::ZERO))` for undeployed addresses, while at the RPC layer, this gets converted to `Err(Error::ContractNotFound)`. When checking if a contract is deployed at the internal state level, use `is_ok_and(|h| h.0 == Felt::ZERO)` to detect undeployed addresses.
Applied to files:
tests/integration/test_account_selection.rs
tests/integration/test_v3_transactions.rs
tests/integration/test_blocks_generation.rs
tests/integration/common/utils.rs
📚 Learning: 2025-09-16T08:50:01.391Z
Learnt from: Abeeujah
PR: 0xSpaceShard/starknet-devnet#848
File: tests/integration/test_messaging.rs:960-964
Timestamp: 2025-09-16T08:50:01.391Z
Learning: In Rust, when binding a reference to a field or method result of a temporary value (like `let ref_var = &temporary.field` or `let ref_var = &temporary["key"]`), the temporary's lifetime is extended to match the lifetime of the reference binding. This prevents dangling references and makes patterns like `let generated_l2_txs_raw = &devnet.send_custom_rpc(...).await.unwrap()["generated_l2_transactions"];` perfectly valid.
Applied to files:
tests/integration/test_messaging.rs
🔇 Additional comments (22)
tests/integration/common/utils.rs (10)
105-120
: LGTM: Result-based success assertion is solid.Typed receipt + explicit checks read well and improve failure messages.
136-141
: LGTM: balance helper now propagates errors.
143-157
: LGTM: precise matching on single felt.Clear error message; good use of bail.
159-175
: LGTM: revert assertion now error-driven.Use of TransactionReceipt and chaining
assert_contains
is clean.
240-244
: LGTM: JSON-equality assertions with anyhow::ensure.
438-441
: LGTM: propagate deployment error with?
.
488-494
: LGTM: equality check now returns Result.
496-502
: LGTM: message extraction with precise errors.
504-512
: LGTM: nested error extraction with precise errors.
693-704
: LGTM:assert_contains
now returns Result with rich message.tests/integration/general_rpc_tests.rs (1)
91-92
: LGTM: unwrap the Result from equality assertion.Matches the new Result-returning helper.
tests/integration/test_subscription_to_tx_status.rs (1)
85-87
: LGTM: updated call-sites now unwrap Result-returning helpers.Also applies to: 95-96, 111-112, 126-127, 137-138, 162-163, 175-176, 212-214, 233-234, 237-238, 260-262, 281-282, 329-329
tests/integration/test_gas_modification.rs (3)
57-61
: LGTM: scenario now propagates errors.
119-141
: LGTM: clearer ensures with mapped errors.Also applies to: 151-165
233-236
: LGTM: call-sites updated to unwrap scenario errors in tests.Also applies to: 245-247
tests/integration/test_v3_transactions.rs (2)
74-76
: LGTM: assertion helpers now awaited and unwrapped.Also applies to: 104-107, 157-160, 308-309
186-201
: LGTM: tests updated to propagate errors via unwrap on awaited helpers.Also applies to: 219-225, 286-287
tests/integration/test_abort_blocks.rs (1)
12-22
: LGTM: helper now returns Result and propagates via anyhowThe conversion to Result with delegated string-contains check via assert_contains is clean.
tests/integration/test_accepting_blocks_on_l1.rs (1)
39-49
: LGTM:assert_latest_accepted_on_l2
now cleanly propagates errorsConsistent use of
?
+anyhow::ensure!
looks good.tests/integration/test_messaging.rs (2)
67-89
: LGTM:increase_balance
returns Result and maps account errorThe error mapping is sufficient for test diagnostics.
92-105
: LGTM:get_balance
now returns Result and uses typed callClear and idiomatic.
tests/integration/test_blocks_generation.rs (1)
113-139
: LGTM: receipts helper uses JSON path checks with explicit error messagesThe guarded hex parsing with
ok_or(anyhow!(...))?
is solid.
async fn assert_pre_confirmed_block_with_tx_hashes( | ||
devnet: &BackgroundDevnet, | ||
tx_count: usize, | ||
) -> Result<(), anyhow::Error> { | ||
let pre_confirmed_block = devnet.get_pre_confirmed_block_with_tx_hashes().await?; | ||
|
||
assert_eq!(pre_confirmed_block.transactions.len(), tx_count); | ||
anyhow::ensure!(pre_confirmed_block.transactions.len() == tx_count); | ||
|
||
for tx_hash in pre_confirmed_block.transactions { | ||
assert_tx_succeeded_pre_confirmed(&tx_hash, &devnet.json_rpc_client).await; | ||
} | ||
|
||
Ok(()) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: dropped Result from assert_tx_succeeded_pre_confirmed
(error silently ignored)
These helpers now return Result; use ?
to fail the helper on error.
- for tx_hash in pre_confirmed_block.transactions {
- assert_tx_succeeded_pre_confirmed(&tx_hash, &devnet.json_rpc_client).await;
- }
+ for tx_hash in pre_confirmed_block.transactions {
+ assert_tx_succeeded_pre_confirmed(&tx_hash, &devnet.json_rpc_client).await?;
+ }
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
async fn assert_pre_confirmed_block_with_tx_hashes( | |
devnet: &BackgroundDevnet, | |
tx_count: usize, | |
) -> Result<(), anyhow::Error> { | |
let pre_confirmed_block = devnet.get_pre_confirmed_block_with_tx_hashes().await?; | |
assert_eq!(pre_confirmed_block.transactions.len(), tx_count); | |
anyhow::ensure!(pre_confirmed_block.transactions.len() == tx_count); | |
for tx_hash in pre_confirmed_block.transactions { | |
assert_tx_succeeded_pre_confirmed(&tx_hash, &devnet.json_rpc_client).await; | |
} | |
Ok(()) | |
} | |
async fn assert_pre_confirmed_block_with_tx_hashes( | |
devnet: &BackgroundDevnet, | |
tx_count: usize, | |
) -> Result<(), anyhow::Error> { | |
let pre_confirmed_block = devnet.get_pre_confirmed_block_with_tx_hashes().await?; | |
anyhow::ensure!(pre_confirmed_block.transactions.len() == tx_count); | |
for tx_hash in pre_confirmed_block.transactions { | |
assert_tx_succeeded_pre_confirmed(&tx_hash, &devnet.json_rpc_client).await?; | |
} | |
Ok(()) | |
} |
🤖 Prompt for AI Agents
In tests/integration/test_blocks_generation.rs around lines 65 to 78, the helper
assert_tx_succeeded_pre_confirmed now returns a Result but its return value is
being ignored; update the loop to propagate errors by awaiting with ? so the
test fails on error. Replace the current for loop body with a call that uses
.await? (e.g., for tx_hash in pre_confirmed_block.transactions {
assert_tx_succeeded_pre_confirmed(&tx_hash, &devnet.json_rpc_client).await?; }),
leaving the function signature returning Result<(), anyhow::Error> intact.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pub async fn assert_tx_succeeded_pre_confirmed<T: Provider>(_tx_hash: &Felt, _client: &T) {}
doesn't return a result
async fn assert_pre_confirmed_block_with_txs( | ||
devnet: &BackgroundDevnet, | ||
tx_count: usize, | ||
) -> Result<(), anyhow::Error> { | ||
let pre_confirmed_block = devnet.get_pre_confirmed_block_with_txs().await?; | ||
|
||
assert_eq!(pre_confirmed_block.transactions.len(), tx_count); | ||
anyhow::ensure!(pre_confirmed_block.transactions.len() == tx_count); | ||
|
||
for tx in pre_confirmed_block.transactions { | ||
assert_tx_succeeded_pre_confirmed(tx.transaction_hash(), &devnet.json_rpc_client).await; | ||
} | ||
|
||
Ok(()) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: dropped Result from assert_tx_succeeded_pre_confirmed
in txs variant
Same issue; propagate with ?
.
- for tx in pre_confirmed_block.transactions {
- assert_tx_succeeded_pre_confirmed(tx.transaction_hash(), &devnet.json_rpc_client).await;
- }
+ for tx in pre_confirmed_block.transactions {
+ assert_tx_succeeded_pre_confirmed(tx.transaction_hash(), &devnet.json_rpc_client).await?;
+ }
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
async fn assert_pre_confirmed_block_with_txs( | |
devnet: &BackgroundDevnet, | |
tx_count: usize, | |
) -> Result<(), anyhow::Error> { | |
let pre_confirmed_block = devnet.get_pre_confirmed_block_with_txs().await?; | |
assert_eq!(pre_confirmed_block.transactions.len(), tx_count); | |
anyhow::ensure!(pre_confirmed_block.transactions.len() == tx_count); | |
for tx in pre_confirmed_block.transactions { | |
assert_tx_succeeded_pre_confirmed(tx.transaction_hash(), &devnet.json_rpc_client).await; | |
} | |
Ok(()) | |
} | |
async fn assert_pre_confirmed_block_with_txs( | |
devnet: &BackgroundDevnet, | |
tx_count: usize, | |
) -> Result<(), anyhow::Error> { | |
let pre_confirmed_block = devnet.get_pre_confirmed_block_with_txs().await?; | |
anyhow::ensure!(pre_confirmed_block.transactions.len() == tx_count); | |
for tx in pre_confirmed_block.transactions { | |
assert_tx_succeeded_pre_confirmed(tx.transaction_hash(), &devnet.json_rpc_client).await?; | |
} | |
Ok(()) | |
} |
🤖 Prompt for AI Agents
In tests/integration/test_blocks_generation.rs around lines 98 to 111, the call
to assert_tx_succeeded_pre_confirmed(...).await ignores its Result return value;
change the call to propagate the error by using ? after .await (i.e., await the
future and use ? to return early on error) so the function returns the error to
the caller; no other logic changes required.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as replied above, pub async fn assert_tx_succeeded_pre_confirmed<T: Provider>(_tx_hash: &Felt, _client: &T)
is yet to be implemented
async fn assert_pre_confirmed_block_with_receipts( | ||
devnet: &BackgroundDevnet, | ||
tx_count: usize, | ||
) -> Result<(), anyhow::Error> { | ||
let pre_confirmed_block = &devnet | ||
.send_custom_rpc("starknet_getBlockWithReceipts", json!({ "block_id": "pre_confirmed" })) | ||
.await | ||
.unwrap(); | ||
.await?; | ||
|
||
assert!(pre_confirmed_block["status"].is_null()); | ||
assert_eq!(pre_confirmed_block["transactions"].as_array().unwrap().len(), tx_count); | ||
anyhow::ensure!(pre_confirmed_block["status"].is_null()); | ||
anyhow::ensure!(pre_confirmed_block["transactions"].as_array().unwrap().len() == tx_count); | ||
|
||
for tx in pre_confirmed_block["transactions"].as_array().unwrap() { | ||
let tx_hash = Felt::from_hex_unchecked(tx["receipt"]["transaction_hash"].as_str().unwrap()); | ||
let tx_hash = Felt::from_hex_unchecked( | ||
tx["receipt"]["transaction_hash"] | ||
.as_str() | ||
.ok_or(anyhow!("failed to parse transaction hash"))?, | ||
); | ||
assert_tx_succeeded_pre_confirmed(&tx_hash, &devnet.json_rpc_client).await; | ||
} | ||
|
||
Ok(()) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: dropped Result from assert_tx_succeeded_pre_confirmed
in receipts helper
Propagate the error to avoid false positives.
- assert_tx_succeeded_pre_confirmed(&tx_hash, &devnet.json_rpc_client).await;
+ assert_tx_succeeded_pre_confirmed(&tx_hash, &devnet.json_rpc_client).await?;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
async fn assert_pre_confirmed_block_with_receipts( | |
devnet: &BackgroundDevnet, | |
tx_count: usize, | |
) -> Result<(), anyhow::Error> { | |
let pre_confirmed_block = &devnet | |
.send_custom_rpc("starknet_getBlockWithReceipts", json!({ "block_id": "pre_confirmed" })) | |
.await | |
.unwrap(); | |
.await?; | |
assert!(pre_confirmed_block["status"].is_null()); | |
assert_eq!(pre_confirmed_block["transactions"].as_array().unwrap().len(), tx_count); | |
anyhow::ensure!(pre_confirmed_block["status"].is_null()); | |
anyhow::ensure!(pre_confirmed_block["transactions"].as_array().unwrap().len() == tx_count); | |
for tx in pre_confirmed_block["transactions"].as_array().unwrap() { | |
let tx_hash = Felt::from_hex_unchecked(tx["receipt"]["transaction_hash"].as_str().unwrap()); | |
let tx_hash = Felt::from_hex_unchecked( | |
tx["receipt"]["transaction_hash"] | |
.as_str() | |
.ok_or(anyhow!("failed to parse transaction hash"))?, | |
); | |
assert_tx_succeeded_pre_confirmed(&tx_hash, &devnet.json_rpc_client).await; | |
} | |
Ok(()) | |
} | |
async fn assert_pre_confirmed_block_with_receipts( | |
devnet: &BackgroundDevnet, | |
tx_count: usize, | |
) -> Result<(), anyhow::Error> { | |
let pre_confirmed_block = &devnet | |
.send_custom_rpc("starknet_getBlockWithReceipts", json!({ "block_id": "pre_confirmed" })) | |
.await?; | |
anyhow::ensure!(pre_confirmed_block["status"].is_null()); | |
anyhow::ensure!(pre_confirmed_block["transactions"].as_array().unwrap().len() == tx_count); | |
for tx in pre_confirmed_block["transactions"].as_array().unwrap() { | |
let tx_hash = Felt::from_hex_unchecked( | |
tx["receipt"]["transaction_hash"] | |
.as_str() | |
.ok_or(anyhow!("failed to parse transaction hash"))?, | |
); | |
assert_tx_succeeded_pre_confirmed(&tx_hash, &devnet.json_rpc_client).await?; | |
} | |
Ok(()) | |
} |
🤖 Prompt for AI Agents
In tests/integration/test_blocks_generation.rs around lines 141 to 162, the call
to assert_tx_succeeded_pre_confirmed is awaiting but ignoring its Result, which
can hide failures; change the call to propagate the Result (use the ? operator
on the awaited call) so the helper returns an error on failure and the test
doesn't produce false positives.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mentioned again, pub async fn assert_tx_succeeded_pre_confirmed<T: Provider>(_tx_hash: &Felt, _client: &T)
is currently unimplemented.
@@ -22,28 +22,30 @@ async fn assert_accepted_on_l1( | |||
for block_hash in block_hashes { | |||
match devnet.json_rpc_client.get_block_with_tx_hashes(BlockId::Hash(*block_hash)).await { | |||
Ok(MaybePreConfirmedBlockWithTxHashes::Block(block)) => { | |||
assert_eq!(block.status, BlockStatus::AcceptedOnL1) | |||
anyhow::ensure!(block.status == BlockStatus::AcceptedOnL1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Problem
I was not aware that anyhow::ensure
only accepts a boolean argument. This reduces the information in the reported error. assert_eq
nicely reports the failure telling us the left
and the right
argument which don't match, but the stack trace is messy. With anyhow::ensure
we get a nice stack trace, but lose the mismatched content.
Suggestion
The best would be to have a custom equality assertion function that would only be used in these helper functions (basically the places where you've now configured anyhow::ensure
). The function would accept two arguments implementing the Debug
trait, return Ok(())
if equal, or return an Err
containing info on left
and right
, but propagating it. Though there might be issues when the two arguments are not of the same type, meaning it would require something like this:
fn assert_eq_propagated<T, U>(a: T, b: U) -> Result<(), anyhow::Error>
where
T: Into<U> + Debug, // T can be converted into U
U: From<T> + Debug, // U can be constructed from T
{
Let me know if you think this is too much of hassle and if it's even feasible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ensure!(user == 0, "only user 0 is allowed");
takes in an optional error message argument, we could use that, but if it don't report as much info as needed, I'd look into implementing your suggestion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@FabijanC what do you think about this?
fn take_abi_from_json(value: &mut serde_json::Value) -> Result<serde_json::Value, anyhow::Error> {
let abi_jsonified = value["abi"].take();
let json_null = serde_json::json!(null);
anyhow::ensure!(
abi_jsonified != json_null,
format!("assertion `left == right` failed, left: {abi_jsonified}, right: {json_null}")
);
Ok(serde_json::from_str(abi_jsonified.as_str().unwrap())?)
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Situations where we assert non-equality (i.e. x != y) or "greater-than" relation (i.e. x > y) are less problematic. Personally, I'd rather we kept using assert_eq
than anyhow::ensure
and now I regret recommending it in the first place. The ideal solution would be to have a macro or a custom function that I proposed above, but I will understand if you don't want to implement one of those.
So: let me know if you will just revert the anyhow::ensure changes or also implement a custom asserter. Sorry once again for the double work on your side.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, It's fine, my bad, I went with providing custom error messages, I'd implement the custom assert, and push.
@coderabbitai review |
Usage related changes
N/A
Development related changes
Custom assertion methods now return results instead of panicking. This change improves debugging as the stack trace is more intuitive to reason about.
The following custom assertion methods were modified to return a result instead of panicking:
assert_tx_succeeded_accepted
assert_tx_succeeded_pre_confirmed
assert_tx_reverted
assert_equal_elements
assert_no_notifications
assert_contains
extract_message_error
extract_nested_error
get_contract_balance
get_contract_balance_by_block_id
assert_tx_aborted
closes #845
Checklist:
./scripts/format.sh
./scripts/clippy_check.sh
./scripts/check_unused_deps.sh
./scripts/check_spelling.sh
./website/README.md
Summary by CodeRabbit