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_signer: 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_open_chain_calls: VecDeque<(ChainOwnership, ApplicationPermissions, Amount, ChainId)>,
82 expected_publish_module_calls: VecDeque<ExpectedPublishModuleCall>,
83 expected_create_application_calls: VecDeque<ExpectedCreateApplicationCall>,
84 expected_create_data_blob_calls: VecDeque<ExpectedCreateDataBlobCall>,
85 key_value_store: KeyValueStore,
86}
87
88impl<Application> Default for MockContractRuntime<Application>
89where
90 Application: Contract,
91{
92 fn default() -> Self {
93 MockContractRuntime::new()
94 }
95}
96
97impl<Application> MockContractRuntime<Application>
98where
99 Application: Contract,
100{
101 pub fn new() -> Self {
103 MockContractRuntime {
104 application_parameters: None,
105 application_id: None,
106 application_creator_chain_id: None,
107 chain_id: None,
108 authenticated_signer: None,
109 block_height: None,
110 round: None,
111 message_is_bouncing: None,
112 message_origin_chain_id: None,
113 authenticated_caller_id: None,
114 timestamp: None,
115 chain_balance: None,
116 owner_balances: None,
117 chain_ownership: None,
118 can_close_chain: None,
119 can_change_application_permissions: None,
120 call_application_handler: None,
121 send_message_requests: Arc::default(),
122 outgoing_transfers: HashMap::new(),
123 created_events: BTreeMap::new(),
124 events: BTreeMap::new(),
125 claim_requests: Vec::new(),
126 expected_service_queries: VecDeque::new(),
127 expected_http_requests: VecDeque::new(),
128 expected_read_data_blob_requests: VecDeque::new(),
129 expected_assert_data_blob_exists_requests: VecDeque::new(),
130 expected_open_chain_calls: VecDeque::new(),
131 expected_publish_module_calls: VecDeque::new(),
132 expected_create_application_calls: VecDeque::new(),
133 expected_create_data_blob_calls: VecDeque::new(),
134 key_value_store: KeyValueStore::mock().to_mut(),
135 }
136 }
137
138 pub fn key_value_store(&self) -> KeyValueStore {
140 self.key_value_store.clone()
141 }
142
143 pub fn root_view_storage_context(&self) -> ViewStorageContext {
145 ViewStorageContext::new_unsafe(self.key_value_store(), Vec::new(), ())
146 }
147
148 pub fn with_application_parameters(
150 mut self,
151 application_parameters: Application::Parameters,
152 ) -> Self {
153 self.application_parameters = Some(application_parameters);
154 self
155 }
156
157 pub fn set_application_parameters(
159 &mut self,
160 application_parameters: Application::Parameters,
161 ) -> &mut Self {
162 self.application_parameters = Some(application_parameters);
163 self
164 }
165
166 pub fn application_parameters(&mut self) -> Application::Parameters {
168 self.application_parameters.clone().expect(
169 "Application parameters have not been mocked, \
170 please call `MockContractRuntime::set_application_parameters` first",
171 )
172 }
173
174 pub fn with_application_id(mut self, application_id: ApplicationId<Application::Abi>) -> Self {
176 self.application_id = Some(application_id);
177 self
178 }
179
180 pub fn set_application_id(
182 &mut self,
183 application_id: ApplicationId<Application::Abi>,
184 ) -> &mut Self {
185 self.application_id = Some(application_id);
186 self
187 }
188
189 pub fn application_id(&mut self) -> ApplicationId<Application::Abi> {
191 self.application_id.expect(
192 "Application ID has not been mocked, \
193 please call `MockContractRuntime::set_application_id` first",
194 )
195 }
196
197 pub fn with_application_creator_chain_id(mut self, chain_id: ChainId) -> Self {
199 self.application_creator_chain_id = Some(chain_id);
200 self
201 }
202
203 pub fn set_application_creator_chain_id(&mut self, chain_id: ChainId) -> &mut Self {
205 self.application_creator_chain_id = Some(chain_id);
206 self
207 }
208
209 pub fn application_creator_chain_id(&mut self) -> ChainId {
211 self.application_creator_chain_id.expect(
212 "Application creator chain ID has not been mocked, \
213 please call `MockContractRuntime::set_application_creator_chain_id` first",
214 )
215 }
216
217 pub fn with_chain_id(mut self, chain_id: ChainId) -> Self {
219 self.chain_id = Some(chain_id);
220 self
221 }
222
223 pub fn set_chain_id(&mut self, chain_id: ChainId) -> &mut Self {
225 self.chain_id = Some(chain_id);
226 self
227 }
228
229 pub fn chain_id(&mut self) -> ChainId {
231 self.chain_id.expect(
232 "Chain ID has not been mocked, \
233 please call `MockContractRuntime::set_chain_id` first",
234 )
235 }
236
237 pub fn with_authenticated_signer(
239 mut self,
240 authenticated_signer: impl Into<Option<AccountOwner>>,
241 ) -> Self {
242 self.authenticated_signer = Some(authenticated_signer.into());
243 self
244 }
245
246 pub fn set_authenticated_signer(
248 &mut self,
249 authenticated_signer: impl Into<Option<AccountOwner>>,
250 ) -> &mut Self {
251 self.authenticated_signer = Some(authenticated_signer.into());
252 self
253 }
254
255 pub fn authenticated_signer(&mut self) -> Option<AccountOwner> {
257 self.authenticated_signer.expect(
258 "Authenticated signer has not been mocked, \
259 please call `MockContractRuntime::set_authenticated_signer` first",
260 )
261 }
262
263 pub fn with_block_height(mut self, block_height: BlockHeight) -> Self {
265 self.block_height = Some(block_height);
266 self
267 }
268
269 pub fn set_block_height(&mut self, block_height: BlockHeight) -> &mut Self {
271 self.block_height = Some(block_height);
272 self
273 }
274
275 pub fn with_round(mut self, round: u32) -> Self {
277 self.round = Some(round);
278 self
279 }
280
281 pub fn set_round(&mut self, round: u32) -> &mut Self {
283 self.round = Some(round);
284 self
285 }
286
287 pub fn block_height(&mut self) -> BlockHeight {
289 self.block_height.expect(
290 "Block height has not been mocked, \
291 please call `MockContractRuntime::set_block_height` first",
292 )
293 }
294
295 pub fn with_message_is_bouncing(
297 mut self,
298 message_is_bouncing: impl Into<Option<bool>>,
299 ) -> Self {
300 self.message_is_bouncing = Some(message_is_bouncing.into());
301 self
302 }
303
304 pub fn set_message_is_bouncing(
306 &mut self,
307 message_is_bouncing: impl Into<Option<bool>>,
308 ) -> &mut Self {
309 self.message_is_bouncing = Some(message_is_bouncing.into());
310 self
311 }
312
313 pub fn message_is_bouncing(&mut self) -> Option<bool> {
316 self.message_is_bouncing.expect(
317 "`message_is_bouncing` flag has not been mocked, \
318 please call `MockContractRuntime::set_message_is_bouncing` first",
319 )
320 }
321
322 pub fn set_message_origin_chain_id(
324 &mut self,
325 message_origin_chain_id: impl Into<Option<ChainId>>,
326 ) -> &mut Self {
327 self.message_origin_chain_id = Some(message_origin_chain_id.into());
328 self
329 }
330
331 pub fn message_origin_chain_id(&mut self) -> Option<ChainId> {
334 self.message_origin_chain_id.expect(
335 "`message_origin_chain_id` has not been mocked, \
336 please call `MockContractRuntime::set_message_origin_chain_id` first",
337 )
338 }
339
340 pub fn with_authenticated_caller_id(
342 mut self,
343 authenticated_caller_id: impl Into<Option<ApplicationId>>,
344 ) -> Self {
345 self.authenticated_caller_id = Some(authenticated_caller_id.into());
346 self
347 }
348
349 pub fn set_authenticated_caller_id(
351 &mut self,
352 authenticated_caller_id: impl Into<Option<ApplicationId>>,
353 ) -> &mut Self {
354 self.authenticated_caller_id = Some(authenticated_caller_id.into());
355 self
356 }
357
358 pub fn authenticated_caller_id(&mut self) -> Option<ApplicationId> {
361 self.authenticated_caller_id.expect(
362 "Authenticated caller ID has not been mocked, \
363 please call `MockContractRuntime::set_authenticated_caller_id` first",
364 )
365 }
366
367 pub fn check_account_permission(
369 &mut self,
370 owner: AccountOwner,
371 ) -> Result<(), AccountPermissionError> {
372 ensure!(
373 self.authenticated_signer() == Some(owner)
374 || self.authenticated_caller_id().map(AccountOwner::from) == Some(owner),
375 AccountPermissionError::NotPermitted(owner)
376 );
377 Ok(())
378 }
379
380 pub fn with_system_time(mut self, timestamp: Timestamp) -> Self {
382 self.timestamp = Some(timestamp);
383 self
384 }
385
386 pub fn set_system_time(&mut self, timestamp: Timestamp) -> &mut Self {
388 self.timestamp = Some(timestamp);
389 self
390 }
391
392 pub fn system_time(&mut self) -> Timestamp {
394 self.timestamp.expect(
395 "System time has not been mocked, \
396 please call `MockContractRuntime::set_system_time` first",
397 )
398 }
399
400 pub fn with_chain_balance(mut self, chain_balance: Amount) -> Self {
402 self.chain_balance = Some(chain_balance);
403 self
404 }
405
406 pub fn set_chain_balance(&mut self, chain_balance: Amount) -> &mut Self {
408 self.chain_balance = Some(chain_balance);
409 self
410 }
411
412 pub fn chain_balance(&mut self) -> Amount {
414 *self.chain_balance_mut()
415 }
416
417 fn chain_balance_mut(&mut self) -> &mut Amount {
419 self.chain_balance.as_mut().expect(
420 "Chain balance has not been mocked, \
421 please call `MockContractRuntime::set_chain_balance` first",
422 )
423 }
424
425 pub fn with_owner_balances(
427 mut self,
428 owner_balances: impl IntoIterator<Item = (AccountOwner, Amount)>,
429 ) -> Self {
430 self.owner_balances = Some(owner_balances.into_iter().collect());
431 self
432 }
433
434 pub fn set_owner_balances(
436 &mut self,
437 owner_balances: impl IntoIterator<Item = (AccountOwner, Amount)>,
438 ) -> &mut Self {
439 self.owner_balances = Some(owner_balances.into_iter().collect());
440 self
441 }
442
443 pub fn with_owner_balance(mut self, owner: AccountOwner, balance: Amount) -> Self {
445 self.set_owner_balance(owner, balance);
446 self
447 }
448
449 pub fn set_owner_balance(&mut self, owner: AccountOwner, balance: Amount) -> &mut Self {
451 self.owner_balances
452 .get_or_insert_with(HashMap::new)
453 .insert(owner, balance);
454 self
455 }
456
457 pub fn owner_balance(&mut self, owner: AccountOwner) -> Amount {
459 *self.owner_balance_mut(owner)
460 }
461
462 fn owner_balance_mut(&mut self, owner: AccountOwner) -> &mut Amount {
464 self.owner_balances
465 .as_mut()
466 .expect(
467 "Owner balances have not been mocked, \
468 please call `MockContractRuntime::set_owner_balances` first",
469 )
470 .get_mut(&owner)
471 .unwrap_or_else(|| {
472 panic!(
473 "Balance for owner {owner} was not mocked, \
474 please include a balance for them in the call to \
475 `MockContractRuntime::set_owner_balances`"
476 )
477 })
478 }
479
480 pub fn send_message(&mut self, destination: ChainId, message: Application::Message) {
482 self.prepare_message(message).send_to(destination)
483 }
484
485 pub fn prepare_message(
487 &mut self,
488 message: Application::Message,
489 ) -> MessageBuilder<Application::Message> {
490 MessageBuilder::new(message, self.send_message_requests.clone())
491 }
492
493 pub fn created_send_message_requests(
495 &self,
496 ) -> MutexGuard<'_, Vec<SendMessageRequest<Application::Message>>> {
497 self.send_message_requests
498 .try_lock()
499 .expect("Unit test should be single-threaded")
500 }
501
502 pub fn transfer(&mut self, source: AccountOwner, destination: Account, amount: Amount) {
505 self.debit(source, amount);
506
507 if Some(destination.chain_id) == self.chain_id {
508 self.credit(destination.owner, amount);
509 } else {
510 let destination_entry = self.outgoing_transfers.entry(destination).or_default();
511 *destination_entry = destination_entry
512 .try_add(amount)
513 .expect("Outgoing transfer value overflow");
514 }
515 }
516
517 fn debit(&mut self, source: AccountOwner, amount: Amount) {
520 let source_balance = if source == AccountOwner::CHAIN {
521 self.chain_balance_mut()
522 } else {
523 self.owner_balance_mut(source)
524 };
525
526 *source_balance = source_balance
527 .try_sub(amount)
528 .expect("Insufficient funds in source account");
529 }
530
531 fn credit(&mut self, destination: AccountOwner, amount: Amount) {
534 let destination_balance = if destination == AccountOwner::CHAIN {
535 self.chain_balance_mut()
536 } else {
537 self.owner_balance_mut(destination)
538 };
539
540 *destination_balance = destination_balance
541 .try_add(amount)
542 .expect("Account balance overflow");
543 }
544
545 pub fn outgoing_transfers(&self) -> &HashMap<Account, Amount> {
547 &self.outgoing_transfers
548 }
549
550 pub fn claim(&mut self, source: Account, destination: Account, amount: Amount) {
552 if Some(source.chain_id) == self.chain_id {
553 self.debit(source.owner, amount);
554
555 if Some(destination.chain_id) == self.chain_id {
556 self.credit(destination.owner, amount);
557 }
558 }
559
560 self.claim_requests.push(ClaimRequest {
561 source,
562 amount,
563 destination,
564 });
565 }
566
567 pub fn claim_requests(&self) -> &[ClaimRequest] {
569 &self.claim_requests
570 }
571
572 pub fn with_chain_ownership(mut self, chain_ownership: ChainOwnership) -> Self {
574 self.chain_ownership = Some(chain_ownership);
575 self
576 }
577
578 pub fn set_chain_ownership(&mut self, chain_ownership: ChainOwnership) -> &mut Self {
580 self.chain_ownership = Some(chain_ownership);
581 self
582 }
583
584 pub fn chain_ownership(&mut self) -> ChainOwnership {
586 self.chain_ownership.clone().expect(
587 "Chain ownership has not been mocked, \
588 please call `MockContractRuntime::set_chain_ownership` first",
589 )
590 }
591
592 pub fn with_can_close_chain(mut self, can_close_chain: bool) -> Self {
594 self.can_close_chain = Some(can_close_chain);
595 self
596 }
597
598 pub fn set_can_close_chain(&mut self, can_close_chain: bool) -> &mut Self {
600 self.can_close_chain = Some(can_close_chain);
601 self
602 }
603
604 pub fn with_can_change_application_permissions(
607 mut self,
608 can_change_application_permissions: bool,
609 ) -> Self {
610 self.can_change_application_permissions = Some(can_change_application_permissions);
611 self
612 }
613
614 pub fn set_can_change_application_permissions(
617 &mut self,
618 can_change_application_permissions: bool,
619 ) -> &mut Self {
620 self.can_change_application_permissions = Some(can_change_application_permissions);
621 self
622 }
623
624 pub fn close_chain(&mut self) -> Result<(), CloseChainError> {
627 let authorized = self.can_close_chain.expect(
628 "Authorization to close the chain has not been mocked, \
629 please call `MockContractRuntime::set_can_close_chain` first",
630 );
631
632 if authorized {
633 Ok(())
634 } else {
635 Err(CloseChainError::NotPermitted)
636 }
637 }
638
639 pub fn change_application_permissions(
642 &mut self,
643 application_permissions: ApplicationPermissions,
644 ) -> Result<(), ChangeApplicationPermissionsError> {
645 let authorized = self.can_change_application_permissions.expect(
646 "Authorization to change the application permissions has not been mocked, \
647 please call `MockContractRuntime::set_can_change_application_permissions` first",
648 );
649
650 if authorized {
651 let application_id = self
652 .application_id
653 .expect("The application doesn't have an ID!")
654 .forget_abi();
655 self.can_close_chain = Some(application_permissions.can_close_chain(&application_id));
656 self.can_change_application_permissions =
657 Some(application_permissions.can_change_application_permissions(&application_id));
658 Ok(())
659 } else {
660 Err(ChangeApplicationPermissionsError::NotPermitted)
661 }
662 }
663
664 pub fn add_expected_open_chain_call(
666 &mut self,
667 ownership: ChainOwnership,
668 application_permissions: ApplicationPermissions,
669 balance: Amount,
670 chain_id: ChainId,
671 ) {
672 self.expected_open_chain_calls.push_back((
673 ownership,
674 application_permissions,
675 balance,
676 chain_id,
677 ));
678 }
679
680 pub fn open_chain(
683 &mut self,
684 ownership: ChainOwnership,
685 application_permissions: ApplicationPermissions,
686 balance: Amount,
687 ) -> ChainId {
688 let (expected_ownership, expected_permissions, expected_balance, chain_id) = self
689 .expected_open_chain_calls
690 .pop_front()
691 .expect("Unexpected open_chain call");
692 assert_eq!(ownership, expected_ownership);
693 assert_eq!(application_permissions, expected_permissions);
694 assert_eq!(balance, expected_balance);
695 chain_id
696 }
697
698 pub fn add_expected_publish_module_call(
700 &mut self,
701 contract: Bytecode,
702 service: Bytecode,
703 vm_runtime: VmRuntime,
704 module_id: ModuleId,
705 ) {
706 self.expected_publish_module_calls
707 .push_back(ExpectedPublishModuleCall {
708 contract,
709 service,
710 vm_runtime,
711 module_id,
712 });
713 }
714
715 pub fn add_expected_create_application_call<Parameters, InstantiationArgument>(
717 &mut self,
718 module_id: ModuleId,
719 parameters: &Parameters,
720 argument: &InstantiationArgument,
721 required_application_ids: Vec<ApplicationId>,
722 application_id: ApplicationId,
723 ) where
724 Parameters: Serialize,
725 InstantiationArgument: Serialize,
726 {
727 let parameters = serde_json::to_vec(parameters)
728 .expect("Failed to serialize `Parameters` type for a cross-application call");
729 let argument = serde_json::to_vec(argument).expect(
730 "Failed to serialize `InstantiationArgument` type for a cross-application call",
731 );
732 self.expected_create_application_calls
733 .push_back(ExpectedCreateApplicationCall {
734 module_id,
735 parameters,
736 argument,
737 required_application_ids,
738 application_id,
739 });
740 }
741
742 pub fn add_expected_create_data_blob_call(&mut self, bytes: Vec<u8>, blob_id: BlobId) {
744 self.expected_create_data_blob_calls
745 .push_back(ExpectedCreateDataBlobCall { bytes, blob_id });
746 }
747
748 pub fn publish_module(
750 &mut self,
751 contract: Bytecode,
752 service: Bytecode,
753 vm_runtime: VmRuntime,
754 ) -> ModuleId {
755 let ExpectedPublishModuleCall {
756 contract: expected_contract,
757 service: expected_service,
758 vm_runtime: expected_vm_runtime,
759 module_id,
760 } = self
761 .expected_publish_module_calls
762 .pop_front()
763 .expect("Unexpected publish_module call");
764 assert_eq!(contract, expected_contract);
765 assert_eq!(service, expected_service);
766 assert_eq!(vm_runtime, expected_vm_runtime);
767 module_id
768 }
769
770 pub fn create_application<Abi, Parameters, InstantiationArgument>(
772 &mut self,
773 module_id: ModuleId,
774 parameters: &Parameters,
775 argument: &InstantiationArgument,
776 required_application_ids: Vec<ApplicationId>,
777 ) -> ApplicationId<Abi>
778 where
779 Abi: ContractAbi,
780 Parameters: Serialize,
781 InstantiationArgument: Serialize,
782 {
783 let ExpectedCreateApplicationCall {
784 module_id: expected_module_id,
785 parameters: expected_parameters,
786 argument: expected_argument,
787 required_application_ids: expected_required_app_ids,
788 application_id,
789 } = self
790 .expected_create_application_calls
791 .pop_front()
792 .expect("Unexpected create_application call");
793 let parameters = serde_json::to_vec(parameters)
794 .expect("Failed to serialize `Parameters` type for a cross-application call");
795 let argument = serde_json::to_vec(argument).expect(
796 "Failed to serialize `InstantiationArgument` type for a cross-application call",
797 );
798 assert_eq!(module_id, expected_module_id);
799 assert_eq!(parameters, expected_parameters);
800 assert_eq!(argument, expected_argument);
801 assert_eq!(required_application_ids, expected_required_app_ids);
802 application_id.with_abi::<Abi>()
803 }
804
805 pub fn create_data_blob(&mut self, bytes: Vec<u8>) -> DataBlobHash {
807 let ExpectedCreateDataBlobCall {
808 bytes: expected_bytes,
809 blob_id,
810 } = self
811 .expected_create_data_blob_calls
812 .pop_front()
813 .expect("Unexpected create_data_blob call");
814 assert_eq!(bytes, expected_bytes);
815 DataBlobHash(blob_id.hash)
816 }
817
818 pub fn with_call_application_handler(
820 mut self,
821 handler: impl FnMut(bool, ApplicationId, Vec<u8>) -> Vec<u8> + 'static,
822 ) -> Self {
823 self.call_application_handler = Some(Box::new(handler));
824 self
825 }
826
827 pub fn set_call_application_handler(
829 &mut self,
830 handler: impl FnMut(bool, ApplicationId, Vec<u8>) -> Vec<u8> + 'static,
831 ) -> &mut Self {
832 self.call_application_handler = Some(Box::new(handler));
833 self
834 }
835
836 pub fn call_application<A: ContractAbi + Send>(
838 &mut self,
839 authenticated: bool,
840 application: ApplicationId<A>,
841 call: &A::Operation,
842 ) -> A::Response {
843 let call_bytes = A::serialize_operation(call)
844 .expect("Failed to serialize `Operation` in test runtime cross-application call");
845
846 let handler = self.call_application_handler.as_mut().expect(
847 "Handler for `call_application` has not been mocked, \
848 please call `MockContractRuntime::set_call_application_handler` first",
849 );
850 let response_bytes = handler(authenticated, application.forget_abi(), call_bytes);
851
852 A::deserialize_response(response_bytes)
853 .expect("Failed to deserialize `Response` in test runtime cross-application call")
854 }
855
856 pub fn emit(&mut self, name: StreamName, value: &Application::EventValue) -> u32 {
858 let value = bcs::to_bytes(value).expect("Failed to serialize event value");
859 let entry = self.created_events.entry(name).or_default();
860 entry.push(value);
861 entry.len() as u32 - 1
862 }
863
864 pub fn add_event(&mut self, chain_id: ChainId, name: StreamName, index: u32, value: &[u8]) {
866 self.events.insert((chain_id, name, index), value.to_vec());
867 }
868
869 pub fn read_event(
873 &mut self,
874 chain_id: ChainId,
875 name: StreamName,
876 index: u32,
877 ) -> Application::EventValue {
878 let value = self
879 .events
880 .get(&(chain_id, name, index))
881 .expect("Event not found");
882 bcs::from_bytes(value).expect("Failed to deserialize event value")
883 }
884
885 pub fn subscribe_to_events(
887 &mut self,
888 _chain_id: ChainId,
889 _application_id: ApplicationId,
890 _name: StreamName,
891 ) {
892 }
894
895 pub fn unsubscribe_from_events(
897 &mut self,
898 _chain_id: ChainId,
899 _application_id: ApplicationId,
900 _name: StreamName,
901 ) {
902 }
904
905 pub fn add_expected_service_query<A: ServiceAbi + Send>(
907 &mut self,
908 application_id: ApplicationId<A>,
909 query: A::Query,
910 response: A::QueryResponse,
911 ) {
912 let query = serde_json::to_string(&query).expect("Failed to serialize query");
913 let response = serde_json::to_string(&response).expect("Failed to serialize response");
914 self.expected_service_queries
915 .push_back((application_id.forget_abi(), query, response));
916 }
917
918 pub fn add_expected_http_request(&mut self, request: http::Request, response: http::Response) {
920 self.expected_http_requests.push_back((request, response));
921 }
922
923 pub fn add_expected_read_data_blob_requests(&mut self, hash: DataBlobHash, response: Vec<u8>) {
925 self.expected_read_data_blob_requests
926 .push_back((hash, response));
927 }
928
929 pub fn add_expected_assert_data_blob_exists_requests(
931 &mut self,
932 hash: DataBlobHash,
933 response: Option<()>,
934 ) {
935 self.expected_assert_data_blob_exists_requests
936 .push_back((hash, response));
937 }
938
939 pub fn query_service<A: ServiceAbi + Send>(
947 &mut self,
948 application_id: ApplicationId<A>,
949 query: A::Query,
950 ) -> A::QueryResponse {
951 let maybe_query = self.expected_service_queries.pop_front();
952 let (expected_id, expected_query, response) =
953 maybe_query.expect("Unexpected service query");
954 assert_eq!(application_id.forget_abi(), expected_id);
955 let query = serde_json::to_string(&query).expect("Failed to serialize query");
956 assert_eq!(query, expected_query);
957 serde_json::from_str(&response).expect("Failed to deserialize response")
958 }
959
960 pub fn http_request(&mut self, request: http::Request) -> http::Response {
968 let maybe_request = self.expected_http_requests.pop_front();
969 let (expected_request, response) = maybe_request.expect("Unexpected HTTP request");
970 assert_eq!(request, expected_request);
971 response
972 }
973
974 pub fn assert_before(&mut self, timestamp: Timestamp) {
980 assert!(self.timestamp.is_some_and(|t| t < timestamp))
981 }
982
983 pub fn read_data_blob(&mut self, hash: DataBlobHash) -> Vec<u8> {
985 let maybe_request = self.expected_read_data_blob_requests.pop_front();
986 let (expected_hash, response) = maybe_request.expect("Unexpected read_data_blob request");
987 assert_eq!(hash, expected_hash);
988 response
989 }
990
991 pub fn assert_data_blob_exists(&mut self, hash: DataBlobHash) {
993 let maybe_request = self.expected_assert_data_blob_exists_requests.pop_front();
994 let (expected_blob_hash, response) =
995 maybe_request.expect("Unexpected assert_data_blob_exists request");
996 assert_eq!(hash, expected_blob_hash);
997 response.expect("Blob does not exist!");
998 }
999
1000 pub fn validation_round(&mut self) -> Option<u32> {
1002 self.round
1003 }
1004}
1005
1006pub type CallApplicationHandler = Box<dyn FnMut(bool, ApplicationId, Vec<u8>) -> Vec<u8>>;
1008
1009#[must_use]
1012pub struct MessageBuilder<Message>
1013where
1014 Message: Serialize,
1015{
1016 authenticated: bool,
1017 is_tracked: bool,
1018 grant: Resources,
1019 message: Message,
1020 send_message_requests: Arc<Mutex<Vec<SendMessageRequest<Message>>>>,
1021}
1022
1023impl<Message> MessageBuilder<Message>
1024where
1025 Message: Serialize,
1026{
1027 pub(crate) fn new(
1029 message: Message,
1030 send_message_requests: Arc<Mutex<Vec<SendMessageRequest<Message>>>>,
1031 ) -> Self {
1032 MessageBuilder {
1033 authenticated: false,
1034 is_tracked: false,
1035 grant: Resources::default(),
1036 message,
1037 send_message_requests,
1038 }
1039 }
1040
1041 pub fn with_tracking(mut self) -> Self {
1044 self.is_tracked = true;
1045 self
1046 }
1047
1048 pub fn with_authentication(mut self) -> Self {
1050 self.authenticated = true;
1051 self
1052 }
1053
1054 pub fn with_grant(mut self, grant: Resources) -> Self {
1056 self.grant = grant;
1057 self
1058 }
1059
1060 pub fn send_to(self, destination: ChainId) {
1062 let request = SendMessageRequest {
1063 destination,
1064 authenticated: self.authenticated,
1065 is_tracked: self.is_tracked,
1066 grant: self.grant,
1067 message: self.message,
1068 };
1069
1070 self.send_message_requests
1071 .try_lock()
1072 .expect("Unit test should be single-threaded")
1073 .push(request);
1074 }
1075}
1076
1077#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1079pub struct ClaimRequest {
1080 source: Account,
1081 destination: Account,
1082 amount: Amount,
1083}