1use std::{
7 collections::{HashMap, VecDeque},
8 mem,
9 sync::Mutex,
10};
11
12use linera_base::{
13 abi::ServiceAbi,
14 data_types::{Amount, BlockHeight, Timestamp},
15 hex, http,
16 identifiers::{AccountOwner, ApplicationId, ChainId},
17};
18use serde::{de::DeserializeOwned, Serialize};
19
20use crate::{DataBlobHash, KeyValueStore, Service, ViewStorageContext};
21
22pub struct MockServiceRuntime<Application>
24where
25 Application: Service,
26{
27 application_parameters: Mutex<Option<Application::Parameters>>,
28 application_id: Mutex<Option<ApplicationId<Application::Abi>>>,
29 chain_id: Mutex<Option<ChainId>>,
30 next_block_height: Mutex<Option<BlockHeight>>,
31 timestamp: Mutex<Option<Timestamp>>,
32 chain_balance: Mutex<Option<Amount>>,
33 owner_balances: Mutex<Option<HashMap<AccountOwner, Amount>>>,
34 query_application_handler: Mutex<Option<QueryApplicationHandler>>,
35 expected_http_requests: Mutex<VecDeque<(http::Request, http::Response)>>,
36 blobs: Mutex<Option<HashMap<DataBlobHash, Vec<u8>>>>,
37 scheduled_operations: Mutex<Vec<Vec<u8>>>,
38 key_value_store: KeyValueStore,
39}
40
41impl<Application> Default for MockServiceRuntime<Application>
42where
43 Application: Service,
44{
45 fn default() -> Self {
46 MockServiceRuntime::new()
47 }
48}
49
50impl<Application> MockServiceRuntime<Application>
51where
52 Application: Service,
53{
54 pub fn new() -> Self {
56 MockServiceRuntime {
57 application_parameters: Mutex::new(None),
58 application_id: Mutex::new(None),
59 chain_id: Mutex::new(None),
60 next_block_height: Mutex::new(None),
61 timestamp: Mutex::new(None),
62 chain_balance: Mutex::new(None),
63 owner_balances: Mutex::new(None),
64 query_application_handler: Mutex::new(None),
65 expected_http_requests: Mutex::new(VecDeque::new()),
66 blobs: Mutex::new(None),
67 scheduled_operations: Mutex::new(vec![]),
68 key_value_store: KeyValueStore::mock(),
69 }
70 }
71
72 pub fn key_value_store(&self) -> KeyValueStore {
74 self.key_value_store.clone()
75 }
76
77 pub fn root_view_storage_context(&self) -> ViewStorageContext {
79 ViewStorageContext::new_unsafe(self.key_value_store(), Vec::new(), ())
80 }
81
82 pub fn with_application_parameters(
84 self,
85 application_parameters: Application::Parameters,
86 ) -> Self {
87 *self.application_parameters.lock().unwrap() = Some(application_parameters);
88 self
89 }
90
91 pub fn set_application_parameters(
93 &self,
94 application_parameters: Application::Parameters,
95 ) -> &Self {
96 *self.application_parameters.lock().unwrap() = Some(application_parameters);
97 self
98 }
99
100 pub fn application_parameters(&self) -> Application::Parameters {
102 Self::fetch_mocked_value(
103 &self.application_parameters,
104 "Application parameters have not been mocked, \
105 please call `MockServiceRuntime::set_application_parameters` first",
106 )
107 }
108
109 pub fn with_application_id(self, application_id: ApplicationId<Application::Abi>) -> Self {
111 *self.application_id.lock().unwrap() = Some(application_id);
112 self
113 }
114
115 pub fn set_application_id(&self, application_id: ApplicationId<Application::Abi>) -> &Self {
117 *self.application_id.lock().unwrap() = Some(application_id);
118 self
119 }
120
121 pub fn application_id(&self) -> ApplicationId<Application::Abi> {
123 Self::fetch_mocked_value(
124 &self.application_id,
125 "Application ID has not been mocked, \
126 please call `MockServiceRuntime::set_application_id` first",
127 )
128 }
129
130 pub fn with_chain_id(self, chain_id: ChainId) -> Self {
132 *self.chain_id.lock().unwrap() = Some(chain_id);
133 self
134 }
135
136 pub fn set_chain_id(&self, chain_id: ChainId) -> &Self {
138 *self.chain_id.lock().unwrap() = Some(chain_id);
139 self
140 }
141
142 pub fn chain_id(&self) -> ChainId {
144 Self::fetch_mocked_value(
145 &self.chain_id,
146 "Chain ID has not been mocked, \
147 please call `MockServiceRuntime::set_chain_id` first",
148 )
149 }
150
151 pub fn with_next_block_height(self, next_block_height: BlockHeight) -> Self {
153 *self.next_block_height.lock().unwrap() = Some(next_block_height);
154 self
155 }
156
157 pub fn set_next_block_height(&self, next_block_height: BlockHeight) -> &Self {
159 *self.next_block_height.lock().unwrap() = Some(next_block_height);
160 self
161 }
162
163 pub fn next_block_height(&self) -> BlockHeight {
165 Self::fetch_mocked_value(
166 &self.next_block_height,
167 "Next block height has not been mocked, \
168 please call `MockServiceRuntime::set_next_block_height` first",
169 )
170 }
171
172 pub fn with_system_time(self, timestamp: Timestamp) -> Self {
174 *self.timestamp.lock().unwrap() = Some(timestamp);
175 self
176 }
177
178 pub fn set_system_time(&self, timestamp: Timestamp) -> &Self {
180 *self.timestamp.lock().unwrap() = Some(timestamp);
181 self
182 }
183
184 pub fn system_time(&self) -> Timestamp {
186 Self::fetch_mocked_value(
187 &self.timestamp,
188 "System time has not been mocked, \
189 please call `MockServiceRuntime::set_system_time` first",
190 )
191 }
192
193 pub fn with_chain_balance(self, chain_balance: Amount) -> Self {
195 *self.chain_balance.lock().unwrap() = Some(chain_balance);
196 self
197 }
198
199 pub fn set_chain_balance(&self, chain_balance: Amount) -> &Self {
201 *self.chain_balance.lock().unwrap() = Some(chain_balance);
202 self
203 }
204
205 pub fn chain_balance(&self) -> Amount {
207 Self::fetch_mocked_value(
208 &self.chain_balance,
209 "Chain balance has not been mocked, \
210 please call `MockServiceRuntime::set_chain_balance` first",
211 )
212 }
213
214 pub fn with_owner_balances(
216 self,
217 owner_balances: impl IntoIterator<Item = (AccountOwner, Amount)>,
218 ) -> Self {
219 *self.owner_balances.lock().unwrap() = Some(owner_balances.into_iter().collect());
220 self
221 }
222
223 pub fn set_owner_balances(
225 &self,
226 owner_balances: impl IntoIterator<Item = (AccountOwner, Amount)>,
227 ) -> &Self {
228 *self.owner_balances.lock().unwrap() = Some(owner_balances.into_iter().collect());
229 self
230 }
231
232 pub fn with_owner_balance(self, owner: AccountOwner, balance: Amount) -> Self {
234 self.set_owner_balance(owner, balance);
235 self
236 }
237
238 pub fn set_owner_balance(&self, owner: AccountOwner, balance: Amount) -> &Self {
240 self.owner_balances
241 .lock()
242 .unwrap()
243 .get_or_insert_with(HashMap::new)
244 .insert(owner, balance);
245 self
246 }
247
248 pub fn owner_balance(&self, owner: AccountOwner) -> Amount {
250 self.owner_balances
251 .lock()
252 .unwrap()
253 .as_mut()
254 .and_then(|owner_balances| owner_balances.get(&owner).copied())
255 .unwrap_or_else(|| {
256 panic!(
257 "Balance for owner {owner} was not mocked, \
258 please include a balance for them with a call to \
259 `MockServiceRuntime::set_owner_balance`"
260 )
261 })
262 }
263
264 pub fn owner_balances(&self) -> Vec<(AccountOwner, Amount)> {
266 self.owner_balances
267 .lock()
268 .unwrap()
269 .as_ref()
270 .expect(
271 "Owner balances have not been mocked, \
272 please call `MockServiceRuntime::set_owner_balances` first",
273 )
274 .iter()
275 .map(|(owner, amount)| (*owner, *amount))
276 .collect()
277 }
278
279 pub fn balance_owners(&self) -> Vec<AccountOwner> {
281 self.owner_balances
282 .lock()
283 .unwrap()
284 .as_ref()
285 .expect(
286 "Owner balances have not been mocked, \
287 please call `MockServiceRuntime::set_owner_balances` first",
288 )
289 .keys()
290 .cloned()
291 .collect()
292 }
293
294 pub fn schedule_raw_operation(&self, operation: Vec<u8>) {
298 self.scheduled_operations.lock().unwrap().push(operation);
299 }
300
301 pub fn schedule_operation(&self, operation: &impl Serialize) {
305 let bytes = bcs::to_bytes(operation).expect("Failed to serialize application operation");
306
307 self.schedule_raw_operation(bytes);
308 }
309
310 pub fn raw_scheduled_operations(&self) -> Vec<Vec<u8>> {
316 mem::take(&mut self.scheduled_operations.lock().unwrap())
317 }
318
319 pub fn scheduled_operations<Operation>(&self) -> Vec<Operation>
327 where
328 Operation: DeserializeOwned,
329 {
330 self.raw_scheduled_operations()
331 .into_iter()
332 .enumerate()
333 .map(|(index, bytes)| {
334 bcs::from_bytes(&bytes).unwrap_or_else(|error| {
335 panic!(
336 "Failed to deserialize scheduled operation #{index} (0x{}): {error}",
337 hex::encode(bytes)
338 )
339 })
340 })
341 .collect()
342 }
343
344 pub fn with_query_application_handler(
346 self,
347 handler: impl FnMut(ApplicationId, Vec<u8>) -> Vec<u8> + Send + 'static,
348 ) -> Self {
349 *self.query_application_handler.lock().unwrap() = Some(Box::new(handler));
350 self
351 }
352
353 pub fn set_query_application_handler(
355 &self,
356 handler: impl FnMut(ApplicationId, Vec<u8>) -> Vec<u8> + Send + 'static,
357 ) -> &Self {
358 *self.query_application_handler.lock().unwrap() = Some(Box::new(handler));
359 self
360 }
361
362 pub fn query_application<A: ServiceAbi>(
364 &self,
365 application: ApplicationId<A>,
366 query: &A::Query,
367 ) -> A::QueryResponse {
368 let query_bytes =
369 serde_json::to_vec(&query).expect("Failed to serialize query to another application");
370
371 let mut handler_guard = self.query_application_handler.lock().unwrap();
372 let handler = handler_guard.as_mut().expect(
373 "Handler for `query_application` has not been mocked, \
374 please call `MockServiceRuntime::set_query_application_handler` first",
375 );
376
377 let response_bytes = handler(application.forget_abi(), query_bytes);
378
379 serde_json::from_slice(&response_bytes)
380 .expect("Failed to deserialize query response from application")
381 }
382
383 pub fn add_expected_http_request(&mut self, request: http::Request, response: http::Response) {
385 self.expected_http_requests
386 .lock()
387 .unwrap()
388 .push_back((request, response));
389 }
390
391 pub fn http_request(&self, request: http::Request) -> http::Response {
399 let maybe_request = self.expected_http_requests.lock().unwrap().pop_front();
400 let (expected_request, response) = maybe_request.expect("Unexpected HTTP request");
401 assert_eq!(request, expected_request);
402 response
403 }
404
405 pub fn with_blobs(self, blobs: impl IntoIterator<Item = (DataBlobHash, Vec<u8>)>) -> Self {
407 *self.blobs.lock().unwrap() = Some(blobs.into_iter().collect());
408 self
409 }
410
411 pub fn set_blobs(&self, blobs: impl IntoIterator<Item = (DataBlobHash, Vec<u8>)>) -> &Self {
413 *self.blobs.lock().unwrap() = Some(blobs.into_iter().collect());
414 self
415 }
416
417 pub fn with_blob(self, hash: impl Into<DataBlobHash>, blob: Vec<u8>) -> Self {
419 self.set_blob(hash, blob);
420 self
421 }
422
423 pub fn set_blob(&self, hash: impl Into<DataBlobHash>, blob: Vec<u8>) -> &Self {
425 self.blobs
426 .lock()
427 .unwrap()
428 .get_or_insert_with(HashMap::new)
429 .insert(hash.into(), blob);
430 self
431 }
432
433 pub fn read_data_blob(&self, hash: DataBlobHash) -> Vec<u8> {
435 self.blobs
436 .lock()
437 .unwrap()
438 .as_ref()
439 .and_then(|blobs| blobs.get(&hash).cloned())
440 .unwrap_or_else(|| {
441 panic!(
442 "Blob for hash {hash:?} has not been mocked, \
443 please call `MockServiceRuntime::set_blob` first"
444 )
445 })
446 }
447
448 pub fn assert_blob_exists(&self, hash: DataBlobHash) {
450 self.blobs
451 .lock()
452 .unwrap()
453 .as_ref()
454 .map(|blobs| blobs.contains_key(&hash))
455 .unwrap_or_else(|| {
456 panic!(
457 "Blob for hash {hash:?} has not been mocked, \
458 please call `MockServiceRuntime::set_blob` first"
459 )
460 });
461 }
462
463 fn fetch_mocked_value<T>(slot: &Mutex<Option<T>>, message: &str) -> T
465 where
466 T: Clone,
467 {
468 slot.lock().unwrap().clone().expect(message)
469 }
470}
471
472pub type QueryApplicationHandler = Box<dyn FnMut(ApplicationId, Vec<u8>) -> Vec<u8> + Send>;