openzeppelin_relayer/domain/relayer/evm/
rpc_utils.rs

1//! Utilities for EVM JSON-RPC response handling and error mapping.
2//!
3//! This module provides helper functions for creating standardized JSON-RPC 2.0 responses
4//! and mapping internal provider errors to appropriate error codes and messages.
5//!
6//! # Main Features
7//!
8//! - Create success and error responses following JSON-RPC 2.0 specification
9//! - Map provider errors to standardized JSON-RPC error codes
10//! - Handle EVM-specific result types and error formatting
11
12use crate::{
13    models::{EvmRpcResult, NetworkRpcResult, OpenZeppelinErrorCodes, RpcErrorCodes},
14    models::{JsonRpcError, JsonRpcId, JsonRpcResponse},
15    services::ProviderError,
16};
17use serde_json;
18
19/// Creates an error response following the JSON-RPC 2.0 specification.
20///
21/// # Arguments
22///
23/// * `id` - The request ID from the original JSON-RPC request
24/// * `code` - The error code (should follow JSON-RPC error code conventions)
25/// * `message` - A short, human-readable error message
26/// * `description` - A more detailed description of the error
27///
28/// # Returns
29///
30/// Returns a `JsonRpcResponse<NetworkRpcResult>` containing the error details
31/// and no result data.
32pub fn create_error_response(
33    id: Option<JsonRpcId>,
34    code: i32,
35    message: &str,
36    description: &str,
37) -> JsonRpcResponse<NetworkRpcResult> {
38    JsonRpcResponse {
39        id,
40        jsonrpc: "2.0".to_string(),
41        result: None,
42        error: Some(JsonRpcError {
43            code,
44            message: message.to_string(),
45            description: description.to_string(),
46        }),
47    }
48}
49
50/// Creates a success response following the JSON-RPC 2.0 specification.
51///
52/// # Arguments
53///
54/// * `id` - The request ID from the original JSON-RPC request
55/// * `result` - The result data to include in the response as a JSON value
56///
57/// # Returns
58///
59/// Returns a `JsonRpcResponse<NetworkRpcResult>` containing the result data
60/// wrapped in an EVM-specific result type, with no error information.
61pub fn create_success_response(
62    id: Option<JsonRpcId>,
63    result: serde_json::Value,
64) -> JsonRpcResponse<NetworkRpcResult> {
65    JsonRpcResponse {
66        id,
67        jsonrpc: "2.0".to_string(),
68        result: Some(NetworkRpcResult::Evm(EvmRpcResult::RawRpcResult(result))),
69        error: None,
70    }
71}
72
73/// Maps provider errors to appropriate JSON-RPC error codes and messages.
74///
75/// This function translates internal provider errors into standardized
76/// JSON-RPC error codes and user-friendly messages that can be returned
77/// to clients. It follows JSON-RPC 2.0 specification for standard errors
78/// and uses OpenZeppelin-specific codes for extended functionality.
79///
80/// # Arguments
81///
82/// * `error` - A reference to the provider error to be mapped
83///
84/// # Returns
85///
86/// Returns a tuple containing:
87/// - `i32` - The error code (following JSON-RPC 2.0 and OpenZeppelin conventions)
88/// - `&'static str` - A static string describing the error type
89///
90/// # Error Code Mappings
91///
92/// - `InvalidAddress` → -32602 ("Invalid params")
93/// - `NetworkConfiguration` → -33004 ("Network configuration error")
94/// - `Timeout` → -33000 ("Request timeout")
95/// - `RateLimited` → -33001 ("Rate limited")
96/// - `BadGateway` → -33002 ("Bad gateway")
97/// - `RequestError` → -33003 ("Request error")
98/// - `Other` and unknown errors → -32603 ("Internal error")
99pub fn map_provider_error(error: &ProviderError) -> (i32, &'static str) {
100    match error {
101        ProviderError::InvalidAddress(_) => (RpcErrorCodes::INVALID_PARAMS, "Invalid params"),
102        ProviderError::NetworkConfiguration(_) => (
103            OpenZeppelinErrorCodes::NETWORK_CONFIGURATION,
104            "Network configuration error",
105        ),
106        ProviderError::Timeout => (OpenZeppelinErrorCodes::TIMEOUT, "Request timeout"),
107        ProviderError::RateLimited => (OpenZeppelinErrorCodes::RATE_LIMITED, "Rate limited"),
108        ProviderError::BadGateway => (OpenZeppelinErrorCodes::BAD_GATEWAY, "Bad gateway"),
109        ProviderError::RequestError { .. } => {
110            (OpenZeppelinErrorCodes::REQUEST_ERROR, "Request error")
111        }
112        ProviderError::Other(_) => (RpcErrorCodes::INTERNAL_ERROR, "Internal error"),
113        _ => (RpcErrorCodes::INTERNAL_ERROR, "Internal error"),
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120    use crate::models::{OpenZeppelinErrorCodes, RpcErrorCodes};
121    use crate::services::{provider::rpc_selector::RpcSelectorError, SolanaProviderError};
122    use serde_json::json;
123
124    #[test]
125    fn test_create_error_response_basic() {
126        let response = create_error_response(
127            Some(JsonRpcId::Number(123)),
128            -32602,
129            "Invalid params",
130            "The provided parameters are invalid",
131        );
132
133        assert_eq!(response.id, Some(JsonRpcId::Number(123)));
134        assert_eq!(response.jsonrpc, "2.0");
135        assert!(response.result.is_none());
136        assert!(response.error.is_some());
137
138        let error = response.error.unwrap();
139        assert_eq!(error.code, -32602);
140        assert!(!error.message.is_empty());
141        assert!(!error.description.is_empty());
142    }
143
144    #[test]
145    fn test_create_error_response_zero_id() {
146        let response = create_error_response(
147            Some(JsonRpcId::Number(0)),
148            -32603,
149            "Internal error",
150            "Something went wrong",
151        );
152
153        assert_eq!(response.id, Some(JsonRpcId::Number(0)));
154        assert_eq!(response.jsonrpc, "2.0");
155        assert!(response.result.is_none());
156        assert!(response.error.is_some());
157    }
158
159    #[test]
160    fn test_create_error_response_max_id() {
161        let response = create_error_response(
162            Some(JsonRpcId::Number(u64::MAX as i64)),
163            -32700,
164            "Parse error",
165            "JSON parsing failed",
166        );
167
168        assert_eq!(response.id, Some(JsonRpcId::Number(u64::MAX as i64)));
169        assert_eq!(response.jsonrpc, "2.0");
170        assert!(response.result.is_none());
171        assert!(response.error.is_some());
172    }
173
174    #[test]
175    fn test_create_error_response_empty_message() {
176        let response = create_error_response(
177            Some(JsonRpcId::Number(42)),
178            -32601,
179            "",
180            "Method not found error",
181        );
182
183        assert_eq!(response.id, Some(JsonRpcId::Number(42)));
184        let error = response.error.unwrap();
185        assert!(error.message.is_empty());
186        assert!(!error.description.is_empty());
187    }
188
189    #[test]
190    fn test_create_error_response_empty_description() {
191        let response =
192            create_error_response(Some(JsonRpcId::Number(99)), -32600, "Invalid Request", "");
193
194        assert_eq!(response.id, Some(JsonRpcId::Number(99)));
195        let error = response.error.unwrap();
196        assert!(!error.message.is_empty());
197        assert!(error.description.is_empty());
198    }
199
200    #[test]
201    fn test_create_error_response_preserves_input() {
202        let message = "Error with unicode: 🚨 ñáéíóú";
203        let description = "Description with symbols: @#$%^&*()";
204        let response =
205            create_error_response(Some(JsonRpcId::Number(500)), -33000, message, description);
206
207        let error = response.error.unwrap();
208        assert!(!error.message.is_empty());
209        assert!(!error.description.is_empty());
210        assert_eq!(error.code, -33000);
211    }
212
213    #[test]
214    fn test_create_error_response_long_strings() {
215        let long_message = "a".repeat(1000);
216        let long_description = "b".repeat(2000);
217        let response = create_error_response(
218            Some(JsonRpcId::Number(777)),
219            -33001,
220            &long_message,
221            &long_description,
222        );
223
224        let error = response.error.unwrap();
225        assert_eq!(error.message.len(), 1000);
226        assert_eq!(error.description.len(), 2000);
227    }
228
229    #[test]
230    fn test_create_error_response_custom_openzeppelin_codes() {
231        let test_cases = vec![
232            OpenZeppelinErrorCodes::TIMEOUT,
233            OpenZeppelinErrorCodes::RATE_LIMITED,
234            OpenZeppelinErrorCodes::BAD_GATEWAY,
235            OpenZeppelinErrorCodes::REQUEST_ERROR,
236            OpenZeppelinErrorCodes::NETWORK_CONFIGURATION,
237        ];
238
239        for code in test_cases {
240            let response = create_error_response(
241                Some(JsonRpcId::Number(1)),
242                code,
243                "Test message",
244                "Test description",
245            );
246            let error = response.error.unwrap();
247            assert_eq!(error.code, code);
248            assert!(!error.message.is_empty());
249            assert!(!error.description.is_empty());
250        }
251    }
252
253    #[test]
254    fn test_create_error_response_standard_json_rpc_codes() {
255        let test_cases = vec![
256            RpcErrorCodes::PARSE,
257            RpcErrorCodes::INVALID_REQUEST,
258            RpcErrorCodes::METHOD_NOT_FOUND,
259            RpcErrorCodes::INVALID_PARAMS,
260            RpcErrorCodes::INTERNAL_ERROR,
261        ];
262
263        for code in test_cases {
264            let response = create_error_response(
265                Some(JsonRpcId::Number(1)),
266                code,
267                "Test message",
268                "Test description",
269            );
270            let error = response.error.unwrap();
271            assert_eq!(error.code, code);
272            assert!(!error.message.is_empty());
273            assert!(!error.description.is_empty());
274        }
275    }
276
277    #[test]
278    fn test_create_success_response_basic() {
279        let result_data = json!({
280            "blockNumber": "0x1234",
281            "hash": "0xabcd"
282        });
283
284        let response = create_success_response(Some(JsonRpcId::Number(456)), result_data.clone());
285
286        assert_eq!(response.id, Some(JsonRpcId::Number(456)));
287        assert_eq!(response.jsonrpc, "2.0");
288        assert!(response.error.is_none());
289        assert!(response.result.is_some());
290
291        match response.result.unwrap() {
292            NetworkRpcResult::Evm(EvmRpcResult::RawRpcResult(value)) => {
293                assert_eq!(value, result_data);
294            }
295            _ => unreachable!("Expected EVM RawRpcResult"),
296        }
297    }
298
299    #[test]
300    fn test_create_success_response_zero_id() {
301        let result_data = json!(null);
302        let response = create_success_response(Some(JsonRpcId::Number(0)), result_data);
303
304        assert_eq!(response.id, Some(JsonRpcId::Number(0)));
305        assert_eq!(response.jsonrpc, "2.0");
306        assert!(response.error.is_none());
307    }
308
309    #[test]
310    fn test_create_success_response_max_id() {
311        let result_data = json!(42);
312        let response =
313            create_success_response(Some(JsonRpcId::Number(u64::MAX as i64)), result_data);
314
315        assert_eq!(response.id, Some(JsonRpcId::Number(u64::MAX as i64)));
316        assert_eq!(response.jsonrpc, "2.0");
317        assert!(response.error.is_none());
318    }
319
320    #[test]
321    fn test_create_success_response_null_result() {
322        let result_data = json!(null);
323        let response = create_success_response(Some(JsonRpcId::Number(100)), result_data.clone());
324
325        match response.result.unwrap() {
326            NetworkRpcResult::Evm(EvmRpcResult::RawRpcResult(value)) => {
327                assert_eq!(value, result_data);
328                assert!(value.is_null());
329            }
330            _ => unreachable!("Expected EVM RawRpcResult"),
331        }
332    }
333
334    #[test]
335    fn test_create_success_response_boolean_result() {
336        let result_data = json!(true);
337        let response = create_success_response(Some(JsonRpcId::Number(200)), result_data.clone());
338
339        match response.result.unwrap() {
340            NetworkRpcResult::Evm(EvmRpcResult::RawRpcResult(value)) => {
341                assert_eq!(value, result_data);
342                assert!(value.is_boolean());
343            }
344            _ => unreachable!("Expected EVM RawRpcResult"),
345        }
346    }
347
348    #[test]
349    fn test_create_success_response_number_result() {
350        let result_data = json!(12345);
351        let response = create_success_response(Some(JsonRpcId::Number(300)), result_data.clone());
352
353        match response.result.unwrap() {
354            NetworkRpcResult::Evm(EvmRpcResult::RawRpcResult(value)) => {
355                assert_eq!(value, result_data);
356                assert!(value.is_number());
357            }
358            _ => unreachable!("Expected EVM RawRpcResult"),
359        }
360    }
361
362    #[test]
363    fn test_create_success_response_string_result() {
364        let result_data = json!("test string");
365        let response = create_success_response(Some(JsonRpcId::Number(400)), result_data.clone());
366
367        match response.result.unwrap() {
368            NetworkRpcResult::Evm(EvmRpcResult::RawRpcResult(value)) => {
369                assert_eq!(value, result_data);
370                assert!(value.is_string());
371            }
372            _ => unreachable!("Expected EVM RawRpcResult"),
373        }
374    }
375
376    #[test]
377    fn test_create_success_response_array_result() {
378        let result_data = json!([1, 2, 3, "test", true, null]);
379        let response = create_success_response(Some(JsonRpcId::Number(500)), result_data.clone());
380
381        match response.result.unwrap() {
382            NetworkRpcResult::Evm(EvmRpcResult::RawRpcResult(value)) => {
383                assert_eq!(value, result_data);
384                assert!(value.is_array());
385                assert_eq!(value.as_array().unwrap().len(), 6);
386            }
387            _ => unreachable!("Expected EVM RawRpcResult"),
388        }
389    }
390
391    #[test]
392    fn test_create_success_response_complex_object() {
393        let result_data = json!({
394            "transactions": [
395                {"hash": "0x123", "value": "1000000000000000000"},
396                {"hash": "0x456", "value": "2000000000000000000"}
397            ],
398            "blockNumber": "0x1a2b3c",
399            "timestamp": 1234567890,
400            "gasUsed": "0x5208",
401            "metadata": {
402                "network": "mainnet",
403                "version": "1.0",
404                "features": ["eip1559", "eip2930"]
405            },
406            "isEmpty": false,
407            "nullField": null
408        });
409
410        let response = create_success_response(Some(JsonRpcId::Number(600)), result_data.clone());
411
412        match response.result.unwrap() {
413            NetworkRpcResult::Evm(EvmRpcResult::RawRpcResult(value)) => {
414                assert_eq!(value, result_data);
415                assert!(value.is_object());
416                assert_eq!(value["blockNumber"], "0x1a2b3c");
417                assert_eq!(value["transactions"].as_array().unwrap().len(), 2);
418                assert!(value["nullField"].is_null());
419            }
420            _ => unreachable!("Expected EVM RawRpcResult"),
421        }
422    }
423
424    #[test]
425    fn test_create_success_response_large_object() {
426        let mut large_object = json!({});
427        let object = large_object.as_object_mut().unwrap();
428
429        // Create a large object with many fields
430        for i in 0..100 {
431            object.insert(format!("field_{}", i), json!(format!("value_{}", i)));
432        }
433
434        let response = create_success_response(Some(JsonRpcId::Number(700)), large_object.clone());
435
436        match response.result.unwrap() {
437            NetworkRpcResult::Evm(EvmRpcResult::RawRpcResult(value)) => {
438                assert_eq!(value, large_object);
439                assert_eq!(value.as_object().unwrap().len(), 100);
440            }
441            _ => unreachable!("Expected EVM RawRpcResult"),
442        }
443    }
444
445    #[test]
446    fn test_map_provider_error_invalid_address() {
447        let error = ProviderError::InvalidAddress("invalid address".to_string());
448        let (code, _message) = map_provider_error(&error);
449
450        assert_eq!(code, RpcErrorCodes::INVALID_PARAMS);
451    }
452
453    #[test]
454    fn test_map_provider_error_invalid_address_empty() {
455        let error = ProviderError::InvalidAddress("".to_string());
456        let (code, _message) = map_provider_error(&error);
457
458        assert_eq!(code, RpcErrorCodes::INVALID_PARAMS);
459    }
460
461    #[test]
462    fn test_map_provider_error_network_configuration() {
463        let error = ProviderError::NetworkConfiguration("network config error".to_string());
464        let (code, _message) = map_provider_error(&error);
465
466        assert_eq!(code, OpenZeppelinErrorCodes::NETWORK_CONFIGURATION);
467    }
468
469    #[test]
470    fn test_map_provider_error_network_configuration_empty() {
471        let error = ProviderError::NetworkConfiguration("".to_string());
472        let (code, _message) = map_provider_error(&error);
473
474        assert_eq!(code, OpenZeppelinErrorCodes::NETWORK_CONFIGURATION);
475    }
476
477    #[test]
478    fn test_map_provider_error_timeout() {
479        let error = ProviderError::Timeout;
480        let (code, _message) = map_provider_error(&error);
481
482        assert_eq!(code, OpenZeppelinErrorCodes::TIMEOUT);
483    }
484
485    #[test]
486    fn test_map_provider_error_rate_limited() {
487        let error = ProviderError::RateLimited;
488        let (code, _message) = map_provider_error(&error);
489
490        assert_eq!(code, OpenZeppelinErrorCodes::RATE_LIMITED);
491    }
492
493    #[test]
494    fn test_map_provider_error_bad_gateway() {
495        let error = ProviderError::BadGateway;
496        let (code, _message) = map_provider_error(&error);
497
498        assert_eq!(code, OpenZeppelinErrorCodes::BAD_GATEWAY);
499    }
500
501    #[test]
502    fn test_map_provider_error_request_error_400() {
503        let error = ProviderError::RequestError {
504            error: "Bad request".to_string(),
505            status_code: 400,
506        };
507        let (code, _message) = map_provider_error(&error);
508
509        assert_eq!(code, OpenZeppelinErrorCodes::REQUEST_ERROR);
510    }
511
512    #[test]
513    fn test_map_provider_error_request_error_500() {
514        let error = ProviderError::RequestError {
515            error: "Internal server error".to_string(),
516            status_code: 500,
517        };
518        let (code, _message) = map_provider_error(&error);
519
520        assert_eq!(code, OpenZeppelinErrorCodes::REQUEST_ERROR);
521    }
522
523    #[test]
524    fn test_map_provider_error_request_error_empty_message() {
525        let error = ProviderError::RequestError {
526            error: "".to_string(),
527            status_code: 404,
528        };
529        let (code, _message) = map_provider_error(&error);
530
531        assert_eq!(code, OpenZeppelinErrorCodes::REQUEST_ERROR);
532    }
533
534    #[test]
535    fn test_map_provider_error_request_error_zero_status() {
536        let error = ProviderError::RequestError {
537            error: "No status".to_string(),
538            status_code: 0,
539        };
540        let (code, _message) = map_provider_error(&error);
541
542        assert_eq!(code, OpenZeppelinErrorCodes::REQUEST_ERROR);
543    }
544
545    #[test]
546    fn test_map_provider_error_other() {
547        let error = ProviderError::Other("some other error".to_string());
548        let (code, _message) = map_provider_error(&error);
549
550        assert_eq!(code, RpcErrorCodes::INTERNAL_ERROR);
551    }
552
553    #[test]
554    fn test_map_provider_error_other_empty() {
555        let error = ProviderError::Other("".to_string());
556        let (code, _message) = map_provider_error(&error);
557
558        assert_eq!(code, RpcErrorCodes::INTERNAL_ERROR);
559    }
560
561    #[test]
562    fn test_map_provider_error_solana_rpc_error() {
563        let solana_error = SolanaProviderError::RpcError("Solana RPC failed".to_string());
564        let error = ProviderError::SolanaRpcError(solana_error);
565        let (code, _message) = map_provider_error(&error);
566
567        // The SolanaRpcError variant should be caught by the wildcard pattern
568        assert_eq!(code, RpcErrorCodes::INTERNAL_ERROR);
569    }
570
571    #[test]
572    fn test_map_provider_error_solana_invalid_address() {
573        let solana_error =
574            SolanaProviderError::InvalidAddress("Invalid Solana address".to_string());
575        let error = ProviderError::SolanaRpcError(solana_error);
576        let (code, _message) = map_provider_error(&error);
577
578        // The SolanaRpcError variant should be caught by the wildcard pattern
579        assert_eq!(code, RpcErrorCodes::INTERNAL_ERROR);
580    }
581
582    #[test]
583    fn test_map_provider_error_solana_selector_error() {
584        let selector_error = RpcSelectorError::NoProviders;
585        let solana_error = SolanaProviderError::SelectorError(selector_error);
586        let error = ProviderError::SolanaRpcError(solana_error);
587        let (code, _message) = map_provider_error(&error);
588
589        // The SolanaRpcError variant should be caught by the wildcard pattern
590        assert_eq!(code, RpcErrorCodes::INTERNAL_ERROR);
591    }
592
593    #[test]
594    fn test_map_provider_error_solana_network_configuration() {
595        let solana_error =
596            SolanaProviderError::NetworkConfiguration("Solana network config error".to_string());
597        let error = ProviderError::SolanaRpcError(solana_error);
598        let (code, _message) = map_provider_error(&error);
599
600        // The SolanaRpcError variant should be caught by the wildcard pattern
601        assert_eq!(code, RpcErrorCodes::INTERNAL_ERROR);
602    }
603
604    #[test]
605    fn test_map_provider_error_wildcard_pattern() {
606        // This test ensures the wildcard pattern works by testing all variations
607        // that should fall through to the default case
608        let test_cases = vec![
609            ProviderError::SolanaRpcError(SolanaProviderError::RpcError("test".to_string())),
610            ProviderError::SolanaRpcError(SolanaProviderError::InvalidAddress("test".to_string())),
611            ProviderError::SolanaRpcError(SolanaProviderError::NetworkConfiguration(
612                "test".to_string(),
613            )),
614            ProviderError::SolanaRpcError(SolanaProviderError::SelectorError(
615                RpcSelectorError::NoProviders,
616            )),
617        ];
618
619        for error in test_cases {
620            let (code, _message) = map_provider_error(&error);
621            assert_eq!(code, RpcErrorCodes::INTERNAL_ERROR);
622        }
623    }
624
625    #[test]
626    fn test_integration_error_response_with_mapped_provider_error() {
627        let provider_error = ProviderError::InvalidAddress("0xinvalid".to_string());
628        let (error_code, error_message) = map_provider_error(&provider_error);
629
630        let response = create_error_response(
631            Some(JsonRpcId::Number(999)),
632            error_code,
633            error_message,
634            "Invalid address provided",
635        );
636
637        assert_eq!(response.id, Some(JsonRpcId::Number(999)));
638        assert_eq!(response.jsonrpc, "2.0");
639        assert!(response.result.is_none());
640
641        let error = response.error.unwrap();
642        assert_eq!(error.code, RpcErrorCodes::INVALID_PARAMS);
643        assert!(!error.message.is_empty());
644        assert!(!error.description.is_empty());
645    }
646
647    #[test]
648    fn test_integration_all_provider_errors_to_responses() {
649        let test_cases = vec![
650            (
651                ProviderError::InvalidAddress("test".to_string()),
652                RpcErrorCodes::INVALID_PARAMS,
653            ),
654            (
655                ProviderError::NetworkConfiguration("test".to_string()),
656                OpenZeppelinErrorCodes::NETWORK_CONFIGURATION,
657            ),
658            (ProviderError::Timeout, OpenZeppelinErrorCodes::TIMEOUT),
659            (
660                ProviderError::RateLimited,
661                OpenZeppelinErrorCodes::RATE_LIMITED,
662            ),
663            (
664                ProviderError::BadGateway,
665                OpenZeppelinErrorCodes::BAD_GATEWAY,
666            ),
667            (
668                ProviderError::RequestError {
669                    error: "test".to_string(),
670                    status_code: 400,
671                },
672                OpenZeppelinErrorCodes::REQUEST_ERROR,
673            ),
674            (
675                ProviderError::Other("test".to_string()),
676                RpcErrorCodes::INTERNAL_ERROR,
677            ),
678        ];
679
680        for (provider_error, expected_code) in test_cases {
681            let (error_code, error_message) = map_provider_error(&provider_error);
682            let response = create_error_response(
683                Some(JsonRpcId::Number(1)),
684                error_code,
685                error_message,
686                "Test integration",
687            );
688
689            assert_eq!(response.id, Some(JsonRpcId::Number(1)));
690            assert_eq!(response.jsonrpc, "2.0");
691            assert!(response.result.is_none());
692
693            let error = response.error.unwrap();
694            assert_eq!(error.code, expected_code);
695            assert!(!error.message.is_empty());
696        }
697    }
698
699    #[test]
700    fn test_response_structure_consistency() {
701        // Test that both success and error responses have consistent structure
702        let success_response =
703            create_success_response(Some(JsonRpcId::Number(100)), json!({"status": "ok"}));
704        let error_response = create_error_response(
705            Some(JsonRpcId::Number(100)),
706            -32603,
707            "Internal error",
708            "Test error",
709        );
710
711        // Both should have same basic structure
712        assert_eq!(success_response.id, error_response.id);
713        assert_eq!(success_response.jsonrpc, error_response.jsonrpc);
714
715        // Success response should have result, not error
716        assert!(success_response.result.is_some());
717        assert!(success_response.error.is_none());
718
719        // Error response should have error, not result
720        assert!(error_response.result.is_none());
721        assert!(error_response.error.is_some());
722    }
723}