1use super::MockResponse;
5use crate::{
6 platform::{
7 types::{evonode::EvoNode, identity::IdentityRequest},
8 Fetch, FetchMany, Query,
9 },
10 sync::block_on,
11 Error, Sdk,
12};
13use arc_swap::ArcSwapOption;
14use dapi_grpc::platform::v0::{Proof, ResponseMetadata};
15use dapi_grpc::{
16 mock::Mockable,
17 platform::v0::{self as proto},
18};
19use dash_context_provider::{ContextProvider, ContextProviderError};
20use dpp::dashcore::Network;
21use dpp::version::PlatformVersion;
22use drive_proof_verifier::FromProof;
23use rs_dapi_client::mock::MockError;
24use rs_dapi_client::{
25 mock::{Key, MockDapiClient},
26 transport::TransportRequest,
27 DapiClient, DumpData, ExecutionResponse,
28};
29use std::{collections::BTreeMap, path::PathBuf, sync::Arc};
30use tokio::sync::{Mutex, OwnedMutexGuard};
31
32#[derive(Debug)]
43pub struct MockDashPlatformSdk {
44 from_proof_expectations: BTreeMap<Key, Vec<u8>>,
45 dapi: Arc<Mutex<MockDapiClient>>,
46 sdk: ArcSwapOption<Sdk>,
47}
48
49impl MockDashPlatformSdk {
50 pub fn prove(&self) -> bool {
56 if let Some(sdk) = self.sdk.load().as_ref() {
57 sdk.prove()
58 } else {
59 panic!("sdk must be set when creating mock ")
60 }
61 }
62
63 pub(crate) fn new(dapi: Arc<Mutex<MockDapiClient>>) -> Self {
69 Self {
70 from_proof_expectations: Default::default(),
71 dapi,
72 sdk: ArcSwapOption::new(None),
73 }
74 }
75
76 pub(crate) fn set_sdk(&mut self, sdk: Sdk) {
77 self.sdk.store(Some(Arc::new(sdk)));
78 }
79
80 pub(crate) fn version<'v>(&self) -> &'v PlatformVersion {
90 if let Some(sdk) = self.sdk.load().as_ref() {
91 sdk.version()
92 } else {
93 panic!("sdk must be set when creating mock ")
94 }
95 }
96
97 #[deprecated(since = "1.4.0", note = "use load_expectations_sync")]
101 pub async fn load_expectations<P: AsRef<std::path::Path> + Send + 'static>(
102 &mut self,
103 dir: P,
104 ) -> Result<&mut Self, Error> {
105 self.load_expectations_sync(dir)
106 }
107
108 pub fn load_expectations_sync<P: AsRef<std::path::Path>>(
116 &mut self,
117 dir: P,
118 ) -> Result<&mut Self, Error> {
119 let prefix = DapiClient::DUMP_FILE_PREFIX;
120
121 let entries = dir.as_ref().read_dir().map_err(|e| {
122 Error::Config(format!(
123 "cannot load mock expectations from {}: {}",
124 dir.as_ref().display(),
125 e
126 ))
127 })?;
128
129 let files: Vec<PathBuf> = entries
130 .into_iter()
131 .filter_map(|x| x.ok())
132 .filter(|f| {
133 f.file_type().is_ok_and(|t| t.is_file())
134 && f.file_name().to_string_lossy().starts_with(prefix)
135 && f.file_name().to_string_lossy().ends_with(".json")
136 })
137 .map(|f| f.path())
138 .collect();
139
140 let mut dapi = block_on(self.dapi.clone().lock_owned())?;
141
142 for filename in &files {
143 let basename = filename.file_name().unwrap().to_str().unwrap();
144 let request_type = basename.split('_').nth(1).unwrap_or_default();
145
146 match request_type {
147 "GetDocumentsRequest" => {
148 load_expectation::<proto::GetDocumentsRequest>(&mut dapi, filename)?
149 }
150 "GetEpochsInfoRequest" => {
151 load_expectation::<proto::GetEpochsInfoRequest>(&mut dapi, filename)?
152 }
153 "GetDataContractRequest" => {
154 load_expectation::<proto::GetDataContractRequest>(&mut dapi, filename)?
155 }
156 "GetDataContractsRequest" => {
157 load_expectation::<proto::GetDataContractsRequest>(&mut dapi, filename)?
158 }
159 "GetDataContractHistoryRequest" => {
160 load_expectation::<proto::GetDataContractHistoryRequest>(&mut dapi, filename)?
161 }
162 "GetDocumentHistoryRequest" => {
163 load_expectation::<proto::GetDocumentHistoryRequest>(&mut dapi, filename)?
164 }
165 "IdentityRequest" => load_expectation::<IdentityRequest>(&mut dapi, filename)?,
166 "GetIdentityRequest" => {
167 load_expectation::<proto::GetIdentityRequest>(&mut dapi, filename)?
168 }
169
170 "GetIdentityBalanceRequest" => {
171 load_expectation::<proto::GetIdentityBalanceRequest>(&mut dapi, filename)?
172 }
173 "GetIdentityContractNonceRequest" => {
174 load_expectation::<proto::GetIdentityContractNonceRequest>(&mut dapi, filename)?
175 }
176 "GetIdentityBalanceAndRevisionRequest" => load_expectation::<
177 proto::GetIdentityBalanceAndRevisionRequest,
178 >(&mut dapi, filename)?,
179 "GetAddressInfoRequest" => {
180 load_expectation::<proto::GetAddressInfoRequest>(&mut dapi, filename)?
181 }
182 "GetAddressesInfosRequest" => {
183 load_expectation::<proto::GetAddressesInfosRequest>(&mut dapi, filename)?
184 }
185 "GetIdentityKeysRequest" => {
186 load_expectation::<proto::GetIdentityKeysRequest>(&mut dapi, filename)?
187 }
188 "GetProtocolVersionUpgradeStateRequest" => load_expectation::<
189 proto::GetProtocolVersionUpgradeStateRequest,
190 >(&mut dapi, filename)?,
191 "GetProtocolVersionUpgradeVoteStatusRequest" => {
192 load_expectation::<proto::GetProtocolVersionUpgradeVoteStatusRequest>(
193 &mut dapi, filename,
194 )?
195 }
196 "GetContestedResourcesRequest" => {
197 load_expectation::<proto::GetContestedResourcesRequest>(&mut dapi, filename)?
198 }
199 "GetContestedResourceVoteStateRequest" => load_expectation::<
200 proto::GetContestedResourceVoteStateRequest,
201 >(&mut dapi, filename)?,
202 "GetContestedResourceVotersForIdentityRequest" => {
203 load_expectation::<proto::GetContestedResourceVotersForIdentityRequest>(
204 &mut dapi, filename,
205 )?
206 }
207 "GetContestedResourceIdentityVotesRequest" => {
208 load_expectation::<proto::GetContestedResourceIdentityVotesRequest>(
209 &mut dapi, filename,
210 )?
211 }
212 "GetVotePollsByEndDateRequest" => {
213 load_expectation::<proto::GetVotePollsByEndDateRequest>(&mut dapi, filename)?
214 }
215 "GetPrefundedSpecializedBalanceRequest" => load_expectation::<
216 proto::GetPrefundedSpecializedBalanceRequest,
217 >(&mut dapi, filename)?,
218 "GetPathElementsRequest" => {
219 load_expectation::<proto::GetPathElementsRequest>(&mut dapi, filename)?
220 }
221 "GetTotalCreditsInPlatformRequest" => load_expectation::<
222 proto::GetTotalCreditsInPlatformRequest,
223 >(&mut dapi, filename)?,
224 "GetIdentityTokenBalancesRequest" => {
225 load_expectation::<proto::GetIdentityTokenBalancesRequest>(&mut dapi, filename)?
226 }
227 "GetIdentitiesTokenBalancesRequest" => load_expectation::<
228 proto::GetIdentitiesTokenBalancesRequest,
229 >(&mut dapi, filename)?,
230 "GetIdentityTokenInfosRequest" => {
231 load_expectation::<proto::GetIdentityTokenInfosRequest>(&mut dapi, filename)?
232 }
233 "GetIdentitiesTokenInfosRequest" => {
234 load_expectation::<proto::GetIdentitiesTokenInfosRequest>(&mut dapi, filename)?
235 }
236 "GetTokenStatusesRequest" => {
237 load_expectation::<proto::GetTokenStatusesRequest>(&mut dapi, filename)?
238 }
239 "GetTokenTotalSupplyRequest" => {
240 load_expectation::<proto::GetTokenTotalSupplyRequest>(&mut dapi, filename)?
241 }
242 "GetGroupInfoRequest" => {
243 load_expectation::<proto::GetGroupInfoRequest>(&mut dapi, filename)?
244 }
245 "GetGroupInfosRequest" => {
246 load_expectation::<proto::GetGroupInfosRequest>(&mut dapi, filename)?
247 }
248 "GetGroupActionsRequest" => {
249 load_expectation::<proto::GetGroupActionsRequest>(&mut dapi, filename)?
250 }
251 "GetGroupActionSignersRequest" => {
252 load_expectation::<proto::GetGroupActionSignersRequest>(&mut dapi, filename)?
253 }
254 "EvoNode" => load_expectation::<EvoNode>(&mut dapi, filename)?,
255 "GetTokenDirectPurchasePricesRequest" => load_expectation::<
256 proto::GetTokenDirectPurchasePricesRequest,
257 >(&mut dapi, filename)?,
258 "GetTokenPerpetualDistributionLastClaimRequest" => {
259 load_expectation::<proto::GetTokenPerpetualDistributionLastClaimRequest>(
260 &mut dapi, filename,
261 )?
262 }
263 "GetTokenPreProgrammedDistributionsRequest" => {
264 load_expectation::<proto::GetTokenPreProgrammedDistributionsRequest>(
265 &mut dapi, filename,
266 )?
267 }
268 "GetAddressesTrunkStateRequest" => {
269 load_expectation::<proto::GetAddressesTrunkStateRequest>(&mut dapi, filename)?
270 }
271 _ => {
272 return Err(Error::Config(format!(
273 "unknown request type {} in {}, missing match arm in load_expectations?",
274 request_type,
275 filename.display()
276 )))
277 }
278 };
279 }
280
281 Ok(self)
282 }
283
284 pub async fn expect_fetch<O: Fetch + MockResponse, Q: Query<<O as Fetch>::Query>>(
335 &mut self,
336 query: Q,
337 object: Option<O>,
338 ) -> Result<&mut Self, Error>
339 where
340 <<O as Fetch>::Request as TransportRequest>::Response: Default,
341 {
342 let (rich, wire) =
343 self.encode_rich_to_wire::<Q, <O as Fetch>::Query, <O as Fetch>::Request>(query);
344 self.expect(&rich, wire, object).await?;
345
346 Ok(self)
347 }
348
349 pub async fn remove_fetch_expectation<O, Q>(&mut self, query: Q) -> bool
353 where
354 O: Fetch,
355 Q: Query<<O as Fetch>::Query>,
356 {
357 let (rich, wire) =
358 self.encode_rich_to_wire::<Q, <O as Fetch>::Query, <O as Fetch>::Request>(query);
359 self.remove(&rich, wire).await
360 }
361
362 pub async fn expect_fetch_many<
393 K: Ord,
394 O: FetchMany<K, R>,
395 Q: Query<<O as FetchMany<K, R>>::Query>,
396 R,
397 >(
398 &mut self,
399 query: Q,
400 objects: Option<R>,
401 ) -> Result<&mut Self, Error>
402 where
403 R: FromIterator<(K, Option<O>)>
404 + MockResponse
405 + FromProof<
406 <O as FetchMany<K, R>>::Query,
407 Request = <O as FetchMany<K, R>>::Query,
408 Response = <<O as FetchMany<K, R>>::Request as TransportRequest>::Response,
409 > + Sync
410 + Send
411 + Default,
412 <<O as FetchMany<K, R>>::Request as TransportRequest>::Response: Default,
413 {
414 let (rich, wire) = self
415 .encode_rich_to_wire::<Q, <O as FetchMany<K, R>>::Query, <O as FetchMany<K, R>>::Request>(
416 query,
417 );
418 self.expect(&rich, wire, objects).await?;
419
420 Ok(self)
421 }
422
423 fn encode_rich_to_wire<Q, R, W>(&self, query: Q) -> (R, W)
435 where
436 Q: Query<R>,
437 R: Query<W> + Mockable,
438 W: TransportRequest,
439 {
440 let sdk_guard = self.sdk.load();
441 let sdk = sdk_guard
442 .as_ref()
443 .expect("sdk must be set when creating mock");
444 let settings = sdk.query_settings();
445 let rich: R = query.query(&settings).expect("query must be correct");
446 let wire: W = rich.query(&settings).expect("wire encoding must succeed");
447 (rich, wire)
448 }
449
450 async fn expect<R: Mockable + std::fmt::Debug, W: TransportRequest, O: MockResponse>(
458 &mut self,
459 rich_request: &R,
460 wire_request: W,
461 returned_object: Option<O>,
462 ) -> Result<(), Error>
463 where
464 W::Response: Default,
465 {
466 let key = Key::new(rich_request);
467
468 if self.from_proof_expectations.contains_key(&key) {
469 return Err(MockError::MockExpectationConflict(format!(
470 "proof expectation key {} already defined for {} request: {:?}",
471 key,
472 std::any::type_name::<R>(),
473 rich_request
474 ))
475 .into());
476 }
477
478 self.from_proof_expectations
479 .insert(key, returned_object.mock_serialize(self));
480
481 let mut dapi_guard = self.dapi.lock().await;
482 dapi_guard.expect(
483 &wire_request,
484 &Ok(ExecutionResponse {
485 inner: Default::default(),
486 retries: 0,
487 address: "http://127.0.0.1".parse().expect("failed to parse address"),
488 }),
489 )?;
490
491 Ok(())
492 }
493
494 async fn remove<R: Mockable, W: TransportRequest>(
496 &mut self,
497 rich_request: &R,
498 wire_request: W,
499 ) -> bool {
500 let key = Key::new(rich_request);
501 let removed_from_proof = self.from_proof_expectations.remove(&key).is_some();
502
503 let mut dapi_guard = self.dapi.lock().await;
504 let removed_from_dapi = dapi_guard.remove(&wire_request);
505
506 removed_from_proof || removed_from_dapi
507 }
508
509 pub(crate) fn parse_proof_with_metadata<I, O: FromProof<I>>(
511 &self,
512 request: O::Request,
513 response: O::Response,
514 ) -> Result<(Option<O>, ResponseMetadata, Proof), drive_proof_verifier::Error>
515 where
516 O::Request: Mockable,
517 Option<O>: MockResponse,
518 {
520 let key = Key::new(&request);
521
522 let data = match self.from_proof_expectations.get(&key) {
523 Some(d) => (
524 Option::<O>::mock_deserialize(self, d),
525 ResponseMetadata::default(),
526 Proof::default(),
527 ),
528 None => {
529 let version = self.version();
530 let provider = self.context_provider()
531 .ok_or(ContextProviderError::InvalidQuorum(
532 "expectation not found and quorum info provider not initialized with sdk.mock().quorum_info_dir()".to_string()
533 ))?;
534 O::maybe_from_proof_with_metadata(
535 request,
536 response,
537 Network::Regtest,
538 version,
539 &provider,
540 )?
541 }
542 };
543
544 Ok(data)
545 }
546 fn context_provider(&self) -> Option<impl ContextProvider> {
548 if let Some(sdk) = self.sdk.load_full() {
549 sdk.clone().context_provider()
550 } else {
551 None
552 }
553 }
554}
555
556fn load_expectation<T: TransportRequest>(
562 dapi_guard: &mut OwnedMutexGuard<MockDapiClient>,
563 path: &PathBuf,
564) -> Result<(), Error> {
565 let data = DumpData::<T>::load(path)
566 .map_err(|e| {
567 Error::Config(format!(
568 "cannot load mock expectations from {}: {}",
569 path.display(),
570 e
571 ))
572 })?
573 .deserialize();
574 dapi_guard.expect(&data.0, &data.1)?;
575 Ok(())
576}