//! 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()); } }