shared.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  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. // Wait before retrying to avoid overwhelming the mint during startup
  72. tokio::time::sleep(Duration::from_secs(1)).await;
  73. }
  74. }
  75. /// Initialize working directory
  76. pub fn init_working_directory(work_dir: &str) -> Result<PathBuf> {
  77. let temp_dir = PathBuf::from_str(work_dir)?;
  78. // Create the temp directory if it doesn't exist
  79. fs::create_dir_all(&temp_dir)?;
  80. Ok(temp_dir)
  81. }
  82. /// Write environment variables to .env file
  83. pub fn write_env_file(temp_dir: &Path, env_vars: &[(&str, &str)]) -> Result<()> {
  84. let mut env_content = String::new();
  85. for (key, value) in env_vars {
  86. env_content.push_str(&format!("{key}={value}\n"));
  87. }
  88. let env_file_path = temp_dir.join(".env");
  89. fs::write(&env_file_path, &env_content)
  90. .map(|_| {
  91. println!(
  92. "Environment variables written to: {}",
  93. env_file_path.display()
  94. );
  95. })
  96. .map_err(|e| anyhow::anyhow!("Could not write .env file: {}", e))
  97. }
  98. /// Wait for .env file to be created
  99. pub async fn wait_for_env_file(temp_dir: &Path, timeout_secs: u64) -> Result<()> {
  100. let env_file_path = temp_dir.join(".env");
  101. let start_time = std::time::Instant::now();
  102. println!(
  103. "Waiting for .env file to be created at: {}",
  104. env_file_path.display()
  105. );
  106. loop {
  107. // Check if timeout has been reached
  108. if start_time.elapsed().as_secs() > timeout_secs {
  109. return Err(anyhow::anyhow!(
  110. "Timeout waiting for .env file at {}",
  111. env_file_path.display()
  112. ));
  113. }
  114. // Check if the file exists
  115. if env_file_path.exists() {
  116. println!(".env file found at: {}", env_file_path.display());
  117. return Ok(());
  118. }
  119. tokio::time::sleep(Duration::from_secs(1)).await;
  120. }
  121. }
  122. /// Setup common logging based on CLI arguments
  123. pub fn setup_logging(common_args: &CommonArgs) {
  124. init_logging(common_args.enable_logging, common_args.log_level);
  125. }
  126. /// Create shutdown handler for graceful termination
  127. pub fn create_shutdown_handler() -> Arc<Notify> {
  128. Arc::new(Notify::new())
  129. }
  130. /// Wait for Ctrl+C signal
  131. pub async fn wait_for_shutdown_signal(shutdown: Arc<Notify>) {
  132. signal::ctrl_c()
  133. .await
  134. .expect("failed to install CTRL+C handler");
  135. println!("\nReceived Ctrl+C, shutting down...");
  136. shutdown.notify_waiters();
  137. }
  138. /// Common mint information display
  139. pub fn display_mint_info(port: u16, temp_dir: &Path, database_type: &str) {
  140. println!("Mint started successfully!");
  141. println!("Mint URL: http://127.0.0.1:{port}");
  142. println!("Temp directory: {temp_dir:?}");
  143. println!("Database type: {database_type}");
  144. }
  145. /// Create settings for a fake wallet mint
  146. pub fn create_fake_wallet_settings(
  147. port: u16,
  148. database: &str,
  149. mnemonic: Option<String>,
  150. signatory_config: Option<(String, String)>, // (url, certs_dir)
  151. fake_wallet_config: Option<cdk_mintd::config::FakeWallet>,
  152. ) -> cdk_mintd::config::Settings {
  153. let engine = DatabaseEngine::from_str(database).expect("valid database");
  154. // If using PostgreSQL, get the connection URL from the environment or use default
  155. let postgres_config = if engine == DatabaseEngine::Postgres {
  156. let url = std::env::var("CDK_MINTD_DATABASE_URL").unwrap_or_else(|_| {
  157. "postgresql://cdk_user:cdk_password@localhost:5432/cdk_mint".to_string()
  158. });
  159. Some(cdk_mintd::config::PostgresConfig {
  160. url,
  161. ..Default::default()
  162. })
  163. } else {
  164. None
  165. };
  166. cdk_mintd::config::Settings {
  167. info: cdk_mintd::config::Info {
  168. url: format!("http://127.0.0.1:{port}"),
  169. quote_ttl: None,
  170. listen_host: "127.0.0.1".to_string(),
  171. listen_port: port,
  172. seed: None,
  173. mnemonic,
  174. signatory_url: signatory_config.as_ref().map(|(url, _)| url.clone()),
  175. signatory_certs: signatory_config
  176. .as_ref()
  177. .map(|(_, certs_dir)| certs_dir.clone()),
  178. input_fee_ppk: None,
  179. http_cache: cache::Config::default(),
  180. logging: cdk_mintd::config::LoggingConfig {
  181. output: cdk_mintd::config::LoggingOutput::Both,
  182. console_level: Some("debug".to_string()),
  183. file_level: Some("debug".to_string()),
  184. },
  185. enable_swagger_ui: None,
  186. },
  187. mint_info: cdk_mintd::config::MintInfo::default(),
  188. ln: cdk_mintd::config::Ln {
  189. ln_backend: cdk_mintd::config::LnBackend::FakeWallet,
  190. invoice_description: None,
  191. min_mint: DEFAULT_MIN_MINT.into(),
  192. max_mint: DEFAULT_MAX_MINT.into(),
  193. min_melt: DEFAULT_MIN_MELT.into(),
  194. max_melt: DEFAULT_MAX_MELT.into(),
  195. },
  196. cln: None,
  197. lnbits: None,
  198. lnd: None,
  199. ldk_node: None,
  200. fake_wallet: fake_wallet_config,
  201. grpc_processor: None,
  202. database: Database {
  203. engine,
  204. postgres: postgres_config,
  205. },
  206. auth_database: None,
  207. mint_management_rpc: None,
  208. auth: None,
  209. prometheus: Some(Default::default()),
  210. }
  211. }
  212. /// Create settings for a CLN mint
  213. pub fn create_cln_settings(
  214. port: u16,
  215. _cln_rpc_path: PathBuf,
  216. mnemonic: String,
  217. cln_config: cdk_mintd::config::Cln,
  218. ) -> cdk_mintd::config::Settings {
  219. cdk_mintd::config::Settings {
  220. info: cdk_mintd::config::Info {
  221. url: format!("http://127.0.0.1:{port}"),
  222. quote_ttl: None,
  223. listen_host: "127.0.0.1".to_string(),
  224. listen_port: port,
  225. seed: None,
  226. mnemonic: Some(mnemonic),
  227. signatory_url: None,
  228. signatory_certs: None,
  229. input_fee_ppk: None,
  230. http_cache: cache::Config::default(),
  231. logging: cdk_mintd::config::LoggingConfig {
  232. output: cdk_mintd::config::LoggingOutput::Both,
  233. console_level: Some("debug".to_string()),
  234. file_level: Some("debug".to_string()),
  235. },
  236. enable_swagger_ui: None,
  237. },
  238. mint_info: cdk_mintd::config::MintInfo::default(),
  239. ln: cdk_mintd::config::Ln {
  240. ln_backend: cdk_mintd::config::LnBackend::Cln,
  241. invoice_description: None,
  242. min_mint: DEFAULT_MIN_MINT.into(),
  243. max_mint: DEFAULT_MAX_MINT.into(),
  244. min_melt: DEFAULT_MIN_MELT.into(),
  245. max_melt: DEFAULT_MAX_MELT.into(),
  246. },
  247. cln: Some(cln_config),
  248. lnbits: None,
  249. lnd: None,
  250. ldk_node: None,
  251. fake_wallet: None,
  252. grpc_processor: None,
  253. database: cdk_mintd::config::Database::default(),
  254. auth_database: None,
  255. mint_management_rpc: None,
  256. auth: None,
  257. prometheus: Some(Default::default()),
  258. }
  259. }
  260. /// Create settings for an LND mint
  261. pub fn create_lnd_settings(
  262. port: u16,
  263. lnd_config: cdk_mintd::config::Lnd,
  264. mnemonic: String,
  265. ) -> cdk_mintd::config::Settings {
  266. cdk_mintd::config::Settings {
  267. info: cdk_mintd::config::Info {
  268. quote_ttl: None,
  269. url: format!("http://127.0.0.1:{port}"),
  270. listen_host: "127.0.0.1".to_string(),
  271. listen_port: port,
  272. seed: None,
  273. mnemonic: Some(mnemonic),
  274. signatory_url: None,
  275. signatory_certs: None,
  276. input_fee_ppk: None,
  277. http_cache: cache::Config::default(),
  278. logging: cdk_mintd::config::LoggingConfig {
  279. output: cdk_mintd::config::LoggingOutput::Both,
  280. console_level: Some("debug".to_string()),
  281. file_level: Some("debug".to_string()),
  282. },
  283. enable_swagger_ui: None,
  284. },
  285. mint_info: cdk_mintd::config::MintInfo::default(),
  286. ln: cdk_mintd::config::Ln {
  287. ln_backend: cdk_mintd::config::LnBackend::Lnd,
  288. invoice_description: None,
  289. min_mint: DEFAULT_MIN_MINT.into(),
  290. max_mint: DEFAULT_MAX_MINT.into(),
  291. min_melt: DEFAULT_MIN_MELT.into(),
  292. max_melt: DEFAULT_MAX_MELT.into(),
  293. },
  294. cln: None,
  295. lnbits: None,
  296. ldk_node: None,
  297. lnd: Some(lnd_config),
  298. fake_wallet: None,
  299. grpc_processor: None,
  300. database: cdk_mintd::config::Database::default(),
  301. auth_database: None,
  302. mint_management_rpc: None,
  303. auth: None,
  304. prometheus: Some(Default::default()),
  305. }
  306. }