shared.rs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. //! Shared utilities for mint integration tests
  2. //!
  3. //! This module provides common functionality used across different
  4. //! integration test binaries to reduce code duplication.
  5. use std::fs;
  6. use std::path::{Path, PathBuf};
  7. use std::str::FromStr;
  8. use std::sync::Arc;
  9. use std::time::Duration;
  10. use anyhow::Result;
  11. use cdk_axum::cache;
  12. use cdk_mintd::config::{Database, DatabaseEngine};
  13. use tokio::signal;
  14. use tokio::sync::Notify;
  15. use tokio_util::sync::CancellationToken;
  16. use crate::cli::{init_logging, CommonArgs};
  17. /// Default minimum mint amount for test mints
  18. const DEFAULT_MIN_MINT: u64 = 1;
  19. /// Default maximum mint amount for test mints
  20. const DEFAULT_MAX_MINT: u64 = 500_000;
  21. /// Default minimum melt amount for test mints
  22. const DEFAULT_MIN_MELT: u64 = 1;
  23. /// Default maximum melt amount for test mints
  24. const DEFAULT_MAX_MELT: u64 = 500_000;
  25. /// Wait for mint to be ready by checking its info endpoint, with optional shutdown signal
  26. pub async fn wait_for_mint_ready_with_shutdown(
  27. port: u16,
  28. timeout_secs: u64,
  29. shutdown_notify: Arc<CancellationToken>,
  30. ) -> Result<()> {
  31. let url = format!("http://127.0.0.1:{port}/v1/info");
  32. let start_time = std::time::Instant::now();
  33. println!("Waiting for mint on port {port} to be ready...");
  34. loop {
  35. // Check if timeout has been reached
  36. if start_time.elapsed().as_secs() > timeout_secs {
  37. return Err(anyhow::anyhow!("Timeout waiting for mint on port {}", port));
  38. }
  39. if shutdown_notify.is_cancelled() {
  40. return Err(anyhow::anyhow!("Canceled waiting for {}", port));
  41. }
  42. tokio::select! {
  43. // Try to make a request to the mint info endpoint
  44. result = reqwest::get(&url) => {
  45. match result {
  46. Ok(response) => {
  47. if response.status().is_success() {
  48. println!("Mint on port {port} is ready");
  49. return Ok(());
  50. } else {
  51. println!(
  52. "Mint on port {} returned status: {}",
  53. port,
  54. response.status()
  55. );
  56. }
  57. }
  58. Err(e) => {
  59. println!("Error connecting to mint on port {port}: {e}");
  60. }
  61. }
  62. }
  63. // Check for shutdown signal
  64. _ = shutdown_notify.cancelled() => {
  65. return Err(anyhow::anyhow!(
  66. "Shutdown requested while waiting for mint on port {}",
  67. port
  68. ));
  69. }
  70. }
  71. }
  72. }
  73. /// Initialize working directory
  74. pub fn init_working_directory(work_dir: &str) -> Result<PathBuf> {
  75. let temp_dir = PathBuf::from_str(work_dir)?;
  76. // Create the temp directory if it doesn't exist
  77. fs::create_dir_all(&temp_dir)?;
  78. Ok(temp_dir)
  79. }
  80. /// Write environment variables to .env file
  81. pub fn write_env_file(temp_dir: &Path, env_vars: &[(&str, &str)]) -> Result<()> {
  82. let mut env_content = String::new();
  83. for (key, value) in env_vars {
  84. env_content.push_str(&format!("{key}={value}\n"));
  85. }
  86. let env_file_path = temp_dir.join(".env");
  87. fs::write(&env_file_path, &env_content)
  88. .map(|_| {
  89. println!(
  90. "Environment variables written to: {}",
  91. env_file_path.display()
  92. );
  93. })
  94. .map_err(|e| anyhow::anyhow!("Could not write .env file: {}", e))
  95. }
  96. /// Wait for .env file to be created
  97. pub async fn wait_for_env_file(temp_dir: &Path, timeout_secs: u64) -> Result<()> {
  98. let env_file_path = temp_dir.join(".env");
  99. let start_time = std::time::Instant::now();
  100. println!(
  101. "Waiting for .env file to be created at: {}",
  102. env_file_path.display()
  103. );
  104. loop {
  105. // Check if timeout has been reached
  106. if start_time.elapsed().as_secs() > timeout_secs {
  107. return Err(anyhow::anyhow!(
  108. "Timeout waiting for .env file at {}",
  109. env_file_path.display()
  110. ));
  111. }
  112. // Check if the file exists
  113. if env_file_path.exists() {
  114. println!(".env file found at: {}", env_file_path.display());
  115. return Ok(());
  116. }
  117. tokio::time::sleep(Duration::from_secs(1)).await;
  118. }
  119. }
  120. /// Setup common logging based on CLI arguments
  121. pub fn setup_logging(common_args: &CommonArgs) {
  122. init_logging(common_args.enable_logging, common_args.log_level);
  123. }
  124. /// Create shutdown handler for graceful termination
  125. pub fn create_shutdown_handler() -> Arc<Notify> {
  126. Arc::new(Notify::new())
  127. }
  128. /// Wait for Ctrl+C signal
  129. pub async fn wait_for_shutdown_signal(shutdown: Arc<Notify>) {
  130. signal::ctrl_c()
  131. .await
  132. .expect("failed to install CTRL+C handler");
  133. println!("\nReceived Ctrl+C, shutting down...");
  134. shutdown.notify_waiters();
  135. }
  136. /// Common mint information display
  137. pub fn display_mint_info(port: u16, temp_dir: &Path, database_type: &str) {
  138. println!("Mint started successfully!");
  139. println!("Mint URL: http://127.0.0.1:{port}");
  140. println!("Temp directory: {temp_dir:?}");
  141. println!("Database type: {database_type}");
  142. }
  143. /// Create settings for a fake wallet mint
  144. pub fn create_fake_wallet_settings(
  145. port: u16,
  146. database: &str,
  147. mnemonic: Option<String>,
  148. signatory_config: Option<(String, String)>, // (url, certs_dir)
  149. fake_wallet_config: Option<cdk_mintd::config::FakeWallet>,
  150. ) -> cdk_mintd::config::Settings {
  151. cdk_mintd::config::Settings {
  152. info: cdk_mintd::config::Info {
  153. url: format!("http://127.0.0.1:{port}"),
  154. listen_host: "127.0.0.1".to_string(),
  155. listen_port: port,
  156. seed: None,
  157. mnemonic,
  158. signatory_url: signatory_config.as_ref().map(|(url, _)| url.clone()),
  159. signatory_certs: signatory_config
  160. .as_ref()
  161. .map(|(_, certs_dir)| certs_dir.clone()),
  162. input_fee_ppk: None,
  163. http_cache: cache::Config::default(),
  164. logging: cdk_mintd::config::LoggingConfig {
  165. output: cdk_mintd::config::LoggingOutput::Both,
  166. console_level: Some("debug".to_string()),
  167. file_level: Some("debug".to_string()),
  168. },
  169. enable_swagger_ui: None,
  170. },
  171. mint_info: cdk_mintd::config::MintInfo::default(),
  172. ln: cdk_mintd::config::Ln {
  173. ln_backend: cdk_mintd::config::LnBackend::FakeWallet,
  174. invoice_description: None,
  175. min_mint: DEFAULT_MIN_MINT.into(),
  176. max_mint: DEFAULT_MAX_MINT.into(),
  177. min_melt: DEFAULT_MIN_MELT.into(),
  178. max_melt: DEFAULT_MAX_MELT.into(),
  179. },
  180. cln: None,
  181. lnbits: None,
  182. lnd: None,
  183. ldk_node: None,
  184. fake_wallet: fake_wallet_config,
  185. grpc_processor: None,
  186. database: Database {
  187. engine: DatabaseEngine::from_str(database).expect("valid database"),
  188. postgres: None,
  189. },
  190. mint_management_rpc: None,
  191. auth: None,
  192. prometheus: Some(Default::default()),
  193. }
  194. }
  195. /// Create settings for a CLN mint
  196. pub fn create_cln_settings(
  197. port: u16,
  198. _cln_rpc_path: PathBuf,
  199. mnemonic: String,
  200. cln_config: cdk_mintd::config::Cln,
  201. ) -> cdk_mintd::config::Settings {
  202. cdk_mintd::config::Settings {
  203. info: cdk_mintd::config::Info {
  204. url: format!("http://127.0.0.1:{port}"),
  205. listen_host: "127.0.0.1".to_string(),
  206. listen_port: port,
  207. seed: None,
  208. mnemonic: Some(mnemonic),
  209. signatory_url: None,
  210. signatory_certs: None,
  211. input_fee_ppk: None,
  212. http_cache: cache::Config::default(),
  213. logging: cdk_mintd::config::LoggingConfig {
  214. output: cdk_mintd::config::LoggingOutput::Both,
  215. console_level: Some("debug".to_string()),
  216. file_level: Some("debug".to_string()),
  217. },
  218. enable_swagger_ui: None,
  219. },
  220. mint_info: cdk_mintd::config::MintInfo::default(),
  221. ln: cdk_mintd::config::Ln {
  222. ln_backend: cdk_mintd::config::LnBackend::Cln,
  223. invoice_description: None,
  224. min_mint: DEFAULT_MIN_MINT.into(),
  225. max_mint: DEFAULT_MAX_MINT.into(),
  226. min_melt: DEFAULT_MIN_MELT.into(),
  227. max_melt: DEFAULT_MAX_MELT.into(),
  228. },
  229. cln: Some(cln_config),
  230. lnbits: None,
  231. lnd: None,
  232. ldk_node: None,
  233. fake_wallet: None,
  234. grpc_processor: None,
  235. database: cdk_mintd::config::Database::default(),
  236. mint_management_rpc: None,
  237. auth: None,
  238. prometheus: Some(Default::default()),
  239. }
  240. }
  241. /// Create settings for an LND mint
  242. pub fn create_lnd_settings(
  243. port: u16,
  244. lnd_config: cdk_mintd::config::Lnd,
  245. mnemonic: String,
  246. ) -> cdk_mintd::config::Settings {
  247. cdk_mintd::config::Settings {
  248. info: cdk_mintd::config::Info {
  249. url: format!("http://127.0.0.1:{port}"),
  250. listen_host: "127.0.0.1".to_string(),
  251. listen_port: port,
  252. seed: None,
  253. mnemonic: Some(mnemonic),
  254. signatory_url: None,
  255. signatory_certs: None,
  256. input_fee_ppk: None,
  257. http_cache: cache::Config::default(),
  258. logging: cdk_mintd::config::LoggingConfig {
  259. output: cdk_mintd::config::LoggingOutput::Both,
  260. console_level: Some("debug".to_string()),
  261. file_level: Some("debug".to_string()),
  262. },
  263. enable_swagger_ui: None,
  264. },
  265. mint_info: cdk_mintd::config::MintInfo::default(),
  266. ln: cdk_mintd::config::Ln {
  267. ln_backend: cdk_mintd::config::LnBackend::Lnd,
  268. invoice_description: None,
  269. min_mint: DEFAULT_MIN_MINT.into(),
  270. max_mint: DEFAULT_MAX_MINT.into(),
  271. min_melt: DEFAULT_MIN_MELT.into(),
  272. max_melt: DEFAULT_MAX_MELT.into(),
  273. },
  274. cln: None,
  275. lnbits: None,
  276. ldk_node: None,
  277. lnd: Some(lnd_config),
  278. fake_wallet: None,
  279. grpc_processor: None,
  280. database: cdk_mintd::config::Database::default(),
  281. mint_management_rpc: None,
  282. auth: None,
  283. prometheus: Some(Default::default()),
  284. }
  285. }