1#![allow(clippy::cast_possible_truncation)]
7
8use std::{
9 collections::{BTreeMap, HashMap, VecDeque},
10 sync::{Arc, Mutex, MutexGuard},
11};
12
13use linera_base::{
14 abi::{ContractAbi, ServiceAbi},
15 data_types::{
16 Amount, ApplicationDescription, ApplicationPermissions, BlockHeight, Bytecode, Resources,
17 SendMessageRequest, Timestamp,
18 },
19 ensure, http,
20 identifiers::{
21 Account, AccountOwner, ApplicationId, BlobId, ChainId, DataBlobHash, ModuleId,
22 OwnerSpender, StreamName,
23 },
24 ownership::{AccountPermissionError, ChainOwnership, ManageChainError},
25 vm::VmRuntime,
26};
27use serde::Serialize;
28
29use crate::{Contract, KeyValueStore, ViewStorageContext};
30
31struct ExpectedPublishModuleCall {
32 contract: Bytecode,
33 service: Bytecode,
34 vm_runtime: VmRuntime,
35 formats: Option<Vec<u8>>,
36 module_id: ModuleId,
37}
38
39struct ExpectedCreateApplicationCall {
40 module_id: ModuleId,
41 parameters: Vec<u8>,
42 argument: Vec<u8>,
43 required_application_ids: Vec<ApplicationId>,
44 application_id: ApplicationId,
45}
46
47struct ExpectedCreateDataBlobCall {
48 bytes: Vec<u8>,
49 blob_id: BlobId,
50}
51
52pub struct MockContractRuntime<Application>
54where
55 Application: Contract,
56{
57 application_parameters: Option<Application::Parameters>,
58 application_id: Option<ApplicationId<Application::Abi>>,
59 application_creator_chain_id: Option<ChainId>,
60 application_descriptions: HashMap<ApplicationId, ApplicationDescription>,
61 chain_id: Option<ChainId>,
62 authenticated_owner: Option<Option<AccountOwner>>,
63 block_height: Option<BlockHeight>,
64 round: Option<u32>,
65 message_is_bouncing: Option<Option<bool>>,
66 message_origin_chain_id: Option<Option<ChainId>>,
67 message_origin_timestamp: Option<Option<Timestamp>>,
68 authenticated_caller_id: Option<Option<ApplicationId>>,
69 timestamp: Option<Timestamp>,
70 chain_balance: Option<Amount>,
71 owner_balances: Option<HashMap<AccountOwner, Amount>>,
72 allowances: HashMap<OwnerSpender, Amount>,
73 chain_ownership: Option<ChainOwnership>,
74 application_permissions: Option<ApplicationPermissions>,
75 can_manage_chain: Option<bool>,
76 call_application_handler: Option<CallApplicationHandler>,
77 send_message_requests: Arc<Mutex<Vec<SendMessageRequest<Application::Message>>>>,
78 outgoing_transfers: HashMap<Account, Amount>,
79 created_events: BTreeMap<StreamName, Vec<Vec<u8>>>,
80 events: BTreeMap<(ChainId, StreamName, u32), Vec<u8>>,
81 claim_requests: Vec<ClaimRequest>,
82 expected_service_queries: VecDeque<(ApplicationId, String, String)>,
83 expected_http_requests: VecDeque<(http::Request, http::Response)>,
84 expected_read_data_blob_requests: VecDeque<(DataBlobHash, Vec<u8>)>,
85 expected_assert_data_blob_exists_requests: VecDeque<(DataBlobHash, Option<()>)>,
86 expected_has_empty_storage_requests: VecDeque<(ApplicationId, bool)>,
87 expected_open_chain_calls: VecDeque<(ChainOwnership, ApplicationPermissions, Amount, ChainId)>,
88 expected_publish_module_calls: VecDeque<ExpectedPublishModuleCall>,
89 expected_create_application_calls: VecDeque<ExpectedCreateApplicationCall>,
90 expected_create_data_blob_calls: VecDeque<ExpectedCreateDataBlobCall>,
91 remaining_fuel: Option<u64>,
92 key_value_store: KeyValueStore,
93}
94
95impl<Application> Default for MockContractRuntime<Application>
96where
97 Application: Contract,
98{
99 fn default() -> Self {
100 MockContractRuntime::new()
101 }
102}
103
104impl<Application> MockContractRuntime<Application>
105where
106 Application: Contract,
107{
108 pub fn new() -> Self {
110 MockContractRuntime {
111 application_parameters: None,
112 application_id: None,
113 application_creator_chain_id: None,
114 application_descriptions: HashMap::new(),
115 chain_id: None,
116 authenticated_owner: None,
117 block_height: None,
118 round: None,
119 message_is_bouncing: None,
120 message_origin_chain_id: None,
121 message_origin_timestamp: None,
122 authenticated_caller_id: None,
123 timestamp: None,
124 chain_balance: None,
125 owner_balances: None,
126 allowances: HashMap::new(),
127 chain_ownership: None,
128 application_permissions: None,
129 can_manage_chain: None,
130 call_application_handler: None,
131 send_message_requests: Arc::default(),
132 outgoing_transfers: HashMap::new(),
133 created_events: BTreeMap::new(),
134 events: BTreeMap::new(),
135 claim_requests: Vec::new(),
136 expected_service_queries: VecDeque::new(),
137 expected_http_requests: VecDeque::new(),
138 expected_read_data_blob_requests: VecDeque::new(),
139 expected_assert_data_blob_exists_requests: VecDeque::new(),
140 expected_has_empty_storage_requests: VecDeque::new(),
141 expected_open_chain_calls: VecDeque::new(),
142 expected_publish_module_calls: VecDeque::new(),
143 expected_create_application_calls: VecDeque::new(),
144 expected_create_data_blob_calls: VecDeque::new(),
145 remaining_fuel: None,
146 key_value_store: KeyValueStore::mock().to_mut(),
147 }
148 }
149
150 pub fn key_value_store(&self) -> KeyValueStore {
152 self.key_value_store.clone()
153 }
154
155 pub fn root_view_storage_context(&self) -> ViewStorageContext {
157 ViewStorageContext::new_unchecked(self.key_value_store(), Vec::new(), ())
158 }
159
160 pub fn with_application_parameters(
162 mut self,
163 application_parameters: Application::Parameters,
164 ) -> Self {
165 self.application_parameters = Some(application_parameters);
166 self
167 }
168
169 pub fn set_application_parameters(
171 &mut self,
172 application_parameters: Application::Parameters,
173 ) -> &mut Self {
174 self.application_parameters = Some(application_parameters);
175 self
176 }
177
178 pub fn application_parameters(&mut self) -> Application::Parameters {
180 self.application_parameters.clone().expect(
181 "Application parameters have not been mocked, \
182 please call `MockContractRuntime::set_application_parameters` first",
183 )
184 }
185
186 pub fn with_application_id(mut self, application_id: ApplicationId<Application::Abi>) -> Self {
188 self.application_id = Some(application_id);
189 self
190 }
191
192 pub fn set_application_id(
194 &mut self,
195 application_id: ApplicationId<Application::Abi>,
196 ) -> &mut Self {
197 self.application_id = Some(application_id);
198 self
199 }
200
201 pub fn application_id(&mut self) -> ApplicationId<Application::Abi> {
203 self.application_id.expect(
204 "Application ID has not been mocked, \
205 please call `MockContractRuntime::set_application_id` first",
206 )
207 }
208
209 pub fn with_application_creator_chain_id(mut self, chain_id: ChainId) -> Self {
211 self.application_creator_chain_id = Some(chain_id);
212 self
213 }
214
215 pub fn set_application_creator_chain_id(&mut self, chain_id: ChainId) -> &mut Self {
217 self.application_creator_chain_id = Some(chain_id);
218 self
219 }
220
221 pub fn application_creator_chain_id(&mut self) -> ChainId {
223 self.application_creator_chain_id.expect(
224 "Application creator chain ID has not been mocked, \
225 please call `MockContractRuntime::set_application_creator_chain_id` first",
226 )
227 }
228
229 pub fn with_application_description(
231 mut self,
232 application_id: ApplicationId,
233 description: ApplicationDescription,
234 ) -> Self {
235 self.application_descriptions
236 .insert(application_id, description);
237 self
238 }
239
240 pub fn set_application_description(
242 &mut self,
243 application_id: ApplicationId,
244 description: ApplicationDescription,
245 ) -> &mut Self {
246 self.application_descriptions
247 .insert(application_id, description);
248 self
249 }
250
251 pub fn read_application_description(
253 &mut self,
254 application_id: ApplicationId,
255 ) -> ApplicationDescription {
256 self.application_descriptions
257 .get(&application_id)
258 .cloned()
259 .unwrap_or_else(|| {
260 panic!(
261 "Application description for {application_id:?} has not been mocked, \
262 please call `MockContractRuntime::set_application_description` first"
263 )
264 })
265 }
266
267 pub fn with_chain_id(mut self, chain_id: ChainId) -> Self {
269 self.chain_id = Some(chain_id);
270 self
271 }
272
273 pub fn set_chain_id(&mut self, chain_id: ChainId) -> &mut Self {
275 self.chain_id = Some(chain_id);
276 self
277 }
278
279 pub fn chain_id(&mut self) -> ChainId {
281 self.chain_id.expect(
282 "Chain ID has not been mocked, \
283 please call `MockContractRuntime::set_chain_id` first",
284 )
285 }
286
287 pub fn with_authenticated_owner(
289 mut self,
290 authenticated_owner: impl Into<Option<AccountOwner>>,
291 ) -> Self {
292 self.authenticated_owner = Some(authenticated_owner.into());
293 self
294 }
295
296 pub fn set_authenticated_owner(
298 &mut self,
299 authenticated_owner: impl Into<Option<AccountOwner>>,
300 ) -> &mut Self {
301 self.authenticated_owner = Some(authenticated_owner.into());
302 self
303 }
304
305 pub fn authenticated_owner(&mut self) -> Option<AccountOwner> {
307 self.authenticated_owner.expect(
308 "Authenticated owner has not been mocked, \
309 please call `MockContractRuntime::set_authenticated_owner` first",
310 )
311 }
312
313 pub fn with_block_height(mut self, block_height: BlockHeight) -> Self {
315 self.block_height = Some(block_height);
316 self
317 }
318
319 pub fn set_block_height(&mut self, block_height: BlockHeight) -> &mut Self {
321 self.block_height = Some(block_height);
322 self
323 }
324
325 pub fn with_round(mut self, round: u32) -> Self {
327 self.round = Some(round);
328 self
329 }
330
331 pub fn set_round(&mut self, round: u32) -> &mut Self {
333 self.round = Some(round);
334 self
335 }
336
337 pub fn block_height(&mut self) -> BlockHeight {
339 self.block_height.expect(
340 "Block height has not been mocked, \
341 please call `MockContractRuntime::set_block_height` first",
342 )
343 }
344
345 pub fn with_message_is_bouncing(
347 mut self,
348 message_is_bouncing: impl Into<Option<bool>>,
349 ) -> Self {
350 self.message_is_bouncing = Some(message_is_bouncing.into());
351 self
352 }
353
354 pub fn set_message_is_bouncing(
356 &mut self,
357 message_is_bouncing: impl Into<Option<bool>>,
358 ) -> &mut Self {
359 self.message_is_bouncing = Some(message_is_bouncing.into());
360 self
361 }
362
363 pub fn message_is_bouncing(&mut self) -> Option<bool> {
366 self.message_is_bouncing.expect(
367 "`message_is_bouncing` flag has not been mocked, \
368 please call `MockContractRuntime::set_message_is_bouncing` first",
369 )
370 }
371
372 pub fn set_message_origin_chain_id(
374 &mut self,
375 message_origin_chain_id: impl Into<Option<ChainId>>,
376 ) -> &mut Self {
377 self.message_origin_chain_id = Some(message_origin_chain_id.into());
378 self
379 }
380
381 pub fn message_origin_chain_id(&mut self) -> Option<ChainId> {
384 self.message_origin_chain_id.expect(
385 "`message_origin_chain_id` has not been mocked, \
386 please call `MockContractRuntime::set_message_origin_chain_id` first",
387 )
388 }
389
390 pub fn set_message_origin_timestamp(
392 &mut self,
393 message_origin_timestamp: impl Into<Option<Timestamp>>,
394 ) -> &mut Self {
395 self.message_origin_timestamp = Some(message_origin_timestamp.into());
396 self
397 }
398
399 pub fn message_origin_timestamp(&mut self) -> Option<Timestamp> {
402 self.message_origin_timestamp.expect(
403 "`message_origin_timestamp` has not been mocked, \
404 please call `MockContractRuntime::set_message_origin_timestamp` first",
405 )
406 }
407
408 pub fn with_authenticated_caller_id(
410 mut self,
411 authenticated_caller_id: impl Into<Option<ApplicationId>>,
412 ) -> Self {
413 self.authenticated_caller_id = Some(authenticated_caller_id.into());
414 self
415 }
416
417 pub fn set_authenticated_caller_id(
419 &mut self,
420 authenticated_caller_id: impl Into<Option<ApplicationId>>,
421 ) -> &mut Self {
422 self.authenticated_caller_id = Some(authenticated_caller_id.into());
423 self
424 }
425
426 pub fn authenticated_caller_id(&mut self) -> Option<ApplicationId> {
429 self.authenticated_caller_id.expect(
430 "Authenticated caller ID has not been mocked, \
431 please call `MockContractRuntime::set_authenticated_caller_id` first",
432 )
433 }
434
435 pub fn check_account_permission(
437 &mut self,
438 owner: AccountOwner,
439 ) -> Result<(), AccountPermissionError> {
440 ensure!(
441 self.authenticated_owner() == Some(owner)
442 || self.authenticated_caller_id().map(AccountOwner::from) == Some(owner),
443 AccountPermissionError::NotPermitted(owner)
444 );
445 Ok(())
446 }
447
448 pub fn with_system_time(mut self, timestamp: Timestamp) -> Self {
450 self.timestamp = Some(timestamp);
451 self
452 }
453
454 pub fn set_system_time(&mut self, timestamp: Timestamp) -> &mut Self {
456 self.timestamp = Some(timestamp);
457 self
458 }
459
460 pub fn system_time(&mut self) -> Timestamp {
462 self.timestamp.expect(
463 "System time has not been mocked, \
464 please call `MockContractRuntime::set_system_time` first",
465 )
466 }
467
468 pub fn with_chain_balance(mut self, chain_balance: Amount) -> Self {
470 self.chain_balance = Some(chain_balance);
471 self
472 }
473
474 pub fn set_chain_balance(&mut self, chain_balance: Amount) -> &mut Self {
476 self.chain_balance = Some(chain_balance);
477 self
478 }
479
480 pub fn chain_balance(&mut self) -> Amount {
482 *self.chain_balance_mut()
483 }
484
485 fn chain_balance_mut(&mut self) -> &mut Amount {
487 self.chain_balance.as_mut().expect(
488 "Chain balance has not been mocked, \
489 please call `MockContractRuntime::set_chain_balance` first",
490 )
491 }
492
493 pub fn with_owner_balances(
495 mut self,
496 owner_balances: impl IntoIterator<Item = (AccountOwner, Amount)>,
497 ) -> Self {
498 self.owner_balances = Some(owner_balances.into_iter().collect());
499 self
500 }
501
502 pub fn set_owner_balances(
504 &mut self,
505 owner_balances: impl IntoIterator<Item = (AccountOwner, Amount)>,
506 ) -> &mut Self {
507 self.owner_balances = Some(owner_balances.into_iter().collect());
508 self
509 }
510
511 pub fn with_owner_balance(mut self, owner: AccountOwner, balance: Amount) -> Self {
513 self.set_owner_balance(owner, balance);
514 self
515 }
516
517 pub fn set_owner_balance(&mut self, owner: AccountOwner, balance: Amount) -> &mut Self {
519 self.owner_balances
520 .get_or_insert_with(HashMap::new)
521 .insert(owner, balance);
522 self
523 }
524
525 pub fn owner_balance(&mut self, owner: AccountOwner) -> Amount {
527 *self.owner_balance_mut(owner)
528 }
529
530 pub fn with_allowances(
532 mut self,
533 allowances: impl IntoIterator<Item = (AccountOwner, AccountOwner, Amount)>,
534 ) -> Self {
535 self.set_allowances(allowances);
536 self
537 }
538
539 pub fn set_allowances(
541 &mut self,
542 allowances: impl IntoIterator<Item = (AccountOwner, AccountOwner, Amount)>,
543 ) -> &mut Self {
544 self.allowances = allowances
545 .into_iter()
546 .filter_map(|(owner, spender, amount)| {
547 if amount == Amount::ZERO {
548 None
549 } else {
550 Some((OwnerSpender::new(owner, spender), amount))
551 }
552 })
553 .collect();
554 self
555 }
556
557 pub fn with_allowance(
559 mut self,
560 owner: AccountOwner,
561 spender: AccountOwner,
562 allowance: Amount,
563 ) -> Self {
564 self.set_allowance(owner, spender, allowance);
565 self
566 }
567
568 pub fn set_allowance(
570 &mut self,
571 owner: AccountOwner,
572 spender: AccountOwner,
573 allowance: Amount,
574 ) -> &mut Self {
575 let owner_spender = OwnerSpender::new(owner, spender);
576 if allowance == Amount::ZERO {
577 self.allowances.remove(&owner_spender);
578 } else {
579 self.allowances.insert(owner_spender, allowance);
580 }
581 self
582 }
583
584 pub fn allowance(&self, owner: AccountOwner, spender: AccountOwner) -> Amount {
586 self.allowances
587 .get(&OwnerSpender::new(owner, spender))
588 .copied()
589 .unwrap_or(Amount::ZERO)
590 }
591
592 pub fn allowances(&self) -> Vec<(AccountOwner, AccountOwner, Amount)> {
594 self.allowances
595 .iter()
596 .map(|(owner_spender, amount)| (owner_spender.owner, owner_spender.spender, *amount))
597 .collect()
598 }
599
600 fn owner_balance_mut(&mut self, owner: AccountOwner) -> &mut Amount {
602 self.owner_balances
603 .as_mut()
604 .expect(
605 "Owner balances have not been mocked, \
606 please call `MockContractRuntime::set_owner_balances` first",
607 )
608 .get_mut(&owner)
609 .unwrap_or_else(|| {
610 panic!(
611 "Balance for owner {owner} was not mocked, \
612 please include a balance for them in the call to \
613 `MockContractRuntime::set_owner_balances`"
614 )
615 })
616 }
617
618 pub fn send_message(&mut self, destination: ChainId, message: Application::Message) {
620 self.prepare_message(message).send_to(destination)
621 }
622
623 pub fn prepare_message(
625 &mut self,
626 message: Application::Message,
627 ) -> MessageBuilder<Application::Message> {
628 MessageBuilder::new(message, self.send_message_requests.clone())
629 }
630
631 pub fn created_send_message_requests(
633 &self,
634 ) -> MutexGuard<'_, Vec<SendMessageRequest<Application::Message>>> {
635 self.send_message_requests
636 .try_lock()
637 .expect("Unit test should be single-threaded")
638 }
639
640 pub fn transfer(&mut self, source: AccountOwner, destination: Account, amount: Amount) {
643 self.debit(source, amount);
644
645 if Some(destination.chain_id) == self.chain_id {
646 self.credit(destination.owner, amount);
647 } else {
648 let destination_entry = self.outgoing_transfers.entry(destination).or_default();
649 *destination_entry = destination_entry
650 .try_add(amount)
651 .expect("Outgoing transfer value overflow");
652 }
653 }
654
655 fn debit(&mut self, source: AccountOwner, amount: Amount) {
658 let source_balance = if source == AccountOwner::CHAIN {
659 self.chain_balance_mut()
660 } else {
661 self.owner_balance_mut(source)
662 };
663
664 *source_balance = source_balance
665 .try_sub(amount)
666 .expect("Insufficient funds in source account");
667 }
668
669 fn credit(&mut self, destination: AccountOwner, amount: Amount) {
672 let destination_balance = if destination == AccountOwner::CHAIN {
673 self.chain_balance_mut()
674 } else {
675 self.owner_balance_mut(destination)
676 };
677
678 *destination_balance = destination_balance
679 .try_add(amount)
680 .expect("Account balance overflow");
681 }
682
683 pub fn outgoing_transfers(&self) -> &HashMap<Account, Amount> {
685 &self.outgoing_transfers
686 }
687
688 pub fn claim(&mut self, source: Account, destination: Account, amount: Amount) {
690 if Some(source.chain_id) == self.chain_id {
691 self.debit(source.owner, amount);
692
693 if Some(destination.chain_id) == self.chain_id {
694 self.credit(destination.owner, amount);
695 }
696 }
697
698 self.claim_requests.push(ClaimRequest {
699 source,
700 amount,
701 destination,
702 });
703 }
704
705 pub fn claim_requests(&self) -> &[ClaimRequest] {
707 &self.claim_requests
708 }
709
710 pub fn approve(&mut self, owner: AccountOwner, spender: AccountOwner, amount: Amount) {
712 self.set_allowance(owner, spender, amount);
713 }
714
715 pub fn transfer_from(
718 &mut self,
719 owner: AccountOwner,
720 spender: AccountOwner,
721 destination: Account,
722 amount: Amount,
723 ) {
724 let owner_spender = OwnerSpender::new(owner, spender);
725 let remaining_allowance = self
726 .allowances
727 .get(&owner_spender)
728 .copied()
729 .unwrap_or(Amount::ZERO)
730 .try_sub(amount)
731 .expect("Insufficient allowance for transfer_from");
732
733 if remaining_allowance == Amount::ZERO {
734 self.allowances.remove(&owner_spender);
735 } else {
736 self.allowances.insert(owner_spender, remaining_allowance);
737 }
738
739 self.debit(owner, amount);
740
741 if Some(destination.chain_id) == self.chain_id {
742 self.credit(destination.owner, amount);
743 } else {
744 let destination_entry = self.outgoing_transfers.entry(destination).or_default();
745 *destination_entry = destination_entry
746 .try_add(amount)
747 .expect("Outgoing transfer value overflow");
748 }
749 }
750
751 pub fn with_chain_ownership(mut self, chain_ownership: ChainOwnership) -> Self {
753 self.chain_ownership = Some(chain_ownership);
754 self
755 }
756
757 pub fn set_chain_ownership(&mut self, chain_ownership: ChainOwnership) -> &mut Self {
759 self.chain_ownership = Some(chain_ownership);
760 self
761 }
762
763 pub fn chain_ownership(&mut self) -> ChainOwnership {
765 self.chain_ownership.clone().expect(
766 "Chain ownership has not been mocked, \
767 please call `MockContractRuntime::set_chain_ownership` first",
768 )
769 }
770
771 pub fn with_application_permissions(
773 mut self,
774 application_permissions: ApplicationPermissions,
775 ) -> Self {
776 self.application_permissions = Some(application_permissions);
777 self
778 }
779
780 pub fn set_application_permissions(
782 &mut self,
783 application_permissions: ApplicationPermissions,
784 ) -> &mut Self {
785 self.application_permissions = Some(application_permissions);
786 self
787 }
788
789 pub fn application_permissions(&mut self) -> ApplicationPermissions {
791 self.application_permissions.clone().expect(
792 "Application permissions have not been mocked, \
793 please call `MockContractRuntime::set_application_permissions` first",
794 )
795 }
796
797 pub fn with_can_manage_chain(mut self, can_manage_chain: bool) -> Self {
800 self.can_manage_chain = Some(can_manage_chain);
801 self
802 }
803
804 pub fn set_can_manage_chain(&mut self, can_manage_chain: bool) -> &mut Self {
807 self.can_manage_chain = Some(can_manage_chain);
808 self
809 }
810
811 pub fn close_chain(&mut self) -> Result<(), ManageChainError> {
814 let authorized = self.can_manage_chain.expect(
815 "Authorization to manage the chain has not been mocked, \
816 please call `MockContractRuntime::set_can_manage_chain` first",
817 );
818
819 if authorized {
820 Ok(())
821 } else {
822 Err(ManageChainError::NotPermitted)
823 }
824 }
825
826 pub fn change_ownership(&mut self, ownership: ChainOwnership) -> Result<(), ManageChainError> {
829 let authorized = self.can_manage_chain.expect(
830 "Authorization to manage the chain has not been mocked, \
831 please call `MockContractRuntime::set_can_manage_chain` first",
832 );
833
834 if authorized {
835 self.chain_ownership = Some(ownership);
836 Ok(())
837 } else {
838 Err(ManageChainError::NotPermitted)
839 }
840 }
841
842 pub fn change_application_permissions(
845 &mut self,
846 application_permissions: ApplicationPermissions,
847 ) -> Result<(), ManageChainError> {
848 let authorized = self.can_manage_chain.expect(
849 "Authorization to manage the chain has not been mocked, \
850 please call `MockContractRuntime::set_can_manage_chain` first",
851 );
852
853 if authorized {
854 let application_id = self
855 .application_id
856 .expect("The application doesn't have an ID!")
857 .forget_abi();
858 self.can_manage_chain = Some(application_permissions.can_manage_chain(&application_id));
859 Ok(())
860 } else {
861 Err(ManageChainError::NotPermitted)
862 }
863 }
864
865 pub fn add_expected_open_chain_call(
867 &mut self,
868 ownership: ChainOwnership,
869 application_permissions: ApplicationPermissions,
870 balance: Amount,
871 chain_id: ChainId,
872 ) {
873 self.expected_open_chain_calls.push_back((
874 ownership,
875 application_permissions,
876 balance,
877 chain_id,
878 ));
879 }
880
881 pub fn open_chain(
884 &mut self,
885 ownership: ChainOwnership,
886 application_permissions: ApplicationPermissions,
887 balance: Amount,
888 ) -> ChainId {
889 let (expected_ownership, expected_permissions, expected_balance, chain_id) = self
890 .expected_open_chain_calls
891 .pop_front()
892 .expect("Unexpected open_chain call");
893 assert_eq!(&ownership, &expected_ownership);
894 assert_eq!(&application_permissions, &expected_permissions);
895 assert_eq!(balance, expected_balance);
896 chain_id
897 }
898
899 pub fn add_expected_publish_module_call(
901 &mut self,
902 contract: Bytecode,
903 service: Bytecode,
904 vm_runtime: VmRuntime,
905 formats: Option<Vec<u8>>,
906 module_id: ModuleId,
907 ) {
908 self.expected_publish_module_calls
909 .push_back(ExpectedPublishModuleCall {
910 contract,
911 service,
912 vm_runtime,
913 formats,
914 module_id,
915 });
916 }
917
918 pub fn add_expected_create_application_call<Parameters, InstantiationArgument>(
920 &mut self,
921 module_id: ModuleId,
922 parameters: Parameters,
923 argument: InstantiationArgument,
924 required_application_ids: Vec<ApplicationId>,
925 application_id: ApplicationId,
926 ) where
927 Parameters: Serialize,
928 InstantiationArgument: Serialize,
929 {
930 let parameters = serde_json::to_vec(¶meters)
931 .expect("Failed to serialize `Parameters` type for a cross-application call");
932 let argument = serde_json::to_vec(&argument).expect(
933 "Failed to serialize `InstantiationArgument` type for a cross-application call",
934 );
935 self.expected_create_application_calls
936 .push_back(ExpectedCreateApplicationCall {
937 module_id,
938 parameters,
939 argument,
940 required_application_ids,
941 application_id,
942 });
943 }
944
945 pub fn add_expected_create_data_blob_call(&mut self, bytes: Vec<u8>, blob_id: BlobId) {
947 self.expected_create_data_blob_calls
948 .push_back(ExpectedCreateDataBlobCall { bytes, blob_id });
949 }
950
951 pub fn publish_module(
953 &mut self,
954 contract: Bytecode,
955 service: Bytecode,
956 vm_runtime: VmRuntime,
957 formats: Option<Vec<u8>>,
958 ) -> ModuleId {
959 let ExpectedPublishModuleCall {
960 contract: expected_contract,
961 service: expected_service,
962 vm_runtime: expected_vm_runtime,
963 formats: expected_formats,
964 module_id,
965 } = self
966 .expected_publish_module_calls
967 .pop_front()
968 .expect("Unexpected publish_module call");
969 assert_eq!(&contract, &expected_contract);
970 assert_eq!(&service, &expected_service);
971 assert_eq!(vm_runtime, expected_vm_runtime);
972 assert_eq!(formats, expected_formats);
973 module_id
974 }
975
976 pub fn create_application<Abi, Parameters, InstantiationArgument>(
978 &mut self,
979 module_id: ModuleId,
980 parameters: &Parameters,
981 argument: &InstantiationArgument,
982 required_application_ids: Vec<ApplicationId>,
983 ) -> ApplicationId<Abi>
984 where
985 Abi: ContractAbi,
986 Parameters: Serialize,
987 InstantiationArgument: Serialize,
988 {
989 let ExpectedCreateApplicationCall {
990 module_id: expected_module_id,
991 parameters: expected_parameters,
992 argument: expected_argument,
993 required_application_ids: expected_required_app_ids,
994 application_id,
995 } = self
996 .expected_create_application_calls
997 .pop_front()
998 .expect("Unexpected create_application call");
999 let parameters = serde_json::to_vec(parameters)
1000 .expect("Failed to serialize `Parameters` type for a cross-application call");
1001 let argument = serde_json::to_vec(argument).expect(
1002 "Failed to serialize `InstantiationArgument` type for a cross-application call",
1003 );
1004 assert_eq!(module_id, expected_module_id);
1005 assert_eq!(parameters, expected_parameters);
1006 assert_eq!(argument, expected_argument);
1007 assert_eq!(
1008 required_application_ids.as_slice(),
1009 expected_required_app_ids.as_slice()
1010 );
1011 application_id.with_abi::<Abi>()
1012 }
1013
1014 pub fn create_data_blob(&mut self, bytes: Vec<u8>) -> DataBlobHash {
1016 let ExpectedCreateDataBlobCall {
1017 bytes: expected_bytes,
1018 blob_id,
1019 } = self
1020 .expected_create_data_blob_calls
1021 .pop_front()
1022 .expect("Unexpected create_data_blob call");
1023 assert_eq!(bytes, expected_bytes);
1024 DataBlobHash(blob_id.hash)
1025 }
1026
1027 pub fn with_call_application_handler(
1029 mut self,
1030 handler: impl FnMut(bool, ApplicationId, Vec<u8>) -> Vec<u8> + 'static,
1031 ) -> Self {
1032 self.call_application_handler = Some(Box::new(handler));
1033 self
1034 }
1035
1036 pub fn set_call_application_handler(
1038 &mut self,
1039 handler: impl FnMut(bool, ApplicationId, Vec<u8>) -> Vec<u8> + 'static,
1040 ) -> &mut Self {
1041 self.call_application_handler = Some(Box::new(handler));
1042 self
1043 }
1044
1045 pub fn call_application<A: ContractAbi + Send>(
1047 &mut self,
1048 authenticated: bool,
1049 application: ApplicationId<A>,
1050 call: &A::Operation,
1051 ) -> A::Response {
1052 let call_bytes = <A as ContractAbi>::serialize_operation(call)
1053 .expect("Failed to serialize `Operation` in test runtime cross-application call");
1054
1055 let handler = self.call_application_handler.as_mut().expect(
1056 "Handler for `call_application` has not been mocked, \
1057 please call `MockContractRuntime::set_call_application_handler` first",
1058 );
1059 let response_bytes = handler(authenticated, application.forget_abi(), call_bytes);
1060
1061 A::deserialize_response(response_bytes)
1062 .expect("Failed to deserialize `Response` in test runtime cross-application call")
1063 }
1064
1065 pub fn emit(&mut self, name: StreamName, value: &Application::EventValue) -> u32 {
1067 let value = bcs::to_bytes(value).expect("Failed to serialize event value");
1068 let entry = self.created_events.entry(name).or_default();
1069 entry.push(value);
1070 entry.len() as u32 - 1
1071 }
1072
1073 pub fn add_event(&mut self, chain_id: ChainId, name: StreamName, index: u32, value: &[u8]) {
1075 self.events.insert((chain_id, name, index), value.to_vec());
1076 }
1077
1078 pub fn read_event(
1082 &mut self,
1083 chain_id: ChainId,
1084 name: StreamName,
1085 index: u32,
1086 ) -> Application::EventValue {
1087 let value = self
1088 .events
1089 .get(&(chain_id, name, index))
1090 .expect("Event not found");
1091 bcs::from_bytes(value).expect("Failed to deserialize event value")
1092 }
1093
1094 pub fn subscribe_to_events(
1096 &mut self,
1097 _chain_id: ChainId,
1098 _application_id: ApplicationId,
1099 _name: StreamName,
1100 ) {
1101 }
1103
1104 pub fn unsubscribe_from_events(
1106 &mut self,
1107 _chain_id: ChainId,
1108 _application_id: ApplicationId,
1109 _name: StreamName,
1110 ) {
1111 }
1113
1114 pub fn add_expected_service_query<A: ServiceAbi + Send>(
1116 &mut self,
1117 application_id: ApplicationId<A>,
1118 query: A::Query,
1119 response: A::QueryResponse,
1120 ) {
1121 let query = serde_json::to_string(&query).expect("Failed to serialize query");
1122 let response = serde_json::to_string(&response).expect("Failed to serialize response");
1123 self.expected_service_queries
1124 .push_back((application_id.forget_abi(), query, response));
1125 }
1126
1127 pub fn add_expected_http_request(&mut self, request: http::Request, response: http::Response) {
1129 self.expected_http_requests.push_back((request, response));
1130 }
1131
1132 pub fn add_expected_read_data_blob_requests(&mut self, hash: DataBlobHash, response: Vec<u8>) {
1134 self.expected_read_data_blob_requests
1135 .push_back((hash, response));
1136 }
1137
1138 pub fn add_expected_assert_data_blob_exists_requests(
1140 &mut self,
1141 hash: DataBlobHash,
1142 response: Option<()>,
1143 ) {
1144 self.expected_assert_data_blob_exists_requests
1145 .push_back((hash, response));
1146 }
1147
1148 pub fn add_expected_has_empty_storage_requests(
1150 &mut self,
1151 application: ApplicationId,
1152 response: bool,
1153 ) {
1154 self.expected_has_empty_storage_requests
1155 .push_back((application, response));
1156 }
1157
1158 pub fn query_service<A: ServiceAbi + Send>(
1166 &mut self,
1167 application_id: ApplicationId<A>,
1168 query: A::Query,
1169 ) -> A::QueryResponse {
1170 let maybe_query = self.expected_service_queries.pop_front();
1171 let (expected_id, expected_query, response) =
1172 maybe_query.expect("Unexpected service query");
1173 assert_eq!(application_id.forget_abi(), expected_id);
1174 let query = serde_json::to_string(&query).expect("Failed to serialize query");
1175 assert_eq!(query, expected_query);
1176 serde_json::from_str(&response).expect("Failed to deserialize response")
1177 }
1178
1179 pub fn http_request(&mut self, request: http::Request) -> http::Response {
1187 let maybe_request = self.expected_http_requests.pop_front();
1188 let (expected_request, response) = maybe_request.expect("Unexpected HTTP request");
1189 assert_eq!(&request, &expected_request);
1190 response
1191 }
1192
1193 pub fn assert_before(&mut self, timestamp: Timestamp) {
1199 assert!(self.timestamp.is_some_and(|t| t < timestamp))
1200 }
1201
1202 pub fn read_data_blob(&mut self, hash: DataBlobHash) -> Vec<u8> {
1204 let maybe_request = self.expected_read_data_blob_requests.pop_front();
1205 let (expected_hash, response) = maybe_request.expect("Unexpected read_data_blob request");
1206 assert_eq!(hash, expected_hash);
1207 response
1208 }
1209
1210 pub fn assert_data_blob_exists(&mut self, hash: DataBlobHash) {
1212 let maybe_request = self.expected_assert_data_blob_exists_requests.pop_front();
1213 let (expected_blob_hash, response) =
1214 maybe_request.expect("Unexpected assert_data_blob_exists request");
1215 assert_eq!(hash, expected_blob_hash);
1216 response.expect("Blob does not exist!");
1217 }
1218
1219 pub fn has_empty_storage(&mut self, application: ApplicationId) -> bool {
1221 let maybe_request = self.expected_has_empty_storage_requests.pop_front();
1222 let (expected_application_id, response) =
1223 maybe_request.expect("Unexpected has_empty_storage request");
1224 assert_eq!(application, expected_application_id);
1225 response
1226 }
1227
1228 pub fn validation_round(&mut self) -> Option<u32> {
1230 self.round
1231 }
1232
1233 pub fn with_remaining_fuel(mut self, remaining_fuel: u64) -> Self {
1235 self.remaining_fuel = Some(remaining_fuel);
1236 self
1237 }
1238
1239 pub fn set_remaining_fuel(&mut self, remaining_fuel: u64) -> &mut Self {
1241 self.remaining_fuel = Some(remaining_fuel);
1242 self
1243 }
1244
1245 pub fn remaining_fuel(&mut self) -> u64 {
1247 self.remaining_fuel.unwrap_or(u64::MAX)
1248 }
1249}
1250
1251pub type CallApplicationHandler = Box<dyn FnMut(bool, ApplicationId, Vec<u8>) -> Vec<u8>>;
1253
1254#[must_use]
1257pub struct MessageBuilder<Message>
1258where
1259 Message: Serialize,
1260{
1261 authenticated: bool,
1262 is_tracked: bool,
1263 grant: Resources,
1264 message: Message,
1265 send_message_requests: Arc<Mutex<Vec<SendMessageRequest<Message>>>>,
1266}
1267
1268impl<Message> MessageBuilder<Message>
1269where
1270 Message: Serialize,
1271{
1272 pub(crate) fn new(
1274 message: Message,
1275 send_message_requests: Arc<Mutex<Vec<SendMessageRequest<Message>>>>,
1276 ) -> Self {
1277 MessageBuilder {
1278 authenticated: false,
1279 is_tracked: false,
1280 grant: Resources::default(),
1281 message,
1282 send_message_requests,
1283 }
1284 }
1285
1286 pub fn with_tracking(mut self) -> Self {
1289 self.is_tracked = true;
1290 self
1291 }
1292
1293 pub fn with_authentication(mut self) -> Self {
1295 self.authenticated = true;
1296 self
1297 }
1298
1299 pub fn with_grant(mut self, grant: Resources) -> Self {
1301 self.grant = grant;
1302 self
1303 }
1304
1305 pub fn send_to(self, destination: ChainId) {
1307 let request = SendMessageRequest {
1308 destination,
1309 authenticated: self.authenticated,
1310 is_tracked: self.is_tracked,
1311 grant: self.grant,
1312 message: self.message,
1313 };
1314
1315 self.send_message_requests
1316 .try_lock()
1317 .expect("Unit test should be single-threaded")
1318 .push(request);
1319 }
1320}
1321
1322#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1324pub struct ClaimRequest {
1325 source: Account,
1326 destination: Account,
1327 amount: Amount,
1328}