main
Drew Owens 0.9.29 source 69992cf 8d ago
//! ISSUE #2 on adom/solidworks-bridge (Michael, 2026-10-13, SOLIDWORKS 2021):
//!   * swx_inspect_bodies reported massKg ~1000x small — exactly volume x 1:
//!     IBody2::GetMassProperties' argument is the DENSITY, and the bridge
//!     passed 0.0 as if it were an accuracy;
//!   * swx_run_script said bridgeCreatedDocument:false for a part the bridge
//!     had just made with swx_new_document (scratchFile:true, saved under
//!     exports\scratch) — it compared TITLES instead of asking owns_doc.

mod common;

use common::{Bridge, TempDir};
use serde_json::{json, Value};

fn s(v: &Value) -> String {
    serde_json::to_string_pretty(v).unwrap_or_default()
}

fn boot(tag: &str) -> (Bridge, TempDir) {
    let machine = TempDir::new(tag);
    std::fs::write(machine.join("windows.json"), "[]").unwrap();
    let b = Bridge::start_with(&[("SWX_LAUNCH_FAKE_DIR", &machine.path())]);
    (b, machine)
}

#[test]
fn inspect_bodies_mass_is_volume_times_the_materials_density_and_matches_mass_properties() {
    let (b, _m) = boot("issue2-mass");
    let r = b.call("swx_run_script", json!({"script": [
        {"op": "new_part"},
        {"op": "sketch_rect", "plane": "Top Plane", "widthMm": 31.8, "heightMm": 15.8},
        {"op": "extrude", "depthMm": 11.3}
    ]}));
    check!(b, "the part is built", r["success"] == true, s(&r));
    // No material yet: SOLIDWORKS' default part density, 1000 kg/m³ — NOT 1.
    let ib = b.call("swx_inspect_bodies", json!({}));
    let body = &ib["bodies"][0];
    let vol = body["volumeMm3"].as_f64().unwrap_or(0.0);
    check!(
        b,
        "no material: massKg = volume x 1000 kg/m³ (the measured bug was volume x 1)",
        (body["massKg"].as_f64().unwrap_or(0.0) - vol * 1.0e-9 * 1000.0).abs() < 1e-12
            && body["densityKgM3"] == 1000.0 && body["densitySource"].is_string(),
        s(body)
    );
    b.call("swx_run_script", json!({"script": [{"op": "set_material", "name": "ABS"}], "targetDocument": r["documentName"]}));
    let ib = b.call("swx_inspect_bodies", json!({}));
    let mp = b.call("swx_mass_properties", json!({}));
    let body = &ib["bodies"][0];
    let mass = body["massKg"].as_f64().unwrap_or(0.0);
    check!(
        b,
        "ABS: massKg = volume x 1020 kg/m³, densityKgM3 1020",
        (mass - vol * 1.0e-9 * 1020.0).abs() < 1e-12 && body["densityKgM3"] == 1020.0,
        s(body)
    );
    check!(
        b,
        "swx_inspect_bodies and swx_mass_properties AGREE on a single-body part",
        (mass - mp["massKg"].as_f64().unwrap_or(-1.0)).abs() < 1e-12 && mp["density"] == body["densityKgM3"],
        format!("{}\n{}", s(body), s(&mp))
    );
}

#[test]
fn an_unreadable_density_reports_no_mass_rather_than_a_wrong_one() {
    let (b, _m) = boot("issue2-unreadable");
    b.call("swx_run_script", json!({"script": [
        {"op": "new_part"},
        {"op": "sketch_rect", "widthMm": 10, "heightMm": 10},
        {"op": "extrude", "depthMm": 10}
    ]}));
    std::fs::write(b.scratch.join("mock-density-unreadable"), "").unwrap();
    let body = b.call("swx_inspect_bodies", json!({}))["bodies"][0].clone();
    check!(
        b,
        "density unreadable: massKg null, densitySource unreadable — never volume x 1",
        body["massKg"].is_null() && body["densitySource"] == "unreadable" && body["volumeMm3"].as_f64().unwrap_or(0.0) > 0.0,
        s(&body)
    );
}

#[test]
fn a_script_into_a_scratch_part_the_bridge_made_is_bridge_created() {
    // The user-writes switch is ON for this bridge so the last case (a USER's
    // document, under the full opt-in) can run at all; the bridge's own
    // documents never need it.
    let machine = TempDir::new("issue2-owner");
    std::fs::write(machine.join("windows.json"), "[]").unwrap();
    let b = Bridge::start_with(&[("SWX_LAUNCH_FAKE_DIR", &machine.path()), ("SWX_ALLOW_USER_WRITES", "1")]);
    let made = b.call("swx_new_document", json!({"type": "part"}));
    let path = made["created"]["path"].as_str().or_else(|| made["path"].as_str()).unwrap_or("").to_string();
    check!(
        b,
        "swx_new_document saved it as a scratch file under exports\\scratch",
        made["documentIdentity"]["scratchFile"] == true,
        s(&made)
    );
    check!(b, "it has a path", !path.is_empty(), s(&made));
    let r = b.call("swx_run_script", json!({"path": path, "script": [
        {"op": "sketch_rect", "widthMm": 20, "heightMm": 10},
        {"op": "extrude", "depthMm": 5}
    ]}));
    check!(
        b,
        "addressed by PATH: bridgeCreatedDocument true (issue #2 measured false), and the safe-undo hint",
        r["success"] == true && r["bridgeCreatedDocument"] == true
            && r["_hint"].as_str().unwrap_or("").contains("created by the bridge"),
        s(&r)
    );
    let title = r["documentName"].clone();
    let r = b.call("swx_run_script", json!({"targetDocument": title, "script": [
        {"op": "sketch_circle", "radiusMm": 2},
        {"op": "extrude", "depthMm": 1}
    ]}));
    check!(b, "...and addressed by title too", r["bridgeCreatedDocument"] == true, s(&r));

    // A USER's document (opened, not made) that happens to carry THE SAME TITLE
    // as the bridge's scratch part, changed under the full opt-in: false. (A
    // title is not an identity — the title-only test said "true" here.)
    let user = TempDir::new("issue2-user");
    let same_name = title.as_str().unwrap_or("Part1.SLDPRT").to_string();
    let upath = user.join(&same_name);
    std::fs::write(&upath, "a user part").unwrap();
    let opened = b.call("swx_open", json!({"path": upath.to_string_lossy()}));
    check!(b, "the user's part opens", opened["success"] == true, s(&opened));
    let r = b.call("swx_run_script", json!({"path": upath.to_string_lossy(), "allowUserWrites": true, "confirm": true,
        "reason": "the user asked for a boss on their bracket", "script": [
        {"op": "sketch_rect", "widthMm": 5, "heightMm": 5},
        {"op": "extrude", "depthMm": 1}
    ]}));
    check!(
        b,
        "the user's document: bridgeCreatedDocument false, and the no-safe-undo warning",
        r["success"] == true && r["bridgeCreatedDocument"] == false && r["_hint"].as_str().unwrap_or("").contains("NOT create"),
        s(&r)
    );
}