1use std::{
7 collections::{BTreeMap, HashMap, VecDeque},
8 sync::{Arc, Mutex, MutexGuard},
9};
10
11use linera_base::{
12 abi::{ContractAbi, ServiceAbi},
13 data_types::{
14 Amount, ApplicationPermissions, BlockHeight, Bytecode, Resources, SendMessageRequest,
15 Timestamp,
16 },
17 ensure, http,
18 identifiers::{
19 Account, AccountOwner, ApplicationId, BlobId, ChainId, DataBlobHash, ModuleId, StreamName,
20 },
21 ownership::{
22 AccountPermissionError, ChainOwnership, ChangeApplicationPermissionsError, CloseChainError,
23 },
24 vm::VmRuntime,
25};
26use serde::Serialize;
27
28use crate::{Contract, KeyValueStore, ViewStorageContext};
29
30struct ExpectedPublishModuleCall {
31 contract: Bytecode,
32 service: Bytecode,
33 vm_runtime: VmRuntime,
34 module_id: ModuleId,
35}
36
37struct ExpectedCreateApplicationCall {
38 module_id: ModuleId,
39 parameters: Vec<u8>,
40 argument: Vec<u8>,
41 required_application_ids: Vec<ApplicationId>,
42 application_id: ApplicationId,
43}
44
45struct ExpectedCreateDataBlobCall {
46 bytes: Vec<u8>,
47 blob_id: BlobId,
48}
49
50pub struct MockContractRuntime<Application>
52where
53 Application: Contract,
54{
55 application_parameters: Option<Application::Parameters>,
56 application_id: Option<ApplicationId<Application::Abi>>,
57 application_creator_chain_id: Option<ChainId>,
58 chain_id: Option<ChainId>,
59 authenticated_owner: Option<Option<AccountOwner>>,
60 block_height: Option<BlockHeight>,
61 round: Option<u32>,
62 message_is_bouncing: Option<Option<bool>>,
63 message_origin_chain_id: Option<Option<ChainId>>,
64 authenticated_caller_id: Option<Option<ApplicationId>>,
65 timestamp: Option<Timestamp>,
66 chain_balance: Option<Amount>,
67 owner_balances: Option<HashMap<AccountOwner, Amount>>,
68 chain_ownership: Option<ChainOwnership>,
69 can_close_chain: Option<bool>,
70 can_change_application_permissions: Option<bool>,
71 call_application_handler: Option<CallApplicationHandler>,
72 send_message_requests: Arc<Mutex<Vec<SendMessageRequest<Application::Message>>>>,
73 outgoing_transfers: HashMap<Account, Amount>,
74 created_events: BTreeMap<StreamName, Vec<Vec<u8>>>,
75 events: BTreeMap<(ChainId, StreamName, u32), Vec<u8>>,
76 claim_requests: Vec<ClaimRequest>,
77 expected_service_queries: VecDeque<(ApplicationId, String, String)>,
78 expected_http_requests: VecDeque<(http::Request, http::Response)>,
79 expected_read_data_blob_requests: VecDeque<(DataBlobHash, Vec<u8>)>,
80 expected_assert_data_blob_exists_requests: VecDeque<(DataBlobHash, Option<()>)>,
81 expected_has_empty_storage_requests: VecDeque<(ApplicationId, bool)>,
82 expected_open_chain_calls: VecDeque<(ChainOwnership, ApplicationPermissions, Amount, ChainId)>,
83 expected_publish_module_calls: VecDeque<ExpectedPublishModuleCall>,
84 expected_create_application_calls: VecDeque<ExpectedCreateApplicationCall>,
85 expected_create_data_blob_calls: VecDeque<ExpectedCreateDataBlobCall>,
86 key_value_store: KeyValueStore,
87}
88
89impl<Application> Default for MockContractRuntime<Application>
90where
91 Application: Contract,
92{
93 fn default() -> Self {
94 MockContractRuntime::new()
95 }
96}
97
98impl<Application> MockContractRuntime<Application>
99where
100 Application: Contract,
101{
102 pub fn new() -> Self {
104 MockContractRuntime {
105 application_parameters: None,
106 application_id: None,
107 application_creator_chain_id: None,
108 chain_id: None,
109 authenticated_owner: None,
110 block_height: None,
111 round: None,
112 message_is_bouncing: None,
113 message_origin_chain_id: None,
114 authenticated_caller_id: None,
115 timestamp: None,
116 chain_balance: None,
117 owner_balances: None,
118 chain_ownership: None,
119 can_close_chain: None,
120 can_change_application_permissions: None,
121 call_application_handler: None,
122 send_message_requests: Arc::default(),
123 outgoing_transfers: HashMap::new(),
124 created_events: BTreeMap::new(),
125 events: BTreeMap::new(),
126 claim_requests: Vec::new(),
127 expected_service_queries: VecDeque::new(),
128 expected_http_requests: VecDeque::new(),
129 expected_read_data_blob_requests: VecDeque::new(),
130 expected_assert_data_blob_exists_requests: VecDeque::new(),
131 expected_has_empty_storage_requests: VecDeque::new(),
132 expected_open_chain_calls: VecDeque::new(),
133 expected_publish_module_calls: VecDeque::new(),
134 expected_create_application_calls: VecDeque::new(),
135 expected_create_data_blob_calls: VecDeque::new(),
136 key_value_store: KeyValueStore::mock().to_mut(),
137 }
138 }
139
140 pub fn key_value_store(&self) -> KeyValueStore {
142 self.key_value_store.clone()
143 }
144
145 pub fn root_view_storage_context(&self) -> ViewStorageContext {
147 ViewStorageContext::new_unchecked(self.key_value_store(), Vec::new(), ())
148 }
149
150 pub fn with_application_parameters(
152 mut self,
153 application_parameters: Application::Parameters,
154 ) -> Self {
155 self.application_parameters = Some(application_parameters);
156 self
157 }
158
159 pub fn set_application_parameters(
161 &mut self,
162 application_parameters: Application::Parameters,
163 ) -> &mut Self {
164 self.application_parameters = Some(application_parameters);
165 self
166 }
167
168 pub fn application_parameters(&mut self) -> Application::Parameters {
170 self.application_parameters.clone().expect(
171 "Application parameters have not been mocked, \
172 please call `MockContractRuntime::set_application_parameters` first",
173 )
174 }
175
176 pub fn with_application_id(mut self, application_id: ApplicationId<Application::Abi>) -> Self {
178 self.application_id = Some(application_id);
179 self
180 }
181
182 pub fn set_application_id(
184 &mut self,
185 application_id: ApplicationId<Application::Abi>,
186 ) -> &mut Self {
187 self.application_id = Some(application_id);
188 self
189 }
190
191 pub fn application_id(&mut self) -> ApplicationId<Application::Abi> {
193 self.application_id.expect(
194 "Application ID has not been mocked, \
195 please call `MockContractRuntime::set_application_id` first",
196 )
197 }
198
199 pub fn with_application_creator_chain_id(mut self, chain_id: ChainId) -> Self {
201 self.application_creator_chain_id = Some(chain_id);
202 self
203 }
204
205 pub fn set_application_creator_chain_id(&mut self, chain_id: ChainId) -> &mut Self {
207 self.application_creator_chain_id = Some(chain_id);
208 self
209 }
210
211 pub fn application_creator_chain_id(&mut self) -> ChainId {
213 self.application_creator_chain_id.expect(
214 "Application creator chain ID has not been mocked, \
215 please call `MockContractRuntime::set_application_creator_chain_id` first",
216 )
217 }
218
219 pub fn with_chain_id(mut self, chain_id: ChainId) -> Self {
221 self.chain_id = Some(chain_id);
222 self
223 }
224
225 pub fn set_chain_id(&mut self, chain_id: ChainId) -> &mut Self {
227 self.chain_id = Some(chain_id);
228 self
229 }
230
231 pub fn chain_id(&mut self) -> ChainId {
233 self.chain_id.expect(
234 "Chain ID has not been mocked, \
235 please call `MockContractRuntime::set_chain_id` first",
236 )
237 }
238
239 pub fn with_authenticated_owner(
241 mut self,
242 authenticated_owner: impl Into<Option<AccountOwner>>,
243 ) -> Self {
244 self.authenticated_owner = Some(authenticated_owner.into());
245 self
246 }
247
248 pub fn set_authenticated_owner(
250 &mut self,
251 authenticated_owner: impl Into<Option<AccountOwner>>,
252 ) -> &mut Self {
253 self.authenticated_owner = Some(authenticated_owner.into());
254 self
255 }
256
257 pub fn authenticated_owner(&mut self) -> Option<AccountOwner> {
259 self.authenticated_owner.expect(
260 "Authenticated owner has not been mocked, \
261 please call `MockContractRuntime::set_authenticated_owner` first",
262 )
263 }
264
265 pub fn with_block_height(mut self, block_height: BlockHeight) -> Self {
267 self.block_height = Some(block_height);
268 self
269 }
270
271 pub fn set_block_height(&mut self, block_height: BlockHeight) -> &mut Self {
273 self.block_height = Some(block_height);
274 self
275 }
276
277 pub fn with_round(mut self, round: u32) -> Self {
279 self.round = Some(round);
280 self
281 }
282
283 pub fn set_round(&mut self, round: u32) -> &mut Self {
285 self.round = Some(round);
286 self
287 }
288
289 pub fn block_height(&mut self) -> BlockHeight {
291 self.block_height.expect(
292 "Block height has not been mocked, \
293 please call `MockContractRuntime::set_block_height` first",
294 )
295 }
296
297 pub fn with_message_is_bouncing(
299 mut self,
300 message_is_bouncing: impl Into<Option<bool>>,
301 ) -> Self {
302 self.message_is_bouncing = Some(message_is_bouncing.into());
303 self
304 }
305
306 pub fn set_message_is_bouncing(
308 &mut self,
309 message_is_bouncing: impl Into<Option<bool>>,
310 ) -> &mut Self {
311 self.message_is_bouncing = Some(message_is_bouncing.into());
312 self
313 }
314
315 pub fn message_is_bouncing(&mut self) -> Option<bool> {
318 self.message_is_bouncing.expect(
319 "`message_is_bouncing` flag has not been mocked, \
320 please call `MockContractRuntime::set_message_is_bouncing` first",
321 )
322 }
323
324 pub fn set_message_origin_chain_id(
326 &mut self,
327 message_origin_chain_id: impl Into<Option<ChainId>>,
328 ) -> &mut Self {
329 self.message_origin_chain_id = Some(message_origin_chain_id.into());
330 self
331 }
332
333 pub fn message_origin_chain_id(&mut self) -> Option<ChainId> {
336 self.message_origin_chain_id.expect(
337 "`message_origin_chain_id` has not been mocked, \
338 please call `MockContractRuntime::set_message_origin_chain_id` first",
339 )
340 }
341
342 pub fn with_authenticated_caller_id(
344 mut self,
345 authenticated_caller_id: impl Into<Option<ApplicationId>>,
346 ) -> Self {
347 self.authenticated_caller_id = Some(authenticated_caller_id.into());
348 self
349 }
350
351 pub fn set_authenticated_caller_id(
353 &mut self,
354 authenticated_caller_id: impl Into<Option<ApplicationId>>,
355 ) -> &mut Self {
356 self.authenticated_caller_id = Some(authenticated_caller_id.into());
357 self
358 }
359
360 pub fn authenticated_caller_id(&mut self) -> Option<ApplicationId> {
363 self.authenticated_caller_id.expect(
364 "Authenticated caller ID has not been mocked, \
365 please call `MockContractRuntime::set_authenticated_caller_id` first",
366 )
367 }
368
369 pub fn check_account_permission(
371 &mut self,
372 owner: AccountOwner,
373 ) -> Result<(), AccountPermissionError> {
374 ensure!(
375 self.authenticated_owner() == Some(owner)
376 || self.authenticated_caller_id().map(AccountOwner::from) == Some(owner),
377 AccountPermissionError::NotPermitted(owner)
378 );
379 Ok(())
380 }
381
382 pub fn with_system_time(mut self, timestamp: Timestamp) -> Self {
384 self.timestamp = Some(timestamp);
385 self
386 }
387
388 pub fn set_system_time(&mut self, timestamp: Timestamp) -> &mut Self {
390 self.timestamp = Some(timestamp);
391 self
392 }
393
394 pub fn system_time(&mut self) -> Timestamp {
396 self.timestamp.expect(
397 "System time has not been mocked, \
398 please call `MockContractRuntime::set_system_time` first",
399 )
400 }
401
402 pub fn with_chain_balance(mut self, chain_balance: Amount) -> Self {
404 self.chain_balance = Some(chain_balance);
405 self
406 }
407
408 pub fn set_chain_balance(&mut self, chain_balance: Amount) -> &mut Self {
410 self.chain_balance = Some(chain_balance);
411 self
412 }
413
414 pub fn chain_balance(&mut self) -> Amount {
416 *self.chain_balance_mut()
417 }
418
419 fn chain_balance_mut(&mut self) -> &mut Amount {
421 self.chain_balance.as_mut().expect(
422 "Chain balance has not been mocked, \
423 please call `MockContractRuntime::set_chain_balance` first",
424 )
425 }
426
427 pub fn with_owner_balances(
429 mut self,
430 owner_balances: impl IntoIterator<Item = (AccountOwner, Amount)>,
431 ) -> Self {
432 self.owner_balances = Some(owner_balances.into_iter().collect());
433 self
434 }
435
436 pub fn set_owner_balances(
438 &mut self,
439 owner_balances: impl IntoIterator<Item = (AccountOwner, Amount)>,
440 ) -> &mut Self {
441 self.owner_balances = Some(owner_balances.into_iter().collect());
442 self
443 }
444
445 pub fn with_owner_balance(mut self, owner: AccountOwner, balance: Amount) -> Self {
447 self.set_owner_balance(owner, balance);
448 self
449 }
450
451 pub fn set_owner_balance(&mut self, owner: AccountOwner, balance: Amount) -> &mut Self {
453 self.owner_balances
454 .get_or_insert_with(HashMap::new)
455 .insert(owner, balance);
456 self
457 }
458
459 pub fn owner_balance(&mut self, owner: AccountOwner) -> Amount {
461 *self.owner_balance_mut(owner)
462 }
463
464 fn owner_balance_mut(&mut self, owner: AccountOwner) -> &mut Amount {
466 self.owner_balances
467 .as_mut()
468 .expect(
469 "Owner balances have not been mocked, \
470 please call `MockContractRuntime::set_owner_balances` first",
471 )
472 .get_mut(&owner)
473 .unwrap_or_else(|| {
474 panic!(
475 "Balance for owner {owner} was not mocked, \
476 please include a balance for them in the call to \
477 `MockContractRuntime::set_owner_balances`"
478 )
479 })
480 }
481
482 pub fn send_message(&mut self, destination: ChainId, message: Application::Message) {
484 self.prepare_message(message).send_to(destination)
485 }
486
487 pub fn prepare_message(
489 &mut self,
490 message: Application::Message,
491 ) -> MessageBuilder<Application::Message> {
492 MessageBuilder::new(message, self.send_message_requests.clone())
493 }
494
495 pub fn created_send_message_requests(
497 &self,
498 ) -> MutexGuard<'_, Vec<SendMessageRequest<Application::Message>>> {
499 self.send_message_requests
500 .try_lock()
501 .expect("Unit test should be single-threaded")
502 }
503
504 pub fn transfer(&mut self, source: AccountOwner, destination: Account, amount: Amount) {
507 self.debit(source, amount);
508
509 if Some(destination.chain_id) == self.chain_id {
510 self.credit(destination.owner, amount);
511 } else {
512 let destination_entry = self.outgoing_transfers.entry(destination).or_default();
513 *destination_entry = destination_entry
514 .try_add(amount)
515 .expect("Outgoing transfer value overflow");
516 }
517 }
518
519 fn debit(&mut self, source: AccountOwner, amount: Amount) {
522 let source_balance = if source == AccountOwner::CHAIN {
523 self.chain_balance_mut()
524 } else {
525 self.owner_balance_mut(source)
526 };
527
528 *source_balance = source_balance
529 .try_sub(amount)
530 .expect("Insufficient funds in source account");
531 }
532
533 fn credit(&mut self, destination: AccountOwner, amount: Amount) {
536 let destination_balance = if destination == AccountOwner::CHAIN {
537 self.chain_balance_mut()
538 } else {
539 self.owner_balance_mut(destination)
540 };
541
542 *destination_balance = destination_balance
543 .try_add(amount)
544 .expect("Account balance overflow");
545 }
546
547 pub fn outgoing_transfers(&self) -> &HashMap<Account, Amount> {
549 &self.outgoing_transfers
550 }
551
552 pub fn claim(&mut self, source: Account, destination: Account, amount: Amount) {
554 if Some(source.chain_id) == self.chain_id {
555 self.debit(source.owner, amount);
556
557 if Some(destination.chain_id) == self.chain_id {
558 self.credit(destination.owner, amount);
559 }
560 }
561
562 self.claim_requests.push(ClaimRequest {
563 source,
564 amount,
565 destination,
566 });
567 }
568
569 pub fn claim_requests(&self) -> &[ClaimRequest] {
571 &self.claim_requests
572 }
573
574 pub fn with_chain_ownership(mut self, chain_ownership: ChainOwnership) -> Self {
576 self.chain_ownership = Some(chain_ownership);
577 self
578 }
579
580 pub fn set_chain_ownership(&mut self, chain_ownership: ChainOwnership) -> &mut Self {
582 self.chain_ownership = Some(chain_ownership);
583 self
584 }
585
586 pub fn chain_ownership(&mut self) -> ChainOwnership {
588 self.chain_ownership.clone().expect(
589 "Chain ownership has not been mocked, \
590 please call `MockContractRuntime::set_chain_ownership` first",
591 )
592 }
593
594 pub fn with_can_close_chain(mut self, can_close_chain: bool) -> Self {
596 self.can_close_chain = Some(can_close_chain);
597 self
598 }
599
600 pub fn set_can_close_chain(&mut self, can_close_chain: bool) -> &mut Self {
602 self.can_close_chain = Some(can_close_chain);
603 self
604 }
605
606 pub fn with_can_change_application_permissions(
609 mut self,
610 can_change_application_permissions: bool,
611 ) -> Self {
612 self.can_change_application_permissions = Some(can_change_application_permissions);
613 self
614 }
615
616 pub fn set_can_change_application_permissions(
619 &mut self,
620 can_change_application_permissions: bool,
621 ) -> &mut Self {
622 self.can_change_application_permissions = Some(can_change_application_permissions);
623 self
624 }
625
626 pub fn close_chain(&mut self) -> Result<(), CloseChainError> {
629 let authorized = self.can_close_chain.expect(
630 "Authorization to close the chain has not been mocked, \
631 please call `MockContractRuntime::set_can_close_chain` first",
632 );
633
634 if authorized {
635 Ok(())
636 } else {
637 Err(CloseChainError::NotPermitted)
638 }
639 }
640
641 pub fn change_application_permissions(
644 &mut self,
645 application_permissions: ApplicationPermissions,
646 ) -> Result<(), ChangeApplicationPermissionsError> {
647 let authorized = self.can_change_application_permissions.expect(
648 "Authorization to change the application permissions has not been mocked, \
649 please call `MockContractRuntime::set_can_change_application_permissions` first",
650 );
651
652 if authorized {
653 let application_id = self
654 .application_id
655 .expect("The application doesn't have an ID!")
656 .forget_abi();
657 self.can_close_chain = Some(application_permissions.can_close_chain(&application_id));
658 self.can_change_application_permissions =
659 Some(application_permissions.can_change_application_permissions(&application_id));
660 Ok(())
661 } else {
662 Err(ChangeApplicationPermissionsError::NotPermitted)
663 }
664 }
665
666 pub fn add_expected_open_chain_call(
668 &mut self,
669 ownership: ChainOwnership,
670 application_permissions: ApplicationPermissions,
671 balance: Amount,
672 chain_id: ChainId,
673 ) {
674 self.expected_open_chain_calls.push_back((
675 ownership,
676 application_permissions,
677 balance,
678 chain_id,
679 ));
680 }
681
682 pub fn open_chain(
685 &mut self,
686 ownership: ChainOwnership,
687 application_permissions: ApplicationPermissions,
688 balance: Amount,
689 ) -> ChainId {
690 let (expected_ownership, expected_permissions, expected_balance, chain_id) = self
691 .expected_open_chain_calls
692 .pop_front()
693 .expect("Unexpected open_chain call");
694 assert_eq!(ownership, expected_ownership);
695 assert_eq!(application_permissions, expected_permissions);
696 assert_eq!(balance, expected_balance);
697 chain_id
698 }
699
700 pub fn add_expected_publish_module_call(
702 &mut self,
703 contract: Bytecode,
704 service: Bytecode,
705 vm_runtime: VmRuntime,
706 module_id: ModuleId,
707 ) {
708 self.expected_publish_module_calls
709 .push_back(ExpectedPublishModuleCall {
710 contract,
711 service,
712 vm_runtime,
713 module_id,
714 });
715 }
716
717 pub fn add_expected_create_application_call<Parameters, InstantiationArgument>(
719 &mut self,
720 module_id: ModuleId,
721 parameters: &Parameters,
722 argument: &InstantiationArgument,
723 required_application_ids: Vec<ApplicationId>,
724 application_id: ApplicationId,
725 ) where
726 Parameters: Serialize,
727 InstantiationArgument: Serialize,
728 {
729 let parameters = serde_json::to_vec(parameters)
730 .expect("Failed to serialize `Parameters` type for a cross-application call");
731 let argument = serde_json::to_vec(argument).expect(
732 "Failed to serialize `InstantiationArgument` type for a cross-application call",
733 );
734 self.expected_create_application_calls
735 .push_back(ExpectedCreateApplicationCall {
736 module_id,
737 parameters,
738 argument,
739 required_application_ids,
740 application_id,
741 });
742 }
743
744 pub fn add_expected_create_data_blob_call(&mut self, bytes: Vec<u8>, blob_id: BlobId) {
746 self.expected_create_data_blob_calls
747 .push_back(ExpectedCreateDataBlobCall { bytes, blob_id });
748 }
749
750 pub fn publish_module(
752 &mut self,
753 contract: Bytecode,
754 service: Bytecode,
755 vm_runtime: VmRuntime,
756 ) -> ModuleId {
757 let ExpectedPublishModuleCall {
758 contract: expected_contract,
759 service: expected_service,
760 vm_runtime: expected_vm_runtime,
761 module_id,
762 } = self
763 .expected_publish_module_calls
764 .pop_front()
765 .expect("Unexpected publish_module call");
766 assert_eq!(contract, expected_contract);
767 assert_eq!(service, expected_service);
768 assert_eq!(vm_runtime, expected_vm_runtime);
769 module_id
770 }
771
772 pub fn create_application<Abi, Parameters, InstantiationArgument>(
774 &mut self,
775 module_id: ModuleId,
776 parameters: &Parameters,
777 argument: &InstantiationArgument,
778 required_application_ids: Vec<ApplicationId>,
779 ) -> ApplicationId<Abi>
780 where
781 Abi: ContractAbi,
782 Parameters: Serialize,
783 InstantiationArgument: Serialize,
784 {
785 let ExpectedCreateApplicationCall {
786 module_id: expected_module_id,
787 parameters: expected_parameters,
788 argument: expected_argument,
789 required_application_ids: expected_required_app_ids,
790 application_id,
791 } = self
792 .expected_create_application_calls
793 .pop_front()
794 .expect("Unexpected create_application call");
795 let parameters = serde_json::to_vec(parameters)
796 .expect("Failed to serialize `Parameters` type for a cross-application call");
797 let argument = serde_json::to_vec(argument).expect(
798 "Failed to serialize `InstantiationArgument` type for a cross-application call",
799 );
800 assert_eq!(module_id, expected_module_id);
801 assert_eq!(parameters, expected_parameters);
802 assert_eq!(argument, expected_argument);
803 assert_eq!(required_application_ids, expected_required_app_ids);
804 application_id.with_abi::<Abi>()
805 }
806
807 pub fn create_data_blob(&mut self, bytes: &[u8]) -> DataBlobHash {
809 let ExpectedCreateDataBlobCall {
810 bytes: expected_bytes,
811 blob_id,
812 } = self
813 .expected_create_data_blob_calls
814 .pop_front()
815 .expect("Unexpected create_data_blob call");
816 assert_eq!(bytes, &expected_bytes);
817 DataBlobHash(blob_id.hash)
818 }
819
820 pub fn with_call_application_handler(
822 mut self,
823 handler: impl FnMut(bool, ApplicationId, Vec<u8>) -> Vec<u8> + 'static,
824 ) -> Self {
825 self.call_application_handler = Some(Box::new(handler));
826 self
827 }
828
829 pub fn set_call_application_handler(
831 &mut self,
832 handler: impl FnMut(bool, ApplicationId, Vec<u8>) -> Vec<u8> + 'static,
833 ) -> &mut Self {
834 self.call_application_handler = Some(Box::new(handler));
835 self
836 }
837
838 pub fn call_application<A: ContractAbi + Send>(
840 &mut self,
841 authenticated: bool,
842 application: ApplicationId<A>,
843 call: &A::Operation,
844 ) -> A::Response {
845 let call_bytes = A::serialize_operation(call)
846 .expect("Failed to serialize `Operation` in test runtime cross-application call");
847
848 let handler = self.call_application_handler.as_mut().expect(
849 "Handler for `call_application` has not been mocked, \
850 please call `MockContractRuntime::set_call_application_handler` first",
851 );
852 let response_bytes = handler(authenticated, application.forget_abi(), call_bytes);
853
854 A::deserialize_response(response_bytes)
855 .expect("Failed to deserialize `Response` in test runtime cross-application call")
856 }
857
858 pub fn emit(&mut self, name: StreamName, value: &Application::EventValue) -> u32 {
860 let value = bcs::to_bytes(value).expect("Failed to serialize event value");
861 let entry = self.created_events.entry(name).or_default();
862 entry.push(value);
863 entry.len() as u32 - 1
864 }
865
866 pub fn add_event(&mut self, chain_id: ChainId, name: StreamName, index: u32, value: &[u8]) {
868 self.events.insert((chain_id, name, index), value.to_vec());
869 }
870
871 pub fn read_event(
875 &mut self,
876 chain_id: ChainId,
877 name: StreamName,
878 index: u32,
879 ) -> Application::EventValue {
880 let value = self
881 .events
882 .get(&(chain_id, name, index))
883 .expect("Event not found");
884 bcs::from_bytes(value).expect("Failed to deserialize event value")
885 }
886
887 pub fn subscribe_to_events(
889 &mut self,
890 _chain_id: ChainId,
891 _application_id: ApplicationId,
892 _name: StreamName,
893 ) {
894 }
896
897 pub fn unsubscribe_from_events(
899 &mut self,
900 _chain_id: ChainId,
901 _application_id: ApplicationId,
902 _name: StreamName,
903 ) {
904 }
906
907 pub fn add_expected_service_query<A: ServiceAbi + Send>(
909 &mut self,
910 application_id: ApplicationId<A>,
911 query: A::Query,
912 response: A::QueryResponse,
913 ) {
914 let query = serde_json::to_string(&query).expect("Failed to serialize query");
915 let response = serde_json::to_string(&response).expect("Failed to serialize response");
916 self.expected_service_queries
917 .push_back((application_id.forget_abi(), query, response));
918 }
919
920 pub fn add_expected_http_request(&mut self, request: http::Request, response: http::Response) {
922 self.expected_http_requests.push_back((request, response));
923 }
924
925 pub fn add_expected_read_data_blob_requests(&mut self, hash: DataBlobHash, response: Vec<u8>) {
927 self.expected_read_data_blob_requests
928 .push_back((hash, response));
929 }
930
931 pub fn add_expected_assert_data_blob_exists_requests(
933 &mut self,
934 hash: DataBlobHash,
935 response: Option<()>,
936 ) {
937 self.expected_assert_data_blob_exists_requests
938 .push_back((hash, response));
939 }
940
941 pub fn add_expected_has_empty_storage_requests(
943 &mut self,
944 application: ApplicationId,
945 response: bool,
946 ) {
947 self.expected_has_empty_storage_requests
948 .push_back((application, response));
949 }
950
951 pub fn query_service<A: ServiceAbi + Send>(
959 &mut self,
960 application_id: ApplicationId<A>,
961 query: &A::Query,
962 ) -> A::QueryResponse {
963 let maybe_query = self.expected_service_queries.pop_front();
964 let (expected_id, expected_query, response) =
965 maybe_query.expect("Unexpected service query");
966 assert_eq!(application_id.forget_abi(), expected_id);
967 let query = serde_json::to_string(query).expect("Failed to serialize query");
968 assert_eq!(query, expected_query);
969 serde_json::from_str(&response).expect("Failed to deserialize response")
970 }
971
972 pub fn http_request(&mut self, request: http::Request) -> http::Response {
980 let maybe_request = self.expected_http_requests.pop_front();
981 let (expected_request, response) = maybe_request.expect("Unexpected HTTP request");
982 assert_eq!(request, expected_request);
983 response
984 }
985
986 pub fn assert_before(&mut self, timestamp: Timestamp) {
992 assert!(self.timestamp.is_some_and(|t| t < timestamp))
993 }
994
995 pub fn read_data_blob(&mut self, hash: DataBlobHash) -> Vec<u8> {
997 let maybe_request = self.expected_read_data_blob_requests.pop_front();
998 let (expected_hash, response) = maybe_request.expect("Unexpected read_data_blob request");
999 assert_eq!(hash, expected_hash);
1000 response
1001 }
1002
1003 pub fn assert_data_blob_exists(&mut self, hash: DataBlobHash) {
1005 let maybe_request = self.expected_assert_data_blob_exists_requests.pop_front();
1006 let (expected_blob_hash, response) =
1007 maybe_request.expect("Unexpected assert_data_blob_exists request");
1008 assert_eq!(hash, expected_blob_hash);
1009 response.expect("Blob does not exist!");
1010 }
1011
1012 pub fn has_empty_storage(&mut self, application: ApplicationId) -> bool {
1014 let maybe_request = self.expected_has_empty_storage_requests.pop_front();
1015 let (expected_application_id, response) =
1016 maybe_request.expect("Unexpected has_empty_storage request");
1017 assert_eq!(application, expected_application_id);
1018 response
1019 }
1020
1021 pub fn validation_round(&mut self) -> Option<u32> {
1023 self.round
1024 }
1025}
1026
1027pub type CallApplicationHandler = Box<dyn FnMut(bool, ApplicationId, Vec<u8>) -> Vec<u8>>;
1029
1030#[must_use]
1033pub struct MessageBuilder<Message>
1034where
1035 Message: Serialize,
1036{
1037 authenticated: bool,
1038 is_tracked: bool,
1039 grant: Resources,
1040 message: Message,
1041 send_message_requests: Arc<Mutex<Vec<SendMessageRequest<Message>>>>,
1042}
1043
1044impl<Message> MessageBuilder<Message>
1045where
1046 Message: Serialize,
1047{
1048 pub(crate) fn new(
1050 message: Message,
1051 send_message_requests: Arc<Mutex<Vec<SendMessageRequest<Message>>>>,
1052 ) -> Self {
1053 MessageBuilder {
1054 authenticated: false,
1055 is_tracked: false,
1056 grant: Resources::default(),
1057 message,
1058 send_message_requests,
1059 }
1060 }
1061
1062 pub fn with_tracking(mut self) -> Self {
1065 self.is_tracked = true;
1066 self
1067 }
1068
1069 pub fn with_authentication(mut self) -> Self {
1071 self.authenticated = true;
1072 self
1073 }
1074
1075 pub fn with_grant(mut self, grant: Resources) -> Self {
1077 self.grant = grant;
1078 self
1079 }
1080
1081 pub fn send_to(self, destination: ChainId) {
1083 let request = SendMessageRequest {
1084 destination,
1085 authenticated: self.authenticated,
1086 is_tracked: self.is_tracked,
1087 grant: self.grant,
1088 message: self.message,
1089 };
1090
1091 self.send_message_requests
1092 .try_lock()
1093 .expect("Unit test should be single-threaded")
1094 .push(request);
1095 }
1096}
1097
1098#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1100pub struct ClaimRequest {
1101 source: Account,
1102 destination: Account,
1103 amount: Amount,
1104}