Skip to content

feat : allow rpc mock to insert multiple duplicate requests #5757

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

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion rpc-client/src/mock_sender.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,40 @@ use {

pub const PUBKEY: &str = "7RoSF9fUmdphVCpabEoefH81WwrW7orsWonXWqTXkKV8";

pub type Mocks = HashMap<RpcRequest, Value>;
#[derive(Default)]
pub struct Mocks(Vec<(RpcRequest, Value)>);
Comment on lines +44 to +45

Choose a reason for hiding this comment

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

How about doing HashMap<RpcRequest, Vec<Value>> to get the best of both worlds?

Also, since this is a breaking change, we need to be a bit more intentional with the change.

Here are my thoughts for going forward with this without breaking people:

  • keep the old pub type Mocks, maybe mark it deprecated
  • add a new pub struct MocksMap(HashMap<RpcRequest, Vec<Value>>), which can always be used internally to the mock sender
  • add a new function to rpc_client.rs and nonblocking/rpc_client.rs to instantiate with MocksMap, which we call new_with_mocks_map.
  • update the old constructors to convert from Mocks to MocksMap internally. We could also deprecate these constructors, but it isn't necessary

I think with that, we should be all good!

Copy link
Author

Choose a reason for hiding this comment

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

Makes sense , will make these changes ty


impl Mocks {
pub fn new() -> Self {
Self(Vec::new())
}

pub fn from<const N: usize>(mocks: [(RpcRequest, Value); N]) -> Self {
Self(mocks.to_vec())
}

pub fn insert(&mut self, request: RpcRequest, response: Value) {
self.0.push((request, response));
}

pub fn remove(&mut self, request: &RpcRequest) -> Option<Value> {
self.0
.iter()
.position(|(r, _)| r == request)
.map(|index| self.0.remove(index).1)
}
}

impl FromIterator<(RpcRequest, Value)> for Mocks {
fn from_iter<T: IntoIterator<Item = (RpcRequest, Value)>>(iter: T) -> Self {
let mut mocks = Mocks::new();
for (request, response) in iter {
mocks.insert(request, response);
}
mocks
}
}

pub struct MockSender {
mocks: RwLock<Mocks>,
url: String,
Expand Down
2 changes: 1 addition & 1 deletion rpc-client/src/nonblocking/rpc_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4707,7 +4707,7 @@ pub(crate) fn parse_keyed_accounts(

#[doc(hidden)]
pub fn create_rpc_client_mocks() -> crate::mock_sender::Mocks {
let mut mocks = std::collections::HashMap::new();
let mut mocks = crate::mock_sender::Mocks::new();

let get_account_request = RpcRequest::GetAccountInfo;
let get_account_response = serde_json::to_value(Response {
Expand Down
74 changes: 71 additions & 3 deletions rpc-client/src/rpc_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3677,7 +3677,7 @@ impl RpcClient {
/// Mocks for documentation examples
#[doc(hidden)]
pub fn create_rpc_client_mocks() -> crate::mock_sender::Mocks {
let mut mocks = std::collections::HashMap::new();
let mut mocks = crate::mock_sender::Mocks::new();

let get_account_request = RpcRequest::GetAccountInfo;
let get_account_response = serde_json::to_value(Response {
Expand Down Expand Up @@ -3948,7 +3948,7 @@ mod tests {
pubkey: pubkey.to_string(),
account: encode_ui_account(&pubkey, &account, UiAccountEncoding::Base64, None, None),
};
let expected_result = vec![(pubkey, account)];
let expected_result = vec![(pubkey, account.clone())];
// Test: without context
{
let mocks: Mocks = [(
Expand Down Expand Up @@ -3987,7 +3987,7 @@ mod tests {
slot: 1,
api_version: None,
},
value: vec![keyed_account],
value: vec![keyed_account.clone()],
}))
.unwrap(),
)]
Expand All @@ -4012,5 +4012,73 @@ mod tests {
.unwrap();
assert_eq!(expected_result, result);
}

// Test: Mock with duplicate requests
{
let expected_result = vec![(pubkey, account.clone()), (pubkey, account.clone())];
let mocks: Mocks = [
(
RpcRequest::GetProgramAccounts,
serde_json::to_value(OptionalContext::Context(Response {
context: RpcResponseContext {
slot: 1,
api_version: None,
},
value: vec![keyed_account.clone()],
}))
.unwrap(),
),
(
RpcRequest::GetProgramAccounts,
serde_json::to_value(OptionalContext::Context(Response {
context: RpcResponseContext {
slot: 1,
api_version: None,
},
value: vec![keyed_account.clone()],
}))
.unwrap(),
),
]
.into_iter()
.collect();
let rpc_client = RpcClient::new_mock_with_mocks("mock_client".to_string(), mocks);
let mut result1 = rpc_client
.get_program_accounts_with_config(
&program_id,
RpcProgramAccountsConfig {
filters: None,
account_config: RpcAccountInfoConfig {
encoding: Some(UiAccountEncoding::Base64),
data_slice: None,
commitment: None,
min_context_slot: None,
},
with_context: Some(true),
sort_results: None,
},
)
.unwrap();

let result2 = rpc_client
.get_program_accounts_with_config(
&program_id,
RpcProgramAccountsConfig {
filters: None,
account_config: RpcAccountInfoConfig {
encoding: Some(UiAccountEncoding::Base64),
data_slice: None,
commitment: None,
min_context_slot: None,
},
with_context: Some(true),
sort_results: None,
},
)
.unwrap();

result1.extend(result2);
assert_eq!(expected_result, result1);
}
}
}