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 schedule_raw_operation(&self, operation: Vec<u8>) {
366 self.scheduled_operations.lock().unwrap().push(operation);
367 }
368
369 pub fn schedule_operation(&self, operation: &<Application::Abi as ContractAbi>::Operation) {
373 let bytes = <Application::Abi as ContractAbi>::serialize_operation(operation)
374 .expect("Failed to serialize application operation");
375
376 self.schedule_raw_operation(bytes);
377 }
378
379 pub fn raw_scheduled_operations(&self) -> Vec<Vec<u8>> {
385 mem::take(&mut self.scheduled_operations.lock().unwrap())
386 }
387
388 pub fn scheduled_operations(&self) -> Vec<<Application::Abi as ContractAbi>::Operation> {
396 self.raw_scheduled_operations()
397 .into_iter()
398 .enumerate()
399 .map(|(index, bytes)| {
400 let hex_bytes = hex::encode(&bytes);
401 <Application::Abi as ContractAbi>::deserialize_operation(bytes).unwrap_or_else(
402 |error| {
403 panic!(
404 "Failed to deserialize scheduled operation #{index} (0x{}): {error}",
405 hex_bytes
406 )
407 },
408 )
409 })
410 .collect()
411 }
412
413 pub fn with_query_application_handler(
415 self,
416 handler: impl FnMut(ApplicationId, Vec<u8>) -> Vec<u8> + Send + 'static,
417 ) -> Self {
418 *self.query_application_handler.lock().unwrap() = Some(Box::new(handler));
419 self
420 }
421
422 pub fn set_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 query_application<A: ServiceAbi>(
433 &self,
434 application: ApplicationId<A>,
435 query: &A::Query,
436 ) -> A::QueryResponse {
437 let query_bytes =
438 serde_json::to_vec(&query).expect("Failed to serialize query to another application");
439
440 let mut handler_guard = self.query_application_handler.lock().unwrap();
441 let handler = handler_guard.as_mut().expect(
442 "Handler for `query_application` has not been mocked, \
443 please call `MockServiceRuntime::set_query_application_handler` first",
444 );
445
446 let response_bytes = handler(application.forget_abi(), query_bytes);
447
448 serde_json::from_slice(&response_bytes)
449 .expect("Failed to deserialize query response from application")
450 }
451
452 pub fn add_expected_http_request(&mut self, request: http::Request, response: http::Response) {
454 self.expected_http_requests
455 .lock()
456 .unwrap()
457 .push_back((request, response));
458 }
459
460 pub fn http_request(&self, request: http::Request) -> http::Response {
468 let maybe_request = self.expected_http_requests.lock().unwrap().pop_front();
469 let (expected_request, response) = maybe_request.expect("Unexpected HTTP request");
470 assert_eq!(request, expected_request);
471 response
472 }
473
474 pub fn with_blobs(self, blobs: impl IntoIterator<Item = (DataBlobHash, Vec<u8>)>) -> Self {
476 *self.blobs.lock().unwrap() = Some(blobs.into_iter().collect());
477 self
478 }
479
480 pub fn set_blobs(&self, blobs: impl IntoIterator<Item = (DataBlobHash, Vec<u8>)>) -> &Self {
482 *self.blobs.lock().unwrap() = Some(blobs.into_iter().collect());
483 self
484 }
485
486 pub fn with_blob(self, hash: impl Into<DataBlobHash>, blob: Vec<u8>) -> Self {
488 self.set_blob(hash, blob);
489 self
490 }
491
492 pub fn set_blob(&self, hash: impl Into<DataBlobHash>, blob: Vec<u8>) -> &Self {
494 self.blobs
495 .lock()
496 .unwrap()
497 .get_or_insert_with(HashMap::new)
498 .insert(hash.into(), blob);
499 self
500 }
501
502 pub fn read_data_blob(&self, hash: DataBlobHash) -> Vec<u8> {
504 self.blobs
505 .lock()
506 .unwrap()
507 .as_ref()
508 .and_then(|blobs| blobs.get(&hash).cloned())
509 .unwrap_or_else(|| {
510 panic!(
511 "Blob for hash {hash:?} has not been mocked, \
512 please call `MockServiceRuntime::set_blob` first"
513 )
514 })
515 }
516
517 pub fn assert_blob_exists(&self, hash: DataBlobHash) {
519 assert!(
520 self.blobs
521 .lock()
522 .unwrap()
523 .as_ref()
524 .is_some_and(|blobs| blobs.contains_key(&hash)),
525 "Blob for hash {hash:?} has not been mocked, \
526 please call `MockServiceRuntime::set_blob` first"
527 );
528 }
529
530 fn fetch_mocked_value<T>(slot: &Mutex<Option<T>>, message: &str) -> T
532 where
533 T: Clone,
534 {
535 slot.lock().unwrap().clone().expect(message)
536 }
537}
538
539pub type QueryApplicationHandler = Box<dyn FnMut(ApplicationId, Vec<u8>) -> Vec<u8> + Send>;