1#![doc = include_str!("../README.md")]
2mod config;
3mod error;
4pub use error::Error;
5use serde::Deserialize;
6pub type Result<T> = std::result::Result<T, error::Error>;
7pub use config::*;
8
9#[derive(Copy, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Default)]
10pub enum OutputFormat {
11 #[default]
12 Table,
14 Tsv,
16 Json,
17}
18
19#[cfg(test)]
20mod tests {
21 use {
22 super::*,
23 std::{path::PathBuf, time::Duration},
24 };
25
26 #[test_log::test]
27 fn test_signer_config_default() -> anyhow::Result<()> {
28 let cfg = super::Signer::default();
29 assert!(!cfg.broadcast);
30 assert_eq!(cfg.vault.len(), 0);
31 Ok(())
32 }
33
34 #[ignore]
35 #[test_log::test]
36 fn test_gpg_config() -> anyhow::Result<()> {
37 let b = "examples/config-gpg.toml";
38 let cfg = FireblocksConfig::new(b, &[])?;
39 cfg.get_key()?;
40 Ok(())
41 }
42
43 #[test_log::test]
44 fn test_config() -> anyhow::Result<()> {
45 let b = "examples/default.toml";
46 let cfg = FireblocksConfig::new(b, &[])?;
47 assert_eq!("blah", cfg.api_key);
48 assert!(cfg.secret_path.is_some());
49 if let Some(p) = cfg.secret_path.as_ref() {
50 assert_eq!(PathBuf::from("examples/test.pem"), *p);
51 }
52 assert_eq!("https://sandbox-api.fireblocks.io/v1", cfg.url);
53 assert_eq!(OutputFormat::Table, cfg.display_config.output);
54 unsafe {
55 std::env::set_var("FIREBLOCKS_SECRET", "override");
56 }
57 let cfg = FireblocksConfig::new(b, &[])?;
58 assert!(cfg.secret.is_some());
59 assert_eq!(String::from("override").as_bytes(), cfg.get_key()?);
60 if let Some(ref k) = cfg.secret_path {
61 assert_eq!(PathBuf::from("examples/test.pem"), *k);
62 }
63
64 assert_eq!(cfg.signer.vault, "0");
65 assert!(!cfg.signer.broadcast);
66 unsafe {
67 std::env::remove_var("FIREBLOCKS_SECRET");
68 }
69 Ok(())
70 }
71
72 #[test_log::test]
73 fn test_config_override() -> anyhow::Result<()> {
74 let b = "examples/default.toml";
75 let cfg_override = "examples/override.toml";
76 let cfg = FireblocksConfig::with_overrides(b, vec![cfg_override])?;
77 assert_eq!("production", cfg.api_key);
78 assert!(cfg.secret_path.is_some());
79 if let Some(p) = cfg.secret_path.as_ref() {
80 assert_eq!(PathBuf::from("examples/test.pem"), *p);
81 }
82 assert_eq!("https://api.fireblocks.io/v1", cfg.url);
83 assert_eq!(OutputFormat::Table, cfg.display_config.output);
84 assert!(cfg.debug);
85 assert!(cfg.mainnet);
86 Ok(())
87 }
88
89 #[test_log::test]
90 fn test_embedded_key() -> anyhow::Result<()> {
91 let b = "examples/default.toml";
92 let cfg_override = "examples/embedded.toml";
93 let cfg = FireblocksConfig::new(b, &[cfg_override])?;
94 assert!(cfg.secret.is_some());
95 let secret = cfg.secret.unwrap();
96 assert_eq!(String::from("i am a secret").as_bytes(), secret.as_bytes());
97 Ok(())
98 }
99
100 #[test_log::test]
101 fn test_duration_parsing() -> anyhow::Result<()> {
102 let b = "examples/default.toml";
103 let cfg = FireblocksConfig::new(b, &[])?;
104
105 assert_eq!(cfg.signer.poll_timeout, Duration::from_secs(120));
107 assert_eq!(cfg.signer.poll_interval, Duration::from_secs(5));
108
109 Ok(())
110 }
111
112 #[test_log::test]
113 fn test_extra_config() -> anyhow::Result<()> {
114 let b = "examples/default.toml";
115 let cfg = FireblocksConfig::new(b, &[])?;
116
117 assert_eq!(cfg.get_extra::<String, _>("rpc_url")?, "https://rpc.com");
119 assert!(!cfg.get_extra::<bool, _>("fail_fast")?);
120 assert_eq!(cfg.get_extra::<i64, _>("timeout")?, 40);
121
122 let key = String::from("rpc_url");
124 assert_eq!(cfg.get_extra::<String, _>(&key)?, "https://rpc.com");
125
126 let result = cfg.get_extra::<String, _>("non_existent");
128 assert!(result.is_err());
129 if let Err(Error::NotPresent { key }) = result {
130 assert_eq!(key, "non_existent");
131 } else {
132 panic!("Expected NotPresent error");
133 }
134
135 assert!(cfg.has_extra("rpc_url"));
137 assert!(cfg.has_extra(String::from("fail_fast")));
138 assert!(cfg.has_extra("timeout"));
139 assert!(!cfg.has_extra("non_existent"));
140
141 let timeout_duration = cfg.get_extra_duration("timeout")?;
143 assert_eq!(timeout_duration, Duration::from_secs(40));
144
145 let result = cfg.get_extra_duration("non_existent");
147 assert!(result.is_err());
148
149 Ok(())
150 }
151
152 #[test_log::test]
153 fn test_duration_defaults() -> anyhow::Result<()> {
154 let b = "examples/notime.toml";
155 let cfg = FireblocksConfig::new(b, &[])?;
156 assert_eq!(cfg.signer.poll_timeout, default_poll_timeout());
158 assert_eq!(cfg.signer.poll_interval, default_poll_interval());
159 Ok(())
160 }
161
162 #[test_log::test]
163 fn test_tilde() -> anyhow::Result<()> {
164 let expanded = format!("{}", expand_tilde("~/blah/default.toml").display());
165 assert!(expanded.contains("/home"));
166 Ok(())
167 }
168
169 #[test_log::test]
170 fn test_xdg_init() {
171 match FireblocksConfig::init() {
174 Ok(_) => {
175 }
177 Err(_) => {
178 }
181 }
182
183 match FireblocksConfig::init_with_profiles(&["test", "production"]) {
185 Ok(_) => {
186 }
188 Err(_) => {
189 }
191 }
192
193 let profiles: Vec<String> = vec!["staging".to_string(), "production".to_string()];
195 match FireblocksConfig::init_with_profiles(&profiles) {
196 Ok(_) => {
197 }
199 Err(_) => {
200 }
202 }
203 }
204}