mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-19 14:45:25 +02:00
columns/rows came straight from params with no cap. layout_lines loops once per requested line and each push_unique scans the growing guide vector, so a count like 200000000 (well inside u32) never returned - blocking the app, CLI, MCP and the TCP control channel. Reject counts above 1000 with an actionable BadParams naming the offending parameter. Photoshop's own dialog caps at 32; 1000 is a generous ceiling whose worst case finishes instantly, so legitimate grids are untouched. Adds regression tests: the issue repro (200M columns, and rows) returns an actionable error immediately, and bounded layouts at the cap still produce their guides. Co-authored-by: Brandon Fenty <bfenty@users.noreply.github.com>
46 lines
2.0 KiB
Rust
46 lines
2.0 KiB
Rust
//! Regression tests for issue #704: `view.newGuideLayout` must reject an absurd
|
|
//! column/row count instead of running an unbounded O(n^2) loop. Photoshop's
|
|
//! New Guide Layout caps at 32; we reject counts above a generous 1000
|
|
//! (still O(10^6) worst case) with an actionable BadParams error.
|
|
use photocraft_engine::Session;
|
|
use serde_json::json;
|
|
|
|
fn session() -> Session {
|
|
let mut s = Session::new();
|
|
s.execute("file.new", json!({"width": 3000, "height": 2000})).unwrap();
|
|
s
|
|
}
|
|
|
|
#[test]
|
|
fn huge_column_count_is_rejected_fast() {
|
|
let mut s = session();
|
|
// The issue's repro: 200 million columns never returned on main.
|
|
let r = s.execute("view.newGuideLayout", json!({"columns": 200000000, "gutter": 0}));
|
|
let e = r.expect_err("absurd column count must be rejected");
|
|
let msg = format!("{e:?}");
|
|
assert!(msg.contains("column") || msg.contains("Column"), "error should mention columns: {msg}");
|
|
assert!(!msg.contains("internal error"), "must be an actionable error, not the panic guard: {msg}");
|
|
}
|
|
|
|
#[test]
|
|
fn huge_row_count_is_rejected_fast() {
|
|
let mut s = session();
|
|
let e = s.execute("view.newGuideLayout", json!({"rows": 200000000})).expect_err("absurd row count must be rejected");
|
|
let msg = format!("{e:?}");
|
|
assert!(msg.contains("row") || msg.contains("Row"), "error should mention rows: {msg}");
|
|
}
|
|
|
|
#[test]
|
|
fn bounded_layouts_still_work() {
|
|
let mut s = session();
|
|
// A grid at the cap must still succeed quickly.
|
|
let v = s.execute("view.newGuideLayout", json!({"columns": 1000, "gutter": 0})).unwrap();
|
|
let n = v["vertical"].as_u64().unwrap();
|
|
assert!((1000..=1001).contains(&n), "1000-column grid should produce ~1000 guides, got {n}");
|
|
let v = s.execute("view.newGuideLayout", json!({"rows": 3, "columns": 3, "clearExisting": true})).unwrap();
|
|
for k in ["vertical", "horizontal"] {
|
|
let n = v[k].as_u64().unwrap();
|
|
assert!((3..=4).contains(&n), "3x3 grid {k} count should be 3-4, got {n}");
|
|
}
|
|
}
|