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