Skip to content

test: voting contract integration test #2

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

Merged
merged 36 commits into from
Jun 3, 2025
Merged

Conversation

linguists
Copy link
Contributor

@linguists linguists commented May 15, 2025

Summary by CodeRabbit

  • Tests
    • Added comprehensive integration tests covering voting permissions, multiple votes, vote expiration, vote withdrawal, and unstaking behavior.
    • Removed outdated greeting-related tests.
    • Introduced utilities for deploying and initializing voting and mock staking pool contracts in tests.
    • Added tests for staking pool deposit, unstake, and voting account ID management.
  • Chores
    • Enabled the near-workspaces development dependency.
    • Broadened .gitignore to exclude all nested target directories and added exclusions for tests/res and res.
  • New Features
    • Added Gitpod configuration for automated development environment setup with cargo-near.
    • Introduced a mock staking pool contract supporting staking, unstaking, and voting functionalities.
  • Refactor
    • Updated Rust toolchain to version 1.86.0 for consistent build environment.
    • Modularized stake-related logic into a utility module with integration-test feature support for stake manipulation.
    • Added contract methods for test-time validator stake management under integration-test feature.
  • Build
    • Added Makefile to streamline building, linting, and testing workflows for Rust WebAssembly projects.
  • Scripts
    • Added scripts to automate creation of mock validators and batch voting on NEAR testnet.
    • Added script to automate voting account creation and contract deployment on NEAR testnet.

Copy link

coderabbitai bot commented May 15, 2025

Walkthrough

The previous greeting-related asynchronous test in tests/test_basics.rs was removed. New asynchronous tests were added to verify contract initialization and voting behavior, including staking interactions. Helper utilities for deploying contracts and setting up the test environment were introduced. The near-workspaces dev dependency was enabled. A .gitpod.yml file was added to automate cargo-near installation. The Rust toolchain was pinned to version 1.86.0. A new mock staking pool contract was introduced with staking, unstaking, and voting functionality. The .gitignore was updated to ignore all nested target directories. The contract code was refactored to modularize stake-related logic into a new utils module with integration-test feature support for stake manipulation and querying. A Makefile was added to automate building, linting, and testing workflows.

Changes

File(s) Change Summary
tests/test_basics.rs Removed all previous greeting-related async test code and imports.
tests/test_init.rs Added async test test_initialization verifying contract deployment and initialization with proposal and deadline.
tests/test_vote.rs Added async tests test_non_validator_cannot_vote, test_simple_vote, test_many_votes, test_vote_expiration, test_withdraw_vote, and test_unstake_after_voting for voting behavior and staking interaction.
tests/test_staking_pool.rs Added async tests test_stake_unstake and test_get_set_voting_account_id for staking pool contract functionality.
tests/utils/mod.rs Added structs VotingInitArgs and MockStakingPoolInitArgs and async helper functions for deploying voting and mock staking pool contracts, environment setup, and account creation.
Cargo.toml Uncommented and enabled near-workspaces dependency in [dev-dependencies] with version 0.18 and unstable feature; added [features] section with default and test.
.gitpod.yml Added new Gitpod configuration file to automate setup by installing cargo-near via an initialization shell command.
.gitignore Modified ignore pattern from /target to **/target to ignore all nested target directories; added tests/res and res to ignore list.
tests/contracts/mock-staking-pool/Cargo.toml Added new Cargo.toml for mock staking pool contract with dependencies and optimized release profile.
tests/contracts/mock-staking-pool/src/lib.rs Added new MockStakingPool contract with staking, unstaking, and voting methods, including internal state and helper functions.
rust-toolchain.toml Changed Rust toolchain from generic "stable" to specific version "1.86.0" with existing components and targets.
makefile Added Makefile with targets for building, linting, testing, and feature-based compilation of wasm artifacts.
src/events.rs Changed impl block lifetime parameter from explicit 'a to anonymous '_ for Event enum.
src/lib.rs Refactored contract to use utils module for stake queries; changed Contract derive from Default to PanicOnDefault; added integration-test gated methods for setting and getting validator stakes.
src/utils.rs Added new utils module abstracting validator stake retrieval and manipulation with conditional compilation for integration testing.
.github/workflows/test.yml Changed test execution command from cargo test to make test in GitHub Actions workflow; added step to install cargo-near.
tests/scripts/create-validators.sh Added script to create and initialize 300 mock validator accounts on NEAR testnet with staking pool contracts.
tests/scripts/vote.sh Added script to automate voting by mock validators on NEAR testnet and query voting results.
tests/scripts/create-proposal.sh Added script to create a voting account and deploy contract on NEAR testnet with proposal and deadline.

Possibly related PRs

  • feat: validator voting contract #1: Initial validator voting contract implementation and CI workflows, providing foundational contract and test setup related to the voting system tested and extended in this PR.

Poem

In the warren of tests, code springs anew,
Async and lively, with contracts to view.
Voting attempts fail, as rabbits foresee,
Proposals are queried with glee.
Out with the old, in with the new—
Rusty paws tapping, tests hopping through!
🐇✨


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between d002a57 and 5aca39f.

📒 Files selected for processing (1)
  • tests/test_vote.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/test_vote.rs
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Code Linter
  • GitHub Check: Tests
✨ 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.

Copy link

@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

🧹 Nitpick comments (3)
tests/test_basics.rs (3)

31-31: Add comment explaining vote parameters

The vote parameter is_vote: true doesn't provide context on what it means. Adding a comment would make the test more understandable.

-        .args_json(json!({"is_vote": true}))
+        // Attempt to vote "yes" on the proposal
+        .args_json(json!({"is_vote": true}))

34-34: Clarify error assertion message

The error assertion message doesn't explain why the failure is expected. A more descriptive message would make the test easier to understand.

-    assert!(outcome.is_failure(), "{:#?}", outcome.into_result().unwrap_err());
+    assert!(outcome.is_failure(), "Vote should fail before user registration. Error: {:#?}", outcome.into_result().unwrap_err());

36-37: Add more complete proposal verification

The test only verifies that the proposal string matches, but should also verify other proposal properties like the vote count or deadline.

-    let contract_proposal = contract.view("get_proposal").args_json(json!({})).await?;
-    assert_eq!(contract_proposal.json::<String>()?, proposal);
+    // Verify all proposal properties
+    let contract_proposal = contract.view("get_proposal").args_json(json!({})).await?;
+    let proposal_data = contract_proposal.json::<serde_json::Value>()?;
+    
+    // Verify the proposal text
+    assert_eq!(proposal_data["text"].as_str().unwrap(), proposal, "Proposal text doesn't match");
+    
+    // Verify other properties like vote counts, deadline, etc.
+    assert_eq!(proposal_data["deadline_timestamp_ms"].as_u64().unwrap(), deadline_timestamp_ms, "Deadline doesn't match");
+    assert_eq!(proposal_data["yes_votes"].as_u64().unwrap(), 0, "Initial yes votes should be 0");
+    assert_eq!(proposal_data["no_votes"].as_u64().unwrap(), 0, "Initial no votes should be 0");
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between ed87409 and 9f4aa1e.

📒 Files selected for processing (1)
  • tests/test_basics.rs (1 hunks)
🧰 Additional context used
🪛 GitHub Actions: Test
tests/test_basics.rs

[error] 13-13: Rust compiler error E0433: use of unresolved module or unlinked crate 'near_workspaces'. Suggestion: add 'near_workspaces' crate to Cargo.toml.


[error] 18-18: Rust compiler error E0277: cannot add integer to 'std::time::SystemTime'. The trait 'std::ops::Add<{integer}>' is not implemented for 'std::time::SystemTime'. Use 'std::time::Duration' for addition.


[error] 6-6: Rust compiler error E0277: the size for values of type '[u8]' cannot be known at compile time. The trait 'std::marker::Sized' is not implemented for '[u8]'. Unsized locals are unstable.

🔇 Additional comments (1)
tests/test_basics.rs (1)

4-10: ⚠️ Potential issue

Fix unsized type error

The use of unsized &[u8] as a local variable is causing a compilation error. The contract bytes should be wrapped in a sized type like Vec<u8> or a reference to it.

#[tokio::test]
async fn test_contract_is_operational() -> Result<(), Box<dyn std::error::Error>> {
-    let contract_wasm = near_workspaces::compile_project("./").await?;
+    let contract_wasm = near_workspaces::compile_project("./").await?;

-    test_initialization(&contract_wasm).await?;
+    test_initialization(contract_wasm.as_slice()).await?;
    Ok(())
}

Likely an incorrect or invalid review comment.

🧰 Tools
🪛 GitHub Actions: Test

[error] 6-6: Rust compiler error E0277: the size for values of type '[u8]' cannot be known at compile time. The trait 'std::marker::Sized' is not implemented for '[u8]'. Unsized locals are unstable.

Copy link

@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: 0

♻️ Duplicate comments (3)
tests/test_basics.rs (3)

25-27: ⚠️ Potential issue

Fix typo in JSON field name

There's a typo in the JSON field name for the deadline timestamp.

    .args_json(json!({
        "proposal": proposal,
-        "deadline_timestamp_md": deadline_timestamp_ms,
+        "deadline_timestamp_ms": deadline_timestamp_ms,
    }))

37-43: 🛠️ Refactor suggestion

