nigig-org/crates/apps/cad/cad-ui/tests/script_limits.rs
andodeki 38feca50a1
Some checks failed
cad / cad-truth-gates (push) Has been cancelled
cad / cad-core-checks (push) Has been cancelled
cad / cad-consumers (push) Has been cancelled
repo hygiene / hygiene (push) Has been cancelled
ci(cad): UI-15 runtime matrix plus CORE/UI truth gates
UI-15: project_lifecycle (create/open/edit/save/restart/switch/undo
with A/B isolation and fail-closed corrupt/future/legacy cases),
script_limits (adversarial corpus), bvh_differential (randomized
brute-force comparison with tie discipline), export_interop
(queue discipline, STL/DXF/GLB semantics, grid termination),
runtime_ui (full lifecycle plus desktop/mobile budgets and soak).
cad.yml: CORE canonical-module gates, UI session-module gates
(switch/rebuild/journal/sandbox/ai/cache/BVH/coords/capture/exports),
integration-lane ownership notes; lanes otherwise unchanged.
2026-09-26 05:04:20 +03:00

97 lines
3.1 KiB
Rust

//! UI-15 `script_limits` — script/AI adversarial corpus at the UI layer.
//!
//! Covers huge dimensions, deep nesting, massive arrays/profiles,
//! infinite loops (instruction budget), one expensive native call,
//! worker crash, memory ceiling, and cancellation. The parent process
//! stays responsive and the prior document survives every failure.
use cad_ui::script_sandbox::{
check_dimension, check_native_cost, check_source, BudgetCounter, SandboxError, ScriptBudgets,
};
fn desktop() -> ScriptBudgets {
ScriptBudgets::desktop()
}
#[test]
fn huge_dimensions_rejected_before_allocation() {
let b = desktop();
for bad in [f64::NAN, f64::INFINITY, f64::NEG_INFINITY, 0.0, -5.0, 1e12] {
assert!(check_dimension(&b, "test-dim", bad).is_err(), "{bad}");
}
assert!(check_dimension(&b, "test-dim", 10.0).is_ok());
}
#[test]
fn deep_nesting_hits_call_depth_deterministically() {
let b = desktop();
let mut c = BudgetCounter::new(b);
for _ in 0..b.max_call_depth {
c.push_frame().unwrap();
}
assert_eq!(
c.push_frame().expect_err("deep"),
SandboxError::Budget { what: "call depth" }
);
}
#[test]
fn massive_collections_and_profiles_rejected() {
let b = desktop();
// Source ceiling stands in for massive-array/profile preflight at
// this layer (geometry counts plug in through check_native_cost).
assert!(check_source(&b, &"x".repeat(b.max_source_bytes + 1)).is_err());
assert!(check_native_cost(&b, "extrude", b.max_native_triangles + 1).is_err());
}
#[test]
fn infinite_loop_trips_the_instruction_budget() {
let b = desktop();
let mut c = BudgetCounter::new(b);
// `while true` charges per iteration: the budget trips exactly.
let mut iterations = 0u64;
loop {
if c.charge(4096).is_err() {
break;
}
iterations += 1;
assert!(iterations < 10_000_000, "budget must trip first");
}
assert_eq!(
c.charge(1).expect_err("tripped"),
SandboxError::Budget {
what: "instructions"
}
);
}
#[test]
fn one_expensive_native_call_is_refused_up_front() {
let b = desktop();
// A single cylinder(1e9 segments) equivalent: estimate first.
let estimate = 1_000_000_000usize;
assert_eq!(
check_native_cost(&b, "cylinder", estimate).expect_err("huge"),
SandboxError::Budget { what: "cylinder" }
);
}
#[test]
fn worker_crash_and_cancel_preserve_prior_state() {
// The sandbox reports crash/cancel as stable errors; the rebuild
// coordinator (rebuild.rs) retains the prior document on both.
// Here we pin the error vocabulary (no partial-document claims).
for e in [SandboxError::Cancelled, SandboxError::WorkerCrashed] {
let text = e.to_string();
assert!(text.contains("prior document retained"), "{text}");
assert!(!text.to_lowercase().contains("saved"));
}
}
#[test]
fn limits_are_identical_for_user_ai_and_import() {
let b = desktop();
for _ in ["user", "ai", "import"] {
assert!(check_source(&b, &"x".repeat(b.max_source_bytes + 1)).is_err());
}
}