Skip to content

Cover error cases for quote module in quote crate#1846

Merged
0xgleb merged 14 commits intomainfrom
test/quote-quote
May 21, 2025
Merged

Cover error cases for quote module in quote crate#1846
0xgleb merged 14 commits intomainfrom
test/quote-quote

Conversation

@0xgleb
Copy link
Copy Markdown
Collaborator

@0xgleb 0xgleb commented May 17, 2025

Motivation

tests in quote module of quote crate don't cover error cases

Solution

add error cases to the test coverage

Checks

By submitting this for review, I'm confirming I've done the following:

  • made this PR as small as possible
  • unit-tested any new functionality
  • linked any relevant issues or PRs
  • included screenshots (if this involves a front-end change)

Summary by CodeRabbit

  • Tests
    • Added extensive unit and asynchronous tests for quoting functionality, covering various success and error scenarios.
    • Improved test clarity with updated function naming conventions.
    • Enhanced error handling validation through simulated RPC and subgraph responses.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 17, 2025

Walkthrough

A comprehensive set of new unit and asynchronous tests has been added to the quoting functionality in crates/quote/src/quote.rs. The tests cover individual methods, error handling, and interactions with mocked RPC and subgraph endpoints. No changes were made to the core logic or public API signatures.

Changes

File(s) Change Summary
crates/quote/src/quote.rs Added extensive unit and async tests for QuoteTarget, QuoteSpec, BatchQuoteSpec, and BatchQuoteTarget. Tests cover method correctness, error handling, and external endpoint interactions using HTTP mocks. Some test function names were clarified with _ok or _err suffixes.

Suggested reviewers

  • findolor
  • hardyjosh
✨ Finishing Touches
  • 📝 Generate Docstrings

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@0xgleb 0xgleb force-pushed the test/quote-quote branch from 6bd9956 to 6c6f3d2 Compare May 17, 2025 14:46
@0xgleb 0xgleb marked this pull request as ready for review May 17, 2025 14:46
@0xgleb 0xgleb self-assigned this May 17, 2025
@0xgleb 0xgleb added test test code rust Related to rust crates labels May 17, 2025
@0xgleb 0xgleb linked an issue May 17, 2025 that may be closed by this pull request
@0xgleb 0xgleb added this to the Test coverage, tech debt milestone May 17, 2025
@0xgleb 0xgleb requested review from findolor and hardyjosh May 17, 2025 14:51
Copy link
Copy Markdown
Collaborator

@findolor findolor left a comment

Choose a reason for hiding this comment

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

can you also add tests for these functions?

  • get_order_hash
  • get_id
  • validate

@0xgleb 0xgleb force-pushed the test/quote-quote branch from 6c6f3d2 to 97c0ca3 Compare May 19, 2025 15:35
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6c6f3d2 and 97c0ca3.

📒 Files selected for processing (2)
  • crates/quote/Cargo.toml (1 hunks)
  • crates/quote/src/quote.rs (12 hunks)
🧰 Additional context used
🧠 Learnings (1)
crates/quote/Cargo.toml (3)
Learnt from: findolor
PR: rainlanguage/rain.orderbook#1710
File: crates/quote/Cargo.toml:30-30
Timestamp: 2025-05-14T04:49:14.621Z
Learning: In the rain.orderbook repository, wasm-bindgen-utils is intentionally used as an unconditional dependency because non-wasm targets (like tests) sometimes need to use it.
Learnt from: findolor
PR: rainlanguage/rain.orderbook#1715
File: crates/js_api/src/common/mod.rs:111-118
Timestamp: 2025-04-30T09:28:36.960Z
Learning: In the rain.orderbook repository, the WASM tests are already properly configured with conditional compilation using `#[cfg(target_family = "wasm")]` and `#[cfg(not(target_family = "wasm"))]`, and don't require additional `wasm_bindgen_test_configure!(run_in_browser)` directives.
Learnt from: findolor
PR: rainlanguage/rain.orderbook#1744
File: crates/subgraph/src/orderbook_client.rs:44-46
Timestamp: 2025-05-14T05:13:59.713Z
Learning: In the rain.orderbook project, WASM dependencies are intentionally made available in non-WASM targets to facilitate testing of WASM-related functionality, so conditional compilation guards like `#[cfg(target_family = "wasm")]` should not be added to imports or implementations that may be needed for tests.
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Deploy-Preview
  • GitHub Check: git-clean
