1use crate::{Cheatcode, Cheatcodes, Result, Vm::*, string};
4use alloy_dyn_abi::{DynSolType, DynSolValue, Resolver, eip712_parser::EncodeType};
5use alloy_primitives::{Address, B256, I256, U256, hex};
6use alloy_sol_types::SolValue;
7use foundry_common::{fmt::serialize_value_as_json, fs};
8use foundry_config::fs_permissions::FsAccessKind;
9use serde_json::{Map, Value};
10use std::{borrow::Cow, collections::BTreeMap};
11
12impl Cheatcode for keyExistsCall {
13 fn apply(&self, _state: &mut Cheatcodes) -> Result {
14 let Self { json, key } = self;
15 check_json_key_exists(json, key)
16 }
17}
18
19impl Cheatcode for keyExistsJsonCall {
20 fn apply(&self, _state: &mut Cheatcodes) -> Result {
21 let Self { json, key } = self;
22 check_json_key_exists(json, key)
23 }
24}
25
26impl Cheatcode for parseJson_0Call {
27 fn apply(&self, _state: &mut Cheatcodes) -> Result {
28 let Self { json } = self;
29 parse_json(json, "$")
30 }
31}
32
33impl Cheatcode for parseJson_1Call {
34 fn apply(&self, _state: &mut Cheatcodes) -> Result {
35 let Self { json, key } = self;
36 parse_json(json, key)
37 }
38}
39
40impl Cheatcode for parseJsonUintCall {
41 fn apply(&self, _state: &mut Cheatcodes) -> Result {
42 let Self { json, key } = self;
43 parse_json_coerce(json, key, &DynSolType::Uint(256))
44 }
45}
46
47impl Cheatcode for parseJsonUintArrayCall {
48 fn apply(&self, _state: &mut Cheatcodes) -> Result {
49 let Self { json, key } = self;
50 parse_json_coerce(json, key, &DynSolType::Array(Box::new(DynSolType::Uint(256))))
51 }
52}
53
54impl Cheatcode for parseJsonIntCall {
55 fn apply(&self, _state: &mut Cheatcodes) -> Result {
56 let Self { json, key } = self;
57 parse_json_coerce(json, key, &DynSolType::Int(256))
58 }
59}
60
61impl Cheatcode for parseJsonIntArrayCall {
62 fn apply(&self, _state: &mut Cheatcodes) -> Result {
63 let Self { json, key } = self;
64 parse_json_coerce(json, key, &DynSolType::Array(Box::new(DynSolType::Int(256))))
65 }
66}
67
68impl Cheatcode for parseJsonBoolCall {
69 fn apply(&self, _state: &mut Cheatcodes) -> Result {
70 let Self { json, key } = self;
71 parse_json_coerce(json, key, &DynSolType::Bool)
72 }
73}
74
75impl Cheatcode for parseJsonBoolArrayCall {
76 fn apply(&self, _state: &mut Cheatcodes) -> Result {
77 let Self { json, key } = self;
78 parse_json_coerce(json, key, &DynSolType::Array(Box::new(DynSolType::Bool)))
79 }
80}
81
82impl Cheatcode for parseJsonAddressCall {
83 fn apply(&self, _state: &mut Cheatcodes) -> Result {
84 let Self { json, key } = self;
85 parse_json_coerce(json, key, &DynSolType::Address)
86 }
87}
88
89impl Cheatcode for parseJsonAddressArrayCall {
90 fn apply(&self, _state: &mut Cheatcodes) -> Result {
91 let Self { json, key } = self;
92 parse_json_coerce(json, key, &DynSolType::Array(Box::new(DynSolType::Address)))
93 }
94}
95
96impl Cheatcode for parseJsonStringCall {
97 fn apply(&self, _state: &mut Cheatcodes) -> Result {
98 let Self { json, key } = self;
99 parse_json_coerce(json, key, &DynSolType::String)
100 }
101}
102
103impl Cheatcode for parseJsonStringArrayCall {
104 fn apply(&self, _state: &mut Cheatcodes) -> Result {
105 let Self { json, key } = self;
106 parse_json_coerce(json, key, &DynSolType::Array(Box::new(DynSolType::String)))
107 }
108}
109
110impl Cheatcode for parseJsonBytesCall {
111 fn apply(&self, _state: &mut Cheatcodes) -> Result {
112 let Self { json, key } = self;
113 parse_json_coerce(json, key, &DynSolType::Bytes)
114 }
115}
116
117impl Cheatcode for parseJsonBytesArrayCall {
118 fn apply(&self, _state: &mut Cheatcodes) -> Result {
119 let Self { json, key } = self;
120 parse_json_coerce(json, key, &DynSolType::Array(Box::new(DynSolType::Bytes)))
121 }
122}
123
124impl Cheatcode for parseJsonBytes32Call {
125 fn apply(&self, _state: &mut Cheatcodes) -> Result {
126 let Self { json, key } = self;
127 parse_json_coerce(json, key, &DynSolType::FixedBytes(32))
128 }
129}
130
131impl Cheatcode for parseJsonBytes32ArrayCall {
132 fn apply(&self, _state: &mut Cheatcodes) -> Result {
133 let Self { json, key } = self;
134 parse_json_coerce(json, key, &DynSolType::Array(Box::new(DynSolType::FixedBytes(32))))
135 }
136}
137
138impl Cheatcode for parseJsonType_0Call {
139 fn apply(&self, _state: &mut Cheatcodes) -> Result {
140 let Self { json, typeDescription } = self;
141 parse_json_coerce(json, "$", &resolve_type(typeDescription)?).map(|v| v.abi_encode())
142 }
143}
144
145impl Cheatcode for parseJsonType_1Call {
146 fn apply(&self, _state: &mut Cheatcodes) -> Result {
147 let Self { json, key, typeDescription } = self;
148 parse_json_coerce(json, key, &resolve_type(typeDescription)?).map(|v| v.abi_encode())
149 }
150}
151
152impl Cheatcode for parseJsonTypeArrayCall {
153 fn apply(&self, _state: &mut Cheatcodes) -> Result {
154 let Self { json, key, typeDescription } = self;
155 let ty = resolve_type(typeDescription)?;
156 parse_json_coerce(json, key, &DynSolType::Array(Box::new(ty))).map(|v| v.abi_encode())
157 }
158}
159
160impl Cheatcode for parseJsonKeysCall {
161 fn apply(&self, _state: &mut Cheatcodes) -> Result {
162 let Self { json, key } = self;
163 parse_json_keys(json, key)
164 }
165}
166
167impl Cheatcode for serializeJsonCall {
168 fn apply(&self, state: &mut Cheatcodes) -> Result {
169 let Self { objectKey, value } = self;
170 *state.serialized_jsons.entry(objectKey.into()).or_default() = serde_json::from_str(value)?;
171 Ok(value.abi_encode())
172 }
173}
174
175impl Cheatcode for serializeBool_0Call {
176 fn apply(&self, state: &mut Cheatcodes) -> Result {
177 let Self { objectKey, valueKey, value } = self;
178 serialize_json(state, objectKey, valueKey, (*value).into())
179 }
180}
181
182impl Cheatcode for serializeUint_0Call {
183 fn apply(&self, state: &mut Cheatcodes) -> Result {
184 let Self { objectKey, valueKey, value } = self;
185 serialize_json(state, objectKey, valueKey, (*value).into())
186 }
187}
188
189impl Cheatcode for serializeInt_0Call {
190 fn apply(&self, state: &mut Cheatcodes) -> Result {
191 let Self { objectKey, valueKey, value } = self;
192 serialize_json(state, objectKey, valueKey, (*value).into())
193 }
194}
195
196impl Cheatcode for serializeAddress_0Call {
197 fn apply(&self, state: &mut Cheatcodes) -> Result {
198 let Self { objectKey, valueKey, value } = self;
199 serialize_json(state, objectKey, valueKey, (*value).into())
200 }
201}
202
203impl Cheatcode for serializeBytes32_0Call {
204 fn apply(&self, state: &mut Cheatcodes) -> Result {
205 let Self { objectKey, valueKey, value } = self;
206 serialize_json(state, objectKey, valueKey, DynSolValue::FixedBytes(*value, 32))
207 }
208}
209
210impl Cheatcode for serializeString_0Call {
211 fn apply(&self, state: &mut Cheatcodes) -> Result {
212 let Self { objectKey, valueKey, value } = self;
213 serialize_json(state, objectKey, valueKey, value.clone().into())
214 }
215}
216
217impl Cheatcode for serializeBytes_0Call {
218 fn apply(&self, state: &mut Cheatcodes) -> Result {
219 let Self { objectKey, valueKey, value } = self;
220 serialize_json(state, objectKey, valueKey, value.to_vec().into())
221 }
222}
223
224impl Cheatcode for serializeBool_1Call {
225 fn apply(&self, state: &mut Cheatcodes) -> Result {
226 let Self { objectKey, valueKey, values } = self;
227 serialize_json(
228 state,
229 objectKey,
230 valueKey,
231 DynSolValue::Array(values.iter().copied().map(DynSolValue::Bool).collect()),
232 )
233 }
234}
235
236impl Cheatcode for serializeUint_1Call {
237 fn apply(&self, state: &mut Cheatcodes) -> Result {
238 let Self { objectKey, valueKey, values } = self;
239 serialize_json(
240 state,
241 objectKey,
242 valueKey,
243 DynSolValue::Array(values.iter().map(|v| DynSolValue::Uint(*v, 256)).collect()),
244 )
245 }
246}
247
248impl Cheatcode for serializeInt_1Call {
249 fn apply(&self, state: &mut Cheatcodes) -> Result {
250 let Self { objectKey, valueKey, values } = self;
251 serialize_json(
252 state,
253 objectKey,
254 valueKey,
255 DynSolValue::Array(values.iter().map(|v| DynSolValue::Int(*v, 256)).collect()),
256 )
257 }
258}
259
260impl Cheatcode for serializeAddress_1Call {
261 fn apply(&self, state: &mut Cheatcodes) -> Result {
262 let Self { objectKey, valueKey, values } = self;
263 serialize_json(
264 state,
265 objectKey,
266 valueKey,
267 DynSolValue::Array(values.iter().copied().map(DynSolValue::Address).collect()),
268 )
269 }
270}
271
272impl Cheatcode for serializeBytes32_1Call {
273 fn apply(&self, state: &mut Cheatcodes) -> Result {
274 let Self { objectKey, valueKey, values } = self;
275 serialize_json(
276 state,
277 objectKey,
278 valueKey,
279 DynSolValue::Array(values.iter().map(|v| DynSolValue::FixedBytes(*v, 32)).collect()),
280 )
281 }
282}
283
284impl Cheatcode for serializeString_1Call {
285 fn apply(&self, state: &mut Cheatcodes) -> Result {
286 let Self { objectKey, valueKey, values } = self;
287 serialize_json(
288 state,
289 objectKey,
290 valueKey,
291 DynSolValue::Array(values.iter().cloned().map(DynSolValue::String).collect()),
292 )
293 }
294}
295
296impl Cheatcode for serializeBytes_1Call {
297 fn apply(&self, state: &mut Cheatcodes) -> Result {
298 let Self { objectKey, valueKey, values } = self;
299 serialize_json(
300 state,
301 objectKey,
302 valueKey,
303 DynSolValue::Array(
304 values.iter().cloned().map(Into::into).map(DynSolValue::Bytes).collect(),
305 ),
306 )
307 }
308}
309
310impl Cheatcode for serializeJsonType_0Call {
311 fn apply(&self, _state: &mut Cheatcodes) -> Result {
312 let Self { typeDescription, value } = self;
313 let ty = resolve_type(typeDescription)?;
314 let value = ty.abi_decode(value)?;
315 let value = serialize_value_as_json(value)?;
316 Ok(value.to_string().abi_encode())
317 }
318}
319
320impl Cheatcode for serializeJsonType_1Call {
321 fn apply(&self, state: &mut Cheatcodes) -> Result {
322 let Self { objectKey, valueKey, typeDescription, value } = self;
323 let ty = resolve_type(typeDescription)?;
324 let value = ty.abi_decode(value)?;
325 serialize_json(state, objectKey, valueKey, value)
326 }
327}
328
329impl Cheatcode for serializeUintToHexCall {
330 fn apply(&self, state: &mut Cheatcodes) -> Result {
331 let Self { objectKey, valueKey, value } = self;
332 let hex = format!("0x{value:x}");
333 serialize_json(state, objectKey, valueKey, hex.into())
334 }
335}
336
337impl Cheatcode for writeJson_0Call {
338 fn apply(&self, state: &mut Cheatcodes) -> Result {
339 let Self { json, path } = self;
340 let json = serde_json::from_str(json).unwrap_or_else(|_| Value::String(json.to_owned()));
341 let json_string = serde_json::to_string_pretty(&json)?;
342 super::fs::write_file(state, path.as_ref(), json_string.as_bytes())
343 }
344}
345
346impl Cheatcode for writeJson_1Call {
347 fn apply(&self, state: &mut Cheatcodes) -> Result {
348 let Self { json: value, path, valueKey } = self;
349
350 let data_path = state.config.ensure_path_allowed(path, FsAccessKind::Read)?;
352 let data_string = fs::read_to_string(&data_path)?;
353 let mut data =
354 serde_json::from_str(&data_string).unwrap_or_else(|_| Value::String(data_string));
355 upsert_json_value(&mut data, value, valueKey)?;
356
357 let json_string = serde_json::to_string_pretty(&data)?;
359 super::fs::write_file(state, path.as_ref(), json_string.as_bytes())
360 }
361}
362
363pub(super) fn check_json_key_exists(json: &str, key: &str) -> Result {
364 let json = parse_json_str(json)?;
365 let values = select(&json, key)?;
366 let exists = !values.is_empty();
367 Ok(exists.abi_encode())
368}
369
370pub(super) fn parse_json(json: &str, path: &str) -> Result {
371 let value = parse_json_str(json)?;
372 let selected = select(&value, path)?;
373 let sol = json_to_sol(&selected)?;
374 Ok(encode(sol))
375}
376
377pub(super) fn parse_json_coerce(json: &str, path: &str, ty: &DynSolType) -> Result {
378 let json = parse_json_str(json)?;
379 let [value] = select(&json, path)?[..] else {
380 bail!("path {path:?} must return exactly one JSON value");
381 };
382
383 parse_json_as(value, ty).map(|v| v.abi_encode())
384}
385
386pub(super) fn parse_json_as(value: &Value, ty: &DynSolType) -> Result<DynSolValue> {
388 let to_string = |v: &Value| {
389 let mut s = v.to_string();
390 s.retain(|c: char| c != '"');
391 s
392 };
393
394 match (value, ty) {
395 (Value::Array(array), ty) => parse_json_array(array, ty),
396 (Value::Object(object), ty) => parse_json_map(object, ty),
397 (Value::String(s), DynSolType::String) => Ok(DynSolValue::String(s.clone())),
398 (Value::String(s), DynSolType::Uint(_) | DynSolType::Int(_)) => string::parse_value(s, ty),
399 _ => string::parse_value(&to_string(value), ty),
400 }
401}
402
403pub(super) fn parse_json_array(array: &[Value], ty: &DynSolType) -> Result<DynSolValue> {
404 match ty {
405 DynSolType::Tuple(types) => {
406 ensure!(array.len() == types.len(), "array length mismatch");
407 let values = array
408 .iter()
409 .zip(types)
410 .map(|(e, ty)| parse_json_as(e, ty))
411 .collect::<Result<Vec<_>>>()?;
412
413 Ok(DynSolValue::Tuple(values))
414 }
415 DynSolType::Array(inner) => {
416 let values =
417 array.iter().map(|e| parse_json_as(e, inner)).collect::<Result<Vec<_>>>()?;
418 Ok(DynSolValue::Array(values))
419 }
420 DynSolType::FixedArray(inner, len) => {
421 ensure!(array.len() == *len, "array length mismatch");
422 let values =
423 array.iter().map(|e| parse_json_as(e, inner)).collect::<Result<Vec<_>>>()?;
424 Ok(DynSolValue::FixedArray(values))
425 }
426 _ => bail!("expected {ty}, found array"),
427 }
428}
429
430pub(super) fn parse_json_map(map: &Map<String, Value>, ty: &DynSolType) -> Result<DynSolValue> {
431 let Some((name, fields, types)) = ty.as_custom_struct() else {
432 bail!("expected {ty}, found JSON object");
433 };
434
435 let mut values = Vec::with_capacity(fields.len());
436 for (field, ty) in fields.iter().zip(types.iter()) {
437 let Some(value) = map.get(field) else { bail!("field {field:?} not found in JSON object") };
438 values.push(parse_json_as(value, ty)?);
439 }
440
441 Ok(DynSolValue::CustomStruct {
442 name: name.to_string(),
443 prop_names: fields.to_vec(),
444 tuple: values,
445 })
446}
447
448pub(super) fn parse_json_keys(json: &str, key: &str) -> Result {
449 let json = parse_json_str(json)?;
450 let values = select(&json, key)?;
451 let [value] = values[..] else {
452 bail!("key {key:?} must return exactly one JSON object");
453 };
454 let Value::Object(object) = value else {
455 bail!("JSON value at {key:?} is not an object");
456 };
457 let keys = object.keys().collect::<Vec<_>>();
458 Ok(keys.abi_encode())
459}
460
461fn parse_json_str(json: &str) -> Result<Value> {
462 serde_json::from_str(json).map_err(|e| fmt_err!("failed parsing JSON: {e}"))
463}
464
465fn json_to_sol(json: &[&Value]) -> Result<Vec<DynSolValue>> {
466 let mut sol = Vec::with_capacity(json.len());
467 for value in json {
468 sol.push(json_value_to_token(value)?);
469 }
470 Ok(sol)
471}
472
473fn select<'a>(value: &'a Value, mut path: &str) -> Result<Vec<&'a Value>> {
474 if path == "." {
476 path = "$";
477 }
478 jsonpath_lib::select(value, &canonicalize_json_path(path))
480 .map_err(|e| fmt_err!("failed selecting from JSON: {:?}", e.to_string()))
481}
482
483fn encode(values: Vec<DynSolValue>) -> Vec<u8> {
484 let bytes = match &values[..] {
486 [] => Vec::new(),
487 [one] => one.abi_encode(),
488 _ => DynSolValue::Array(values).abi_encode(),
489 };
490 bytes.abi_encode()
491}
492
493pub(super) fn canonicalize_json_path(path: &str) -> Cow<'_, str> {
496 if !path.starts_with('$') { format!("${path}").into() } else { path.into() }
497}
498
499pub(super) fn json_value_to_token(value: &Value) -> Result<DynSolValue> {
506 match value {
507 Value::Null => Ok(DynSolValue::FixedBytes(B256::ZERO, 32)),
508 Value::Bool(boolean) => Ok(DynSolValue::Bool(*boolean)),
509 Value::Array(array) => {
510 array.iter().map(json_value_to_token).collect::<Result<_>>().map(DynSolValue::Array)
511 }
512 value @ Value::Object(_) => {
513 let ordered_object: BTreeMap<String, Value> =
515 serde_json::from_value(value.clone()).unwrap();
516 ordered_object
517 .values()
518 .map(json_value_to_token)
519 .collect::<Result<_>>()
520 .map(DynSolValue::Tuple)
521 }
522 Value::Number(number) => {
523 if let Some(f) = number.as_f64() {
524 if f.fract() == 0.0 {
527 let s = number.to_string();
533
534 if s.contains('e') {
539 let fallback_s = f.to_string();
545 if let Ok(n) = fallback_s.parse() {
546 return Ok(DynSolValue::Uint(n, 256));
547 }
548 if let Ok(n) = I256::from_dec_str(&fallback_s) {
549 return Ok(DynSolValue::Int(n, 256));
550 }
551 }
552
553 if let Ok(n) = s.parse() {
554 return Ok(DynSolValue::Uint(n, 256));
555 }
556 if let Ok(n) = s.parse() {
557 return Ok(DynSolValue::Int(n, 256));
558 }
559 }
560 }
561
562 Err(fmt_err!("unsupported JSON number: {number}"))
563 }
564 Value::String(string) => {
565 if let Some(mut val) = string.strip_prefix("0x") {
567 let s;
568 if val.len() == 39 {
569 return Err(format!("Cannot parse \"{val}\" as an address. If you want to specify address, prepend zero to the value.").into());
570 }
571 if !val.len().is_multiple_of(2) {
572 s = format!("0{val}");
573 val = &s[..];
574 }
575 if let Ok(bytes) = hex::decode(val) {
576 return Ok(match bytes.len() {
577 20 => DynSolValue::Address(Address::from_slice(&bytes)),
578 32 => DynSolValue::FixedBytes(B256::from_slice(&bytes), 32),
579 _ => DynSolValue::Bytes(bytes),
580 });
581 }
582 }
583
584 if let Ok(n) = string.parse::<I256>()
589 && i64::try_from(n).is_err()
590 {
591 return Ok(DynSolValue::Int(n, 256));
592 } else if let Ok(n) = string.parse::<U256>()
593 && u64::try_from(n).is_err()
594 {
595 return Ok(DynSolValue::Uint(n, 256));
596 }
597
598 Ok(DynSolValue::String(string.to_owned()))
600 }
601 }
602}
603
604fn serialize_json(
613 state: &mut Cheatcodes,
614 object_key: &str,
615 value_key: &str,
616 value: DynSolValue,
617) -> Result {
618 let value = serialize_value_as_json(value)?;
619 let map = state.serialized_jsons.entry(object_key.into()).or_default();
620 map.insert(value_key.into(), value);
621 let stringified = serde_json::to_string(map).unwrap();
622 Ok(stringified.abi_encode())
623}
624
625pub(super) fn resolve_type(type_description: &str) -> Result<DynSolType> {
627 if let Ok(ty) = DynSolType::parse(type_description) {
628 return Ok(ty);
629 };
630
631 if let Ok(encoded) = EncodeType::parse(type_description) {
632 let main_type = encoded.types[0].type_name;
633 let mut resolver = Resolver::default();
634 for t in encoded.types {
635 resolver.ingest(t.to_owned());
636 }
637
638 return Ok(resolver.resolve(main_type)?);
639 };
640
641 bail!("type description should be a valid Solidity type or a EIP712 `encodeType` string")
642}
643
644pub(super) fn upsert_json_value(data: &mut Value, value: &str, key: &str) -> Result<()> {
657 let canonical_key = canonicalize_json_path(key);
659 let parts: Vec<&str> = canonical_key
660 .strip_prefix("$.")
661 .unwrap_or(key)
662 .split('.')
663 .filter(|s| !s.is_empty())
664 .collect();
665
666 if parts.is_empty() {
667 return Err(fmt_err!("'valueKey' cannot be empty or just '$'"));
668 }
669
670 if let Some((key_to_insert, path_to_parent)) = parts.split_last() {
673 let mut current_level = data;
674
675 for segment in path_to_parent {
676 if !current_level.is_object() {
677 return Err(fmt_err!("path segment '{segment}' does not resolve to an object."));
678 }
679 current_level = current_level
680 .as_object_mut()
681 .unwrap()
682 .entry(segment.to_string())
683 .or_insert(Value::Object(Map::new()));
684 }
685
686 if let Some(parent_obj) = current_level.as_object_mut() {
688 parent_obj.insert(
689 key_to_insert.to_string(),
690 serde_json::from_str(value).unwrap_or_else(|_| Value::String(value.to_owned())),
691 );
692 } else {
693 return Err(fmt_err!("final destination is not an object, cannot insert key."));
694 }
695 }
696
697 Ok(())
698}
699
700#[cfg(test)]
701mod tests {
702 use super::*;
703 use alloy_primitives::FixedBytes;
704 use proptest::strategy::Strategy;
705 use serde_json::json;
706
707 fn contains_tuple(value: &DynSolValue) -> bool {
708 match value {
709 DynSolValue::Tuple(_) | DynSolValue::CustomStruct { .. } => true,
710 DynSolValue::Array(v) | DynSolValue::FixedArray(v) => {
711 v.first().is_some_and(contains_tuple)
712 }
713 _ => false,
714 }
715 }
716
717 fn fixup_guessable(value: DynSolValue) -> DynSolValue {
722 match value {
723 DynSolValue::Array(mut v) | DynSolValue::FixedArray(mut v) => {
724 if let Some(DynSolValue::Bytes(_)) = v.first() {
725 v.retain(|v| {
726 let len = v.as_bytes().unwrap().len();
727 len != 32 && len != 20
728 })
729 }
730 DynSolValue::Array(v.into_iter().map(fixup_guessable).collect())
731 }
732 DynSolValue::FixedBytes(v, _) => DynSolValue::FixedBytes(v, 32),
733 DynSolValue::Bytes(v) if v.len() == 32 => {
734 DynSolValue::FixedBytes(FixedBytes::from_slice(&v), 32)
735 }
736 DynSolValue::Bytes(v) if v.len() == 20 => DynSolValue::Address(Address::from_slice(&v)),
737 _ => value,
738 }
739 }
740
741 fn guessable_types() -> impl proptest::strategy::Strategy<Value = DynSolValue> {
742 proptest::arbitrary::any::<DynSolValue>()
743 .prop_map(fixup_guessable)
744 .prop_filter("tuples are not supported", |v| !contains_tuple(v))
745 .prop_filter("filter out values without type", |v| v.as_type().is_some())
746 }
747
748 use proptest::prelude::ProptestConfig;
750 proptest::proptest! {
751 #![proptest_config(ProptestConfig {
752 cases: 99,
753 failure_persistence: None,
755 ..Default::default()
756 })]
757
758 #[test]
759 fn test_json_roundtrip_guessed(v in guessable_types()) {
760 let json = serialize_value_as_json(v.clone()).unwrap();
761 let value = json_value_to_token(&json).unwrap();
762
763 let decoded = v.as_type().unwrap().abi_decode(&value.abi_encode()).unwrap();
765 assert_eq!(decoded, v);
766 }
767
768 #[test]
769 fn test_json_roundtrip(v in proptest::arbitrary::any::<DynSolValue>().prop_filter("filter out values without type", |v| v.as_type().is_some())) {
770 let json = serialize_value_as_json(v.clone()).unwrap();
771 let value = parse_json_as(&json, &v.as_type().unwrap()).unwrap();
772 assert_eq!(value, v);
773 }
774 }
775
776 #[test]
777 fn test_upsert_json_value() {
778 let scenarios = vec![
780 (json!({}), "foo", r#""bar""#, json!({"foo": "bar"})),
782 (json!({"foo": "bar"}), "foo", "123", json!({"foo": 123})),
784 (json!({}), "a.b.c", r#""baz""#, json!({"a": {"b": {"c": "baz"}}})),
786 (json!({"a": {"b": {}}}), "a.b.c", "true", json!({"a": {"b": {"c": true}}})),
788 (json!({}), "a.b", r#"{"d": "e"}"#, json!({"a": {"b": {"d": "e"}}})),
790 (json!({}), "myArray", r#"[1, "test", null]"#, json!({"myArray": [1, "test", null]})),
792 ];
793
794 for (mut initial, key, value_str, expected) in scenarios {
795 upsert_json_value(&mut initial, value_str, key).unwrap();
796 assert_eq!(initial, expected);
797 }
798
799 let error_scenarios = vec![
800 (
802 json!({"a": "a string value"}),
803 "a.b",
804 r#""bar""#,
805 "final destination is not an object, cannot insert key.",
806 ),
807 (json!({}), "", r#""bar""#, "'valueKey' cannot be empty or just '$'"),
809 (json!({}), "$.", r#""bar""#, "'valueKey' cannot be empty or just '$'"),
811 ];
812
813 for (mut initial, key, value_str, error_msg) in error_scenarios {
814 let result = upsert_json_value(&mut initial, value_str, key);
815 assert!(result.is_err(), "Expected an error for key: '{key}' but got Ok");
816 assert!(
817 result.unwrap_err().to_string().contains(error_msg),
818 "Error message for key '{key}' did not contain '{error_msg}'"
819 );
820 }
821 }
822}