Files
vectorcraft/apps/vectorcraft-cli/tests/it/script.rs
T
dotCooCoo cc03fade6e Scripts: the growth budget counts every art store, with CLI and source fuzz tests
A document a script opens now counts the art in its graph designs and in
the original of the pattern being edited toward the growth allowance.
`Document::art_roots` and `art_roots_mut` list every tree of a document's
art; `any_node`, `edit_nodes_where` and the script's object count use them.

`script.run` checks the depth of `args` without recursion before it copies
or writes them out, so `args` nested 10,000 levels deep is refused with the
place it went past 64 levels instead of overflowing the stack.

New tests: graph-design art and the growth allowance, `args` nested 10,000
levels deep from a script (a RangeError) and from a caller, `run --cmd
script.run` on the command line with a report and with a thrown error, and
a property test that sends arbitrary source through `script.run`.
2026-11-09 22:00:04 -08:00

40 lines
2.0 KiB
Rust

use std::process::Command;
use serde_json::{Value, json};
const BIN: &str = env!("CARGO_BIN_EXE_vectorcraft-cli");
/// `run` with `script.run` after `file.new`, with `params`.
fn run_script(params: &Value) -> std::process::Output {
Command::new(BIN).args(["run", "--cmd", "file.new", "--cmd", "script.run", "--params", &params.to_string()]).output().unwrap()
}
/// `run` runs a script headless: the reply holds the report, with what the script logged and the
/// documents it changed.
#[test]
fn run_gives_the_report_of_a_script() {
let source = "vc.run('shape.rectangle', {x: 0, y: 0, width: 10, height: 10}); vc.log(vc.args.n * 2);";
let out = run_script(&json!({"source": source, "name": "double.js", "args": {"n": 21}}));
assert!(out.status.success(), "{}", String::from_utf8_lossy(&out.stderr));
let lines: Vec<Value> = String::from_utf8_lossy(&out.stdout).lines().map(|l| serde_json::from_str(l).unwrap()).collect();
assert_eq!(lines.len(), 2, "{lines:?}");
assert_eq!(lines[1]["command"], "script.run");
let report = &lines[1]["result"];
assert_eq!(report["ok"], true, "{report}");
assert_eq!(report["name"], "double.js");
assert_eq!(report["log"], json!([42]), "{report}");
assert_eq!(report["changed"].as_array().map(Vec::len), Some(1), "{report}");
}
/// A script that throws fails its step with the script's message, and the steps before it ran.
#[test]
fn run_fails_the_step_of_a_script_that_throws() {
let out = run_script(&json!({"source": "vc.run('shape.rectangle', {x: 0, y: 0, width: 10, height: 10}); throw new Error('no margin given');"}));
assert!(!out.status.success());
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(stderr.contains("step 1 (script.run)") && stderr.contains("no margin given"), "{stderr}");
let printed: Vec<Value> = String::from_utf8_lossy(&out.stdout).lines().map(|l| serde_json::from_str(l).unwrap()).collect();
assert_eq!(printed.len(), 1, "only file.new printed: {printed:?}");
assert_eq!(printed[0]["command"], "file.new");
}