-
Notifications
You must be signed in to change notification settings - Fork 245
Expand file tree
/
Copy pathassertions.rs
More file actions
110 lines (90 loc) · 3.5 KB
/
assertions.rs
File metadata and controls
110 lines (90 loc) · 3.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
use serde_json::Value;
use solana_sdk::signature::Signature;
use std::str::FromStr;
/// Trait for common RPC response assertions
pub trait RpcAssertions {
/// Assert the response indicates success
fn assert_success(&self);
/// Assert the response contains an error with the given code
fn assert_error(&self, expected_code: i32);
/// Assert the response has a valid signature
fn assert_has_signature(&self) -> Signature;
/// Assert the response has a specific field
fn assert_has_field(&self, field: &str);
/// Get a field value from the response
fn get_field(&self, field: &str) -> Option<&Value>;
}
impl RpcAssertions for Value {
fn assert_success(&self) {
if let Some(error) = self.get("error") {
panic!(
"Expected successful response, but got error: {}",
serde_json::to_string_pretty(error).unwrap()
);
}
// Success is simply the absence of an error field
// Different endpoints return different response structures:
// - Transaction endpoints: have "signature" field
// - Config endpoints: return data directly (fee_payers, validation_config, etc.)
// - Fee estimation endpoints: return fee fields directly
// - JSON-RPC wrapped responses: have "result" field
}
fn assert_error(&self, expected_code: i32) {
let error =
self.get("error").expect("Expected error in response, but got successful result");
let code =
error.get("code").and_then(|c| c.as_i64()).expect("Error response missing code field");
assert_eq!(
code,
expected_code as i64,
"Expected error code {}, got {}. Full error: {}",
expected_code,
code,
serde_json::to_string_pretty(error).unwrap()
);
}
fn assert_has_signature(&self) -> Signature {
// Check for signature in multiple possible locations
let sig_str = self
.get("signature")
.or_else(|| self.get("result").and_then(|r| r.get("signature")))
.and_then(|s| s.as_str())
.expect("Response does not contain a signature");
Signature::from_str(sig_str).expect("Invalid signature format")
}
fn assert_has_field(&self, field: &str) {
let value = self.get(field).or_else(|| self.get("result").and_then(|r| r.get(field)));
assert!(
value.is_some(),
"Expected field '{}' in response, got: {}",
field,
serde_json::to_string_pretty(self).unwrap()
);
}
fn get_field(&self, field: &str) -> Option<&Value> {
self.get(field).or_else(|| self.get("result").and_then(|r| r.get(field)))
}
}
/// Assertions for transaction responses
pub trait TransactionAssertions {
/// Assert the transaction blockhash is valid (43-44 chars base58)
fn assert_valid_blockhash(&self);
}
impl TransactionAssertions for Value {
fn assert_valid_blockhash(&self) {
let blockhash = self
.get_field("blockhash")
.and_then(|b| b.as_str())
.expect("Response missing blockhash field");
// Solana blockhashes are typically 44 chars in base58
assert!(
blockhash.len() >= 43 && blockhash.len() <= 44,
"Invalid blockhash format: {blockhash}"
);
}
}
/// Standard JSON-RPC error codes for reference
pub struct JsonRpcErrorCodes;
impl JsonRpcErrorCodes {
pub const METHOD_NOT_FOUND: i32 = -32601;
}