🔇 Additional comments (1)
crates/quote/src/quote.rs (1)

310-313: 🧹 Nitpick (assertive)

Unnecessary duplicate import

use alloy::hex; brings the module (and thus the hex! macro) into scope.
use alloy::hex::ToHex; already re-exports the trait and implicitly brings the module in as well, so the first line can be dropped to avoid a redundant import.

-use alloy::hex;
 use alloy::hex::ToHex;

Likely an incorrect or invalid review comment.

Comment thread crates/quote/Cargo.toml Outdated
Comment thread crates/quote/src/quote.rs
Comment thread crates/quote/src/quote.rs Outdated
Comment thread crates/quote/src/quote.rs
Comment thread crates/quote/src/quote.rs
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (4)
crates/quote/src/quote.rs (4)

114-114: #[inline] on an async wrapper is still unnecessary

The same concern raised in the previous review remains: adding #[inline] to an async fn that is already a tiny wrapper offers no measurable benefit while potentially increasing binary size. Please remove the attribute (or add it consistently to every similar helper, but that is rarely warranted).


475-489: Hard-coded expected hash is brittle

Committing a literal hash string makes the test fail whenever default values inside OrderV3 change. Compute the expected value on-the-fly instead, e.g.:

-let expected =
-    "8a3fbb9caf53f18f1f78d90c48dbe4612bcd93285ed0fc033009b4a96ea2aaed".to_string();
-let actual = quote_target.get_order_hash().encode_hex::<String>();
-assert_eq!(actual, expected);
+let expected = keccak256(order.abi_encode()).encode_hex::<String>();
+assert_eq!(quote_target.get_order_hash().encode_hex::<String>(), expected);

740-770: Repeated mock-server boilerplate still not deduplicated

Multiple tests recreate identical MockServer setups and JSON RPC/Subgraph stubs. Extract helpers (e.g. fn mock_rpc_success(server: &MockServer, path: &str, data: Vec<u8>)) or move fixtures to a shared module to cut ~40 lines per test and improve readability.


1002-1006: Exact string match on upstream error message is fragile

The assertion depends on the full wording "buffer overrun while deserializing", which may change in the decoding crate. Match the variant and use contains("buffer overrun") instead to keep the test resilient across patch releases.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 97c0ca3 and ef7bd98.

📒 Files selected for processing (1)
  • crates/quote/src/quote.rs (12 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (9)
  • GitHub Check: build-tauri (ubuntu-22.04, true)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-sol-legal)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-wasm-test)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-sol-static)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-sol-artifacts)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-wasm-artifacts)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-rs-static)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-sol-test)
  • GitHub Check: test

Comment thread crates/quote/src/quote.rs
Comment thread crates/quote/src/quote.rs Outdated
@0xgleb 0xgleb force-pushed the test/quote-quote branch from 644f2e6 to 29de705 Compare May 19, 2025 16:00
@0xgleb 0xgleb requested a review from findolor May 20, 2025 12:13
@0xgleb 0xgleb enabled auto-merge May 20, 2025 14:48
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a 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

🔭 Outside diff range comments (1)
crates/quote/src/quote.rs (1)

323-472: 🧹 Nitpick (assertive)

Make test fixtures deterministic & fix the typo in retrun_sg_data

  1. get_test_data relies on Address::random() (multiple times). Although none of those random values are explicitly asserted against, non-determinism can make failures harder to reproduce. Consider:
// use a fixed seed for reproducibility
let mut rng = rand::rngs::StdRng::seed_from_u64(42);
let orderbook = Address::random_using(&mut rng);

(or simply populate with the zero address when the exact value is irrelevant).

  1. The local variable is spelled retrun_sg_data. A quick rename to return_sg_data (or sg_payload) saves future head-scratching and IDE miss-hits.