Add user registration step before voting

The test expects the vote to fail, but doesn't explain why. It's likely that users need to register before voting.

Expand the test to include the complete user flow:

    let user_account = sandbox.dev_create_account().await?;
+    
+    // First try voting without registration (should fail)
    let outcome = user_account
        .call(contract.id(), "vote")
        .args_json(json!({"is_vote": true}))
        .transact()
        .await?;
-    assert!(outcome.is_failure(), "{:#?}", outcome.into_result().unwrap_err());
+    assert!(outcome.is_failure(), "Vote should fail before user registration. Error: {:#?}", outcome.into_result().unwrap_err());
+    
+    // Register the user (if the contract has such functionality)
+    let register_outcome = user_account
+        .call(contract.id(), "register_voter")
+        .args_json(json!({}))
+        .transact()
+        .await?;
+    assert!(register_outcome.is_success(), "User registration failed: {:#?}", register_outcome);
+    
+    // Now try voting after registration (should succeed)
+    let vote_outcome = user_account
+        .call(contract.id(), "vote")
+        .args_json(json!({"is_vote": true}))
+        .transact()
+        .await?;
+    assert!(vote_outcome.is_success(), "Vote failed after registration: {:#?}", vote_outcome);

This provides a complete test of the voting workflow and clearly documents the expected behavior.


1-2: ⚠️ Potential issue

Add the missing near_workspaces import

The imports look good, but the code is using near_workspaces without having it as a dependency according to the pipeline failures.

Add the near_workspaces dependency to your Cargo.toml file:

[dev-dependencies]
+ near-workspaces = "0.9.0"

This will resolve the pipeline errors showing "use of unresolved module or unlinked crate near_workspaces".

🧹 Nitpick comments (2)
tests/test_basics.rs (2)

18-21: Improved timestamp calculation

The timestamp calculation looks good, but could be made a bit more readable.

Consider simplifying the timestamp calculation:

-    let deadline_timestamp_ms = (SystemTime::now()
-        .duration_since(UNIX_EPOCH)
-        .unwrap()
-        .as_millis() + 10 * 60 * 1000) as u64;
+    // Current time + 10 minutes in milliseconds
+    let deadline_timestamp_ms = SystemTime::now()
+        .duration_since(UNIX_EPOCH)
+        .expect("Time went backwards")
+        .as_millis()
+        .checked_add(10 * 60 * 1000)
+        .expect("Timestamp overflow")
+        as u64;

This makes the code more robust by using expect with meaningful error messages and checked_add to prevent overflow.


31-35: Add more comprehensive contract state verification

The current checks verify only the proposal and deadline. Consider adding more assertions to validate the complete contract state.

    let contract_deadline = contract.view("get_deadline_timestamp").args_json(json!({})).await?;
    assert_eq!(contract_deadline.json::<u64>()?, deadline_timestamp_ms);

    let contract_proposal = contract.view("get_proposal").args_json(json!({})).await?;
    assert_eq!(contract_proposal.json::<String>()?, proposal);
+
+    // Add assertions for initial voting state
+    let vote_count = contract.view("get_vote_count").args_json(json!({})).await?;
+    assert_eq!(vote_count.json::<u64>()?, 0, "Initial vote count should be zero");
+
+    let voters_count = contract.view("get_voters_count").args_json(json!({})).await?;
+    assert_eq!(voters_count.json::<u64>()?, 0, "Initial voters count should be zero");

These additional checks ensure the contract is initialized with the expected default values for voting-related state.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 9f4aa1e and 225fa0e.

📒 Files selected for processing (1)
  • tests/test_basics.rs (1 hunks)
🧰 Additional context used
🪛 GitHub Actions: Test
tests/test_basics.rs

[error] 13-13: failed to resolve: use of unresolved module or unlinked crate near_workspaces. Suggestion: add near_workspaces to Cargo.toml using cargo add near_workspaces.


[error] 6-6: failed to resolve: use of unresolved module or unlinked crate near_workspaces. Suggestion: add near_workspaces to Cargo.toml using cargo add near_workspaces.


[error] 6-6: the size for values of type [u8] cannot be known at compilation time. The trait Sized is not implemented for [u8]. All local variables must have a statically known size.


[error] 6-6: the size for values of type [u8] cannot be known at compilation time. The trait Sized is not implemented for [u8]. All local variables must have a statically known size.


[error] 6-6: the size for values of type [u8] cannot be known at compilation time. The trait Sized is not implemented for [u8]. All local variables must have a statically known size.


[error] 6-6: the size for values of type [u8] cannot be known at compilation time. The trait Sized is not implemented for [u8]. All local variables must have a statically known size.

🔇 Additional comments (1)
tests/test_basics.rs (1)

4-10: ⚠️ Potential issue

Fix unsized type error in the contract_wasm handling

The pipeline is failing with "the size for values of type [u8] cannot be known at compilation time" errors.

Modify the function to use a Vec instead of [u8]:

#[tokio::test]
async fn test_contract_is_operational() -> Result<(), Box<dyn std::error::Error>> {
-    let contract_wasm = near_workspaces::compile_project("./").await?;
+    let contract_wasm: Vec<u8> = near_workspaces::compile_project("./").await?;

    test_initialization(&contract_wasm).await?;
    Ok(())
}

Similarly, update the helper function signature:

