mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-12 10:03:51 +00:00
122 lines
4.9 KiB
Rust
122 lines
4.9 KiB
Rust
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<u8> {
|
|
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"));
|
|
}
|