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