app
SOLIDWORKS Bridge
Public Made by Adomby adom
Launch, model and quit SOLIDWORKS from any Adom container. Nothing is ever lost.
main
Drew Owens
0.6.9 source: launch resilience, honest counts, ownership persistence, drain, quit ladder — 21 adversary passes, 303 te…
cf16621
1mo ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
//! ADVERSARIAL REVIEW OF 0.5.0 — import / document-lifecycle robustness.
//!
//! The drain verb is well guarded where it counts: it re-reads each document's
//! path immediately before closing and re-checks `is_import_temp`, so a
//! document saved out of the temp dir between enumerate and close is skipped
//! (TOCTOU covered); it is bounded per call by `max` and a 40s wall; and the
//! server loop stops on no-progress, so 1342 cannot wedge or spin the host.
//! `cmd_import` fails closed on an unreadable count, so it never double-imports.
//!
//! What is NOT sound is the ownership PROOF itself. `is_import_temp` requires
//! two path markers to be PRESENT, not to be in the launcher's actual
//! `…\Temp\swx<pid>\IC~~\…` relationship — so a path can carry both markers in
//! the wrong order, or in unrelated folders, and be closed WITHOUT A SAVE
//! PROMPT. Closing a user's document is data loss.
//!
//! Every #[test] here FAILS on 33d56d7.
mod common;
use common::{Bridge, TempDir};
use serde_json::{json, Value};
use swx_bridge::comhost::host::is_import_temp;
/// THE RESOLVED `%TEMP%` these assertions are about.
///
/// `is_import_temp` anchors to the temp root the process resolves, and on this
/// Linux host that is `/tmp` — which no Windows path can ever sit under. The
/// rule under test is "is this path under the USER'S resolved temp dir", so the
/// test says which dir that is. (0.5.3: the anchor used to have a shape-test
/// fallback that made this unnecessary, and that fallback qualified a restored
/// profile backup on another volume.)
fn with_live_temp() {
std::env::set_var("SWX_TEMP_ROOT", swx_bridge::comhost::mock::MOCK_TEMP_ROOT);
}
fn s(v: &Value) -> String {
serde_json::to_string_pretty(v).unwrap_or_default()
}
// ════════════════════════════════════════════════════════════════════════════
// 1. THE OWNERSHIP PROOF — markers present is not markers in relationship
// ════════════════════════════════════════════════════════════════════════════
/// A REAL import temp is always `…\Temp\swx<pid>\…\IC~~\…` — the `IC~~`
/// conversion folder is a DESCENDANT of the `swx<pid>` temp folder. `is_import_temp`
/// checks only that `\temp\swx` and `\ic~~\` each appear SOMEWHERE, in any order.
/// A path with `\IC~~\` BEFORE `\Temp\swx` cannot be a launcher temp, yet it
/// qualifies — and `cmd_close_import_temps` closes what qualifies, with
/// `CloseDoc` (no save prompt).
#[test]
fn markers_in_the_wrong_order_are_not_an_import_temp() {
with_live_temp();
// IC~~ segment first, temp\swx second: impossible for a real launcher temp.
let contrived = r"C:\work\IC~~\drafts\Temp\swx_export\customer-master.sldprt";
assert!(
!is_import_temp(contrived),
"'{contrived}' was accepted as a drainable import temp although its IC~~ segment PRECEDES \
its Temp\\swx segment — the reverse of the launcher's real layout. is_import_temp \
requires the two markers to be present, not to stand in the swx<pid>\\…\\IC~~ \
relationship, so a user path carrying both tokens is closed without a save prompt."
);
}
/// The same looseness with a plausible user directory: someone keeps design
/// backups under a folder they named `swx-archive` inside a `Temp` folder, with
/// an `IC~~` subfolder of their own. Both markers, adjacent to nothing the
/// launcher made — and the file has a real, saved location the user would lose.
#[test]
fn a_user_folder_that_happens_to_contain_both_tokens_is_not_a_temp() {
with_live_temp();
let user = r"D:\Temp\swx-archive\2026\IC~~\customer-final.sldprt";
assert!(
!is_import_temp(user),
"'{user}' — a user's own saved file under folders they named — matched the import-temp \
proof and would be closed without a save prompt. The proof must be the CONTIGUOUS \
launcher pattern (Temp\\swx<digits>\\…\\IC~~\\), not 'both tokens appear'."
);
}
/// And the proof should be anchored to the launcher's pid form: `swx` followed
/// by digits. `\temp\swx` matches `\temp\swxanything`, so a user folder literally
/// named `swxstuff` under any `Temp` qualifies the whole path.
#[test]
fn the_swx_marker_should_be_the_launchers_pid_form_not_any_swx_prefix() {
with_live_temp();
let user = r"C:\Users\drew\AppData\Local\Temp\swxnotes\IC~~\keep.sldprt";
assert!(
!is_import_temp(user),
"'{user}' qualified because '\\temp\\swx' matches 'swxnotes'. The launcher's folder is \
swx<pid> — swx followed by DIGITS. Matching any swx-prefixed folder name widens the \
proof past what the launcher actually writes."
);
}
/// The cases that MUST still match, so a fix does not throw the baby out — a
/// real temp with forward slashes, 8.3 short name, and mixed case. These pass
/// today and must keep passing.
#[test]
fn genuine_temps_still_match_after_any_tightening() {
with_live_temp();
for p in [
r"C:\Users\drew\AppData\Local\Temp\swx107688\IC~~\First.step.SLDPRT",
"C:/Users/drew/AppData/Local/Temp/swx107688/IC~~/First.step.SLDPRT",
r"C:\USERS\DREW\APPDATA\LOCAL\TEMP\SWX107688\IC~~\FIRST.STEP.SLDPRT",
] {
assert!(is_import_temp(p), "a genuine launcher temp must still be recognised: {p}");
}
}
// ════════════════════════════════════════════════════════════════════════════
// 4. THE MOCK/REAL DIVERGENCE the Builder flagged
// ════════════════════════════════════════════════════════════════════════════
/// The real backend's `doc_info` reads `GetPathName`, which for an import temp
/// returns the `…\IC~~\…` path — NON-EMPTY. The mock's `doc_info` returns
/// `path:""` for the same document (unsaved → empty), and its comment claims
/// "both backends give it". They do not: `is_import_temp`, the write gate's
/// "destination exists" check, `save_as`, and path-keyed ownership all read
/// `doc_info.path`, and for a temp the mock feeds them "" while the real
/// backend feeds them the temp path. Every path-keyed consumer is therefore
/// UNTESTED for a temp, and could treat a real temp (non-empty path) differently
/// from what the mock proves.
#[test]
fn a_mock_import_temp_carries_the_path_a_real_one_would() {
with_live_temp();
let b = Bridge::start();
b.call("swx_start", json!({"reason": "importing a STEP file"}));
// A fixture the importer will accept in mock mode.
let step = TempDir::new("adv5-step");
let file = step.join("First.step");
std::fs::write(&file, b"ISO-10303-21;\n").unwrap();
let imp = b.call("swx_import", json!({"filePath": file.to_string_lossy()}));
assert!(imp["success"] == true, "precondition: import ran: {}", s(&imp));
let docs = b.call("swx_docs", json!({}));
let list = docs["docs"].as_array().cloned().unwrap_or_default();
let temp = list.iter().find(|d| {
d.get("title").and_then(|t| t.as_str()).map(|t| t.contains("First")).unwrap_or(false)
});
assert!(temp.is_some(), "precondition: the imported doc is listed: {}", s(&docs));
let path = temp.unwrap().get("path").and_then(|p| p.as_str()).unwrap_or("");
assert!(
is_import_temp(path),
"the mock reports path='{path}' for an import temp, so `is_import_temp` — the ownership \
proof the whole drain relies on — is FALSE against the mock's own doc_info. The real \
backend's GetPathName returns the IC~~ path, so the path-keyed consumers (the drain \
match, the write gate's exists-check, save_as, ownership-by-path) are exercised on a \
value the real backend never produces for a temp.\ndoc: {}",
s(temp.unwrap())
);
}