-async fn test_initialization(contract_wasm: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
+async fn test_initialization(contract_wasm: &Vec<u8>) -> Result<(), Box<dyn std::error::Error>> {

This will resolve the sized type requirement for local variables.

Likely an incorrect or invalid review comment.

🧰 Tools
🪛 GitHub Actions: Test

[error] 6-6: failed to resolve: use of unresolved module or unlinked crate near_workspaces. Suggestion: add near_workspaces to Cargo.toml using cargo add near_workspaces.


[error] 6-6: the size for values of type [u8] cannot be known at compilation time. The trait Sized is not implemented for [u8]. All local variables must have a statically known size.


[error] 6-6: the size for values of type [u8] cannot be known at compilation time. The trait Sized is not implemented for [u8]. All local variables must have a statically known size.


[error] 6-6: the size for values of type [u8] cannot be known at compilation time. The trait Sized is not implemented for [u8]. All local variables must have a statically known size.


[error] 6-6: the size for values of type [u8] cannot be known at compilation time. The trait Sized is not implemented for [u8]. All local variables must have a statically known size.

Copy link

@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: 0

🧹 Nitpick comments (3)
tests/test_vote.rs (1)

6-23: Well-structured test for permission enforcement

This test correctly verifies that non-validator accounts cannot vote on the contract. The test follows a clear pattern:

  1. Deploy and initialize the contract
  2. Create a non-validator user account
  3. Attempt to vote and verify the operation fails

The error handling with ? operator and the clear assertion message are good practices.

Consider enhancing the assertion message to be more specific about why the test should fail:

assert!(
    outcome.is_failure(),
-   "{:#?}",
+   "Non-validator accounts should not be able to vote: {:#?}",
    outcome.into_result().unwrap_err()
);
tests/test_init.rs (1)

6-23: Effective contract initialization verification

This test properly validates that the contract's state after initialization matches the expected values:

  1. The deadline timestamp is correctly stored and retrievable
  2. The proposal string is properly initialized and accessible

The test is well-structured with clear assertions and proper error handling.

Consider using a single view call pattern for consistency between the two contract calls:

let contract_deadline = contract
    .view("get_deadline_timestamp")
    .args_json(json!({}))
    .await?;
assert_eq!(
    contract_deadline.json::<u64>()?,
    init_args.deadline_timestamp_ms
);

-let contract_proposal = contract.view("get_proposal").args_json(json!({})).await?;
+let contract_proposal = contract
+    .view("get_proposal")
+    .args_json(json!({}))
+    .await?;
assert_eq!(contract_proposal.json::<String>()?, init_args.proposal);
tests/utils/mod.rs (1)

12-35: Effective contract deployment utility

The deploy_voting_contract function provides a useful abstraction for deploying and initializing the contract in a test environment. It correctly:

  1. Compiles the contract from the current project
  2. Creates a sandbox test environment
  3. Deploys the contract
  4. Initializes it with test values
  5. Returns all necessary components for testing

Consider making the function more flexible by allowing parameterization of the proposal and deadline:

pub async fn deploy_voting_contract(
+   proposal: Option<String>,
+   deadline_offset_ms: Option<u64>
) -> Result<(Contract, Worker<Sandbox>, InitArgs), Box<dyn std::error::Error>> {
    let contract_wasm = near_workspaces::compile_project("./").await?;
    let sandbox = near_workspaces::sandbox().await?;
    let contract = sandbox.dev_deploy(&contract_wasm).await?;

    // Initialize contract
    let init_args = InitArgs {
-       proposal: "test_proposal".to_string(),
+       proposal: proposal.unwrap_or_else(|| "test_proposal".to_string()),
        deadline_timestamp_ms: (SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_millis()
-           + 10 * 60 * 1000) as u64,
+           + deadline_offset_ms.unwrap_or(10 * 60 * 1000)) as u64,
    };

    // Rest of the function remains the same

This would allow tests to customize these values when needed while maintaining the default behavior.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 9da9e84 and dead2b0.

📒 Files selected for processing (4)
  • tests/test_basics.rs (0 hunks)
  • tests/test_init.rs (1 hunks)
  • tests/test_vote.rs (1 hunks)
  • tests/utils/mod.rs (1 hunks)
💤 Files with no reviewable changes (1)
  • tests/test_basics.rs
🧰 Additional context used
🧬 Code Graph Analysis (2)
tests/test_vote.rs (1)
tests/utils/mod.rs (1)
  • deploy_voting_contract (12-35)
tests/test_init.rs (1)
tests/utils/mod.rs (1)
  • deploy_voting_contract (12-35)
🔇 Additional comments (4)
tests/test_vote.rs (1)

1-5: LGTM: Appropriate imports and module usage

Clean import of serde_json::json for JSON serialization and modular organization using the utility module.

tests/test_init.rs (1)

1-5: LGTM: Clean imports and modular organization

Good use of the utility module and appropriate JSON serialization imports.

tests/utils/mod.rs (2)

1-5: LGTM: Appropriate imports for contract testing

Good selection of imports for NEAR SDK, workspaces, JSON serialization, and time handling.


6-10: Well-defined initialization arguments struct

The InitArgs struct is properly defined with the NEAR serialization attribute and contains the necessary fields for contract initialization.

Copy link

@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: 1

🧹 Nitpick comments (5)
tests/contracts/mock-staking-pool/src/lib.rs (5)

1-8: Imports and constants look good, but consider adding more descriptive constants

The imports are appropriate for the contract functionality. The VOTE_GAS constant is well-defined, but consider adding more descriptive constants for other magic numbers that might appear in the contract, such as minimum staking amounts.


9-12: External contract interface should specify return type

The external contract trait is properly defined, but the vote method doesn't specify a return type. Although it might not return anything, it's a best practice to explicitly specify () as the return type for clarity.

trait VotingContract {
-    fn vote(&mut self, is_vote: bool);
+    fn vote(&mut self, is_vote: bool) -> ();
}

42-53: Staking method is well-implemented but lacks event emission

The staking implementation correctly handles deposits and updates balances. Consider adding event logging (using near_sdk::log!) to provide transparency for users and enable off-chain tracking of staking actions.

pub fn stake(&mut self) {
    let amount = env::attached_deposit().as_yoctonear();
    require!(amount > 0u128, "Invalid stake amount");

    let account_id = env::predecessor_account_id();
    let balance = self.internal_account_staked_balance(&account_id);
    self.accounts.insert(account_id.clone(), balance + amount);
    self.total_staked_balance += amount;

    self.internal_restake();
+   near_sdk::log!("Account {} staked {}", account_id, amount);
}

69-76: Owner-only method should use #[private] decorator

The voting method is described in comments as an "Owner's method" and correctly verifies ownership, but it would be clearer to use the #[private] decorator to make this requirement explicit in the contract interface.

/// Owner's method.
/// Calls `vote(is_vote)` on the given voting contract account ID on behalf of the pool.
+#[private]
pub fn vote(&mut self, voting_account_id: AccountId, is_vote: bool) -> Promise {
-   self.assert_owner();
    ext_voting::ext(voting_account_id)
        .with_static_gas(VOTE_GAS)
        .vote(is_vote)
}

87-90: Consider adding more view methods for contract state inspection

The contract lacks view methods to expose its state (total staked balance, individual account balances, etc.). Adding these would make it more testable and usable in integration tests.

fn assert_owner(&self) {
    require!(env::predecessor_account_id() == self.owner_id, "Not owner");
}

+/// Returns the staked balance of the given account.
+pub fn get_account_staked_balance(&self, account_id: AccountId) -> U128 {
+    U128(self.internal_account_staked_balance(&account_id))
+}
+
+/// Returns the total staked balance.
+pub fn get_total_staked_balance(&self) -> U128 {
+    U128(self.total_staked_balance)
+}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between dead2b0 and 4f965aa.

⛔ Files ignored due to path filters (1)
  • tests/contracts/mock-staking-pool/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • .gitignore (1 hunks)
  • tests/contracts/mock-staking-pool/Cargo.toml (1 hunks)
  • tests/contracts/mock-staking-pool/src/lib.rs (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • .gitignore
  • tests/contracts/mock-staking-pool/Cargo.toml
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Tests
  • GitHub Check: Code Linter
🔇 Additional comments (3)
tests/contracts/mock-staking-pool/src/lib.rs (3)

14-27: Contract state structure is well-defined

The contract state structure is well-organized with appropriate storage keys and fields. Using PanicOnDefault is a good practice to prevent accidental initialization without proper parameters.


29-40: Initialization method follows best practices

The initialization method is properly decorated with #[init] and #[private], and correctly sets up all contract state. The use of LookupMap for accounts is an appropriate choice for storing account balances.


78-85: Internal helper methods are well-implemented

The helper methods for account balance retrieval and restaking are well-implemented. The account balance retrieval properly handles the case when an account doesn't exist, and the restaking method correctly creates a staking promise.

Copy link

@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: 1

♻️ Duplicate comments (1)
tests/contracts/mock-staking-pool/src/lib.rs (1)

59-71: Unstake must return tokens or be documented as a mock

The current unstake implementation only updates internal balances and restakes the pool's total, but never returns the unstaked amount to the caller nor requests an actual protocol-level unstake.

You should either implement the transfer/unstake logic or clearly document that this is a simplified mock:

+    /// Note: This is a test-only mock. `unstake` updates balances but
+    /// does not perform protocol-level unstake or transfer tokens back to the user.
     pub fn unstake(&mut self, amount: U128) -> Promise {
         let amount = amount.0;
         require!(amount > 0u128, "Invalid unstake amount");

         let account_id = env::predecessor_account_id();
         let balance = self.internal_account_staked_balance(&account_id);
         require!(balance >= amount, "Not enough stake");

         self.accounts.insert(account_id, balance - amount);
         self.total_staked_balance -= amount;

         self.internal_restake()
     }
🧹 Nitpick comments (4)
tests/contracts/mock-staking-pool/src/lib.rs (4)

89-100: Improve error handling in callback

The on_stake_action callback only logs a generic "Stake action failed" message without providing any details about the error. This makes debugging difficult.

Consider enhancing the error handling to include more specific information:

     #[private]
     pub fn on_stake_action(&self, #[callback_result] result: Result<String, PromiseError>) {
         if result.is_err() {
-            log!("Stake action failed");
+            match result.unwrap_err() {
+                PromiseError::Failed => log!("Stake action failed: execution failed"),
+                PromiseError::NotReady => log!("Stake action failed: promise not ready"),
+            }
             return;
         }

         log!(
             "Validator stake amount: {}",
             env::validator_stake(&env::current_account_id())
         );
     }

46-57: Consider adding deposit limit for testing safety

The deposit_and_stake method accepts any non-zero amount without an upper limit. For a test contract, it might be beneficial to add a maximum deposit limit to prevent accidental large deposits.

     #[payable]
     pub fn deposit_and_stake(&mut self) -> Promise {
         let amount = env::attached_deposit().as_yoctonear();
         require!(amount > 0u128, "Invalid stake amount");
+        // For test safety, limit maximum deposit
+        require!(amount <= 100_000_000_000_000_000_000_000_000, "Deposit exceeds test limit");

         let account_id = env::predecessor_account_id();
         let balance = self.internal_account_staked_balance(&account_id);
         self.accounts.insert(account_id, balance + amount);
         self.total_staked_balance += amount;

         self.internal_restake()
     }

102-104: Optimize account balance lookup

The current implementation of internal_account_staked_balance dereferences the result of the get operation, which is slightly less efficient and requires an extra unwrap_or.

Consider using get_or_default for a more concise and potentially more efficient implementation:

     fn internal_account_staked_balance(&self, account_id: &AccountId) -> Balance {
-        *self.accounts.get(account_id).unwrap_or(&0u128)
+        self.accounts.get(account_id).copied().unwrap_or_default()
     }

82-87: Rename method to better reflect its purpose

The method get_staked_balance returns both the validator stake and total staked balance, but its name suggests it only returns a single balance.

Consider renaming this method to better reflect what it returns:

-    pub fn get_staked_balance(&self) -> (U128, U128) {
+    pub fn get_stake_info(&self) -> (U128, U128) {
         (
             U128::from(env::validator_stake(&env::current_account_id()).as_yoctonear()),
             U128::from(self.total_staked_balance),
         )
     }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between e3bf88b and ccd21be.

📒 Files selected for processing (2)
  • tests/contracts/mock-staking-pool/src/lib.rs (1 hunks)
  • tests/test_vote.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/test_vote.rs
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Tests
  • GitHub Check: Code Linter
🔇 Additional comments (1)
tests/contracts/mock-staking-pool/src/lib.rs (1)

75-80: 🛠️ Refactor suggestion

Add validation for voting contract before invocation

The vote method directly calls the external voting contract without validating if it exists first. In a production environment, this could lead to wasted gas if the contract doesn't exist.

Consider adding a check using Promise::new(voting_account_id.clone()).account_id() to verify the account exists before proceeding:

     pub fn vote(&mut self, voting_account_id: AccountId, is_vote: bool) -> Promise {
         self.assert_owner();
+        // First verify the voting contract exists
+        Promise::new(voting_account_id.clone())
+            .account_id()
+            .then(
         ext_voting::ext(voting_account_id)
             .with_static_gas(VOTE_GAS)
             .vote(is_vote)
+            )
     }

Likely an incorrect or invalid review comment.

Copy link

@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 (1)
tests/contracts/mock-staking-pool/src/lib.rs (1)

64-76: Unstake does not return tokens – document or implement

As noted in previous reviews, unstake only updates internal balances and
calls internal_restake, leaving the user without their tokens.
Either make this explicit in docs or transfer the amount back via
Promise::new(account_id).transfer(amount). (Tagging as duplicate of the
prior comment for traceability.)

🧹 Nitpick comments (8)
.gitignore (2)

1-1: Good: Recursive target directory ignore
The pattern **/target correctly ignores all nested Cargo/build target directories across workspaces. As a nitpick, you could add a trailing slash (**/target/) to clarify that only directories (not files named “target”) are being ignored.


2-3: Ignore compiled WASM artifact directories
Ignoring tests/res and res ensures your generated WebAssembly outputs aren’t committed. If these folders always live at the repository root, consider anchoring them with a leading slash (e.g., /tests/res/ and /res/) to avoid accidentally ignoring any other res directories deeper in the tree.

src/lib.rs (3)

55-58: Consider deferring the format! evaluation to panic-only path

require! eagerly evaluates its second argument, so format!("{} is not a validator", account_id) allocates even when the check passes (i.e. for every honest validator vote).
For hot paths it’s preferable to avoid the cost by passing a &str literal or building the string lazily only on failure.

- require!(stake > 0, format!("{} is not a validator", account_id));
+ require!(
+     stake > 0,
+     "{} is not a validator",
+     account_id            // interpolated only if the requirement fails
+ );

97-108: Epoch-height cache may skip the very first ping()

last_epoch_height starts at 0.
If the contract is initialised during epoch 0, the very first call to ping() (or any
method that calls it) in the same epoch will not refresh stakes because
cur_epoch_height == self.last_epoch_height.
Initialising last_epoch_height to the current epoch removes this edge case:

- last_epoch_height: 0,
+ last_epoch_height: env::epoch_height(),

This change does not affect correctness but avoids a surprising no-op on the first call.


168-178: set_validator_stake needn’t require &mut self

The helper only mutates the mock validator map in utils, not the contract
instance. Accepting &self makes the method callable on an immutable
reference and avoids needless &mut borrows in tests:

- pub fn set_validator_stake(&mut self, validator_account_id: AccountId, amount: U128) {
+ pub fn set_validator_stake(&self, validator_account_id: AccountId, amount: U128) {

( utils::set_validator_stake already performs the mutation inside storage. )

src/utils.rs (1)

35-46: Repeated full-map (de)serialization can be expensive

Every call to validator_stake / validator_total_stake under the
integration-test feature deserialises the whole map from storage. In large
test scenarios this dominates gas/time.

If you keep this design, consider caching the map in State for the duration
of the test, or store stakes in individual keys
(e.g. __validator__:<account_id>). Either approach avoids O(n) work for
each lookup and makes tests that iterate over many validators much faster.

makefile (2)

1-1: Drop the superfluous quotes in RUSTFLAGS

Including the double quotes here pushes them into the environment variable,
resulting in " being passed to the compiler and breaking flag
concatenation in some shells.

-RUSTFLAGS = "-C link-arg=-s"
+RUSTFLAGS = -C link-arg=-s

22-25: Avoid redundant rustup target add on every build

rustup target add wasm32-unknown-unknown is idempotent but incurs network
latency when executed in CI for every make invocation. Cache the check or
move it to project setup (e.g. in justfile, ci.yml, or a dedicated
make setup target) to speed up incremental builds.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 5860416 and 3d39015.

⛔ Files ignored due to path filters (1)
  • tests/contracts/mock-staking-pool/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • .gitignore (1 hunks)
  • Cargo.toml (1 hunks)
  • makefile (1 hunks)
  • src/events.rs (1 hunks)
  • src/lib.rs (9 hunks)
  • src/utils.rs (1 hunks)
  • tests/contracts/mock-staking-pool/Cargo.toml (1 hunks)
  • tests/contracts/mock-staking-pool/src/lib.rs (1 hunks)
  • tests/test_vote.rs (1 hunks)
  • tests/utils/mod.rs (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/events.rs
🚧 Files skipped from review as they are similar to previous changes (4)
  • Cargo.toml
  • tests/contracts/mock-staking-pool/Cargo.toml
  • tests/test_vote.rs
  • tests/utils/mod.rs
🧰 Additional context used
🧬 Code Graph Analysis (2)
tests/contracts/mock-staking-pool/src/lib.rs (2)
src/lib.rs (2)
  • vote (51-86)
  • set_validator_stake (171-173)
src/utils.rs (1)
  • set_validator_stake (29-33)
src/lib.rs (2)
src/utils.rs (4)
  • validator_stake (35-40)
  • validator_total_stake (42-47)
  • set_validator_stake (29-33)
  • get_validator_stake (20-26)
tests/contracts/mock-staking-pool/src/lib.rs (1)
  • set_validator_stake (18-18)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Tests
  • GitHub Check: Code Linter
🔇 Additional comments (1)
tests/contracts/mock-staking-pool/src/lib.rs (1)

2-2:

✅ Verification successful

near_sdk::store may not contain LookupMap in older SDK versions

LookupMap lives under near_sdk::collections up to v4.x and was moved to
near_sdk::store only in v5.
If your workspace still depends on < 5.0, the current import fails to compile.

-use near_sdk::store::LookupMap;
+use near_sdk::collections::LookupMap     // v4.x
+// use near_sdk::store::LookupMap        // v5.x

Please verify the SDK version in Cargo.toml and choose the correct path.


🏁 Script executed:

#!/bin/bash
# Locate all Cargo.toml files and display any near-sdk dependency lines to determine the SDK version
grep -R "near-sdk" -n . | grep "Cargo.toml"

Length of output: 226


No changes needed for LookupMap import in v5.12.0
The project’s Cargo.toml specifies near-sdk = "5.12.0" (including in tests/contracts/mock-staking-pool/Cargo.toml), so LookupMap is correctly imported from near_sdk::store.

Copy link

@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: 0

🧹 Nitpick comments (6)
tests/utils/mod.rs (6)

30-37: Consider handling time calculation error more safely.

The unwrap() on duration_since(UNIX_EPOCH) assumes the system time is always after the UNIX epoch. While this is typically true, consider using .expect("System time is before UNIX EPOCH") to provide a clearer error message if this ever fails.

    let init_args = VotingInitArgs {
        proposal: "test_proposal".to_string(),
        deadline_timestamp_ms: (SystemTime::now()
            .duration_since(UNIX_EPOCH)
-           .unwrap()
+           .expect("System time is before UNIX EPOCH")
            .as_millis()
            + 10 * 60 * 1000) as u64,
    };

39-43: Consider capturing the initialization outcome.

The initialization result is discarded with let _ = .... Consider capturing this value to make it available for tests that might need to inspect it.

-    let _ = contract
+    let init_outcome = contract
        .call("new")
        .args_json(json!(init_args))
        .transact()
        .await?;

59-65: Use .expect() with a descriptive message for error handling.

The hardcoded public key is unlikely to fail parsing, but for better error messages, replace unwrap() with expect() that includes a descriptive message.

    let init_args = MockStakingPoolInitArgs {
        owner_id: owner.id().clone(),
        stake_public_key: PublicKey::from_str(
            "ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp",
        )
-        .unwrap(),
+        .expect("Failed to parse hardcoded public key"),
        voting_account_id,
    };

66-70: Consider capturing the initialization outcome.

Similar to the voting contract initialization, consider capturing the result instead of discarding it.

-    let _ = contract
+    let init_outcome = contract
        .call("new")
        .args_json(json!(init_args))
        .transact()
        .await?;

85-91: Fix inconsistent indentation and use .expect() for better error handling.

The indentation of .unwrap() is inconsistent with the rest of the code. Also, as with the other instances, consider using .expect() for better error messages.

    let init_args = MockStakingPoolInitArgs {
        owner_id: owner.id().clone(),
        stake_public_key: PublicKey::from_str(
            "ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp",
        )
-            .unwrap(),
+        .expect("Failed to parse hardcoded public key"),
        voting_account_id,
    };

135-136: Improve error handling for root account retrieval.

The unwrap() call for the root account should be replaced with proper error handling to make the code more robust.

-    let root = sandbox.root_account().unwrap();
+    let root = sandbox.root_account().expect("Failed to get sandbox root account");
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 4702ce3 and b1b18c3.

📒 Files selected for processing (6)
  • makefile (1 hunks)
  • src/lib.rs (9 hunks)
  • tests/contracts/mock-staking-pool/src/lib.rs (1 hunks)
  • tests/test_init.rs (1 hunks)
  • tests/test_vote.rs (1 hunks)
  • tests/utils/mod.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/lib.rs
  • tests/test_init.rs
  • makefile
  • tests/contracts/mock-staking-pool/src/lib.rs
  • tests/test_vote.rs
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Tests
  • GitHub Check: Code Linter
🔇 Additional comments (4)
tests/utils/mod.rs (4)

97-102: Good practice: Asserting transaction success.

The explicit assertion of transaction success with a detailed error message is a good practice. This helps catch issues early with clear diagnostics.


1-8: Clean and focused imports.

The imports are well-organized and appropriately scoped for the functionality in this module.


9-20: Well-structured contract initialization argument structs.

The VotingInitArgs and MockStakingPoolInitArgs structs are clearly defined with appropriate serialization attributes.


109-117: Well-designed environment setup functions.

The setup_env and setup_env_many functions provide a clean API for tests to set up their test environment with single or multiple staking pool contracts.

Also applies to: 119-128

Copy link

@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: 1

🧹 Nitpick comments (1)
.github/workflows/test.yml (1)

35-35: Step name should reflect the actual command
The step is still named “Run cargo test” even though it now invokes make test. For clarity, update the name to something like “Run tests” or “Run make test”.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between b1b18c3 and d8c6e4d.

📒 Files selected for processing (5)
  • .github/workflows/test.yml (1 hunks)
  • makefile (1 hunks)
  • src/lib.rs (9 hunks)
  • tests/test_init.rs (1 hunks)
  • tests/test_vote.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • tests/test_init.rs
  • makefile
  • src/lib.rs
  • tests/test_vote.rs
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Code Linter

Copy link

@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

🧹 Nitpick comments (4)
src/utils.rs (4)

7-8: Consider handling deserialization errors more gracefully.

Using unwrap() will panic if deserialization fails. While this might be acceptable for integration tests where failing fast is useful, consider using a more robust error handling approach if this code could be used in other contexts.

-        |validators| near_sdk::borsh::from_slice(&validators).unwrap(),
+        |validators| near_sdk::borsh::from_slice(&validators).expect("Failed to deserialize validators map"),

15-16: Consider handling serialization errors more gracefully.

Using unwrap() will panic if serialization fails. Consider using expect() with a descriptive message.

-        &near_sdk::borsh::to_vec(&validators).unwrap(),
+        &near_sdk::borsh::to_vec(&validators).expect("Failed to serialize validators map"),

19-23: Consider handling deserialization errors more gracefully in get_validator_total_stake.

Same issue with unwrap() as mentioned earlier.

-        .map_or(0, |amount| near_sdk::borsh::from_slice(&amount).unwrap())
+        .map_or(0, |amount| near_sdk::borsh::from_slice(&amount).expect("Failed to deserialize total stake"))

25-31: Consider handling serialization errors more gracefully in set_validator_total_stake.

Same issue with unwrap() as mentioned earlier.

-        &near_sdk::borsh::to_vec(&amount).unwrap(),
+        &near_sdk::borsh::to_vec(&amount).expect("Failed to serialize total stake"),
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between a921c2e and 6952f98.

📒 Files selected for processing (4)
  • src/lib.rs (9 hunks)
  • src/utils.rs (1 hunks)
  • tests/test_vote.rs (1 hunks)
  • tests/utils/mod.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/lib.rs
  • tests/test_vote.rs
  • tests/utils/mod.rs
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Code Linter
  • GitHub Check: Tests
🔇 Additional comments (3)
src/utils.rs (3)

33-40: Properly handles the case when a validator doesn't exist.

Good use of unwrap_or_default() to safely handle the case where a validator doesn't have a stake yet.


58-63: Clean implementation of feature-based delegation.

The function elegantly delegates to the appropriate implementation based on the compilation feature.


65-70: Clean implementation of feature-based delegation for total stake.

The function elegantly delegates to the appropriate implementation based on the compilation feature.

Copy link

@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

🧹 Nitpick comments (3)
tests/test_vote.rs (3)

110-132: Clarify test assertions and error messages

The conditional assertions in this loop are checking if the voting process completes after a certain number of votes (200 out of 300), but the error message and logic could be clearer.

Improve the assertion error messages and clarify the test's intent:

for (index, staking_pool_contract) in staking_pool_contracts.iter().enumerate() {
    sandbox.fast_forward(500).await?;
    let block = sandbox.view_block().await?;

    let outcome = owner
        .call(voting_contract.id(), "ping")
        .gas(Gas::from_tgas(300))
        .transact()
        .await?;

    if index <= 200 {
        assert!(
            outcome.is_success(),
-           "{:#?}",
+           "Ping should succeed while voting is still in progress (vote {}/201). Error: {:#?}",
+           index + 1,
            outcome.into_result().unwrap_err()
        );
    } else {
        assert!(
            outcome.is_failure(),
-           "Ping should failed since vote is finished",
+           "Ping should fail after vote #{} since voting is complete (requires 201/300 votes)",
+           index + 1
        );
        break;
    }

149-156: Reduce excessive logging in loop iterations

Printing detailed vote information for every iteration in a loop with 300 iterations will generate a lot of output, making it difficult to parse test results and potentially slowing down test execution.

Consider reducing the verbosity by logging only at specific intervals:

-       println!("validator #{} voted at epoch ({})", index, block.epoch_id());
-       println!(
-           "Votes: {:#?}",
-           voting_contract
-               .view("get_votes")
-               .await?
-               .json::<HashMap<AccountId, String>>()?
-       );
+       // Log only at the beginning, end, and at specific intervals
+       if index == 0 || index == 200 || index % 50 == 0 {
+           println!("validator #{} voted at epoch ({})", index, block.epoch_id());
+           println!(
+               "Votes: {:#?}",
+               voting_contract
+                   .view("get_votes")
+                   .await?
+                   .json::<HashMap<AccountId, String>>()?
+           );
+       }

1-8: Consider importing specific utility functions

The current approach imports all functions from the utils module. For better clarity and to avoid potential name conflicts, consider importing specific functions that are used in this file.

mod utils;
- use utils::*;
+ use utils::{create_account, deploy_voting_contract, setup_env, setup_env_many};
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between dd1abfb and 2608bdf.

📒 Files selected for processing (2)
  • makefile (1 hunks)
  • tests/test_vote.rs (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • makefile
🧰 Additional context used
🧬 Code Graph Analysis (1)
tests/test_vote.rs (1)
tests/utils/mod.rs (4)
  • deploy_voting_contract (22-43)
  • setup_env (110-126)
  • create_account (151-163)
  • setup_env_many (128-149)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Tests
  • GitHub Check: Code Linter

Comment on lines 34 to 80
#[tokio::test]
async fn test_simple_vote() -> Result<(), Box<dyn std::error::Error>> {
let (staking_pool_contract, voting_contract, sandbox, owner) = setup_env().await?;

let alice = create_account(&sandbox, "alice", 10000).await?;
let outcome = alice
.call(staking_pool_contract.id(), "deposit_and_stake")
.args_json(json!({}))
.gas(Gas::from_tgas(250))
.deposit(NearToken::from_near(1000))
.transact()
.await?;
assert!(
outcome.is_success(),
"{:#?}",
outcome.into_result().unwrap_err()
);

let staked_balance = voting_contract
.view("get_validator_stake")
.args_json(json!({
"validator_account_id": staking_pool_contract.id()
}))
.await?;
println!(
"user account: {}, {:#?}",
alice.id(),
staked_balance.json::<String>()?
);

let outcome = owner
.call(staking_pool_contract.id(), "vote")
.args_json(json!({
"voting_account_id": voting_contract.id(),
"is_vote": true
}))
.gas(Gas::from_tgas(200))
.transact()
.await?;
assert!(
outcome.is_success(),
"{:#?}",
outcome.into_result().unwrap_err()
);

Ok(())
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance test validation for vote recording

The test successfully verifies the staked balance, but doesn't verify that the vote was actually recorded after calling the vote method. Adding verification of the vote state would make this test more comprehensive.

Add verification of the vote state after the vote operation:

    assert!(
        outcome.is_success(),
        "{:#?}",
        outcome.into_result().unwrap_err()
    );

+   // Verify the vote was recorded
+   let votes = voting_contract
+       .view("get_votes")
+       .await?
+       .json::<HashMap<AccountId, String>>()?;
+   assert!(votes.contains_key(staking_pool_contract.id()), "Vote not recorded");
+   assert_eq!(votes.get(staking_pool_contract.id()).unwrap(), "1000", "Incorrect vote amount");

    Ok(())
}
🤖 Prompt for AI Agents
In tests/test_vote.rs around lines 34 to 80, the test_simple_vote function
verifies staking but does not check if the vote was recorded after calling the
vote method. To fix this, add a call to the voting_contract to query the vote
state for the owner or relevant account after the vote transaction completes.
Then assert that the vote state matches the expected value (e.g., true) to
ensure the vote was properly recorded.

Comment on lines 82 to 160
#[tokio::test] // it takes around 50 minutes
async fn test_many_votes() -> Result<(), Box<dyn std::error::Error>> {
let (staking_pool_contracts, voting_contract, sandbox, owner) = setup_env_many(300).await?;

let alice = create_account(&sandbox, "alice", 50000).await?;

for staking_pool_contract in staking_pool_contracts.iter() {
let outcome = alice
.call(staking_pool_contract.id(), "deposit_and_stake")
.args_json(json!({}))
.gas(Gas::from_tgas(250))
.deposit(NearToken::from_near(100))
.transact()
.await?;
assert!(
outcome.is_success(),
"{:#?}",
outcome.into_result().unwrap_err()
);

let total_staked = voting_contract.view("get_validator_total_stake").await?;
println!(
"total staked: {}, {:#?}",
alice.id(),
total_staked.json::<String>()?
);
}

for (index, staking_pool_contract) in staking_pool_contracts.iter().enumerate() {
sandbox.fast_forward(500).await?;
let block = sandbox.view_block().await?;

let outcome = owner
.call(voting_contract.id(), "ping")
.gas(Gas::from_tgas(300))
.transact()
.await?;

if index <= 200 {
assert!(
outcome.is_success(),
"{:#?}",
outcome.into_result().unwrap_err()
);
} else {
assert!(
outcome.is_failure(),
"Ping should failed since vote is finished",
);
break;
}

let outcome = owner
.call(staking_pool_contract.id(), "vote")
.args_json(json!({
"voting_account_id": voting_contract.id(),
"is_vote": true
}))
.gas(Gas::from_tgas(200))
.transact()
.await?;

assert!(
outcome.is_success(),
"{:#?}",
outcome.into_result().unwrap_err()
);
println!("validator #{} voted at epoch ({})", index, block.epoch_id());
println!(
"Votes: {:#?}",
voting_contract
.view("get_votes")
.await?
.json::<HashMap<AccountId, String>>()?
);
}

Ok(())
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Optimize long-running test execution

This test takes approximately 50 minutes to run, which is excessively long for a unit test that might be run frequently during development. Long-running tests can slow down development cycles and CI pipelines.

Consider these improvements:

  1. Parameterize the test to allow running with fewer staking pools during regular testing:
#[tokio::test] // it takes around 50 minutes
async fn test_many_votes() -> Result<(), Box<dyn std::error::Error>> {
-   let (staking_pool_contracts, voting_contract, sandbox, owner) = setup_env_many(300).await?;
+   // Use fewer contracts (e.g., 10) for regular testing, more for comprehensive testing
+   let staking_pool_count = option_env!("FULL_TEST").map_or(10, |_| 300);
+   let (staking_pool_contracts, voting_contract, sandbox, owner) = setup_env_many(staking_pool_count).await?;

    // Rest of the test...
    
    // Adjust the assertion accordingly
-   if index <= 200 {
+   if index <= staking_pool_count * 2 / 3 { // Maintain the same proportion for vote completion
  1. Consider splitting the test into smaller, more focused tests that test specific aspects of the functionality.

  2. Use batching or parallelization where possible to speed up operations.

📝 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
#[tokio::test] // it takes around 50 minutes
async fn test_many_votes() -> Result<(), Box<dyn std::error::Error>> {
let (staking_pool_contracts, voting_contract, sandbox, owner) = setup_env_many(300).await?;
let alice = create_account(&sandbox, "alice", 50000).await?;
for staking_pool_contract in staking_pool_contracts.iter() {
let outcome = alice
.call(staking_pool_contract.id(), "deposit_and_stake")
.args_json(json!({}))
.gas(Gas::from_tgas(250))
.deposit(NearToken::from_near(100))
.transact()
.await?;
assert!(
outcome.is_success(),
"{:#?}",
outcome.into_result().unwrap_err()
);
let total_staked = voting_contract.view("get_validator_total_stake").await?;
println!(
"total staked: {}, {:#?}",
alice.id(),
total_staked.json::<String>()?
);
}
for (index, staking_pool_contract) in staking_pool_contracts.iter().enumerate() {
sandbox.fast_forward(500).await?;
let block = sandbox.view_block().await?;
let outcome = owner
.call(voting_contract.id(), "ping")
.gas(Gas::from_tgas(300))
.transact()
.await?;
if index <= 200 {
assert!(
outcome.is_success(),
"{:#?}",
outcome.into_result().unwrap_err()
);
} else {
assert!(
outcome.is_failure(),
"Ping should failed since vote is finished",
);
break;
}
let outcome = owner
.call(staking_pool_contract.id(), "vote")
.args_json(json!({
"voting_account_id": voting_contract.id(),
"is_vote": true
}))
.gas(Gas::from_tgas(200))
.transact()
.await?;
assert!(
outcome.is_success(),
"{:#?}",
outcome.into_result().unwrap_err()
);
println!("validator #{} voted at epoch ({})", index, block.epoch_id());
println!(
"Votes: {:#?}",
voting_contract
.view("get_votes")
.await?
.json::<HashMap<AccountId, String>>()?
);
}
Ok(())
}
#[tokio::test] // it takes around 50 minutes
async fn test_many_votes() -> Result<(), Box<dyn std::error::Error>> {
// Use fewer contracts (e.g., 10) for regular testing, more for comprehensive testing
let staking_pool_count = option_env!("FULL_TEST").map_or(10, |_| 300);
let (staking_pool_contracts, voting_contract, sandbox, owner) = setup_env_many(staking_pool_count).await?;
let alice = create_account(&sandbox, "alice", 50000).await?;
for staking_pool_contract in staking_pool_contracts.iter() {
let outcome = alice
.call(staking_pool_contract.id(), "deposit_and_stake")
.args_json(json!({}))
.gas(Gas::from_tgas(250))
.deposit(NearToken::from_near(100))
.transact()
.await?;
assert!(
outcome.is_success(),
"{:#?}",
outcome.into_result().unwrap_err()
);
let total_staked = voting_contract.view("get_validator_total_stake").await?;
println!(
"total staked: {}, {:#?}",
alice.id(),
total_staked.json::<String>()?
);
}
for (index, staking_pool_contract) in staking_pool_contracts.iter().enumerate() {
sandbox.fast_forward(500).await?;
let block = sandbox.view_block().await?;
let outcome = owner
.call(voting_contract.id(), "ping")
.gas(Gas::from_tgas(300))
.transact()
.await?;
if index <= staking_pool_count * 2 / 3 { // Maintain the same proportion for vote completion
assert!(
outcome.is_success(),
"{:#?}",
outcome.into_result().unwrap_err()
);
} else {
assert!(
outcome.is_failure(),
"Ping should failed since vote is finished",
);
break;
}
let outcome = owner
.call(staking_pool_contract.id(), "vote")
.args_json(json!({
"voting_account_id": voting_contract.id(),
"is_vote": true
}))
.gas(Gas::from_tgas(200))
.transact()
.await?;
assert!(
outcome.is_success(),
"{:#?}",
outcome.into_result().unwrap_err()
);
println!("validator #{} voted at epoch ({})", index, block.epoch_id());
println!(
"Votes: {:#?}",
voting_contract
.view("get_votes")
.await?
.json::<HashMap<AccountId, String>>()?
);
}
Ok(())
}
🤖 Prompt for AI Agents
In tests/test_vote.rs around lines 82 to 160, the test_many_votes function runs
for about 50 minutes, which is too long for regular unit testing. To fix this,
parameterize the test to accept a smaller number of staking pools for faster
runs during development, and keep the large number for more exhaustive CI runs.
Additionally, split the test into smaller tests focusing on specific behaviors
like staking, voting, and pinging to isolate failures and reduce runtime. Where
possible, batch or parallelize calls such as deposits and votes to speed up
execution without changing test logic.

Copy link

@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: 0

♻️ Duplicate comments (3)
tests/test_vote.rs (3)

9-32: Fix redundant sandbox creation

There's a redundant sandbox creation at line 22 which is inconsistent with the test setup. The voting contract is deployed to the first sandbox, but you're trying to create a user account in a separate sandbox environment.

Apply this diff to use the initially created sandbox:

async fn test_non_validator_cannot_vote() -> Result<(), Box<dyn std::error::Error>> {
    let sandbox = near_workspaces::sandbox().await?;
    let (contract, _) = deploy_voting_contract(
        &sandbox,
        (SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_millis()
            + 10 * 60 * 1000) as u64,
    )
    .await?;

-   let sandbox = near_workspaces::sandbox().await?;
    let user_account = sandbox.dev_create_account().await?;
    let outcome = user_account
        .call(contract.id(), "vote")
        .args_json(json!({"is_vote": true}))
        .transact()
        .await?;
    assert!(outcome.is_failure(),);

    Ok(())
}

34-80: Enhance test validation for vote recording

The test successfully verifies the staking balance and that the vote transaction completes successfully, but it doesn't verify that the vote was actually recorded in the contract state.

Add verification of the vote state after the vote operation:

    assert!(
        outcome.is_success(),
        "{:#?}",
        outcome.into_result().unwrap_err()
    );

+   // Verify the vote was recorded
+   let votes = voting_contract
+       .view("get_votes")
+       .await?
+       .json::<HashMap<AccountId, String>>()?;
+   assert!(votes.contains_key(staking_pool_contract.id()), "Vote not recorded");
+   assert_eq!(votes.get(staking_pool_contract.id()).unwrap(), "1000", "Incorrect vote amount");

    Ok(())
}

82-160: Optimize long-running test execution

This test takes approximately 50 minutes to run, which is extremely long for a unit test that might be run frequently during development.

Consider these improvements:

  1. Parameterize the test to allow running with fewer staking pools during regular testing:
#[tokio::test] // it takes around 50 minutes
async fn test_many_votes() -> Result<(), Box<dyn std::error::Error>> {
-   let (staking_pool_contracts, voting_contract, sandbox, owner) = setup_env_many(300).await?;
+   // Use fewer contracts (e.g., 10) for regular testing, more for comprehensive testing
+   let staking_pool_count = option_env!("FULL_TEST").map_or(10, |_| 300);
+   let (staking_pool_contracts, voting_contract, sandbox, owner) = setup_env_many(staking_pool_count).await?;

    // Rest of the test...
    
    // Adjust the assertion accordingly
-   if index <= 200 {
+   if index <= staking_pool_count * 2 / 3 { // Maintain the same proportion for vote completion
  1. Consider splitting the test into smaller, more focused tests that test specific aspects of the functionality.

  2. Use batching or parallelization where possible to speed up operations.

🧹 Nitpick comments (4)
tests/test_vote.rs (4)

162-213: Improve error assertion in expiration test

The assertion on line 210 is checking for a specific error message using string contains, which can be fragile if the error message changes.

Consider using a more robust approach to verify the error condition:

-   assert!(outcome.into_result().unwrap_err().to_string().contains("Smart contract panicked: Voting deadline has already passed"));
+   // Extract the error and check its properties
+   let err = outcome.into_result().unwrap_err();
+   // Check error type and code if available
+   assert!(matches!(err.kind(), near_workspaces::types::ErrorKind::Contract(_)));
+   assert!(err.to_string().contains("Voting deadline has already passed"), 
+          "Expected deadline error message, got: {}", err);

This approach provides better error messages and is slightly more structured in checking the error type first.


52-63: Add more descriptive debug message

The debug print statement could be more descriptive about what value it's displaying.

    println!(
-       "user account: {}, {:#?}",
+       "Staked balance for validator {}: {:#?}",
        alice.id(),
        staked_balance.json::<String>()?
    );

102-108: Improve debug message clarity

The print statement about total staked amount should reference the staking pool account rather than the alice account.

    println!(
-       "total staked: {}, {:#?}",
+       "Total staked after deposit from {}: {:#?}",
        alice.id(),
        total_staked.json::<String>()?
    );

149-157: Consider reducing debug output in loop

Printing detailed vote information for all 300 iterations creates excessive output that makes it difficult to find important information.

Consider limiting the output to key points in the test:

        println!("validator #{} voted at epoch ({})", index, block.epoch_id());
-       println!(
-           "Votes: {:#?}",
-           voting_contract
-               .view("get_votes")
-               .await?
-               .json::<HashMap<AccountId, String>>()?
-       );
+       // Only print detailed vote information occasionally to reduce output volume
+       if index % 50 == 0 || index > 190 {
+           println!(
+               "Votes at milestone {}: {:#?}",
+               index,
+               voting_contract
+                   .view("get_votes")
+                   .await?
+                   .json::<HashMap<AccountId, String>>()?
+           );
+       }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 2608bdf and ffdab32.

📒 Files selected for processing (2)
  • tests/test_vote.rs (1 hunks)
  • tests/utils/mod.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/utils/mod.rs
🧰 Additional context used
🧬 Code Graph Analysis (1)
tests/test_vote.rs (1)
tests/utils/mod.rs (4)
  • deploy_voting_contract (22-43)
  • setup_env (110-127)
  • create_account (152-164)
  • setup_env_many (129-150)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Code Linter
  • GitHub Check: Tests
🔇 Additional comments (1)
tests/test_vote.rs (1)

1-8: LGTM: Imports and module setup are appropriate

The imports and module setup look correct for these integration tests, utilizing the shared utilities from the utils module.

Copy link

@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: 0

♻️ Duplicate comments (3)
tests/test_vote.rs (3)

9-32: Fix redundant sandbox creation

The redundant second sandbox creation at line 22 is still present. This creates confusion since the voting contract is deployed to the first sandbox, but the user account is created in a different sandbox instance.


34-80: Enhance test validation for vote recording

The test successfully verifies the staked balance, but doesn't verify that the vote was actually recorded after calling the vote method. Adding verification of the vote state would make this test more comprehensive.


82-160: Optimize long-running test execution

This test takes approximately 50 minutes to run, which is excessively long for a unit test that might be run frequently during development. Long-running tests can slow down development cycles and CI pipelines.

🧹 Nitpick comments (1)
tests/test_vote.rs (1)

1-380: Consider refactoring for improved maintainability

The test suite is comprehensive but could benefit from reducing code duplication and improving consistency:

  1. Extract common staking operations: Multiple tests repeat similar staking setup patterns that could be extracted into helper functions.

  2. Define constants for magic numbers: Hardcoded values like 1000 NEAR, 100 NEAR, gas amounts, and timeouts should be extracted as named constants.

  3. Standardize gas usage: Gas amounts are specified inconsistently across similar operations.

Example improvements:

// At the top of the file
const DEFAULT_STAKE_AMOUNT: u128 = 1000;
const LARGE_STAKE_AMOUNT: u128 = 100;
const VOTE_GAS: u64 = 200;
const STAKE_GAS: u64 = 250;

// Helper function for common staking pattern
async fn setup_staking_for_user(
    user: &Account,
    pool: &Contract,
    amount: u128,
) -> Result<(), Box<dyn std::error::Error>> {
    let outcome = user
        .call(pool.id(), "deposit_and_stake")
        .args_json(json!({}))
        .gas(Gas::from_tgas(STAKE_GAS))
        .deposit(NearToken::from_near(amount))
        .transact()
        .await?;
    
    assert!(outcome.is_success(), "{:#?}", outcome.into_result().unwrap_err());
    Ok(())
}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between ffdab32 and 3776197.

📒 Files selected for processing (2)
  • tests/test_vote.rs (1 hunks)
  • tests/utils/mod.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/utils/mod.rs
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Code Linter
  • GitHub Check: Tests
🔇 Additional comments (3)
tests/test_vote.rs (3)

162-218: Well-implemented vote expiration test

This test effectively validates the vote expiration functionality with proper setup, time manipulation, and specific error message verification. The test flow is logical and comprehensive.


220-289: Comprehensive vote withdrawal test

This test thoroughly validates the vote withdrawal functionality with proper verification of vote state before and after withdrawal operations. The test flow is well-structured and covers the withdrawal scenario comprehensively.


291-380: Excellent edge case coverage for unstaking after voting

This test effectively validates the important edge case where unstaking occurs after voting. It properly verifies that votes persist after unstaking and that subsequent votes correctly update the vote state. The test logic is sound and comprehensive.

Copy link

@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 (5)
tests/contracts/mock-staking-pool/src/lib.rs (3)

57-57: Pass a reference to insert instead of moving account_id.

LookupMap::insert expects &AccountId. Moving the value forces an allocation and breaks subsequent use. Use a reference:

- self.accounts.insert(account_id, balance + amount);
+ self.accounts.insert(&account_id, balance + amount);

71-71: Pass a reference to insert instead of moving account_id.

Same issue as in the deposit path - LookupMap::insert expects &AccountId.

- self.accounts.insert(account_id.clone(), balance - amount);
+ self.accounts.insert(&account_id, balance - amount);

108-108: internal_account_staked_balance dereferences an integer reference.

LookupMap::get returns Option<Balance>, not Option<&Balance>. The current code fails to compile (expected u128, found &u128).

- *self.accounts.get(account_id).unwrap_or(&0u128)
+ self.accounts.get(account_id).unwrap_or(0u128)
tests/test_vote.rs (2)

22-22: Fix redundant sandbox creation.

You're creating a second sandbox instance after already creating one at line 11. This is unnecessary and potentially confusing since the voting contract is deployed to the first sandbox.

- let sandbox = near_workspaces::sandbox().await?;
- let user_account = sandbox.dev_create_account().await?;
+ let user_account = sandbox.dev_create_account().await?;

59-72: Enhance test validation for vote recording.

The test successfully verifies the staked balance, but doesn't verify that the vote was actually recorded after calling the vote method. Adding verification of the vote state would make this test more comprehensive.

Add verification of the vote state after the vote operation:

    assert!(
        outcome.is_success(),
        "{:#?}",
        outcome.into_result().unwrap_err()
    );

+   // Verify the vote was recorded
+   let votes = voting_contract
+       .view("get_votes")
+       .await?
+       .json::<HashMap<AccountId, String>>()?;
+   assert!(votes.contains_key(staking_pool_contract.id()), "Vote not recorded");

    Ok(())
🧹 Nitpick comments (3)
tests/scripts/vote.sh (1)

5-5: Fix variable interpolation quoting.

The surrounding quotes actually unquote the variable, which can cause unexpected behavior if the variable contains spaces or special characters.

-    VALIDATOR_ID="mock-validator-"${i}".testnet"
+    VALIDATOR_ID="mock-validator-${i}.testnet"
🧰 Tools
🪛 Shellcheck (0.10.0)

[warning] 5-5: The surrounding quotes actually unquote this. Remove or escape them.

(SC2027)

tests/scripts/create-validators.sh (2)

7-7: Fix variable interpolation quoting.

The surrounding quotes actually unquote the variable, which can cause unexpected behavior if the variable contains spaces or special characters.

-    VALIDATOR_ID="mock-validator-"${i}".testnet"
+    VALIDATOR_ID="mock-validator-${i}.testnet"
🧰 Tools
🪛 Shellcheck (0.10.0)

[warning] 7-7: The surrounding quotes actually unquote this. Remove or escape them.

(SC2027)


13-13: Complete the incomplete comment.

The comment on line 13 appears to be incomplete and should be finished or removed.

-    # stake some NEAR to 
+    # stake some NEAR to the validator
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3776197 and 808ea28.

📒 Files selected for processing (5)
  • tests/contracts/mock-staking-pool/src/lib.rs (1 hunks)
  • tests/scripts/create-validators.sh (1 hunks)
  • tests/scripts/vote.sh (1 hunks)
  • tests/test_vote.rs (1 hunks)
  • tests/utils/mod.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/utils/mod.rs
🧰 Additional context used
🧬 Code Graph Analysis (3)
tests/scripts/create-validators.sh (1)
tests/contracts/mock-staking-pool/src/lib.rs (2)
  • new (40-48)
  • deposit_and_stake (51-61)
tests/test_vote.rs (1)
tests/utils/mod.rs (4)
  • deploy_voting_contract (24-45)
  • setup_env (112-131)
  • create_account (156-168)
  • setup_env_many (133-154)
tests/contracts/mock-staking-pool/src/lib.rs (2)
src/lib.rs (2)
  • vote (51-86)
  • set_validator_stake (171-173)
src/utils.rs (1)
  • set_validator_stake (43-56)
🪛 Shellcheck (0.10.0)
tests/scripts/vote.sh

[error] 1-1: Tips depend on target shell and yours is unknown. Add a shebang or a 'shell' directive.

(SC2148)


[warning] 5-5: The surrounding quotes actually unquote this. Remove or escape them.

(SC2027)

tests/scripts/create-validators.sh

[error] 1-1: Tips depend on target shell and yours is unknown. Add a shebang or a 'shell' directive.

(SC2148)


[warning] 7-7: The surrounding quotes actually unquote this. Remove or escape them.

(SC2027)

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Code Linter
  • GitHub Check: Tests
🔇 Additional comments (3)
tests/contracts/mock-staking-pool/src/lib.rs (1)

74-74: Excellent implementation of token transfer in unstake.

The unstake method now properly transfers tokens back to the user, addressing the previous concern about mock behavior consistency.

tests/test_vote.rs (2)

78-79: Well-handled long-running test optimization.

Good use of the #[ignore] attribute for the 50-minute test. This prevents it from running during regular development while still keeping it available for comprehensive testing when needed.


342-346: Good test coverage for unstaking behavior.

The test properly verifies that votes remain recorded even after unstaking, which is important for testing the voting contract's behavior with dynamic stake changes.

Copy link

@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: 0

🧹 Nitpick comments (4)
tests/test_staking_pool.rs (4)

9-26: Consider more robust balance assertions

The balance range check (8999..9000) may be too narrow and could lead to flaky tests if gas costs vary. Consider using a wider range or calculating expected balance more precisely.

-    assert!((8999..9000).contains(&balance));
+    // Account for gas fees with more generous range
+    assert!((8990..9010).contains(&balance), "Expected balance around 9000 NEAR, got {}", balance);

27-44: Enhance test coverage with staking pool state verification

While the balance check verifies unstaking worked, consider also verifying the staking pool's internal state (e.g., total staked amount) to ensure complete test coverage.

Add verification of staking pool state after unstaking:

     let balance = alice.view_account().await?.balance.as_near();
     assert!((9999..10000).contains(&balance));
+    
+    // Verify staking pool shows zero staked amount
+    let total_staked = staking_pool_contract
+        .view("get_total_staked_balance")
+        .await?
+        .json::<String>()?;
+    assert_eq!(total_staked, "0");

49-57: Extract duplicate timestamp logic to utility function

The timestamp calculation logic is duplicated from setup_env. Consider extracting this to a utility function for better maintainability.

In tests/utils/mod.rs, add:

pub fn get_future_timestamp_ms(minutes_ahead: u64) -> u64 {
    (SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap()
        .as_millis()
        + minutes_ahead * 60 * 1000) as u64
}

Then use it here:

-    let (new_voting_contract, _) = deploy_voting_contract(
-        &sandbox,
-        (SystemTime::now()
-            .duration_since(UNIX_EPOCH)
-            .unwrap()
-            .as_millis()
-            + 10 * 60 * 1000) as u64,
-    )
-    .await?;
+    let (new_voting_contract, _) = deploy_voting_contract(
+        &sandbox,
+        get_future_timestamp_ms(10),
+    )
+    .await?;

9-104: Consider extracting magic numbers to constants

The test uses magic numbers (1000, 10000) that could be made more maintainable as named constants.

Add constants at the top of the file:

 mod utils;
 
+const ALICE_INITIAL_BALANCE: u128 = 10000;
+const STAKE_AMOUNT: u128 = 1000;
+const GAS_LIMIT: u64 = 250;
+
 #[tokio::test]

Then use these constants throughout the tests to improve readability and maintainability.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between f3469c8 and a7e3f1f.

📒 Files selected for processing (1)
  • tests/test_staking_pool.rs (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
tests/test_staking_pool.rs (1)
tests/utils/mod.rs (3)
  • create_account (156-168)
  • deploy_voting_contract (24-45)
  • setup_env (112-131)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Tests
  • GitHub Check: Code Linter
🔇 Additional comments (2)
tests/test_staking_pool.rs (2)

1-7: LGTM: Clean imports and module structure

The imports are well-organized and appropriate for integration testing with NEAR blockchain. The local utils module import suggests good test code organization.


90-101: Good cross-contract state verification

Excellent test design! Verifying that the voting contract correctly shows the validator stake demonstrates proper integration between the staking pool and voting contracts.

Copy link

@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

🧹 Nitpick comments (7)
tests/scripts/create-proposal.sh (2)

1-1: Enforce strict mode and portable shebang
Use a more portable shebang and enable strict error handling to catch failures early:

-#!/bin/bash
+#!/usr/bin/env bash
+set -euo pipefail

This will make the script exit on errors, treat unset variables as failures, and handle pipes correctly.


10-10: Safely construct JSON args and verify deployment
Inline JSON concatenation risks injection and quoting bugs. Use a here-doc or jq to build the payload, and check the deployment’s exit status:

read -r -d '' INIT_ARGS <<EOF
{
  "proposal": "$PROPOSAL_DESCRIPTION",
  "deadline_timestamp_ms": $DEADLINE_TIMESTAMP
}
EOF

if ! near contract deploy \
  "$VOTING_ACCOUNT_ID" \
  use-file validator_voting.wasm \
  with-init-call new \
  json-args "$INIT_ARGS" \
  prepaid-gas '100.0 Tgas' \
  attached-deposit '0 NEAR' \
  network-config testnet \
  sign-with-legacy-keychain \
  send; then
  echo "ERROR: Contract deployment failed" >&2
  exit 1
fi

This improves readability, prevents JSON quoting pitfalls, and ensures failures are caught.

tests/scripts/vote.sh (2)

1-4: Enable strict error handling in the script.

Add set -euo pipefail right after the shebang to ensure the script exits on errors, undefined variables, and pipeline failures, preventing silent failures.


6-6: Refine variable assignment quoting.

Change:

VALIDATOR_ID="mock-validator-"${i}".testnet"

to:

VALIDATOR_ID="mock-validator-${i}.testnet"

This avoids unintentional word splitting and improves readability.

🧰 Tools
🪛 Shellcheck (0.10.0)

[warning] 6-6: The surrounding quotes actually unquote this. Remove or escape them.

(SC2027)

tests/scripts/create-validators.sh (3)

1-5: Enable strict error handling in the script.

Add set -euo pipefail after the shebang to catch errors, undefined variables, and pipeline failures early.


8-8: Refine variable assignment quoting.

Replace:

VALIDATOR_ID="mock-node-"${i}".testnet"

with:

VALIDATOR_ID="mock-node-${i}.testnet"

to ensure consistent quoting and prevent unexpected splitting.

🧰 Tools
🪛 Shellcheck (0.10.0)

[warning] 8-8: The surrounding quotes actually unquote this. Remove or escape them.

(SC2027)


10-10: Remove or update commented-out code.

The commented # near account create-account sponsor-by-faucet-service line appears outdated. Remove or update this example to keep the script clean and maintainable.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between a7e3f1f and d002a57.

📒 Files selected for processing (3)
  • tests/scripts/create-proposal.sh (1 hunks)
  • tests/scripts/create-validators.sh (1 hunks)
  • tests/scripts/vote.sh (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
tests/scripts/create-validators.sh (1)
tests/contracts/mock-staking-pool/src/lib.rs (2)
  • new (40-48)
  • deposit_and_stake (51-61)
🪛 Shellcheck (0.10.0)
tests/scripts/create-validators.sh

[warning] 8-8: The surrounding quotes actually unquote this. Remove or escape them.

(SC2027)

tests/scripts/vote.sh

[warning] 6-6: The surrounding quotes actually unquote this. Remove or escape them.

(SC2027)

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Tests
  • GitHub Check: Code Linter
🔇 Additional comments (1)
tests/scripts/create-validators.sh (1)

13-13: Verify the contract file path.

Ensure mock_staking_pool.wasm is correctly referenced relative to the script’s location. If the file resides elsewhere (e.g., under tests/contracts/mock-staking-pool/target/), update the path accordingly to avoid runtime errors.

@linguists linguists requested a review from arkff202310 June 3, 2025 04:01
@linguists linguists merged commit 8d2e760 into main Jun 3, 2025
7 checks passed
@linguists linguists deleted the test/integration-test branch June 3, 2025 04:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants