main
Drew Owens 0.9.0: bridge data moved out of the install cache (recovery survives updates), closed-in-place records, gates route thr… 5d02ca9 20d ago
//! ADVERSARIAL REVIEW OF 0.7.5 — pass twenty-seven.
//!
//! The classifier change is right and the pass-26 findings are closed: the kind
//! is read from `GetType`, carried as a type NAME through `ready.docType`, and
//! re-parsed with `from_type_name` — never re-derived from the file name the
//! sweep itself chose. `from_extension` looks only at the last path segment, so
//! the `…\Drawings\` misclassification is gone. The `≥ original size` rule is
//! correctly scoped to `kind == None` only (`host.rs:431`), so a part that
//! legitimately shrinks — a suppressed feature, a deleted body, a lightweight
//! save — is still held to its own 24 KB floor and not to its old size. That
//! second half of the coordinator's question needed no change and got none.
//!
//! What is left is not the classifier. It is the two places the UNKNOWN branch
//! touches SOLIDWORKS, neither of which this suite can reach.

mod common;
use common::*;
use serde_json::json;
use swx_bridge::disk::DocKind;

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

/// THE DOCUMENT'S OWN FILE PATH IS NOT CONSULTED.
///
/// The naming chain, verbatim from `com.rs:1086`:
///
/// ```ignore
/// let ext = match kind.or_else(|| DocKind::from_extension(title)) { … };
/// ```
///
/// `kind` is `GetType`, `title` is `GetTitle`. **`path` — `GetPathName`, which
/// the same function read four lines earlier and already uses for
/// `original_bytes` — is never asked.** It is the strongest evidence in the
/// room: the user's own file, named by SOLIDWORKS, on disk.
///
/// It matters because `GetTitle` does not reliably carry the extension.
/// SOLIDWORKS returns the title as the shell shows it, so with "hide extensions
/// for known file types" on — the Windows DEFAULT — a saved assembly's title is
/// `bracket`, not `bracket.SLDASM`. Combine that with one unreadable `GetType`
/// and a document whose kind is sitting in plain text in `path` is classified
/// unknown, named `.sldrecover`, and held to the drawing floor plus its original
/// size.
///
/// For a saved document that last part is survivable — refusing is loud and the
/// user's file is still on disk. The reason to fix it anyway is that it is one
/// `.or_else` against evidence already in hand, and it narrows the `.sldrecover`
/// path to the only case that truly has no extension anywhere: a pathless
/// document. Which is the case the next test is about.
#[test]
fn a_saved_assembly_is_classified_from_its_path_when_the_type_cannot_be_read() {
    // GetType unreadable — the condition this sweep exists for.
    let kind: Option<DocKind> = None;
    // GetTitle with Explorer's default "hide extensions" setting.
    let title = "bracket";
    // GetPathName. Read four lines earlier in the same function, for size.
    let path = "C:\\work\\bracket.SLDASM";

    // The production expression, copied — as 0.7.6 fixed it. The path is asked
    // before the title, because the title is the weaker evidence of the two:
    // SOLIDWORKS reports it as the shell shows it, and Explorer hides known
    // extensions by default.
    let chosen = kind
        .or_else(|| DocKind::from_extension(path))
        .or_else(|| DocKind::from_extension(title));

    assert!(
        chosen.is_some(),
        "a saved assembly was classified UNKNOWN while its kind was written in its own path, \
         {path:?}. The naming chain asks GetType, then the TITLE's extension, and stops — it \
         never asks the path, though the same function reads it for originalBytes. GetTitle \
         carries no extension whenever Explorer hides known file types, which is the default."
    );
}

/// WHAT `.sldrecover` COSTS, AND WHO IS TOLD.
///
/// `unique_recovery_path` gives an unknown-kind document the extension
/// `sldrecover`, which is not a format SOLIDWORKS knows. Two consequences, and
/// the bridge is silent about both.
///
/// FIRST, AND I CANNOT TEST IT HERE: `SaveAs3` dispatches its output format on
/// the extension. This codebase already knows that — `blocked_by_extension`
/// exists precisely because `.wrl` makes SaveAs3 raise a VRML option sheet, so
/// the extension demonstrably selects a writer. What `SaveAs3` does with an
/// extension matching NO writer is not something I will assert from memory; I
/// got a format claim wrong in pass 23 by doing exactly that. But there are only
/// two possibilities and both need an answer before a live run:
///
///   * It FAILS. The record says `preserved: false`, the document stays open,
///     nothing is lost — and 0.7.5's entire unknown-kind path (the strictest
///     floor, the `≥ original size` rule, this extension) is unreachable in
///     production, because the file it verifies is never written. A guard that
///     cannot be reached is the sixth this review has found shipping inert.
///   * It SUCCEEDS by falling back to some default writer. Then the bytes on
///     disk may not be the document's native format at all, and the marker check
///     would be validating a file whose format nobody chose.
///
/// The mock cannot distinguish these: it writes the file itself. Only the live
/// install can answer it, with one scripted `SaveAs3` to a junk extension on a
/// scratch document — which is a write, so it is not mine to run.
///
/// SECOND, AND THIS IS WHAT THE TEST PINS: if such a file IS written, it is the
/// rescue of a document that was never saved — the only copy of that work — and
/// SOLIDWORKS will not open it, because the shell and the dispatcher both go by
/// extension. `swx_recover_list` is the one verb that shows these files, and its
/// hint says "Reopen any of them with swx_open." For every `.sldrecover` file
/// that instruction is wrong, and nothing anywhere tells the user to rename it.
/// The verb has the file names in hand and could say so per file.
#[test]
fn recover_list_says_how_to_open_a_file_solidworks_will_not_open() {
    let scratch = TempDir::new("adv26-recover");
    let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[]);

    // A sweep that preserved one document whose kind could not be established.
    let root = scratch.join("recovery").join("2026-09-12T00-00-00");
    std::fs::create_dir_all(&root).unwrap();
    let mut body = vec![0u8; 400_000];
    body[4..8].copy_from_slice(&[0x00, 0x00, 0x00, 0x04]);
    std::fs::write(root.join("Assem1.sldrecover"), &body).unwrap();

    let r = b.call("swx_recover_list", json!({}));
    let text = s(&r).to_ascii_lowercase();
    let listed = text.contains("sldrecover");
    assert!(listed, "precondition: the unknown-kind rescue is listed at all: {}", s(&r));

    assert!(
        text.contains("rename") || text.contains("sldasm") || text.contains("extension"),
        "swx_recover_list shows a .sldrecover file and tells the user \"Reopen any of them with \
         swx_open\". SOLIDWORKS dispatches on extension, so that is the one instruction that \
         cannot work for this file — and it is the rescue of a document that was NEVER SAVED, so \
         it is the user's only copy. Nothing in the response mentions renaming it or what to \
         rename it to. Response: {}",
        s(&r)
    );
}