1use std::path::PathBuf;
2
3use electrsd::bitcoind::bitcoincore_rpc::Auth;
4
5use smplx_regtest::Regtest;
6use smplx_regtest::client::RegtestClient;
7
8use smplx_sdk::global::set_global_config;
9use smplx_sdk::provider::{
10 ElementsRpc, EsploraProvider, ProviderInfo, ProviderTrait, SimplexProvider, SimplicityNetwork,
11};
12use smplx_sdk::signer::Signer;
13use smplx_sdk::utils::random_mnemonic;
14
15use crate::config::TestConfig;
16use crate::error::TestError;
17use crate::network_utils::NetworkUtils;
18
19#[allow(dead_code)]
20pub struct TestContext {
21 _client: Option<RegtestClient>,
22 _provider_info: ProviderInfo,
24 config: TestConfig,
25 signer: Signer,
26}
27
28impl TestContext {
29 pub fn new(config_path: PathBuf) -> Result<Self, TestError> {
30 let config = TestConfig::from_file(&config_path)?;
31
32 let _ = set_global_config(
34 config
35 .verbosity
36 .expect("This will be set")
37 .try_into()
38 .expect("Validated in CLI"),
39 );
40
41 let (signer, provider_info, client) = Self::setup(&config)?;
42
43 Ok(Self {
44 _client: client,
45 _provider_info: provider_info,
46 config,
47 signer,
48 })
49 }
50
51 pub fn create_signer(&self, mnemonic: &str) -> Signer {
52 let provider: Box<dyn ProviderTrait> = if self._provider_info.elements_url.is_some() {
53 Box::new(SimplexProvider::new(
55 self._provider_info.esplora_url.clone(),
56 self._provider_info.elements_url.clone().unwrap(),
57 self._provider_info.auth.clone().unwrap(),
58 *self.get_network(),
59 ))
60 } else {
61 Box::new(EsploraProvider::new(
63 self._provider_info.esplora_url.clone(),
64 *self.get_network(),
65 ))
66 };
67
68 Signer::new(mnemonic, provider)
69 }
70
71 pub fn random_signer(&self) -> Signer {
72 self.create_signer(random_mnemonic().as_str())
73 }
74
75 pub fn get_default_signer(&self) -> &Signer {
76 &self.signer
77 }
78
79 pub fn get_default_provider(&self) -> &dyn ProviderTrait {
80 self.signer.get_provider()
81 }
82
83 pub fn get_config(&self) -> &TestConfig {
84 &self.config
85 }
86
87 pub fn get_network(&self) -> &SimplicityNetwork {
88 self.signer.get_provider().get_network()
89 }
90
91 pub fn get_network_utils(&self) -> NetworkUtils {
92 assert!(
93 self._client.is_some(),
94 "Network utils only available in Regtest network"
95 );
96
97 let regtest_rpc = ElementsRpc::new(
98 self._provider_info.elements_url.clone().unwrap(),
99 self._provider_info.auth.clone().unwrap(),
100 )
101 .expect("Failed to create rpc client for network utils");
102
103 let network = self.get_network();
104 let esplora = EsploraProvider::new(self._provider_info.esplora_url.clone(), *network);
105
106 NetworkUtils::new(regtest_rpc, esplora)
107 }
108
109 fn setup(config: &TestConfig) -> Result<(Signer, ProviderInfo, Option<RegtestClient>), TestError> {
110 let client: Option<RegtestClient>;
111 let provider_info: ProviderInfo;
112 let signer: Signer;
113
114 match config.esplora.clone() {
115 Some(esplora) => match config.rpc.clone() {
116 Some(rpc) => {
117 let auth = Auth::UserPass(rpc.username, rpc.password);
119 let provider = Box::new(SimplexProvider::new(
120 esplora.url.clone(),
121 rpc.url.clone(),
122 auth.clone(),
123 SimplicityNetwork::default_regtest(),
124 ));
125
126 provider_info = ProviderInfo {
127 esplora_url: esplora.url,
128 elements_url: Some(rpc.url),
129 auth: Some(auth),
130 };
131 signer = Signer::new(config.mnemonic.as_str(), provider);
132 client = None;
133 }
134 None => {
135 let network = match esplora.network.as_str() {
137 "Liquid" => SimplicityNetwork::Liquid,
138 "LiquidTestnet" => SimplicityNetwork::LiquidTestnet,
139 "ElementsRegtest" => SimplicityNetwork::default_regtest(),
140 other => return Err(TestError::BadNetworkName(other.to_string())),
141 };
142 let provider = Box::new(EsploraProvider::new(esplora.url.clone(), network));
143
144 provider_info = ProviderInfo {
145 esplora_url: esplora.url,
146 elements_url: None,
147 auth: None,
148 };
149 signer = Signer::new(config.mnemonic.as_str(), provider);
150 client = None;
151 }
152 },
153 None => {
154 let (regtest_client, regtest_signer) = Regtest::from_config(&config.to_regtest_config())?;
156
157 provider_info = ProviderInfo {
158 esplora_url: regtest_client.esplora_url(),
159 elements_url: Some(regtest_client.rpc_url()),
160 auth: Some(regtest_client.auth()),
161 };
162 signer = regtest_signer;
163 client = Some(regtest_client);
164 }
165 }
166
167 Ok((signer, provider_info, client))
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use std::fs;
174
175 use super::*;
176
177 #[test]
178 fn invalid_network_returns_error() {
179 let config = r#"
180 mnemonic = "exist carry drive collect lend cereal occur much tiger just involve mean"
181 bitcoins = 10000
182
183 [esplora]
184 url = "http://localhost:3000"
185 network = "InvalidNetwork"
186 "#;
187
188 let path = std::env::temp_dir().join("smplx_test_invalid_network.toml");
189 fs::write(&path, config).unwrap();
190
191 let result = TestContext::new(path);
192 let Err(e) = result else {
193 panic!("expected BadNetworkName error")
194 };
195 assert!(
196 matches!(e, TestError::BadNetworkName(ref s) if s == "InvalidNetwork"),
197 "expected BadNetworkName, got: {e}"
198 );
199 }
200}