1use std::{
7 collections::{HashMap, VecDeque},
8 mem,
9 sync::Mutex,
10};
11
12use linera_base::{
13 abi::{ContractAbi, ServiceAbi},
14 data_types::{Amount, ApplicationDescription, BlockHeight, Timestamp},
15 hex, http,
16 identifiers::{AccountOwner, ApplicationId, ChainId, DataBlobHash},
17};
18
19use crate::{KeyValueStore, Service, ViewStorageContext};
20
21pub struct MockServiceRuntime<Application>
23where
24 Application: Service,
25{
26 application_parameters: Mutex<Option<Application::Parameters>>,
27 application_id: Mutex<Option<ApplicationId<Application::Abi>>>,
28 application_creator_chain_id: Mutex<Option<ChainId>>,
29 application_descriptions: Mutex<HashMap<ApplicationId, ApplicationDescription>>,
30 chain_id: Mutex<Option<ChainId>>,
31 next_block_height: Mutex<Option<BlockHeight>>,
32 timestamp: Mutex<Option<Timestamp>>,
33 chain_balance: Mutex<Option<Amount>>,
34 owner_balances: Mutex<Option<HashMap<AccountOwner, Amount>>>,
35 query_application_handler: Mutex<Option<QueryApplicationHandler>>,
36 expected_http_requests: Mutex<VecDeque<(http::Request, http::Response)>>,
37 blobs: Mutex<Option<HashMap<DataBlobHash, Vec<u8>>>>,
38 scheduled_operations: Mutex<Vec<Vec<u8>>>,
39 key_value_store: KeyValueStore,
40}
41
42impl<Application> Default for MockServiceRuntime<Application>
43where
44 Application: Service,
45{
46 fn default() -> Self {
47 MockServiceRuntime::new()
48 }
49}
50
51impl<Application> MockServiceRuntime<Application>
52where
53 Application: Service,
54{
55 pub fn new() -> Self {
57 MockServiceRuntime {
58 application_parameters: Mutex::new(None),
59 application_id: Mutex::new(None),
60 application_creator_chain_id: Mutex::new(None),
61 application_descriptions: Mutex::new(HashMap::new()),
62 chain_id: Mutex::new(None),
63 next_block_height: Mutex::new(None),
64 timestamp: Mutex::new(None),
65 chain_balance: Mutex::new(None),
66 owner_balances: Mutex::new(None),
67 query_application_handler: Mutex::new(None),
68 expected_http_requests: Mutex::new(VecDeque::new()),
69 blobs: Mutex::new(None),
70 scheduled_operations: Mutex::new(vec![]),
71 key_value_store: KeyValueStore::mock(),
72 }
73 }
74
75 pub fn key_value_store(&self) -> KeyValueStore {
77 self.key_value_store.clone()
78 }
79
80 pub fn root_view_storage_context(&self) -> ViewStorageContext {
82 ViewStorageContext::new_unchecked(self.key_value_store(), Vec::new(), ())
83 }
84
85 pub fn with_application_parameters(
87 self,
88 application_parameters: Application::Parameters,
89 ) -> Self {
90 *self.application_parameters.lock().unwrap() = Some(application_parameters);
91 self
92 }
93
94 pub fn set_application_parameters(
96 &self,
97 application_parameters: Application::Parameters,
98 ) -> &Self {
99 *self.application_parameters.lock().unwrap() = Some(application_parameters);
100 self
101 }
102
103 pub fn application_parameters(&self) -> Application::Parameters {
105 Self::fetch_mocked_value(
106 &self.application_parameters,
107 "Application parameters have not been mocked, \
108 please call `MockServiceRuntime::set_application_parameters` first",
109 )
110 }
111
112 pub fn with_application_id(self, application_id: ApplicationId<Application::Abi>) -> Self {
114 *self.application_id.lock().unwrap() = Some(application_id);
115 self
116 }
117
118 pub fn set_application_id(&self, application_id: ApplicationId<Application::Abi>) -> &Self {
120 *self.application_id.lock().unwrap() = Some(application_id);
121 self
122 }
123
124 pub fn application_id(&self) -> ApplicationId<Application::Abi> {
126 Self::fetch_mocked_value(
127 &self.application_id,
128 "Application ID has not been mocked, \
129 please call `MockServiceRuntime::set_application_id` first",
130 )
131 }
132
133 pub fn with_application_creator_chain_id(self, application_creator_chain_id: ChainId) -> Self {
135 *self.application_creator_chain_id.lock().unwrap() = Some(application_creator_chain_id);
136 self
137 }
138
139 pub fn set_application_creator_chain_id(&self, application_creator_chain_id: ChainId) -> &Self {
141 *self.application_creator_chain_id.lock().unwrap() = Some(application_creator_chain_id);
142 self
143 }
144
145 pub fn application_creator_chain_id(&self) -> ChainId {
147 Self::fetch_mocked_value(
148 &self.application_creator_chain_id,
149 "Application creator chain ID has not been mocked, \
150 please call `MockServiceRuntime::set_application_creator_chain_id` first",
151 )
152 }
153
154 pub fn with_application_description(
156 self,
157 application_id: ApplicationId,
158 description: ApplicationDescription,
159 ) -> Self {
160 self.application_descriptions
161 .lock()
162 .unwrap()
163 .insert(application_id, description);
164 self
165 }
166
167 pub fn set_application_description(
169 &self,
170 application_id: ApplicationId,
171 description: ApplicationDescription,
172 ) -> &Self {
173 self.application_descriptions
174 .lock()
175 .unwrap()
176 .insert(application_id, description);
177 self
178 }
179
180 pub fn read_application_description(
182 &self,
183 application_id: ApplicationId,
184 ) -> ApplicationDescription {
185 self.application_descriptions
186 .lock()
187 .unwrap()
188 .get(&application_id)
189 .cloned()
190 .unwrap_or_else(|| {
191 panic!(
192 "Application description for {application_id:?} has not been mocked, \
193 please call `MockServiceRuntime::set_application_description` first"
194 )
195 })
196 }
197
198 pub fn with_chain_id(self, chain_id: ChainId) -> Self {
200 *self.chain_id.lock().unwrap() = Some(chain_id);
201 self
202 }
203
204 pub fn set_chain_id(&self, chain_id: ChainId) -> &Self {
206 *self.chain_id.lock().unwrap() = Some(chain_id);
207 self
208 }
209
210 pub fn chain_id(&self) -> ChainId {
212 Self::fetch_mocked_value(
213 &self.chain_id,
214 "Chain ID has not been mocked, \
215 please call `MockServiceRuntime::set_chain_id` first",
216 )
217 }
218
219 pub fn with_next_block_height(self, next_block_height: BlockHeight) -> Self {
221 *self.next_block_height.lock().unwrap() = Some(next_block_height);
222 self
223 }
224
225 pub fn set_next_block_height(&self, next_block_height: BlockHeight) -> &Self {
227 *self.next_block_height.lock().unwrap() = Some(next_block_height);
228 self
229 }
230
231 pub fn next_block_height(&self) -> BlockHeight {
233 Self::fetch_mocked_value(
234 &self.next_block_height,
235 "Next block height has not been mocked, \
236 please call `MockServiceRuntime::set_next_block_height` first",
237 )
238 }
239
240 pub fn with_system_time(self, timestamp: Timestamp) -> Self {
242 *self.timestamp.lock().unwrap() = Some(timestamp);
243 self
244 }
245
246 pub fn set_system_time(&self, timestamp: Timestamp) -> &Self {
248 *self.timestamp.lock().unwrap() = Some(timestamp);
249 self
250 }
251
252 pub fn system_time(&self) -> Timestamp {
254 Self::fetch_mocked_value(
255 &self.timestamp,
256 "System time has not been mocked, \
257 please call `MockServiceRuntime::set_system_time` first",
258 )
259 }
260
261 pub fn with_chain_balance(self, chain_balance: Amount) -> Self {
263 *self.chain_balance.lock().unwrap() = Some(chain_balance);
264 self
265 }
266
267 pub fn set_chain_balance(&self, chain_balance: Amount) -> &Self {
269 *self.chain_balance.lock().unwrap() = Some(chain_balance);
270 self
271 }
272
273 pub fn chain_balance(&self) -> Amount {
275 Self::fetch_mocked_value(
276 &self.chain_balance,
277 "Chain balance has not been mocked, \
278 please call `MockServiceRuntime::set_chain_balance` first",
279 )
280 }
281
282 pub fn with_owner_balances(
284 self,
285 owner_balances: impl IntoIterator<Item = (AccountOwner, Amount)>,
286 ) -> Self {
287 *self.owner_balances.lock().unwrap() = Some(owner_balances.into_iter().collect());
288 self
289 }
290
291 pub fn set_owner_balances(
293 &self,
294 owner_balances: impl IntoIterator<Item = (AccountOwner, Amount)>,
295 ) -> &Self {
296 *self.owner_balances.lock().unwrap() = Some(owner_balances.into_iter().collect());
297 self
298 }
299
300 pub fn with_owner_balance(self, owner: AccountOwner, balance: Amount) -> Self {
302 self.set_owner_balance(owner, balance);
303 self
304 }
305
306 pub fn set_owner_balance(&self, owner: AccountOwner, balance: Amount) -> &Self {
308 self.owner_balances
309 .lock()
310 .unwrap()
311 .get_or_insert_with(HashMap::new)
312 .insert(owner, balance);
313 self
314 }
315
316 pub fn owner_balance(&self, owner: AccountOwner) -> Amount {
318 self.owner_balances
319 .lock()
320 .unwrap()
321 .as_mut()
322 .and_then(|owner_balances| owner_balances.get(&owner).copied())
323 .unwrap_or_else(|| {
324 panic!(
325 "Balance for owner {owner} was not mocked, \
326 please include a balance for them with a call to \
327 `MockServiceRuntime::set_owner_balance`"
328 )
329 })
330 }
331
332 pub fn owner_balances(&self) -> Vec<(AccountOwner, Amount)> {
334 self.owner_balances
335 .lock()
336 .unwrap()
337 .as_ref()
338 .expect(
339 "Owner balances have not been mocked, \
340 please call `MockServiceRuntime::set_owner_balances` first",
341 )
342 .iter()
343 .map(|(owner, amount)| (*owner, *amount))
344 .collect()
345 }
346
347 pub fn balance_owners(&self) -> Vec<AccountOwner> {
349 self.owner_balances
350 .lock()
351 .unwrap()
352 .as_ref()
353 .expect(
354 "Owner balances have not been mocked, \
355 please call `MockServiceRuntime::set_owner_balances` first",
356 )
357 .keys()
358 .copied()
359 .collect()
360 }
361
362 pub fn allowance(&self, _owner: AccountOwner, _spender: AccountOwner) -> Amount {
364 Amount::ZERO
365 }
366
367 pub fn allowances(&self) -> Vec<(AccountOwner, AccountOwner, Amount)> {
369 Vec::new()
370 }
371
372 pub fn schedule_raw_operation(&self, operation: Vec<u8>) {
376 self.scheduled_operations.lock().unwrap().push(operation);
377 }
378
379 pub fn schedule_operation(&self, operation: &<Application::Abi as ContractAbi>::Operation) {
383 let bytes = <Application::Abi as ContractAbi>::serialize_operation(operation)
384 .expect("Failed to serialize application operation");
385
386 self.schedule_raw_operation(bytes);
387 }
388
389 pub fn raw_scheduled_operations(&self) -> Vec<Vec<u8>> {
395 mem::take(&mut self.scheduled_operations.lock().unwrap())
396 }
397
398 pub fn scheduled_operations(&self) -> Vec<<Application::Abi as ContractAbi>::Operation> {
406 self.raw_scheduled_operations()
407 .into_iter()
408 .enumerate()
409 .map(|(index, bytes)| {
410 let hex_bytes = hex::encode(&bytes);
411 <Application::Abi as ContractAbi>::deserialize_operation(bytes).unwrap_or_else(
412 |error| {
413 panic!(
414 "Failed to deserialize scheduled operation #{index} (0x{hex_bytes}): {error}"
415 )
416 },
417 )
418 })
419 .collect()
420 }
421
422 pub fn with_query_application_handler(
424 self,
425 handler: impl FnMut(ApplicationId, Vec<u8>) -> Vec<u8> + Send + 'static,
426 ) -> Self {
427 *self.query_application_handler.lock().unwrap() = Some(Box::new(handler));
428 self
429 }
430
431 pub fn set_query_application_handler(
433 &self,
434 handler: impl FnMut(ApplicationId, Vec<u8>) -> Vec<u8> + Send + 'static,
435 ) -> &Self {
436 *self.query_application_handler.lock().unwrap() = Some(Box::new(handler));
437 self
438 }
439
440 pub fn query_application<A: ServiceAbi>(
442 &self,
443 application: ApplicationId<A>,
444 query: &A::Query,
445 ) -> A::QueryResponse {
446 let query_bytes =
447 serde_json::to_vec(&query).expect("Failed to serialize query to another application");
448
449 let mut handler_guard = self.query_application_handler.lock().unwrap();
450 let handler = handler_guard.as_mut().expect(
451 "Handler for `query_application` has not been mocked, \
452 please call `MockServiceRuntime::set_query_application_handler` first",
453 );
454
455 let response_bytes = handler(application.forget_abi(), query_bytes);
456
457 serde_json::from_slice(&response_bytes)
458 .expect("Failed to deserialize query response from application")
459 }
460
461 pub fn add_expected_http_request(&mut self, request: http::Request, response: http::Response) {
463 self.expected_http_requests
464 .lock()
465 .unwrap()
466 .push_back((request, response));
467 }
468
469 #[expect(clippy::needless_pass_by_value)]
477 pub fn http_request(&self, request: http::Request) -> http::Response {
478 let maybe_request = self.expected_http_requests.lock().unwrap().pop_front();
479 let (expected_request, response) = maybe_request.expect("Unexpected HTTP request");
480 assert_eq!(&request, &expected_request);
481 response
482 }
483
484 pub fn with_blobs(self, blobs: impl IntoIterator<Item = (DataBlobHash, Vec<u8>)>) -> Self {
486 *self.blobs.lock().unwrap() = Some(blobs.into_iter().collect());
487 self
488 }
489
490 pub fn set_blobs(&self, blobs: impl IntoIterator<Item = (DataBlobHash, Vec<u8>)>) -> &Self {
492 *self.blobs.lock().unwrap() = Some(blobs.into_iter().collect());
493 self
494 }
495
496 pub fn with_blob(self, hash: impl Into<DataBlobHash>, blob: Vec<u8>) -> Self {
498 self.set_blob(hash, blob);
499 self
500 }
501
502 pub fn set_blob(&self, hash: impl Into<DataBlobHash>, blob: Vec<u8>) -> &Self {
504 self.blobs
505 .lock()
506 .unwrap()
507 .get_or_insert_with(HashMap::new)
508 .insert(hash.into(), blob);
509 self
510 }
511
512 pub fn read_data_blob(&self, hash: DataBlobHash) -> Vec<u8> {
514 self.blobs
515 .lock()
516 .unwrap()
517 .as_ref()
518 .and_then(|blobs| blobs.get(&hash).cloned())
519 .unwrap_or_else(|| {
520 panic!(
521 "Blob for hash {hash:?} has not been mocked, \
522 please call `MockServiceRuntime::set_blob` first"
523 )
524 })
525 }
526
527 pub fn assert_blob_exists(&self, hash: DataBlobHash) {
529 assert!(
530 self.blobs
531 .lock()
532 .unwrap()
533 .as_ref()
534 .is_some_and(|blobs| blobs.contains_key(&hash)),
535 "Blob for hash {hash:?} has not been mocked, \
536 please call `MockServiceRuntime::set_blob` first"
537 );
538 }
539
540 fn fetch_mocked_value<T>(slot: &Mutex<Option<T>>, message: &str) -> T
542 where
543 T: Clone,
544 {
545 slot.lock().unwrap().clone().expect(message)
546 }
547}
548
549pub type QueryApplicationHandler = Box<dyn FnMut(ApplicationId, Vec<u8>) -> Vec<u8> + Send>;