drive_abci/abci/
config.rs1use crate::utils::from_opt_str_or_number;
4use serde::{Deserialize, Serialize};
5
6#[derive(Clone, Debug, Serialize, Deserialize)]
11pub struct AbciConfig {
12 #[serde(rename = "abci_consensus_bind_address")]
18 pub consensus_bind_address: String,
19
20 #[serde(default = "AbciConfig::default_genesis_height")]
22 pub genesis_height: u64,
23
24 #[serde(default = "AbciConfig::default_genesis_core_height")]
26 pub genesis_core_height: u32,
27
28 #[serde(default)]
30 pub chain_id: String,
31
32 #[serde(default)]
35 pub log: crate::logging::LogConfigs,
36
37 #[serde(default, deserialize_with = "from_opt_str_or_number")]
39 pub proposer_tx_processing_time_limit: Option<u16>,
40}
41
42impl AbciConfig {
43 pub(crate) fn default_genesis_height() -> u64 {
44 1
45 }
46
47 pub(crate) fn default_genesis_core_height() -> u32 {
48 1
49 }
50}
51
52impl Default for AbciConfig {
53 fn default() -> Self {
54 Self {
55 consensus_bind_address: "tcp://127.0.0.1:1234".to_string(),
56 genesis_height: AbciConfig::default_genesis_height(),
57 genesis_core_height: AbciConfig::default_genesis_core_height(),
58 chain_id: "chain_id".to_string(),
59 log: Default::default(),
60 proposer_tx_processing_time_limit: Default::default(),
61 }
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 #[test]
70 fn default_genesis_height_returns_one() {
71 assert_eq!(AbciConfig::default_genesis_height(), 1);
72 }
73
74 #[test]
75 fn default_genesis_core_height_returns_one() {
76 assert_eq!(AbciConfig::default_genesis_core_height(), 1);
77 }
78
79 #[test]
80 fn default_config_has_expected_values() {
81 let config = AbciConfig::default();
82 assert_eq!(config.consensus_bind_address, "tcp://127.0.0.1:1234");
83 assert_eq!(config.genesis_height, 1);
84 assert_eq!(config.genesis_core_height, 1);
85 assert_eq!(config.chain_id, "chain_id");
86 assert!(config.log.is_empty());
87 assert!(config.proposer_tx_processing_time_limit.is_none());
88 }
89
90 #[test]
91 fn config_serializes_and_deserializes_roundtrip() {
92 let config = AbciConfig {
95 consensus_bind_address: "tcp://0.0.0.0:5678".to_string(),
96 genesis_height: 42,
97 genesis_core_height: 10,
98 chain_id: "test-chain".to_string(),
99 log: Default::default(),
100 proposer_tx_processing_time_limit: None,
101 };
102
103 let serialized = serde_json::to_string(&config).expect("should serialize");
104 let deserialized: AbciConfig =
105 serde_json::from_str(&serialized).expect("should deserialize");
106
107 assert_eq!(deserialized.consensus_bind_address, "tcp://0.0.0.0:5678");
108 assert_eq!(deserialized.genesis_height, 42);
109 assert_eq!(deserialized.genesis_core_height, 10);
110 assert_eq!(deserialized.chain_id, "test-chain");
111 assert!(deserialized.proposer_tx_processing_time_limit.is_none());
112 }
113
114 #[test]
115 fn config_deserialization_uses_defaults_for_missing_fields() {
116 let json = r#"{"abci_consensus_bind_address": "tcp://1.2.3.4:9999"}"#;
118 let config: AbciConfig = serde_json::from_str(json).expect("should deserialize");
119
120 assert_eq!(config.consensus_bind_address, "tcp://1.2.3.4:9999");
121 assert_eq!(config.genesis_height, 1);
122 assert_eq!(config.genesis_core_height, 1);
123 assert_eq!(config.chain_id, "");
124 assert!(config.proposer_tx_processing_time_limit.is_none());
125 }
126
127 #[test]
128 fn config_serialization_uses_renamed_field() {
129 let config = AbciConfig::default();
130 let serialized = serde_json::to_value(&config).expect("should serialize");
131
132 assert!(serialized.get("abci_consensus_bind_address").is_some());
134 assert!(serialized.get("consensus_bind_address").is_none());
135 }
136
137 #[test]
138 fn config_clone_produces_equal_values() {
139 let config = AbciConfig {
140 consensus_bind_address: "unix:///tmp/test.sock".to_string(),
141 genesis_height: 100,
142 genesis_core_height: 50,
143 chain_id: "clone-test".to_string(),
144 log: Default::default(),
145 proposer_tx_processing_time_limit: Some(1000),
146 };
147
148 let cloned = config.clone();
149 assert_eq!(cloned.consensus_bind_address, config.consensus_bind_address);
150 assert_eq!(cloned.genesis_height, config.genesis_height);
151 assert_eq!(cloned.genesis_core_height, config.genesis_core_height);
152 assert_eq!(cloned.chain_id, config.chain_id);
153 assert_eq!(
154 cloned.proposer_tx_processing_time_limit,
155 config.proposer_tx_processing_time_limit
156 );
157 }
158
159 #[test]
160 fn config_debug_format_is_not_empty() {
161 let config = AbciConfig::default();
162 let debug_str = format!("{:?}", config);
163 assert!(!debug_str.is_empty());
164 assert!(debug_str.contains("AbciConfig"));
165 }
166
167 #[test]
168 fn config_deserializes_proposer_tx_processing_time_limit_from_string() {
169 let json = r#"{"abci_consensus_bind_address": "tcp://x:1", "proposer_tx_processing_time_limit": "250"}"#;
171 let config: AbciConfig = serde_json::from_str(json).expect("should deserialize");
172 assert_eq!(config.proposer_tx_processing_time_limit, Some(250));
173 }
174
175 #[test]
176 fn config_rejects_raw_number_for_proposer_tx_processing_time_limit() {
177 let json = r#"{"abci_consensus_bind_address": "tcp://x:1", "proposer_tx_processing_time_limit": 750}"#;
180 let result = serde_json::from_str::<AbciConfig>(json);
181 assert!(result.is_err());
182 }
183
184 #[test]
185 fn config_deserializes_null_proposer_tx_processing_time_limit() {
186 let json = r#"{"abci_consensus_bind_address": "tcp://x:1", "proposer_tx_processing_time_limit": null}"#;
187 let config: AbciConfig = serde_json::from_str(json).expect("should deserialize");
188 assert!(config.proposer_tx_processing_time_limit.is_none());
189 }
190
191 #[test]
192 fn config_deserializes_empty_string_as_none_for_proposer_tx_processing_time_limit() {
193 let json = r#"{"abci_consensus_bind_address": "tcp://x:1", "proposer_tx_processing_time_limit": ""}"#;
194 let config: AbciConfig = serde_json::from_str(json).expect("should deserialize");
195 assert!(config.proposer_tx_processing_time_limit.is_none());
196 }
197}