All checks were successful
Python CI / test (push) Successful in 1m28s
Lint / lint (push) Successful in 1m4s
Tests / test (3.11) (push) Successful in 1m41s
Tests / test (3.12) (push) Successful in 1m42s
Tests / test (3.13) (push) Successful in 1m35s
Tests / test (3.14) (push) Successful in 1m15s
597 lines
21 KiB
Python
597 lines
21 KiB
Python
"""
|
|
Tests for asset_heal_dialog module
|
|
"""
|
|
|
|
import pytest
|
|
import os
|
|
import shutil
|
|
from pathlib import Path
|
|
from unittest.mock import Mock, MagicMock, patch, call
|
|
from PyQt6.QtWidgets import QMessageBox, QFileDialog
|
|
from PyQt6.QtCore import Qt
|
|
|
|
|
|
class TestAssetHealDialog:
|
|
"""Tests for AssetHealDialog class"""
|
|
|
|
@pytest.fixture
|
|
def mock_project(self, tmp_path):
|
|
"""Create a mock project with folder_path"""
|
|
project = Mock()
|
|
project.folder_path = str(tmp_path / "project")
|
|
os.makedirs(project.folder_path, exist_ok=True)
|
|
|
|
# Create assets folder
|
|
assets_path = os.path.join(project.folder_path, "assets")
|
|
os.makedirs(assets_path, exist_ok=True)
|
|
|
|
project.asset_manager = Mock()
|
|
project.pages = []
|
|
return project
|
|
|
|
@pytest.fixture
|
|
def mock_page_with_image(self):
|
|
"""Create a mock page with an image element"""
|
|
from pyPhotoAlbum.models import ImageData
|
|
|
|
page = Mock()
|
|
element = Mock(spec=ImageData)
|
|
element.image_path = "assets/image.jpg"
|
|
page.layout = Mock()
|
|
page.layout.elements = [element]
|
|
return page
|
|
|
|
def test_init(self, qtbot, mock_project):
|
|
"""Test AssetHealDialog initialization"""
|
|
from pyPhotoAlbum.asset_heal_dialog import AssetHealDialog
|
|
|
|
with patch.object(AssetHealDialog, '_scan_missing_assets'):
|
|
dialog = AssetHealDialog(mock_project)
|
|
qtbot.addWidget(dialog)
|
|
|
|
assert dialog.project is mock_project
|
|
assert dialog.search_paths == []
|
|
assert dialog.missing_assets == set()
|
|
assert dialog.windowTitle() == "Heal Missing Assets"
|
|
|
|
def test_init_ui(self, qtbot, mock_project):
|
|
"""Test UI initialization"""
|
|
from pyPhotoAlbum.asset_heal_dialog import AssetHealDialog
|
|
|
|
with patch.object(AssetHealDialog, '_scan_missing_assets'):
|
|
dialog = AssetHealDialog(mock_project)
|
|
qtbot.addWidget(dialog)
|
|
|
|
# Check that UI elements exist
|
|
assert dialog.missing_list is not None
|
|
assert dialog.search_list is not None
|
|
|
|
def test_scan_missing_assets_no_missing(self, qtbot, mock_project, tmp_path):
|
|
"""Test scanning when no assets are missing"""
|
|
from pyPhotoAlbum.asset_heal_dialog import AssetHealDialog
|
|
from pyPhotoAlbum.models import ImageData
|
|
|
|
# Create a valid image in assets folder
|
|
img_path = os.path.join(mock_project.folder_path, "assets", "image.jpg")
|
|
Path(img_path).touch()
|
|
|
|
element = Mock(spec=ImageData)
|
|
element.image_path = "assets/image.jpg"
|
|
|
|
page = Mock()
|
|
page.layout = Mock()
|
|
page.layout.elements = [element]
|
|
|
|
mock_project.pages = [page]
|
|
|
|
dialog = AssetHealDialog(mock_project)
|
|
qtbot.addWidget(dialog)
|
|
|
|
assert len(dialog.missing_assets) == 0
|
|
assert dialog.missing_list.count() == 1 # "No missing assets found!" message
|
|
item = dialog.missing_list.item(0)
|
|
assert "No missing assets" in item.text()
|
|
|
|
def test_scan_missing_assets_absolute_path(self, qtbot, mock_project):
|
|
"""Test scanning detects absolute paths as needing healing"""
|
|
from pyPhotoAlbum.asset_heal_dialog import AssetHealDialog
|
|
from pyPhotoAlbum.models import ImageData
|
|
|
|
element = Mock(spec=ImageData)
|
|
element.image_path = "/absolute/path/to/image.jpg"
|
|
|
|
page = Mock()
|
|
page.layout = Mock()
|
|
page.layout.elements = [element]
|
|
|
|
mock_project.pages = [page]
|
|
|
|
dialog = AssetHealDialog(mock_project)
|
|
qtbot.addWidget(dialog)
|
|
|
|
assert "/absolute/path/to/image.jpg" in dialog.missing_assets
|
|
|
|
def test_scan_missing_assets_not_in_assets_folder(self, qtbot, mock_project):
|
|
"""Test scanning detects paths not in assets/ folder"""
|
|
from pyPhotoAlbum.asset_heal_dialog import AssetHealDialog
|
|
from pyPhotoAlbum.models import ImageData
|
|
|
|
element = Mock(spec=ImageData)
|
|
element.image_path = "images/photo.jpg" # Not in assets/
|
|
|
|
page = Mock()
|
|
page.layout = Mock()
|
|
page.layout.elements = [element]
|
|
|
|
mock_project.pages = [page]
|
|
|
|
dialog = AssetHealDialog(mock_project)
|
|
qtbot.addWidget(dialog)
|
|
|
|
assert "images/photo.jpg" in dialog.missing_assets
|
|
|
|
def test_scan_missing_assets_file_missing(self, qtbot, mock_project):
|
|
"""Test scanning detects missing files in assets/ folder"""
|
|
from pyPhotoAlbum.asset_heal_dialog import AssetHealDialog
|
|
from pyPhotoAlbum.models import ImageData
|
|
|
|
element = Mock(spec=ImageData)
|
|
element.image_path = "assets/missing.jpg" # File doesn't exist
|
|
|
|
page = Mock()
|
|
page.layout = Mock()
|
|
page.layout.elements = [element]
|
|
|
|
mock_project.pages = [page]
|
|
|
|
dialog = AssetHealDialog(mock_project)
|
|
qtbot.addWidget(dialog)
|
|
|
|
assert "assets/missing.jpg" in dialog.missing_assets
|
|
|
|
def test_scan_missing_assets_non_image_elements_ignored(self, qtbot, mock_project):
|
|
"""Test that non-ImageData elements are ignored"""
|
|
from pyPhotoAlbum.asset_heal_dialog import AssetHealDialog
|
|
|
|
# TextBox element (not ImageData)
|
|
element = Mock()
|
|
element.image_path = None
|
|
|
|
page = Mock()
|
|
page.layout = Mock()
|
|
page.layout.elements = [element]
|
|
|
|
mock_project.pages = [page]
|
|
|
|
dialog = AssetHealDialog(mock_project)
|
|
qtbot.addWidget(dialog)
|
|
|
|
assert len(dialog.missing_assets) == 0
|
|
|
|
def test_scan_missing_assets_empty_image_path(self, qtbot, mock_project):
|
|
"""Test that elements with empty image_path are ignored"""
|
|
from pyPhotoAlbum.asset_heal_dialog import AssetHealDialog
|
|
from pyPhotoAlbum.models import ImageData
|
|
|
|
element = Mock(spec=ImageData)
|
|
element.image_path = ""
|
|
|
|
page = Mock()
|
|
page.layout = Mock()
|
|
page.layout.elements = [element]
|
|
|
|
mock_project.pages = [page]
|
|
|
|
dialog = AssetHealDialog(mock_project)
|
|
qtbot.addWidget(dialog)
|
|
|
|
assert len(dialog.missing_assets) == 0
|
|
|
|
def test_add_search_path(self, qtbot, mock_project, tmp_path):
|
|
"""Test adding a search path"""
|
|
from pyPhotoAlbum.asset_heal_dialog import AssetHealDialog
|
|
|
|
with patch.object(AssetHealDialog, '_scan_missing_assets'):
|
|
dialog = AssetHealDialog(mock_project)
|
|
qtbot.addWidget(dialog)
|
|
|
|
search_path = str(tmp_path / "search")
|
|
os.makedirs(search_path, exist_ok=True)
|
|
|
|
with patch.object(QFileDialog, 'getExistingDirectory', return_value=search_path):
|
|
dialog._add_search_path()
|
|
|
|
assert search_path in dialog.search_paths
|
|
assert dialog.search_list.count() == 1
|
|
|
|
def test_add_search_path_duplicate(self, qtbot, mock_project, tmp_path):
|
|
"""Test adding duplicate search path is ignored"""
|
|
from pyPhotoAlbum.asset_heal_dialog import AssetHealDialog
|
|
|
|
with patch.object(AssetHealDialog, '_scan_missing_assets'):
|
|
dialog = AssetHealDialog(mock_project)
|
|
qtbot.addWidget(dialog)
|
|
|
|
search_path = str(tmp_path / "search")
|
|
os.makedirs(search_path, exist_ok=True)
|
|
|
|
with patch.object(QFileDialog, 'getExistingDirectory', return_value=search_path):
|
|
dialog._add_search_path()
|
|
dialog._add_search_path()
|
|
|
|
assert dialog.search_paths.count(search_path) == 1
|
|
assert dialog.search_list.count() == 1
|
|
|
|
def test_add_search_path_cancelled(self, qtbot, mock_project):
|
|
"""Test cancelling search path dialog"""
|
|
from pyPhotoAlbum.asset_heal_dialog import AssetHealDialog
|
|
|
|
with patch.object(AssetHealDialog, '_scan_missing_assets'):
|
|
dialog = AssetHealDialog(mock_project)
|
|
qtbot.addWidget(dialog)
|
|
|
|
with patch.object(QFileDialog, 'getExistingDirectory', return_value=""):
|
|
dialog._add_search_path()
|
|
|
|
assert len(dialog.search_paths) == 0
|
|
|
|
def test_remove_search_path(self, qtbot, mock_project, tmp_path):
|
|
"""Test removing a search path"""
|
|
from pyPhotoAlbum.asset_heal_dialog import AssetHealDialog
|
|
|
|
with patch.object(AssetHealDialog, '_scan_missing_assets'):
|
|
dialog = AssetHealDialog(mock_project)
|
|
qtbot.addWidget(dialog)
|
|
|
|
search_path = str(tmp_path / "search")
|
|
dialog.search_paths.append(search_path)
|
|
dialog.search_list.addItem(search_path)
|
|
|
|
dialog.search_list.setCurrentRow(0)
|
|
dialog._remove_search_path()
|
|
|
|
assert len(dialog.search_paths) == 0
|
|
assert dialog.search_list.count() == 0
|
|
|
|
def test_remove_search_path_none_selected(self, qtbot, mock_project):
|
|
"""Test removing when no path is selected"""
|
|
from pyPhotoAlbum.asset_heal_dialog import AssetHealDialog
|
|
|
|
with patch.object(AssetHealDialog, '_scan_missing_assets'):
|
|
dialog = AssetHealDialog(mock_project)
|
|
qtbot.addWidget(dialog)
|
|
|
|
dialog.search_paths.append("/some/path")
|
|
dialog.search_list.addItem("/some/path")
|
|
|
|
dialog.search_list.setCurrentRow(-1) # No selection
|
|
dialog._remove_search_path()
|
|
|
|
# Should not remove anything
|
|
assert len(dialog.search_paths) == 1
|
|
|
|
@patch('pyPhotoAlbum.models.set_asset_resolution_context')
|
|
def test_attempt_healing_no_missing_assets(self, mock_set_context, qtbot, mock_project):
|
|
"""Test healing when there are no missing assets"""
|
|
from pyPhotoAlbum.asset_heal_dialog import AssetHealDialog
|
|
|
|
mock_project.pages = []
|
|
|
|
dialog = AssetHealDialog(mock_project)
|
|
qtbot.addWidget(dialog)
|
|
|
|
with patch.object(QMessageBox, 'information') as mock_info:
|
|
dialog._attempt_healing()
|
|
|
|
mock_info.assert_called_once()
|
|
args = mock_info.call_args[0]
|
|
assert "Assets found: 0" in args[2]
|
|
|
|
@patch('pyPhotoAlbum.models.set_asset_resolution_context')
|
|
def test_attempt_healing_resolve_relative_path(self, mock_set_context, qtbot, mock_project, tmp_path):
|
|
"""Test healing by resolving relative path"""
|
|
from pyPhotoAlbum.asset_heal_dialog import AssetHealDialog
|
|
from pyPhotoAlbum.models import ImageData
|
|
|
|
# Create the actual image file outside project
|
|
external_img = tmp_path / "external" / "image.jpg"
|
|
external_img.parent.mkdir(exist_ok=True)
|
|
external_img.touch()
|
|
|
|
# Element with relative path that resolves to external image
|
|
element = Mock(spec=ImageData)
|
|
rel_path = os.path.relpath(str(external_img), mock_project.folder_path)
|
|
element.image_path = rel_path
|
|
|
|
page = Mock()
|
|
page.layout = Mock()
|
|
page.layout.elements = [element]
|
|
|
|
mock_project.pages = [page]
|
|
|
|
dialog = AssetHealDialog(mock_project)
|
|
qtbot.addWidget(dialog)
|
|
|
|
# Mock the import_asset method
|
|
mock_project.asset_manager.import_asset.return_value = "assets/image.jpg"
|
|
|
|
with patch.object(QMessageBox, 'information') as mock_info:
|
|
dialog._attempt_healing()
|
|
|
|
# Should have imported the asset
|
|
mock_project.asset_manager.import_asset.assert_called()
|
|
|
|
@patch('pyPhotoAlbum.models.set_asset_resolution_context')
|
|
def test_attempt_healing_absolute_path_exists(self, mock_set_context, qtbot, mock_project, tmp_path):
|
|
"""Test healing absolute path that exists"""
|
|
from pyPhotoAlbum.asset_heal_dialog import AssetHealDialog
|
|
from pyPhotoAlbum.models import ImageData
|
|
|
|
# Create image at absolute path
|
|
abs_img = tmp_path / "image.jpg"
|
|
abs_img.touch()
|
|
|
|
element = Mock(spec=ImageData)
|
|
element.image_path = str(abs_img)
|
|
|
|
page = Mock()
|
|
page.layout = Mock()
|
|
page.layout.elements = [element]
|
|
|
|
mock_project.pages = [page]
|
|
|
|
dialog = AssetHealDialog(mock_project)
|
|
qtbot.addWidget(dialog)
|
|
|
|
mock_project.asset_manager.import_asset.return_value = "assets/image.jpg"
|
|
|
|
with patch.object(QMessageBox, 'information') as mock_info:
|
|
dialog._attempt_healing()
|
|
|
|
# Should import the asset
|
|
mock_project.asset_manager.import_asset.assert_called_with(str(abs_img))
|
|
# Should update element path
|
|
assert element.image_path == "assets/image.jpg"
|
|
|
|
@patch('pyPhotoAlbum.models.set_asset_resolution_context')
|
|
def test_attempt_healing_search_path_by_filename(self, mock_set_context, qtbot, mock_project, tmp_path):
|
|
"""Test healing by finding file in search path by filename"""
|
|
from pyPhotoAlbum.asset_heal_dialog import AssetHealDialog
|
|
from pyPhotoAlbum.models import ImageData
|
|
|
|
# Create search path with image
|
|
search_dir = tmp_path / "search"
|
|
search_dir.mkdir()
|
|
found_img = search_dir / "photo.jpg"
|
|
found_img.touch()
|
|
|
|
element = Mock(spec=ImageData)
|
|
element.image_path = "/missing/path/photo.jpg"
|
|
|
|
page = Mock()
|
|
page.layout = Mock()
|
|
page.layout.elements = [element]
|
|
|
|
mock_project.pages = [page]
|
|
|
|
with patch.object(AssetHealDialog, '_scan_missing_assets'):
|
|
dialog = AssetHealDialog(mock_project)
|
|
qtbot.addWidget(dialog)
|
|
|
|
dialog.missing_assets.add("/missing/path/photo.jpg")
|
|
dialog.search_paths.append(str(search_dir))
|
|
|
|
mock_project.asset_manager.import_asset.return_value = "assets/photo.jpg"
|
|
|
|
with patch.object(QMessageBox, 'information'):
|
|
dialog._attempt_healing()
|
|
|
|
mock_project.asset_manager.import_asset.assert_called_with(str(found_img))
|
|
|
|
@patch('pyPhotoAlbum.models.set_asset_resolution_context')
|
|
def test_attempt_healing_search_path_by_relative_structure(self, mock_set_context, qtbot, mock_project, tmp_path):
|
|
"""Test healing by finding file in search path with same relative structure"""
|
|
from pyPhotoAlbum.asset_heal_dialog import AssetHealDialog
|
|
from pyPhotoAlbum.models import ImageData
|
|
|
|
# Create search path with subdirectory structure
|
|
search_dir = tmp_path / "search"
|
|
subdir = search_dir / "photos"
|
|
subdir.mkdir(parents=True)
|
|
found_img = subdir / "image.jpg"
|
|
found_img.touch()
|
|
|
|
element = Mock(spec=ImageData)
|
|
element.image_path = "photos/image.jpg"
|
|
|
|
page = Mock()
|
|
page.layout = Mock()
|
|
page.layout.elements = [element]
|
|
|
|
mock_project.pages = [page]
|
|
|
|
with patch.object(AssetHealDialog, '_scan_missing_assets'):
|
|
dialog = AssetHealDialog(mock_project)
|
|
qtbot.addWidget(dialog)
|
|
|
|
dialog.missing_assets.add("photos/image.jpg")
|
|
dialog.search_paths.append(str(search_dir))
|
|
|
|
mock_project.asset_manager.import_asset.return_value = "assets/image.jpg"
|
|
|
|
with patch.object(QMessageBox, 'information'):
|
|
dialog._attempt_healing()
|
|
|
|
mock_project.asset_manager.import_asset.assert_called_with(str(found_img))
|
|
|
|
@patch('pyPhotoAlbum.models.set_asset_resolution_context')
|
|
def test_attempt_healing_restore_to_assets_folder(self, mock_set_context, qtbot, mock_project, tmp_path):
|
|
"""Test healing by restoring file to assets folder"""
|
|
from pyPhotoAlbum.asset_heal_dialog import AssetHealDialog
|
|
from pyPhotoAlbum.models import ImageData
|
|
|
|
# Create source image
|
|
source_img = tmp_path / "source" / "image.jpg"
|
|
source_img.parent.mkdir()
|
|
source_img.touch()
|
|
|
|
element = Mock(spec=ImageData)
|
|
element.image_path = "assets/image.jpg" # Already correct path, just missing file
|
|
|
|
page = Mock()
|
|
page.layout = Mock()
|
|
page.layout.elements = [element]
|
|
|
|
mock_project.pages = [page]
|
|
|
|
with patch.object(AssetHealDialog, '_scan_missing_assets'):
|
|
dialog = AssetHealDialog(mock_project)
|
|
qtbot.addWidget(dialog)
|
|
|
|
dialog.missing_assets.add("assets/image.jpg")
|
|
dialog.search_paths.append(str(source_img.parent))
|
|
|
|
with patch('shutil.copy2') as mock_copy:
|
|
with patch.object(QMessageBox, 'information'):
|
|
dialog._attempt_healing()
|
|
|
|
# Should copy file, not import
|
|
mock_copy.assert_called_once()
|
|
assert not mock_project.asset_manager.import_asset.called
|
|
|
|
@patch('pyPhotoAlbum.models.set_asset_resolution_context')
|
|
def test_attempt_healing_not_found(self, mock_set_context, qtbot, mock_project):
|
|
"""Test healing when asset cannot be found"""
|
|
from pyPhotoAlbum.asset_heal_dialog import AssetHealDialog
|
|
from pyPhotoAlbum.models import ImageData
|
|
|
|
element = Mock(spec=ImageData)
|
|
element.image_path = "/missing/image.jpg"
|
|
|
|
page = Mock()
|
|
page.layout = Mock()
|
|
page.layout.elements = [element]
|
|
|
|
mock_project.pages = [page]
|
|
|
|
dialog = AssetHealDialog(mock_project)
|
|
qtbot.addWidget(dialog)
|
|
|
|
with patch.object(QMessageBox, 'information') as mock_info:
|
|
dialog._attempt_healing()
|
|
|
|
args = mock_info.call_args[0]
|
|
assert "Still missing: 1" in args[2]
|
|
assert "/missing/image.jpg" in args[2]
|
|
|
|
@patch('pyPhotoAlbum.models.set_asset_resolution_context')
|
|
def test_attempt_healing_import_error(self, mock_set_context, qtbot, mock_project, tmp_path):
|
|
"""Test healing when import_asset raises an error"""
|
|
from pyPhotoAlbum.asset_heal_dialog import AssetHealDialog
|
|
from pyPhotoAlbum.models import ImageData
|
|
|
|
# Create image
|
|
img = tmp_path / "image.jpg"
|
|
img.touch()
|
|
|
|
element = Mock(spec=ImageData)
|
|
element.image_path = str(img)
|
|
|
|
page = Mock()
|
|
page.layout = Mock()
|
|
page.layout.elements = [element]
|
|
|
|
mock_project.pages = [page]
|
|
|
|
dialog = AssetHealDialog(mock_project)
|
|
qtbot.addWidget(dialog)
|
|
|
|
# Mock import to raise error
|
|
mock_project.asset_manager.import_asset.side_effect = Exception("Import failed")
|
|
|
|
with patch.object(QMessageBox, 'information') as mock_info:
|
|
dialog._attempt_healing()
|
|
|
|
# Should be in still_missing list
|
|
args = mock_info.call_args[0]
|
|
assert "Still missing: 1" in args[2]
|
|
|
|
@patch('pyPhotoAlbum.models.set_asset_resolution_context')
|
|
def test_attempt_healing_many_missing(self, mock_set_context, qtbot, mock_project):
|
|
"""Test healing with more than 10 missing assets (tests truncation)"""
|
|
from pyPhotoAlbum.asset_heal_dialog import AssetHealDialog
|
|
from pyPhotoAlbum.models import ImageData
|
|
|
|
pages = []
|
|
for i in range(15):
|
|
element = Mock(spec=ImageData)
|
|
element.image_path = f"/missing/image{i}.jpg"
|
|
|
|
page = Mock()
|
|
page.layout = Mock()
|
|
page.layout.elements = [element]
|
|
pages.append(page)
|
|
|
|
mock_project.pages = pages
|
|
|
|
dialog = AssetHealDialog(mock_project)
|
|
qtbot.addWidget(dialog)
|
|
|
|
with patch.object(QMessageBox, 'information') as mock_info:
|
|
dialog._attempt_healing()
|
|
|
|
args = mock_info.call_args[0]
|
|
# Should show "... and X more"
|
|
assert "and 5 more" in args[2]
|
|
|
|
@patch('pyPhotoAlbum.models.set_asset_resolution_context')
|
|
def test_attempt_healing_multiple_elements_same_path(self, mock_set_context, qtbot, mock_project, tmp_path):
|
|
"""Test healing when multiple elements reference the same missing path"""
|
|
from pyPhotoAlbum.asset_heal_dialog import AssetHealDialog
|
|
from pyPhotoAlbum.models import ImageData
|
|
|
|
# Create image
|
|
img = tmp_path / "image.jpg"
|
|
img.touch()
|
|
|
|
# Two elements with same path
|
|
element1 = Mock(spec=ImageData)
|
|
element1.image_path = str(img)
|
|
element2 = Mock(spec=ImageData)
|
|
element2.image_path = str(img)
|
|
|
|
page = Mock()
|
|
page.layout = Mock()
|
|
page.layout.elements = [element1, element2]
|
|
|
|
mock_project.pages = [page]
|
|
|
|
dialog = AssetHealDialog(mock_project)
|
|
qtbot.addWidget(dialog)
|
|
|
|
mock_project.asset_manager.import_asset.return_value = "assets/image.jpg"
|
|
|
|
with patch.object(QMessageBox, 'information'):
|
|
dialog._attempt_healing()
|
|
|
|
# Both elements should be updated
|
|
assert element1.image_path == "assets/image.jpg"
|
|
assert element2.image_path == "assets/image.jpg"
|
|
|
|
@patch('pyPhotoAlbum.models.set_asset_resolution_context')
|
|
def test_attempt_healing_rescans_after(self, mock_set_context, qtbot, mock_project):
|
|
"""Test that _scan_missing_assets is called after healing"""
|
|
from pyPhotoAlbum.asset_heal_dialog import AssetHealDialog
|
|
|
|
mock_project.pages = []
|
|
|
|
dialog = AssetHealDialog(mock_project)
|
|
qtbot.addWidget(dialog)
|
|
|
|
with patch.object(dialog, '_scan_missing_assets') as mock_scan:
|
|
with patch.object(QMessageBox, 'information'):
|
|
dialog._attempt_healing()
|
|
|
|
# Should rescan after healing
|
|
mock_scan.assert_called_once()
|