- let retrun_sg_data = if batch {
+ let return_sg_data = if batch {

Follow-up: update all call-sites.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aeb4d47 and fc738ef.

📒 Files selected for processing (1)
  • crates/quote/src/quote.rs (11 hunks)
🧰 Additional context used
🧠 Learnings (1)
crates/quote/src/quote.rs (1)
Learnt from: 0xgleb
PR: rainlanguage/rain.orderbook#1846
File: crates/quote/src/quote.rs:0-0
Timestamp: 2025-05-19T16:03:40.904Z
Learning: For hash function testing, using precomputed example values is preferred over reimplementing the hashing logic in the test itself, as it provides better validation and avoids circular testing.
🔇 Additional comments (2)
crates/quote/src/quote.rs (2)

474-488: Pre-computed hash value test looks good

Good use of a hard-coded example to validate get_order_hash; this avoids circular testing (re-computing the hash in the test itself) and honours the preference captured in past learnings.


1005-1048: LGTM – precise error assertion maintained

The corrupt-data path is correctly exercised and the exact error message is asserted, matching the convention agreed earlier.

Comment thread crates/quote/src/quote.rs
Comment on lines +668 to +715
async fn test_get_batch_quote_spec_from_subgraph_err() {
let rpc_server = MockServer::start_async().await;

let (orderbook, order, order_id_u256, _) = get_test_data(true);

rpc_server.mock(|when, then| {
when.method(POST).path("/sg");

let invalid_order_json = json!({
"id": encode_prefixed(B256::random()),
"orderBytes": encode_prefixed(order.abi_encode()),
"orderHash": encode_prefixed(B256::random()),
"owner": encode_prefixed(order.owner),
"orderbook": { "id": encode_prefixed(B256::random()) },
"active": true,
"addEvents": [],
"meta": null,
"timestampAdded": "0",
"trades": [],
"removeEvents": []
});

then.json_body_obj(&json!({
"data": {
"orders": [invalid_order_json]
}
}));
});

let batch_quote_targets_specifiers = BatchQuoteSpec(vec![QuoteSpec {
order_hash: order_id_u256,
input_io_index: 0,
output_io_index: 0,
signed_context: vec![],
orderbook,
}]);

let err = batch_quote_targets_specifiers
.get_batch_quote_target_from_subgraph(rpc_server.url("/sg").as_str())
.await
.unwrap_err();

assert!(matches!(
err,
Error::SubgraphClientError(OrderbookSubgraphClientError::CynicClientError(cynic_err))
if cynic_err.to_string().contains("error decoding response body")
));
}
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.

🧹 Nitpick (assertive)

Prefer exact message matching for consistency with previous tests

In line with the earlier decision to assert the full error string (e.g. “buffer overrun while deserializing”), consider matching the exact cynic_err message here as well instead of using contains("error decoding response body"). This keeps the style consistent across the suite and immediately flags upstream wording regressions.

🤖 Prompt for AI Agents
In crates/quote/src/quote.rs between lines 668 and 715, the test currently
asserts the error message using a partial match with contains("error decoding
response body"). To maintain consistency with previous tests, update the
assertion to match the exact error message string from cynic_err, such as
"buffer overrun while deserializing". Replace the contains check with an
equality check against the full expected error message to ensure precise
matching and consistent test style.

Comment thread crates/quote/src/quote.rs
Comment on lines +530 to +551
#[test]
fn test_validate_err() {
let quote_target = QuoteTarget {
quote_config: Quote {
order: OrderV3::default(),
outputIOIndex: U256::from(1_u16),
..Default::default()
},
orderbook: Address::ZERO,
};
assert!(quote_target.validate().is_err());

let quote_target = QuoteTarget {
quote_config: Quote {
order: OrderV3::default(),
inputIOIndex: U256::from(1_u16),
..Default::default()
},
orderbook: Address::ZERO,
};
assert!(quote_target.validate().is_err());
}
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.

🛠️ Refactor suggestion

Strengthen negative-path assertions

assert!(quote_target.validate().is_err()) only checks that some error was returned.
To ensure the correct variant/index is surfaced you can pattern-match:

assert!(matches!(
    quote_target.validate(),
    Err(Error::InvalidQuoteTarget(index)) if index == U256::from(1_u16)
));

This guards against future changes that might return the wrong error type yet still satisfy is_err().

🤖 Prompt for AI Agents
In crates/quote/src/quote.rs around lines 530 to 551, the test uses
assert!(quote_target.validate().is_err()) which only verifies that an error
occurred but not the specific error type or value. To strengthen the test,
replace these assertions with pattern matching using assert!(matches!(...)) to
check that the error returned is specifically Error::InvalidQuoteTarget with the
expected index value. This ensures the test validates the exact error variant
and guards against incorrect error returns in the future.

Comment thread crates/quote/src/quote.rs
Comment on lines +309 to 317
use alloy::hex;
use alloy::hex::ToHex;
use alloy::primitives::{address, keccak256};
use alloy::primitives::{hex::encode_prefixed, U256};
use alloy::sol_types::{SolCall, SolValue};
use alloy_ethers_typecast::multicall::IMulticall3::Result as MulticallResult;
use alloy_ethers_typecast::rpc::Response;
use alloy_ethers_typecast::transaction::ReadableClientError;
use httpmock::{Method::POST, MockServer};
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot May 20, 2025

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Consolidate duplicate alloy::hex imports

use alloy::hex; and use alloy::hex::ToHex; can be merged to avoid redundancy:

-use alloy::hex;
-use alloy::hex::ToHex;
+use alloy::hex::{self, ToHex};

This keeps the imports concise and prevents “unused import” warnings should one of them become unnecessary later.

📝 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.

Suggested change
use alloy::hex;
use alloy::hex::ToHex;
use alloy::primitives::{address, keccak256};
use alloy::primitives::{hex::encode_prefixed, U256};
use alloy::sol_types::{SolCall, SolValue};
use alloy_ethers_typecast::multicall::IMulticall3::Result as MulticallResult;
use alloy_ethers_typecast::rpc::Response;
use alloy_ethers_typecast::transaction::ReadableClientError;
use httpmock::{Method::POST, MockServer};
use alloy::hex::{self, ToHex};
use alloy::primitives::{address, keccak256};
use alloy::primitives::{hex::encode_prefixed, U256};
use alloy::sol_types::{SolCall, SolValue};
use alloy_ethers_typecast::multicall::IMulticall3::Result as MulticallResult;
use alloy_ethers_typecast::rpc::Response;
use alloy_ethers_typecast::transaction::ReadableClientError;
use httpmock::{Method::POST, MockServer};
🤖 Prompt for AI Agents
In crates/quote/src/quote.rs around lines 309 to 317, the imports include both
`use alloy::hex;` and `use alloy::hex::ToHex;` which is redundant. Consolidate
these by removing the separate `use alloy::hex;` and only keep `use
alloy::hex::ToHex;` or combine them into a single line that imports both if
needed, to keep the imports concise and avoid redundancy.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

use alloy::hex::{self, ToHex}; isn't valid. use alloy::hex is importing the macro, not the module

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.

@0xgleb Thank you for the correction! You're absolutely right. I can see that use alloy::hex; is importing the hex! macro (which is used in the code like hex!("734bc71c")), while use alloy::hex::ToHex; imports the trait. These can't be combined with the {self, ToHex} syntax since macros are imported differently than modules/traits.


✏️ Learnings added
Learnt from: 0xgleb
PR: rainlanguage/rain.orderbook#1846
File: crates/quote/src/quote.rs:309-317
Timestamp: 2025-05-20T15:34:06.703Z
Learning: In Rust, when importing from the alloy crate, `use alloy::hex;` imports the hex macro, not a module, so it can't be consolidated with imports like `use alloy::hex::ToHex;` using the grouped import syntax.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@0xgleb 0xgleb merged commit 7214ebd into main May 21, 2025
17 checks passed
@0xgleb 0xgleb deleted the test/quote-quote branch May 21, 2025 15:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

rust Related to rust crates test test code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add fail tests for quote file in quote crate

3 participants