    fn content_navigation_fixture() -> EditorAssets {
        let props = AssetSelection::Folder("assets/Props".into());
        EditorAssets {
            folders: vec![
                crate::assets::AssetFolder {
                    path: "assets".into(),
                    name: "assets".into(),
                    parent: None,
                },
                crate::assets::AssetFolder {
                    path: "assets/Props".into(),
                    name: "Props".into(),
                    parent: Some("assets".into()),
                },
            ],
            assets: Vec::new(),
            current_folder: "assets".into(),
            selected: Some(props.clone()),
            selections: vec![props.clone()],
            selection_anchor: Some(props.clone()),
            dragging: Some(props),
            status: String::new(),
            catalog_revision: 1,
        }
    }

    #[test]
    fn folder_navigation_clears_hidden_selection_and_drag_state() {
        let mut world = World::new();
        world.insert_resource(content_navigation_fixture());

        navigate_content_folder(&mut world, "assets/Props".into());

        let assets = world.resource::<EditorAssets>();
        assert_eq!(assets.current_folder, "assets/Props");
        assert!(assets.selected.is_none());
        assert!(assets.selections.is_empty());
        assert!(assets.selection_anchor.is_none());
        assert!(assets.dragging.is_none());
    }

    #[test]
    fn visibility_filter_changes_clear_hidden_selection_but_noops_preserve_it() {
        let mut world = World::new();
        world.insert_resource(content_navigation_fixture());

        clear_selection_when_visible_results_change(&mut world, false);
        assert_eq!(world.resource::<EditorAssets>().selections.len(), 1);

        clear_selection_when_visible_results_change(&mut world, true);
        let assets = world.resource::<EditorAssets>();
        assert!(assets.selected.is_none());
        assert!(assets.selections.is_empty());
        assert_eq!(
            assets.status,
            "Selection cleared because visible results changed"
        );
    }

    #[test]
    fn context_targeting_preserves_selected_batches_and_selects_unselected_subassets() {
        let mut world = World::new();
        let mut assets = content_navigation_fixture();
        assets.assets.push(EditorAsset {
            label: "Desk".into(),
            path: Some("assets/desk.glb".into()),
            folder_path: "assets".into(),
            kind: EditorAssetKind::Model,
        });
        let folder = AssetSelection::Folder("assets/Props".into());
        let model = AssetSelection::File("assets/desk.glb".into());
        assets.select_all(&[folder.clone(), model.clone()]);
        world.insert_resource(assets);

        prepare_context_menu_selection(&mut world, model);
        assert_eq!(world.resource::<EditorAssets>().selections.len(), 2);

        let subasset = AssetSelection::SubAsset {
            parent_path: "assets/desk.glb".into(),
            sub_asset_id: "mesh:0".into(),
            label: "Desk Top".into(),
            kind: AssetSubAssetKind::Mesh,
            source_path: None,
        };
        prepare_context_menu_selection(&mut world, subasset.clone());
        let assets = world.resource::<EditorAssets>();
        assert_eq!(assets.selected, Some(subasset.clone()));
        assert_eq!(assets.selections, vec![subasset]);
    }

    #[test]
    fn batch_selection_counts_distinguish_nested_and_non_filesystem_items() {
        let mut world = World::new();
        let mut assets = content_navigation_fixture();
        assets.assets.extend([
            EditorAsset {
                label: "Desk".into(),
                path: Some("assets/Props/desk.glb".into()),
                folder_path: "assets/Props".into(),
                kind: EditorAssetKind::Model,
            },
            EditorAsset {
                label: "Cube".into(),
                path: None,
                folder_path: BUILTINS_FOLDER.into(),
                kind: EditorAssetKind::Primitive(shared::PrimitiveShape::Box),
            },
        ]);
        assets.selections = vec![
            AssetSelection::Folder("assets/Props".into()),
            AssetSelection::File("assets/Props/desk.glb".into()),
            AssetSelection::SubAsset {
                parent_path: "assets/Props/desk.glb".into(),
                sub_asset_id: "mesh:0".into(),
                label: "Desk Top".into(),
                kind: AssetSubAssetKind::Mesh,
                source_path: None,
            },
            AssetSelection::Builtin("Cube".into()),
        ];
        assets.selected = assets.selections.last().cloned();
        world.insert_resource(assets);

        assert_eq!(selected_content_paths(&world), vec!["assets/Props"]);
        assert_eq!(collapsed_and_unaffected_selection_counts(&world), (1, 2));
    }

    #[test]
    fn cancelling_reviewed_content_transaction_restores_browser_and_scene_state() {
        let mut world = World::new();
        let mut assets = content_navigation_fixture();
        assets.status = "Before review".into();
        world.insert_resource(assets);
        let mut scene_io = SceneIo::default();
        scene_io.status = "Scene status before review".into();
        world.insert_resource(scene_io);
        let snapshot = capture_content_browser_snapshot(&world);
        let pending = PendingContentTransaction {
            preview: content_pipeline::ContentTransactionPreview {
                operations: Vec::new(),
                affected_asset_ids: Vec::new(),
                reference_rewrites: Vec::new(),
                collisions: Vec::new(),
            },
            guarded_paths: Vec::new(),
            clear_cut_clipboard: false,
            clear_drag: false,
            browser_snapshot: snapshot.clone(),
        };

        {
            let mut assets = world.resource_mut::<EditorAssets>();
            assets.current_folder = "assets/Changed".into();
            assets.clear_selection();
            assets.dragging = None;
            assets.status = "Review open".into();
        }
        world.resource_mut::<SceneIo>().status = "Transaction review".into();
        restore_cancelled_content_transaction(&mut world, &pending);

        let assets = world.resource::<EditorAssets>();
        assert_eq!(assets.current_folder, snapshot.current_folder);
        assert_eq!(assets.selected, snapshot.selected);
        assert_eq!(assets.selections, snapshot.selections);
        assert_eq!(assets.selection_anchor, snapshot.selection_anchor);
        assert_eq!(assets.dragging, snapshot.dragging);
        assert_eq!(assets.status, snapshot.asset_status);
        assert_eq!(world.resource::<SceneIo>().status, snapshot.scene_status);
    }

    #[test]
    fn cancelling_drag_review_restores_selection_but_clears_stale_drag() {
        let mut world = World::new();
        world.insert_resource(content_navigation_fixture());
        world.insert_resource(SceneIo::default());
        let pending = PendingContentTransaction {
            preview: content_pipeline::ContentTransactionPreview {
                operations: Vec::new(),
                affected_asset_ids: Vec::new(),
                reference_rewrites: Vec::new(),
                collisions: Vec::new(),
            },
            guarded_paths: Vec::new(),
            clear_cut_clipboard: false,
            clear_drag: true,
            browser_snapshot: capture_content_browser_snapshot(&world),
        };

        restore_cancelled_content_transaction(&mut world, &pending);

        let assets = world.resource::<EditorAssets>();
        assert!(assets.selected.is_some());
        assert_eq!(assets.selections.len(), 1);
        assert!(assets.dragging.is_none());
    }

    #[test]
    fn reviewed_model_import_rolls_back_files_when_artifact_processing_fails() {
        let root = std::env::temp_dir().join(format!(
            "blacksite-reviewed-model-import-{}",
            uuid::Uuid::new_v4()
        ));
        let project = root.join("project");
        let destination = PathBuf::from("assets/Target");
        fs::create_dir_all(project.join(&destination)).unwrap();
        let source = root.join("broken.gltf");
        fs::write(
            &source,
            r#"{"asset":{"version":"2.0"},"buffers":[{"uri":"mesh.bin","byteLength":12}]}"#,
        )
        .unwrap();
        fs::write(root.join("mesh.bin"), [0_u8]).unwrap();
        let plan = crate::assets::plan_external_assets_import_at(
            &project,
            std::slice::from_ref(&source),
            &destination,
        )
        .unwrap();
        let mut world = World::new();
        world.insert_resource(AssetRegistry::default());

        let error = commit_reviewed_asset_import(&mut world, &plan).unwrap_err();

        assert!(error.contains("processing rolled back"));
        assert!(!project.join("assets/Target/broken.gltf").exists());
        assert!(!project.join("assets/Target/mesh.bin").exists());
        assert!(!project.join(content_pipeline::REGISTRY_PATH).exists());
        assert!(!project
            .join(content_pipeline::RUNTIME_CATALOG_PATH)
            .exists());
        assert!(world.resource::<AssetRegistry>().records.is_empty());
        fs::remove_dir_all(root).unwrap();
    }

    #[test]
    fn import_to_keeps_browser_location_and_pins_explicit_destination() {
        let mut world = World::new();
        let mut assets = content_navigation_fixture();
        assets.current_folder = "assets/Props".into();
        world.insert_resource(assets);
        world.insert_resource(AssetBrowserUiState::default());
        world.init_resource::<SceneIo>();

        begin_import_to(&mut world);

        assert_eq!(
            world
                .resource::<AssetBrowserUiState>()
                .pending_import_destination
                .as_deref(),
            Some("assets/Props")
        );
        request_import_to_destination(&mut world, "assets/Props".into());
        assert_eq!(
            world.resource::<SceneIo>().request,
            Some(SceneIoRequest::ImportAssets {
                destination: "assets/Props".into()
            })
        );
        assert_eq!(
            world.resource::<EditorAssets>().current_folder,
            "assets/Props"
        );
    }

    #[test]
    fn locating_content_asset_navigates_to_parent_and_selects_the_file() {
        let mut world = World::new();
        let mut assets = content_navigation_fixture();
        assets.assets.push(EditorAsset {
            label: "Desk".into(),
            path: Some("assets/Props/desk.material.ron".into()),
            folder_path: "assets/Props".into(),
            kind: EditorAssetKind::Material,
        });
        world.insert_resource(assets);

        locate_content_asset(&mut world, "assets/Props/desk.material.ron");

        let assets = world.resource::<EditorAssets>();
        assert_eq!(assets.current_folder, "assets/Props");
        assert_eq!(
            assets.selected,
            Some(AssetSelection::File(
                "assets/Props/desk.material.ron".into()
            ))
        );
        assert_eq!(assets.selections, vec![assets.selected.clone().unwrap()]);
        assert_eq!(assets.status, "Located assets/Props/desk.material.ron");
    }

    #[test]
    fn locating_missing_content_asset_reports_the_broken_reference() {
        let mut world = World::new();
        world.insert_resource(content_navigation_fixture());

        locate_content_asset(&mut world, "assets/Props/missing.material.ron");

        let assets = world.resource::<EditorAssets>();
        assert_eq!(assets.current_folder, "assets");
        assert!(assets.selected.is_some());
        assert_eq!(
            assets.status,
            "Referenced project asset is missing: assets/Props/missing.material.ron"
        );
    }

    #[test]
    fn model_material_clear_restores_source_and_removes_only_target_orphan() {
        let active_slot = shared::ComponentInstanceId::new("slot:active");
        let removed_orphan = shared::ComponentInstanceId::new("slot:removed");
        let retained_orphan = shared::ComponentInstanceId::new("slot:retained");
        let material = MaterialRef::new(
            EditorAssetRef::new("material-id", "", "Desk")
                .with_source_path("assets/Props/desk.material.ron"),
        );
        let mut settings = ImportSettings {
            material_slots: vec![ModelMaterialSlotSelection {
                slot_id: active_slot.clone(),
                selection: ModelMaterialSelection::Project(material.clone()),
            }],
            orphaned_material_slots: vec![
                shared::OrphanedModelMaterialSelection {
                    slot_id: removed_orphan.clone(),
                    last_known_name: "Removed".into(),
                    material: material.clone(),
                },
                shared::OrphanedModelMaterialSelection {
                    slot_id: retained_orphan.clone(),
                    last_known_name: "Retained".into(),
                    material,
                },
            ],
            ..Default::default()
        };

        set_model_material_slot_selection(
            &mut settings,
            &active_slot,
            ModelMaterialSelection::Source,
        );
        assert!(clear_orphaned_model_material_selection(
            &mut settings,
            &removed_orphan
        ));

        assert!(matches!(
            settings.material_slots[0].selection,
            ModelMaterialSelection::Source
        ));
        assert_eq!(settings.orphaned_material_slots.len(), 1);
        assert_eq!(settings.orphaned_material_slots[0].slot_id, retained_orphan);
        assert!(!clear_orphaned_model_material_selection(
            &mut settings,
            &removed_orphan
        ));
    }

    #[test]
    fn rename_target_never_falls_back_to_an_unselected_current_folder() {
        let mut assets = content_navigation_fixture();
        assets.current_folder = "assets/Props".into();
        assets.clear_selection();
        let mut world = World::new();
        world.insert_resource(assets);

        assert_eq!(selected_content_path(&world), None);
    }

    #[test]
    fn embedded_subassets_never_become_filesystem_operation_targets() {
        let selection = AssetSelection::SubAsset {
            parent_path: "assets/Props/desk.glb".into(),
            sub_asset_id: "mesh:top".into(),
            label: "Desk Top".into(),
            kind: AssetSubAssetKind::Mesh,
            source_path: None,
        };
        let mut assets = content_navigation_fixture();
        assets.assets.push(EditorAsset {
            label: "Desk".into(),
            path: Some("assets/Props/desk.glb".into()),
            folder_path: "assets/Props".into(),
            kind: EditorAssetKind::Model,
        });
        assets.selected = Some(selection.clone());
        assets.selections = vec![selection];
        let mut world = World::new();
        world.insert_resource(assets);

        assert_eq!(selected_content_path(&world), None);
        assert!(selected_content_paths(&world).is_empty());
        assert!(build_delete_request(&world).is_none());
    }

    #[test]
    fn details_width_respects_content_and_pane_minimums() {
        assert_eq!(details_width_for_layout(1_000.0, 220.0, 100.0), 200.0);
        assert_eq!(details_width_for_layout(1_000.0, 220.0, 900.0), 600.0);
        assert_eq!(details_width_for_layout(1_000.0, 220.0, 420.0), 420.0);
    }

    #[test]
    fn details_width_uses_reclaimed_tree_space() {
        assert_eq!(details_width_for_layout(1_000.0, 0.0, 900.0), 830.0);
    }

    #[test]
    fn content_shortcuts_are_suppressed_while_a_browser_modal_is_active() {
        let mut world = World::new();
        world.insert_resource(AssetBrowserUiState::default());
        assert!(!content_browser_modal_active(&world));

        world
            .resource_mut::<AssetBrowserUiState>()
            .pending_path_edit = Some(ContentPathEdit {
            kind: ContentPathEditKind::NewFolder {
                parent: "assets".into(),
            },
            name: "New Folder".into(),
        });

        assert!(content_browser_modal_active(&world));
    }

    #[test]
    fn external_move_review_requires_complete_non_reused_identity_choices() {
        let fingerprint = shared::AssetSourceFingerprint::from_bytes(b"matching source");
        let conflict = |new_path: &str| content_pipeline::ExternalMoveConflict {
            new_path: new_path.into(),
            candidate_paths: vec!["assets/Old/A.png".into(), "assets/Old/B.png".into()],
            kind: shared::AssetKind::Texture,
            fingerprint: fingerprint.clone(),
        };
        let mut review = crate::assets::ExternalMoveRepairReview {
            root: PathBuf::from("/unused"),
            previous: shared::AssetRegistryDocument::default(),
            conflicts: vec![conflict("assets/New/A.png"), conflict("assets/New/B.png")],
            choices: vec![None, None],
            open: true,
        };

        assert!(!external_move_choices_valid(&review));
        review.choices = vec![
            Some(ExternalMoveRepairChoice::Preserve(
                "assets/Old/A.png".into(),
            )),
            Some(ExternalMoveRepairChoice::Preserve(
                "assets/Old/A.png".into(),
            )),
        ];
        assert!(!external_move_choices_valid(&review));
        review.choices[1] = Some(ExternalMoveRepairChoice::RegisterNew);
        assert!(external_move_choices_valid(&review));
    }
