Skip to content

Commit 655fc1a

Browse files
authored
rpc: add unit tests for RpcModuleSelection (paradigmxyz#11883)
1 parent eee5e0d commit 655fc1a

File tree

1 file changed

+232
-3
lines changed

1 file changed

+232
-3
lines changed

crates/rpc/rpc-server-types/src/module.rs

Lines changed: 232 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,12 @@ impl FromStr for RpcModuleSelection {
199199
}
200200
let mut modules = s.split(',').map(str::trim).peekable();
201201
let first = modules.peek().copied().ok_or(ParseError::VariantNotFound)?;
202-
match first {
203-
"all" | "All" => Ok(Self::All),
204-
"none" | "None" => Ok(Self::Selection(Default::default())),
202+
// We convert to lowercase to make the comparison case-insensitive
203+
//
204+
// This is a way to allow typing "all" and "ALL" and "All" and "aLl" etc.
205+
match first.to_lowercase().as_str() {
206+
"all" => Ok(Self::All),
207+
"none" => Ok(Self::Selection(Default::default())),
205208
_ => Self::try_from_selection(modules),
206209
}
207210
}
@@ -329,3 +332,229 @@ impl Serialize for RethRpcModule {
329332
s.serialize_str(self.as_ref())
330333
}
331334
}
335+
336+
#[cfg(test)]
337+
mod test {
338+
use super::*;
339+
340+
#[test]
341+
fn test_all_modules() {
342+
let all_modules = RpcModuleSelection::all_modules();
343+
assert_eq!(all_modules.len(), RethRpcModule::variant_count());
344+
}
345+
346+
#[test]
347+
fn test_standard_modules() {
348+
let standard_modules = RpcModuleSelection::standard_modules();
349+
let expected_modules: HashSet<RethRpcModule> =
350+
HashSet::from([RethRpcModule::Eth, RethRpcModule::Net, RethRpcModule::Web3]);
351+
assert_eq!(standard_modules, expected_modules);
352+
}
353+
354+
#[test]
355+
fn test_default_ipc_modules() {
356+
let default_ipc_modules = RpcModuleSelection::default_ipc_modules();
357+
assert_eq!(default_ipc_modules, RpcModuleSelection::all_modules());
358+
}
359+
360+
#[test]
361+
fn test_try_from_selection_success() {
362+
let selection = vec!["eth", "admin"];
363+
let config = RpcModuleSelection::try_from_selection(selection).unwrap();
364+
assert_eq!(config, RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]));
365+
}
366+
367+
#[test]
368+
fn test_rpc_module_selection_len() {
369+
let all_modules = RpcModuleSelection::All;
370+
let standard = RpcModuleSelection::Standard;
371+
let selection = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]);
372+
373+
assert_eq!(all_modules.len(), RethRpcModule::variant_count());
374+
assert_eq!(standard.len(), 3);
375+
assert_eq!(selection.len(), 2);
376+
}
377+
378+
#[test]
379+
fn test_rpc_module_selection_is_empty() {
380+
let empty_selection = RpcModuleSelection::from(HashSet::new());
381+
assert!(empty_selection.is_empty());
382+
383+
let non_empty_selection = RpcModuleSelection::from([RethRpcModule::Eth]);
384+
assert!(!non_empty_selection.is_empty());
385+
}
386+
387+
#[test]
388+
fn test_rpc_module_selection_iter_selection() {
389+
let all_modules = RpcModuleSelection::All;
390+
let standard = RpcModuleSelection::Standard;
391+
let selection = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]);
392+
393+
assert_eq!(all_modules.iter_selection().count(), RethRpcModule::variant_count());
394+
assert_eq!(standard.iter_selection().count(), 3);
395+
assert_eq!(selection.iter_selection().count(), 2);
396+
}
397+
398+
#[test]
399+
fn test_rpc_module_selection_to_selection() {
400+
let all_modules = RpcModuleSelection::All;
401+
let standard = RpcModuleSelection::Standard;
402+
let selection = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]);
403+
404+
assert_eq!(all_modules.to_selection(), RpcModuleSelection::all_modules());
405+
assert_eq!(standard.to_selection(), RpcModuleSelection::standard_modules());
406+
assert_eq!(
407+
selection.to_selection(),
408+
HashSet::from([RethRpcModule::Eth, RethRpcModule::Admin])
409+
);
410+
}
411+
412+
#[test]
413+
fn test_rpc_module_selection_are_identical() {
414+
// Test scenario: both selections are `All`
415+
//
416+
// Since both selections include all possible RPC modules, they should be considered
417+
// identical.
418+
let all_modules = RpcModuleSelection::All;
419+
assert!(RpcModuleSelection::are_identical(Some(&all_modules), Some(&all_modules)));
420+
421+
// Test scenario: both `http` and `ws` are `None`
422+
//
423+
// When both arguments are `None`, the function should return `true` because no modules are
424+
// selected.
425+
assert!(RpcModuleSelection::are_identical(None, None));
426+
427+
// Test scenario: both selections contain identical sets of specific modules
428+
//
429+
// In this case, both selections contain the same modules (`Eth` and `Admin`),
430+
// so they should be considered identical.
431+
let selection1 = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]);
432+
let selection2 = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]);
433+
assert!(RpcModuleSelection::are_identical(Some(&selection1), Some(&selection2)));
434+
435+
// Test scenario: one selection is `All`, the other is `Standard`
436+
//
437+
// `All` includes all possible modules, while `Standard` includes a specific set of modules.
438+
// Since `Standard` does not cover all modules, these two selections should not be
439+
// considered identical.
440+
let standard = RpcModuleSelection::Standard;
441+
assert!(!RpcModuleSelection::are_identical(Some(&all_modules), Some(&standard)));
442+
443+
// Test scenario: one is `None`, the other is an empty selection
444+
//
445+
// When one selection is `None` and the other is an empty selection (no modules),
446+
// they should be considered identical because neither selects any modules.
447+
let empty_selection = RpcModuleSelection::Selection(HashSet::new());
448+
assert!(RpcModuleSelection::are_identical(None, Some(&empty_selection)));
449+
assert!(RpcModuleSelection::are_identical(Some(&empty_selection), None));
450+
451+
// Test scenario: one is `None`, the other is a non-empty selection
452+
//
453+
// If one selection is `None` and the other contains modules, they should not be considered
454+
// identical because `None` represents no selection, while the other explicitly
455+
// selects modules.
456+
let non_empty_selection = RpcModuleSelection::from([RethRpcModule::Eth]);
457+
assert!(!RpcModuleSelection::are_identical(None, Some(&non_empty_selection)));
458+
assert!(!RpcModuleSelection::are_identical(Some(&non_empty_selection), None));
459+
460+
// Test scenario: `All` vs. non-full selection
461+
//
462+
// If one selection is `All` (which includes all modules) and the other contains only a
463+
// subset of modules, they should not be considered identical.
464+
let partial_selection = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Net]);
465+
assert!(!RpcModuleSelection::are_identical(Some(&all_modules), Some(&partial_selection)));
466+
467+
// Test scenario: full selection vs `All`
468+
//
469+
// If the other selection explicitly selects all available modules, it should be identical
470+
// to `All`.
471+
let full_selection =
472+
RpcModuleSelection::from(RethRpcModule::modules().into_iter().collect::<HashSet<_>>());
473+
assert!(RpcModuleSelection::are_identical(Some(&all_modules), Some(&full_selection)));
474+
475+
// Test scenario: different non-empty selections
476+
//
477+
// If the two selections contain different sets of modules, they should not be considered
478+
// identical.
479+
let selection3 = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Net]);
480+
let selection4 = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Web3]);
481+
assert!(!RpcModuleSelection::are_identical(Some(&selection3), Some(&selection4)));
482+
483+
// Test scenario: `Standard` vs an equivalent selection
484+
// The `Standard` selection includes a predefined set of modules. If we explicitly create
485+
// a selection with the same set of modules, they should be considered identical.
486+
let matching_standard =
487+
RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Net, RethRpcModule::Web3]);
488+
assert!(RpcModuleSelection::are_identical(Some(&standard), Some(&matching_standard)));
489+
490+
// Test scenario: `Standard` vs non-matching selection
491+
//
492+
// If the selection does not match the modules included in `Standard`, they should not be
493+
// considered identical.
494+
let non_matching_standard =
495+
RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Net]);
496+
assert!(!RpcModuleSelection::are_identical(Some(&standard), Some(&non_matching_standard)));
497+
}
498+
499+
#[test]
500+
fn test_rpc_module_selection_from_str() {
501+
// Test empty string returns default selection
502+
let result = RpcModuleSelection::from_str("");
503+
assert!(result.is_ok());
504+
assert_eq!(result.unwrap(), RpcModuleSelection::Selection(Default::default()));
505+
506+
// Test "all" (case insensitive) returns All variant
507+
let result = RpcModuleSelection::from_str("all");
508+
assert!(result.is_ok());
509+
assert_eq!(result.unwrap(), RpcModuleSelection::All);
510+
511+
let result = RpcModuleSelection::from_str("All");
512+
assert!(result.is_ok());
513+
assert_eq!(result.unwrap(), RpcModuleSelection::All);
514+
515+
let result = RpcModuleSelection::from_str("ALL");
516+
assert!(result.is_ok());
517+
assert_eq!(result.unwrap(), RpcModuleSelection::All);
518+
519+
// Test "none" (case insensitive) returns empty selection
520+
let result = RpcModuleSelection::from_str("none");
521+
assert!(result.is_ok());
522+
assert_eq!(result.unwrap(), RpcModuleSelection::Selection(Default::default()));
523+
524+
let result = RpcModuleSelection::from_str("None");
525+
assert!(result.is_ok());
526+
assert_eq!(result.unwrap(), RpcModuleSelection::Selection(Default::default()));
527+
528+
let result = RpcModuleSelection::from_str("NONE");
529+
assert!(result.is_ok());
530+
assert_eq!(result.unwrap(), RpcModuleSelection::Selection(Default::default()));
531+
532+
// Test valid selections: "eth,admin"
533+
let result = RpcModuleSelection::from_str("eth,admin");
534+
assert!(result.is_ok());
535+
let expected_selection =
536+
RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]);
537+
assert_eq!(result.unwrap(), expected_selection);
538+
539+
// Test valid selection with extra spaces: " eth , admin "
540+
let result = RpcModuleSelection::from_str(" eth , admin ");
541+
assert!(result.is_ok());
542+
assert_eq!(result.unwrap(), expected_selection);
543+
544+
// Test invalid selection should return error
545+
let result = RpcModuleSelection::from_str("invalid,unknown");
546+
assert!(result.is_err());
547+
assert_eq!(result.unwrap_err(), ParseError::VariantNotFound);
548+
549+
// Test single valid selection: "eth"
550+
let result = RpcModuleSelection::from_str("eth");
551+
assert!(result.is_ok());
552+
let expected_selection = RpcModuleSelection::from([RethRpcModule::Eth]);
553+
assert_eq!(result.unwrap(), expected_selection);
554+
555+
// Test single invalid selection: "unknown"
556+
let result = RpcModuleSelection::from_str("unknown");
557+
assert!(result.is_err());
558+
assert_eq!(result.unwrap_err(), ParseError::VariantNotFound);
559+
}
560+
}

0 commit comments

Comments
 (0)