Skip to content
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

feat: improved mock server for unit and provider testing #1952

Open
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

0xAlcibiades
Copy link

@0xAlcibiades 0xAlcibiades commented Jan 25, 2025

Motivation

As a user of the ecosystem, I need a robust way to unit test code without having to connect to a node or fork alloy, for example when I need the presence of a provider I control the responses of. The existing mock provider was limited in that it could not handle concurrent requests, nor did it have a proper handle for management.

Solution

This pull request implements a much improved mock provider that can be used, and is usable, for flexible unit tests across the ecosystem where a provider is involved.

Additionally, there are tests, which could be included here at will.

#[cfg(test)]
mod tests {
    use super::*;
    use crate::test_utils::RUSTLS;
    use alloy::primitives::U256;
    use alloy::providers::{Provider, ProviderBuilder};
    use alloy::transports::ipc::IpcConnect;
    use once_cell::sync::Lazy;
    use serde_json::json;
    use telemetry::TRACING;

    /// Test the basic functionality of the mock IPC server:
    /// - Server creation and startup
    /// - Adding a response
    /// - Connecting a provider
    /// - Making a request and receiving the mocked response
    /// - Clean shutdown
    #[tokio::test(flavor = "multi_thread")]
    async fn test_mock_ipc_server() -> eyre::Result<()> {
        // Initialize logging
        Lazy::force(&RUSTLS);
        Lazy::force(&TRACING);

        // Create and start the mock server
        let server = MockIpcServer::new();
        let path = server.path();
        let handle = server.spawn().await?;

        // Queue a mock response for eth_getBalance
        handle
            .add_reply(json!({
                "jsonrpc": "2.0",
                "id": 0,
                "result": "0x0000000000000000000000000000000000000000000000000000000000000064"
            }))
            .await;

        // Create a provider connected to our mock
        let provider = ProviderBuilder::new().on_ipc(IpcConnect::new(path)).await?;

        // Make a request and verify the response
        let balance = provider
            .get_balance("0x742d35Cc6634C0532925a3b844Bc454e4438f44e".parse()?)
            .await?;

        assert_eq!(balance, U256::from(100));

        // Clean shutdown
        handle.shutdown().await;
        Ok(())
    }
}

However, I didn't want to presume to add dependencies to this crate.

PR Checklist

  • Added Tests
  • Added Documentation
  • Breaking changes

Copy link
Member

@mattsse mattsse left a comment

Choose a reason for hiding this comment

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

supportive

some suggestions

crates/transport-ipc/src/mock.rs Outdated Show resolved Hide resolved
crates/transport-ipc/src/mock.rs Outdated Show resolved Hide resolved
crates/transport-ipc/src/mock.rs Outdated Show resolved Hide resolved
crates/transport-ipc/src/mock.rs Outdated Show resolved Hide resolved
crates/transport-ipc/src/mock.rs Outdated Show resolved Hide resolved
@0xAlcibiades 0xAlcibiades requested a review from mattsse January 25, 2025 11:34
Copy link
Member

@mattsse mattsse left a comment

Choose a reason for hiding this comment

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

smol pedantic nit

crates/transport-ipc/src/mock.rs Outdated Show resolved Hide resolved
@0xAlcibiades 0xAlcibiades requested a review from mattsse January 28, 2025 15:32
@0xAlcibiades 0xAlcibiades changed the title WIP: feat: improved mock server for unit and provider testing feat: improved mock server for unit and provider testing Jan 28, 2025
Copy link
Member

@mattsse mattsse left a comment

Choose a reason for hiding this comment

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

last suggestion to remove channel because none of this does IO

Comment on lines +224 to +236
// Spawn a new task to handle this request
request_handlers.push(tokio::spawn(async move {
// Get the next queued response or generate an error
let response = inner.replies.lock().pop_front().map_or_else(|| {
warn!("No queued response available for request");
serde_json::to_vec(&json!({
"jsonrpc": "2.0",
"id": request.get("id"),
"error": {
"code": -32603,
"message": "No response queued"
}
})).expect("JSON serialization cannot fail")
Copy link
Member

Choose a reason for hiding this comment

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

imo we can get rid of this additional roundtrip entirely.
we don't need this and can write the response right away.

// Spawn a new task to handle this request
request_handlers.push(tokio::spawn(async move {
// Get the next queued response or generate an error
let response = inner.replies.lock().pop_front().map_or_else(|| {
Copy link
Member

Choose a reason for hiding this comment

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

this is also problematic because there's no guarantee that this task will pop the item that was at first position when the task was spawned

@mattsse
Copy link
Member

mattsse commented Jan 30, 2025

ah one more problem here is that this uses unix socket which is incompatible with windows,

we have LocalSocketStream for that

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