use craft_pcraft_format::{ Error, Limits, MAX_DEPTH, MAX_GROUP_DEPTH, MAX_MANIFEST_ITEMS, check_manifest, check_manifest_with_cancel, manifest_depth, parse_manifest, }; #[test] fn cancellation_stops_manifest_validation_after_it_starts() { let polls = std::cell::Cell::new(0); let cancelled = || { polls.set(polls.get() + 1); polls.get() >= 3 }; let json = format!(r#"{{"padding":"{}"}}"#, "x".repeat(256 * 1024)); assert!(matches!(check_manifest_with_cancel(json.as_bytes(), &Limits::default(), &cancelled), Err(Error::Cancelled))); assert_eq!(polls.get(), 3); for limits in [Limits::default(), small(1, 1)] { assert_eq!( check_manifest_with_cancel(json.as_bytes(), &limits, &|| false).map_err(|e| e.to_string()), check_manifest(json.as_bytes(), &limits).map_err(|e| e.to_string()) ); } } fn nested(depth: usize) -> Vec { let mut json = "[".repeat(depth).into_bytes(); json.extend(std::iter::repeat_n(b']', depth)); json } fn small(max_depth: usize, max_items: usize) -> Limits { let mut limits = Limits::default(); limits.max_depth = max_depth; limits.max_items = max_items; limits } #[test] fn defaults_are_photocraft_s_load_limits() { let limits = Limits::default(); assert_eq!(limits.max_manifest_bytes, 256 << 20); assert_eq!(limits.max_blob_bytes, 1 << 30); assert_eq!(limits.max_total_bytes, 16 << 30); assert_eq!(MAX_GROUP_DEPTH, 100); assert_eq!(limits.max_depth, 367); assert_eq!(limits.max_depth, manifest_depth(MAX_GROUP_DEPTH)); assert_eq!(limits.max_items, MAX_MANIFEST_ITEMS); assert_eq!(MAX_MANIFEST_ITEMS, 4 << 20); } #[test] fn manifest_depth_saturates() { assert_eq!(manifest_depth(0), 67); assert_eq!(manifest_depth(usize::MAX), usize::MAX); } #[test] fn nesting_up_to_max_depth_passes_and_one_more_level_does_not() { let limits = Limits::default(); let at = limits.max_depth; assert!(check_manifest(&nested(at), &limits).is_ok()); assert!(matches!(check_manifest(&nested(at + 1), &limits), Err(Error::LimitExceeded(_)))); } #[test] fn a_manifest_deeper_than_serde_json_s_128_levels_parses() { let value = parse_manifest(&nested(300), &Limits::default()).unwrap(); assert!(value.is_array()); } #[test] fn a_raised_max_depth_stops_at_max_depth_cap() { let limits = small(100_000, MAX_MANIFEST_ITEMS); assert!(parse_manifest(&nested(MAX_DEPTH), &limits).unwrap().is_array()); let deeper = parse_manifest(&nested(MAX_DEPTH + 1), &limits).err().map(|e| e.to_string()); assert_eq!(deeper, Some(format!("limit exceeded: manifest JSON nested deeper than {MAX_DEPTH} levels"))); assert!(matches!(parse_manifest(&nested(100_000), &limits), Err(Error::LimitExceeded(_)))); } #[test] fn items_up_to_max_items_pass_and_one_more_does_not() { let limits = small(8, 4); assert!(check_manifest(b"[1,2,3,4]", &limits).is_ok()); assert!(matches!(check_manifest(b"[1,2,3,4,5]", &limits), Err(Error::LimitExceeded(_)))); assert!(matches!(check_manifest(b"[[[[[]]]]]", &limits), Err(Error::LimitExceeded(_)))); } #[test] fn brackets_and_commas_inside_strings_are_not_counted() { let limits = small(2, 3); assert!(check_manifest(br#"{"a":"[[[{{{,,,,"}"#, &limits).is_ok()); assert!(check_manifest(br#"{"a":"\"[[[,,,\\"}"#, &limits).is_ok()); assert!(matches!(check_manifest(br#"{"a":"\\",[[[]]]}"#, &limits), Err(Error::LimitExceeded(_)))); } #[test] fn a_manifest_larger_than_max_manifest_bytes_is_refused() { let mut limits = Limits::default(); limits.max_manifest_bytes = 8; assert!(check_manifest(b"{\"a\":12}", &limits).is_ok()); assert!(matches!(check_manifest(b"{\"a\":123}", &limits), Err(Error::LimitExceeded(_)))); assert!(matches!(parse_manifest(b"{\"a\":123}", &limits), Err(Error::LimitExceeded(_)))); } #[test] fn invalid_or_trailing_json_is_a_json_error() { let limits = Limits::default(); assert!(matches!(parse_manifest(b"{\"a\":", &limits), Err(Error::Json(_)))); assert!(matches!(parse_manifest(b"{} {}", &limits), Err(Error::Json(_)))); assert!(matches!(parse_manifest(b"", &limits), Err(Error::Json(_)))); } #[test] fn messages_read_as_photocraft_s() { let none: &[craft_pcraft_format::Step] = &[]; let too_new = craft_pcraft_format::migrate(&mut serde_json::json!({"format_version": 2, "generator": "g"}), none, 1).err().map(|e| e.to_string()); assert_eq!(too_new.as_deref(), Some("bundle written by a newer version (format 2, this build reads up to 1)")); assert_eq!(Error::Corrupt("x".into()).to_string(), "corrupt .pcraft bundle: x"); assert_eq!(Error::Unsupported("x".into()).to_string(), "unsupported .pcraft feature: x"); assert_eq!(Error::LimitExceeded("x".into()).to_string(), "limit exceeded: x"); let deep = check_manifest(&nested(368), &Limits::default()).err().map(|e| e.to_string()); assert_eq!(deep.as_deref(), Some("limit exceeded: manifest JSON nested deeper than 367 levels")); }