From 4704b9280446070077cce9cc494762b2afd43606 Mon Sep 17 00:00:00 2001 From: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com> Date: Wed, 27 Aug 2025 04:33:38 -0700 Subject: [PATCH] ci(tests): fix broken tests and add type hints (#1062) * ci: expand pyright ignore rules to vendored and tests * tests: comment out unused Mocks for further evaluation * tests: fix broken tests, add type hints * chore: address type feedback * chore: remove unused qtbot parameter --- pyproject.toml | 3 +- src/tagstudio/core/library/alchemy/library.py | 29 ++++- tests/conftest.py | 31 ++--- tests/macros/test_dupe_entries.py | 9 +- tests/macros/test_folders_tags.py | 7 +- tests/macros/test_missing_files.py | 5 + tests/macros/test_refresh_dir.py | 8 +- tests/macros/test_sidecar.py | 37 ------ tests/qt/test_build_tag_panel.py | 69 ++++++++--- tests/qt/test_field_containers.py | 45 ++++--- tests/qt/test_file_path_options.py | 26 ++-- tests/qt/test_flow_widget.py | 9 +- tests/qt/test_folders_to_tags.py | 10 +- tests/qt/test_global_settings.py | 5 + tests/qt/test_item_thumb.py | 12 +- tests/qt/test_preview_panel.py | 14 ++- tests/qt/test_qt_driver.py | 79 ++---------- tests/qt/test_tag_panel.py | 16 ++- tests/qt/test_tag_search_panel.py | 10 +- tests/test_driver.py | 5 + tests/test_json_migration.py | 4 +- tests/test_library.py | 115 +++++++++++------- tests/test_search.py | 7 +- tests/test_translations.py | 5 + 24 files changed, 335 insertions(+), 225 deletions(-) delete mode 100644 tests/macros/test_sidecar.py diff --git a/pyproject.toml b/pyproject.toml index ac74be2e..5b47e95e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -84,12 +84,13 @@ ignore_errors = true qt_api = "pyside6" [tool.pyright] -ignore = [".venv/**"] +ignore = ["src/tagstudio/qt/helpers/vendored/pydub/", ".venv/**"] include = ["src/tagstudio", "tests"] reportAny = false reportIgnoreCommentWithoutRule = false reportImplicitStringConcatenation = false reportMissingTypeArgument = false +reportMissingTypeStubs = false # reportOptionalMemberAccess = false reportUnannotatedClassAttribute = false reportUnknownArgumentType = false diff --git a/src/tagstudio/core/library/alchemy/library.py b/src/tagstudio/core/library/alchemy/library.py index fe9d4e29..9dfdd8e7 100644 --- a/src/tagstudio/core/library/alchemy/library.py +++ b/src/tagstudio/core/library/alchemy/library.py @@ -209,7 +209,7 @@ class Library: """Class for the Library object, and all CRUD operations made upon it.""" library_dir: Path | None = None - storage_path: Path | None + storage_path: Path | str | None engine: Engine | None = None folder: Folder | None included_files: set[Path] = set() @@ -333,7 +333,9 @@ class Library: else: return tag.name - def open_library(self, library_dir: Path, storage_path: Path | None = None) -> LibraryStatus: + def open_library( + self, library_dir: Path, storage_path: Path | str | None = None + ) -> LibraryStatus: is_new: bool = True if storage_path == ":memory:": self.storage_path = storage_path @@ -341,6 +343,7 @@ class Library: return self.open_sqlite_library(library_dir, is_new) else: self.storage_path = library_dir / TS_FOLDER_NAME / self.SQL_FILENAME + assert isinstance(self.storage_path, Path) if self.verify_ts_folder(library_dir) and (is_new := not self.storage_path.exists()): json_path = library_dir / TS_FOLDER_NAME / self.JSON_FILENAME if json_path.exists(): @@ -1213,7 +1216,7 @@ class Library: value: str | datetime | None = None, ) -> bool: logger.info( - "add_field_to_entry", + "[Library][add_field_to_entry]", entry_id=entry_id, field_type=field, field_id=field_id, @@ -1388,6 +1391,12 @@ class Library: self, entry_ids: int | list[int], tag_ids: int | list[int] | set[int] ) -> bool: """Add one or more tags to one or more entries.""" + logger.info( + "[Library][add_tags_to_entries]", + entry_ids=entry_ids, + tag_ids=tag_ids, + ) + entry_ids_ = [entry_ids] if isinstance(entry_ids, int) else entry_ids tag_ids_ = [tag_ids] if isinstance(tag_ids, int) else tag_ids with Session(self.engine, expire_on_commit=False) as session: @@ -1732,18 +1741,26 @@ class Library: value=field.value, ) - def merge_entries(self, from_entry: Entry, into_entry: Entry) -> None: + def merge_entries(self, from_entry: Entry, into_entry: Entry) -> bool: """Add fields and tags from the first entry to the second, and then delete the first.""" + success = True for field in from_entry.fields: - self.add_field_to_entry( + result = self.add_field_to_entry( entry_id=into_entry.id, field_id=field.type_key, value=field.value, ) + if not result: + success = False tag_ids = [tag.id for tag in from_entry.tags] - self.add_tags_to_entries(into_entry.id, tag_ids) + add_result = self.add_tags_to_entries(into_entry.id, tag_ids) self.remove_entries([from_entry.id]) + if not add_result: + success = False + + return success + @property def tag_color_groups(self) -> dict[str, list[TagColorGroup]]: """Return every TagColorGroup in the library.""" diff --git a/tests/conftest.py b/tests/conftest.py index 65aa31b0..b63dbb65 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,10 @@ +# Copyright (C) 2025 +# Licensed under the GPL-3.0 License. +# Created for TagStudio: https://github.com/CyanVoxel/TagStudio + + import sys +from collections.abc import Callable, Generator from pathlib import Path from tempfile import TemporaryDirectory from unittest.mock import Mock, patch @@ -26,6 +32,7 @@ def file_mediatypes_library(): status = lib.open_library(Path(""), ":memory:") assert status.success + assert lib.folder entry1 = Entry( folder=lib.folder, @@ -64,18 +71,19 @@ def library_dir(): @pytest.fixture -def library(request, library_dir: Path): +def library(request, library_dir: Path): # pyright: ignore # when no param is passed, use the default library_path = library_dir if hasattr(request, "param"): if isinstance(request.param, TemporaryDirectory): - library_path = Path(request.param.name) + library_path = Path(request.param.name) # pyright: ignore[reportArgumentType] else: library_path = Path(request.param) lib = Library() status = lib.open_library(library_path, ":memory:") assert status.success + assert lib.folder tag = Tag( name="foo", @@ -133,7 +141,7 @@ def search_library() -> Library: @pytest.fixture -def entry_min(library): +def entry_min(library: Library): yield next(library.all_entries()) @@ -143,7 +151,7 @@ def entry_full(library: Library): @pytest.fixture -def qt_driver(qtbot, library, library_dir: Path): +def qt_driver(library: Library, library_dir: Path): class Args: settings_file = library_dir / "settings.toml" cache_file = library_dir / "tagstudio.ini" @@ -151,31 +159,26 @@ def qt_driver(qtbot, library, library_dir: Path): ci = True with patch("tagstudio.qt.ts_qt.Consumer"), patch("tagstudio.qt.ts_qt.CustomRunnable"): - driver = QtDriver(Args()) + driver = QtDriver(Args()) # pyright: ignore[reportArgumentType] driver.app = Mock() driver.main_window = Mock() - driver.main_window.preview_panel = Mock() - driver.main_window.thumb_grid = Mock() driver.main_window.thumb_size = 128 driver.item_thumbs = [] - driver.main_window.menu_bar.autofill_action = Mock() driver.copy_buffer = {"fields": [], "tags": []} - driver.main_window.menu_bar.copy_fields_action = Mock() - driver.main_window.menu_bar.paste_fields_action = Mock() driver.lib = library # TODO - downsize this method and use it # driver.start() - driver.frame_content = list(library.all_entries()) + driver.frame_content = [e.id for e in library.all_entries()] yield driver @pytest.fixture -def generate_tag(): - def inner(name, **kwargs): +def generate_tag() -> Generator[Callable[..., Tag]]: + def inner(name: str, **kwargs) -> Tag: # pyright: ignore params = dict(name=name, color_namespace="tagstudio-standard", color_slug="red") | kwargs - return Tag(**params) + return Tag(**params) # pyright: ignore[reportArgumentType] yield inner diff --git a/tests/macros/test_dupe_entries.py b/tests/macros/test_dupe_entries.py index f3f7dbf4..94351425 100644 --- a/tests/macros/test_dupe_entries.py +++ b/tests/macros/test_dupe_entries.py @@ -1,13 +1,20 @@ +# Copyright (C) 2025 +# Licensed under the GPL-3.0 License. +# Created for TagStudio: https://github.com/CyanVoxel/TagStudio + from pathlib import Path +from tagstudio.core.library.alchemy.library import Library from tagstudio.core.library.alchemy.models import Entry from tagstudio.core.utils.dupe_files import DupeRegistry CWD = Path(__file__).parent -def test_refresh_dupe_files(library): +def test_refresh_dupe_files(library: Library): library.library_dir = Path("/tmp/") + assert library.folder + entry = Entry( folder=library.folder, path=Path("bar/foo.txt"), diff --git a/tests/macros/test_folders_tags.py b/tests/macros/test_folders_tags.py index 1bda4bd7..0daa7da8 100644 --- a/tests/macros/test_folders_tags.py +++ b/tests/macros/test_folders_tags.py @@ -1,7 +1,12 @@ +# Copyright (C) 2025 +# Licensed under the GPL-3.0 License. +# Created for TagStudio: https://github.com/CyanVoxel/TagStudio + +from tagstudio.core.library.alchemy.library import Library from tagstudio.qt.modals.folders_to_tags import folders_to_tags -def test_folders_to_tags(library): +def test_folders_to_tags(library: Library): folders_to_tags(library) entry = [x for x in library.all_entries(with_joins=True) if "bar.md" in str(x.path)][0] assert {x.name for x in entry.tags} == {"two", "bar"} diff --git a/tests/macros/test_missing_files.py b/tests/macros/test_missing_files.py index 975efc41..209090f9 100644 --- a/tests/macros/test_missing_files.py +++ b/tests/macros/test_missing_files.py @@ -1,3 +1,7 @@ +# Copyright (C) 2025 +# Licensed under the GPL-3.0 License. +# Created for TagStudio: https://github.com/CyanVoxel/TagStudio + from pathlib import Path from tempfile import TemporaryDirectory @@ -16,6 +20,7 @@ def test_refresh_missing_files(library: Library): registry = MissingRegistry(library=library) # touch the file `one/two/bar.md` but in wrong location to simulate a moved file + assert library.library_dir (library.library_dir / "bar.md").touch() # no files actually exist, so it should return all entries diff --git a/tests/macros/test_refresh_dir.py b/tests/macros/test_refresh_dir.py index 3bdbafa2..f692fc7d 100644 --- a/tests/macros/test_refresh_dir.py +++ b/tests/macros/test_refresh_dir.py @@ -1,9 +1,14 @@ +# Copyright (C) 2025 +# Licensed under the GPL-3.0 License. +# Created for TagStudio: https://github.com/CyanVoxel/TagStudio + from pathlib import Path from tempfile import TemporaryDirectory import pytest from tagstudio.core.enums import LibraryPrefs +from tagstudio.core.library.alchemy.library import Library from tagstudio.core.utils.refresh_dir import RefreshDirTracker CWD = Path(__file__).parent @@ -11,7 +16,8 @@ CWD = Path(__file__).parent @pytest.mark.parametrize("exclude_mode", [True, False]) @pytest.mark.parametrize("library", [TemporaryDirectory()], indirect=True) -def test_refresh_new_files(library, exclude_mode): +def test_refresh_new_files(library: Library, exclude_mode: bool): + assert library.library_dir # Given library.set_prefs(LibraryPrefs.IS_EXCLUDE_LIST, exclude_mode) library.set_prefs(LibraryPrefs.EXTENSION_LIST, [".md"]) diff --git a/tests/macros/test_sidecar.py b/tests/macros/test_sidecar.py deleted file mode 100644 index 61425787..00000000 --- a/tests/macros/test_sidecar.py +++ /dev/null @@ -1,37 +0,0 @@ -# import shutil -# from pathlib import Path -# from tempfile import TemporaryDirectory - -# import pytest -# from tagstudio.core.enums import MacroID -# from tagstudio.core.library.alchemy.fields import _FieldID - - -# @pytest.mark.parametrize("library", [TemporaryDirectory()], indirect=True) -def test_sidecar_macro(qt_driver, library, cwd, entry_full): - # TODO: Rework and finalize sidecar loading + macro systems. - pass - # entry_full.path = Path("newgrounds/foo.txt") - - # fixture = cwd / "fixtures/sidecar_newgrounds.json" - # dst = library.library_dir / "newgrounds" / (entry_full.path.name + ".json") - # dst.parent.mkdir() - # shutil.copy(fixture, dst) - - # qt_driver.frame_content = [entry_full] - # qt_driver.run_macro(MacroID.SIDECAR, entry_full.id) - - # entry = library.get_entry_full(entry_full.id) - # new_fields = ( - # (_FieldID.DESCRIPTION.name, "NG description"), - # (_FieldID.ARTIST.name, "NG artist"), - # (_FieldID.SOURCE.name, "https://ng.com"), - # ) - # found = [(field.type.key, field.value) for field in entry.fields] - - # # `new_fields` should be subset of `found` - # for field in new_fields: - # assert field in found, f"Field not found: {field} / {found}" - - # expected_tags = {"ng_tag", "ng_tag2"} - # assert {x.name in expected_tags for x in entry.tags} diff --git a/tests/qt/test_build_tag_panel.py b/tests/qt/test_build_tag_panel.py index 014843cb..5d5332d5 100644 --- a/tests/qt/test_build_tag_panel.py +++ b/tests/qt/test_build_tag_panel.py @@ -1,22 +1,37 @@ -from tagstudio.core.library.alchemy.models import Tag -from tagstudio.qt.modals.build_tag import BuildTagPanel +# Copyright (C) 2025 +# Licensed under the GPL-3.0 License. +# Created for TagStudio: https://github.com/CyanVoxel/TagStudio + + +from collections.abc import Callable + +from pytestqt.qtbot import QtBot + +from tagstudio.core.library.alchemy.library import Library +from tagstudio.core.library.alchemy.models import Tag, TagAlias +from tagstudio.qt.modals.build_tag import BuildTagPanel, CustomTableItem from tagstudio.qt.translations import Translations -def test_build_tag_panel_add_sub_tag_callback(library, generate_tag): +def test_build_tag_panel_add_sub_tag_callback( + qtbot: QtBot, library: Library, generate_tag: Callable[..., Tag] +): parent = library.add_tag(generate_tag("xxx", id=123)) child = library.add_tag(generate_tag("xx", id=124)) assert child assert parent panel: BuildTagPanel = BuildTagPanel(library, child) + qtbot.addWidget(panel) panel.add_parent_tag_callback(parent.id) assert len(panel.parent_ids) == 1 -def test_build_tag_panel_remove_subtag_callback(library, generate_tag): +def test_build_tag_panel_remove_subtag_callback( + qtbot: QtBot, library: Library, generate_tag: Callable[..., Tag] +): parent = library.add_tag(generate_tag("xxx", id=123)) child = library.add_tag(generate_tag("xx", id=124)) assert child @@ -29,6 +44,7 @@ def test_build_tag_panel_remove_subtag_callback(library, generate_tag): assert child panel: BuildTagPanel = BuildTagPanel(library, child) + qtbot.addWidget(panel) panel.remove_parent_tag_callback(parent.id) @@ -40,31 +56,39 @@ import os os.environ["QT_QPA_PLATFORM"] = "offscreen" -def test_build_tag_panel_add_alias_callback(library, generate_tag): +def test_build_tag_panel_add_alias_callback( + qtbot: QtBot, library: Library, generate_tag: Callable[..., Tag] +): tag = library.add_tag(generate_tag("xxx", id=123)) assert tag panel: BuildTagPanel = BuildTagPanel(library, tag) + qtbot.addWidget(panel) panel.add_alias_callback() assert panel.aliases_table.rowCount() == 1 -def test_build_tag_panel_remove_alias_callback(library, generate_tag): - tag = library.add_tag(generate_tag("xxx", id=123)) +def test_build_tag_panel_remove_alias_callback( + qtbot: QtBot, library: Library, generate_tag: Callable[..., Tag] +): + tag: Tag | None = library.add_tag(generate_tag("xxx", id=123)) assert tag library.update_tag(tag, [], {"alias", "alias_2"}, {123, 124}) tag = library.get_tag(tag.id) + assert tag assert "alias" in tag.alias_strings assert "alias_2" in tag.alias_strings panel: BuildTagPanel = BuildTagPanel(library, tag) + qtbot.addWidget(panel) - alias = library.get_alias(tag.id, tag.alias_ids[0]) + alias: TagAlias | None = library.get_alias(tag.id, tag.alias_ids[0]) + assert alias panel.remove_alias_callback(alias.name, alias.id) @@ -73,7 +97,9 @@ def test_build_tag_panel_remove_alias_callback(library, generate_tag): assert alias.name not in panel.alias_names -def test_build_tag_panel_set_parent_tags(library, generate_tag): +def test_build_tag_panel_set_parent_tags( + qtbot: QtBot, library: Library, generate_tag: Callable[..., Tag] +): parent = library.add_tag(generate_tag("parent", id=123)) child = library.add_tag(generate_tag("child", id=124)) assert parent @@ -84,30 +110,37 @@ def test_build_tag_panel_set_parent_tags(library, generate_tag): child = library.get_tag(child.id) panel: BuildTagPanel = BuildTagPanel(library, child) + qtbot.addWidget(panel) assert len(panel.parent_ids) == 1 assert panel.parent_tags_scroll_layout.count() == 1 -def test_build_tag_panel_add_aliases(library, generate_tag): - tag = library.add_tag(generate_tag("xxx", id=123)) +def test_build_tag_panel_add_aliases( + qtbot: QtBot, library: Library, generate_tag: Callable[..., Tag] +): + tag: Tag | None = library.add_tag(generate_tag("xxx", id=123)) assert tag library.update_tag(tag, [], {"alias", "alias_2"}, {123, 124}) tag = library.get_tag(tag.id) + assert tag assert "alias" in tag.alias_strings assert "alias_2" in tag.alias_strings panel: BuildTagPanel = BuildTagPanel(library, tag) + qtbot.addWidget(panel) widget = panel.aliases_table.cellWidget(0, 1) + assert isinstance(widget, CustomTableItem) alias_names: set[str] = set() alias_names.add(widget.text()) widget = panel.aliases_table.cellWidget(1, 1) + assert isinstance(widget, CustomTableItem) alias_names.add(widget.text()) assert "alias" in alias_names @@ -123,35 +156,41 @@ def test_build_tag_panel_add_aliases(library, generate_tag): assert len(panel.alias_names) == 2 -def test_build_tag_panel_set_aliases(library, generate_tag): - tag = library.add_tag(generate_tag("xxx", id=123)) +def test_build_tag_panel_set_aliases( + qtbot: QtBot, library: Library, generate_tag: Callable[..., Tag] +): + tag: Tag | None = library.add_tag(generate_tag("xxx", id=123)) assert tag library.update_tag(tag, [], {"alias"}, {123}) tag = library.get_tag(tag.id) + assert tag assert len(tag.alias_ids) == 1 panel: BuildTagPanel = BuildTagPanel(library, tag) + qtbot.addWidget(panel) assert panel.aliases_table.rowCount() == 1 assert len(panel.alias_names) == 1 assert len(panel.alias_ids) == 1 -def test_build_tag_panel_set_tag(library, generate_tag): +def test_build_tag_panel_set_tag(qtbot: QtBot, library: Library, generate_tag: Callable[..., Tag]): tag = library.add_tag(generate_tag("xxx", id=123)) assert tag panel: BuildTagPanel = BuildTagPanel(library, tag) + qtbot.addWidget(panel) assert panel.tag assert panel.tag.name == "xxx" -def test_build_tag_panel_build_tag(library): +def test_build_tag_panel_build_tag(qtbot: QtBot, library: Library): panel: BuildTagPanel = BuildTagPanel(library) + qtbot.addWidget(panel) tag: Tag = panel.build_tag() diff --git a/tests/qt/test_field_containers.py b/tests/qt/test_field_containers.py index 61df7365..8ba3d3f3 100644 --- a/tests/qt/test_field_containers.py +++ b/tests/qt/test_field_containers.py @@ -1,7 +1,15 @@ +# Copyright (C) 2025 +# Licensed under the GPL-3.0 License. +# Created for TagStudio: https://github.com/CyanVoxel/TagStudio + + +from tagstudio.core.library.alchemy.library import Library +from tagstudio.core.library.alchemy.models import Entry, Tag from tagstudio.qt.controller.widgets.preview_panel_controller import PreviewPanel +from tagstudio.qt.ts_qt import QtDriver -def test_update_selection_empty(qt_driver, library): +def test_update_selection_empty(qt_driver: QtDriver, library: Library): panel = PreviewPanel(library, qt_driver) # Clear the library selection (selecting 1 then unselecting 1) @@ -14,7 +22,7 @@ def test_update_selection_empty(qt_driver, library): assert container.isHidden() -def test_update_selection_single(qt_driver, library, entry_full): +def test_update_selection_single(qt_driver: QtDriver, library: Library, entry_full: Entry): panel = PreviewPanel(library, qt_driver) # Select the single entry @@ -26,7 +34,7 @@ def test_update_selection_single(qt_driver, library, entry_full): assert not container.isHidden() -def test_update_selection_multiple(qt_driver, library): +def test_update_selection_multiple(qt_driver: QtDriver, library: Library): # TODO: Implement mixed field editing. Currently these containers will be hidden, # same as the empty selection behavior. panel = PreviewPanel(library, qt_driver) @@ -41,7 +49,7 @@ def test_update_selection_multiple(qt_driver, library): assert container.isHidden() -def test_add_tag_to_selection_single(qt_driver, library, entry_full): +def test_add_tag_to_selection_single(qt_driver: QtDriver, library: Library, entry_full: Entry): panel = PreviewPanel(library, qt_driver) assert {t.id for t in entry_full.tags} == {1000} @@ -54,11 +62,11 @@ def test_add_tag_to_selection_single(qt_driver, library, entry_full): panel.field_containers_widget.add_tags_to_selected(2000) # Then reload entry - refreshed_entry = next(library.all_entries(with_joins=True)) + refreshed_entry: Entry = next(library.all_entries(with_joins=True)) assert {t.id for t in refreshed_entry.tags} == {1000, 2000} -def test_add_same_tag_to_selection_single(qt_driver, library, entry_full): +def test_add_same_tag_to_selection_single(qt_driver: QtDriver, library: Library, entry_full: Entry): panel = PreviewPanel(library, qt_driver) assert {t.id for t in entry_full.tags} == {1000} @@ -75,7 +83,7 @@ def test_add_same_tag_to_selection_single(qt_driver, library, entry_full): assert {t.id for t in refreshed_entry.tags} == {1000} -def test_add_tag_to_selection_multiple(qt_driver, library): +def test_add_tag_to_selection_multiple(qt_driver: QtDriver, library: Library): panel = PreviewPanel(library, qt_driver) all_entries = library.all_entries(with_joins=True) @@ -102,8 +110,8 @@ def test_add_tag_to_selection_multiple(qt_driver, library): # Then reload all entries and recheck the presence of tag 1000 refreshed_entries = library.all_entries(with_joins=True) - tag_present_on_some: bool = False - tag_absent_on_some: bool = False + tag_present_on_some = False + tag_absent_on_some = False for e in refreshed_entries: if 1000 in [t.id for t in e.tags]: @@ -115,7 +123,7 @@ def test_add_tag_to_selection_multiple(qt_driver, library): assert not tag_absent_on_some -def test_meta_tag_category(qt_driver, library, entry_full): +def test_meta_tag_category(qt_driver: QtDriver, library: Library, entry_full: Entry): panel = PreviewPanel(library, qt_driver) # Ensure the Favorite tag is on entry_full @@ -131,20 +139,25 @@ def test_meta_tag_category(qt_driver, library, entry_full): match i: case 0: # Check if the container is the Meta Tags category - assert container.title == f"

{library.get_tag(2).name}

" + tag: Tag | None = library.get_tag(2) + assert tag + assert container.title == f"

{tag.name}

" case 1: # Check if the container is the Tags category assert container.title == "

Tags

" case 2: # Make sure the container isn't a duplicate Tags category assert container.title != "

Tags

" + case _: + pass -def test_custom_tag_category(qt_driver, library, entry_full): +def test_custom_tag_category(qt_driver: QtDriver, library: Library, entry_full: Entry): panel = PreviewPanel(library, qt_driver) # Set tag 1000 (foo) as a category - tag = library.get_tag(1000) + tag: Tag | None = library.get_tag(1000) + assert tag tag.is_category = True library.update_tag( tag, @@ -163,10 +176,14 @@ def test_custom_tag_category(qt_driver, library, entry_full): match i: case 0: # Check if the container is the Meta Tags category - assert container.title == f"

{library.get_tag(2).name}

" + tag_2: Tag | None = library.get_tag(2) + assert tag_2 + assert container.title == f"

{tag_2.name}

" case 1: # Check if the container is the custom "foo" category assert container.title == f"

{tag.name}

" case 2: # Make sure the container isn't a plain Tags category assert container.title != "

Tags

" + case _: + pass diff --git a/tests/qt/test_file_path_options.py b/tests/qt/test_file_path_options.py index 095a4bf5..c7f4be9f 100644 --- a/tests/qt/test_file_path_options.py +++ b/tests/qt/test_file_path_options.py @@ -1,4 +1,10 @@ +# Copyright (C) 2025 +# Licensed under the GPL-3.0 License. +# Created for TagStudio: https://github.com/CyanVoxel/TagStudio + + import os +from collections.abc import Callable from pathlib import Path from unittest.mock import patch @@ -53,7 +59,10 @@ def test_filepath_setting(qtbot: QtBot, qt_driver: QtDriver, filepath_option: Sh ], ) def test_file_path_display( - qt_driver: QtDriver, library: Library, filepath_option: ShowFilepathOption, expected_path + qt_driver: QtDriver, + library: Library, + filepath_option: ShowFilepathOption, + expected_path: Callable[[Library], Path], ): panel = PreviewPanel(library, qt_driver) @@ -68,12 +77,12 @@ def test_file_path_display( assert isinstance(entry, Entry) filename = entry.path assert library.library_dir is not None - panel._file_attributes_widget.update_stats(filepath=library.library_dir / filename) + panel._file_attributes_widget.update_stats(filepath=library.library_dir / filename) # pyright: ignore[reportPrivateUsage] # Generate the expected file string. # This is copied directly from the file_attributes.py file # can be imported as a function in the future - display_path = expected_path(library) + display_path: Path = expected_path(library) file_str: str = "" separator: str = f"{os.path.sep}" # Gray for i, part in enumerate(display_path.parts): @@ -86,7 +95,7 @@ def test_file_path_display( file_str += f"{'\u200b'.join(part_)}" # Assert the file path is displayed correctly - assert panel._file_attributes_widget.file_label.text() == file_str + assert panel._file_attributes_widget.file_label.text() == file_str # pyright: ignore[reportPrivateUsage] @pytest.mark.parametrize( @@ -107,7 +116,10 @@ def test_file_path_display( ], ) def test_title_update( - qt_driver: QtDriver, filepath_option: ShowFilepathOption, expected_title, library_dir: Path + qt_driver: QtDriver, + filepath_option: ShowFilepathOption, + expected_title: Callable[[Path, str], str], + library_dir: Path, ): base_title = qt_driver.base_title @@ -135,7 +147,7 @@ def test_title_update( qt_driver.main_window.menu_bar.folders_to_tags_action = QAction(menu_bar) # Trigger the update - qt_driver._init_library(library_dir, open_status) + qt_driver._init_library(library_dir, open_status) # pyright: ignore[reportPrivateUsage] # Assert the title is updated correctly - qt_driver.main_window.setWindowTitle.assert_called_with(expected_title(library_dir, base_title)) + qt_driver.main_window.setWindowTitle.assert_called_with(expected_title(library_dir, base_title)) # pyright: ignore[reportAttributeAccessIssue] diff --git a/tests/qt/test_flow_widget.py b/tests/qt/test_flow_widget.py index a03f2116..74cec926 100644 --- a/tests/qt/test_flow_widget.py +++ b/tests/qt/test_flow_widget.py @@ -1,10 +1,15 @@ +# Copyright (C) 2025 +# Licensed under the GPL-3.0 License. +# Created for TagStudio: https://github.com/CyanVoxel/TagStudio + + from PySide6.QtCore import QRect from PySide6.QtWidgets import QPushButton, QWidget from tagstudio.qt.flowlayout import FlowLayout -def test_flow_layout_happy_path(qtbot): +def test_flow_layout_happy_path(): class Window(QWidget): def __init__(self): super().__init__() @@ -15,4 +20,4 @@ def test_flow_layout_happy_path(qtbot): window = Window() assert window.flow_layout.count() - assert window.flow_layout._do_layout(QRect(0, 0, 0, 0), test_only=False) + assert window.flow_layout._do_layout(QRect(0, 0, 0, 0), test_only=False) # pyright: ignore[reportPrivateUsage] diff --git a/tests/qt/test_folders_to_tags.py b/tests/qt/test_folders_to_tags.py index 5e47a383..7a7637e2 100644 --- a/tests/qt/test_folders_to_tags.py +++ b/tests/qt/test_folders_to_tags.py @@ -1,7 +1,13 @@ -from tagstudio.qt.modals.folders_to_tags import generate_preview_data +# Copyright (C) 2025 +# Licensed under the GPL-3.0 License. +# Created for TagStudio: https://github.com/CyanVoxel/TagStudio -def test_generate_preview_data(library, snapshot): +from tagstudio.core.library.alchemy.library import Library +from tagstudio.qt.modals.folders_to_tags import BranchData, generate_preview_data + + +def test_generate_preview_data(library: Library, snapshot: BranchData): preview = generate_preview_data(library) assert preview == snapshot diff --git a/tests/qt/test_global_settings.py b/tests/qt/test_global_settings.py index 8325c154..27a64e09 100644 --- a/tests/qt/test_global_settings.py +++ b/tests/qt/test_global_settings.py @@ -1,3 +1,8 @@ +# Copyright (C) 2025 +# Licensed under the GPL-3.0 License. +# Created for TagStudio: https://github.com/CyanVoxel/TagStudio + + from pathlib import Path from tagstudio.core.global_settings import GlobalSettings, Theme diff --git a/tests/qt/test_item_thumb.py b/tests/qt/test_item_thumb.py index 07884087..5f170e28 100644 --- a/tests/qt/test_item_thumb.py +++ b/tests/qt/test_item_thumb.py @@ -1,12 +1,20 @@ +# Copyright (C) 2025 +# Licensed under the GPL-3.0 License. +# Created for TagStudio: https://github.com/CyanVoxel/TagStudio + + import pytest from tagstudio.core.library.alchemy.enums import ItemType +from tagstudio.qt.ts_qt import QtDriver from tagstudio.qt.widgets.item_thumb import BadgeType, ItemThumb @pytest.mark.parametrize("new_value", (True, False)) -def test_badge_visual_state(library, qt_driver, entry_min, new_value): - thumb = ItemThumb(ItemType.ENTRY, qt_driver.lib, qt_driver, (100, 100), 0) +def test_badge_visual_state(qt_driver: QtDriver, entry_min: int, new_value: bool): + thumb = ItemThumb( + ItemType.ENTRY, qt_driver.lib, qt_driver, (100, 100), show_filename_label=False + ) qt_driver.frame_content = [entry_min] qt_driver.selected = [0] diff --git a/tests/qt/test_preview_panel.py b/tests/qt/test_preview_panel.py index ab8a7845..38c0bf2b 100644 --- a/tests/qt/test_preview_panel.py +++ b/tests/qt/test_preview_panel.py @@ -1,7 +1,15 @@ +# Copyright (C) 2025 +# Licensed under the GPL-3.0 License. +# Created for TagStudio: https://github.com/CyanVoxel/TagStudio + + +from tagstudio.core.library.alchemy.library import Library +from tagstudio.core.library.alchemy.models import Entry from tagstudio.qt.controller.widgets.preview_panel_controller import PreviewPanel +from tagstudio.qt.ts_qt import QtDriver -def test_update_selection_empty(qt_driver, library): +def test_update_selection_empty(qt_driver: QtDriver, library: Library): panel = PreviewPanel(library, qt_driver) # Clear the library selection (selecting 1 then unselecting 1) @@ -13,7 +21,7 @@ def test_update_selection_empty(qt_driver, library): assert not panel.add_buttons_enabled -def test_update_selection_single(qt_driver, library, entry_full): +def test_update_selection_single(qt_driver: QtDriver, library: Library, entry_full: Entry): panel = PreviewPanel(library, qt_driver) # Select the single entry @@ -24,7 +32,7 @@ def test_update_selection_single(qt_driver, library, entry_full): assert panel.add_buttons_enabled -def test_update_selection_multiple(qt_driver, library): +def test_update_selection_multiple(qt_driver: QtDriver, library: Library): panel = PreviewPanel(library, qt_driver) # Select the multiple entries diff --git a/tests/qt/test_qt_driver.py b/tests/qt/test_qt_driver.py index 867e0c81..43106dda 100644 --- a/tests/qt/test_qt_driver.py +++ b/tests/qt/test_qt_driver.py @@ -1,77 +1,19 @@ -from typing import TYPE_CHECKING +# Copyright (C) 2025 +# Licensed under the GPL-3.0 License. +# Created for TagStudio: https://github.com/CyanVoxel/TagStudio -from tagstudio.core.library.alchemy.enums import BrowsingState -from tagstudio.core.library.json.library import ItemType + +from tagstudio.core.library.alchemy.enums import BrowsingState, ItemType +from tagstudio.qt.ts_qt import QtDriver from tagstudio.qt.widgets.item_thumb import ItemThumb -if TYPE_CHECKING: - from tagstudio.qt.ts_qt import QtDriver -# def test_update_thumbs(qt_driver): -# qt_driver.frame_content = [ -# Entry( -# folder=qt_driver.lib.folder, -# path=Path("/tmp/foo"), -# fields=qt_driver.lib.default_fields, -# ) -# ] - -# qt_driver.item_thumbs = [] -# for _ in range(3): -# qt_driver.item_thumbs.append( -# ItemThumb( -# mode=ItemType.ENTRY, -# library=qt_driver.lib, -# driver=qt_driver, -# thumb_size=(100, 100), -# ) -# ) - -# qt_driver.update_thumbs() - -# for idx, thumb in enumerate(qt_driver.item_thumbs): -# # only first item is visible -# assert thumb.isVisible() == (idx == 0) - - -# def test_toggle_item_selection_bridge(qt_driver, entry_min): -# # mock some props since we're not running `start()` -# qt_driver.autofill_action = Mock() -# qt_driver.sort_fields_action = Mock() - -# # set the content manually -# qt_driver.frame_content = [entry_min] * 3 - -# qt_driver.filter.page_size = 3 -# qt_driver._init_thumb_grid() -# assert len(qt_driver.item_thumbs) == 3 - -# # select first item -# qt_driver.toggle_item_selection(0, append=False, bridge=False) -# assert qt_driver.selected == [0] - -# # add second item to selection -# qt_driver.toggle_item_selection(1, append=False, bridge=True) -# assert qt_driver.selected == [0, 1] - -# # add third item to selection -# qt_driver.toggle_item_selection(2, append=False, bridge=True) -# assert qt_driver.selected == [0, 1, 2] - -# # select third item only -# qt_driver.toggle_item_selection(2, append=False, bridge=False) -# assert qt_driver.selected == [2] - -# qt_driver.toggle_item_selection(0, append=False, bridge=True) -# assert qt_driver.selected == [0, 1, 2] - - -def test_browsing_state_update(qt_driver: "QtDriver"): +def test_browsing_state_update(qt_driver: QtDriver): # Given for entry in qt_driver.lib.all_entries(with_joins=True): thumb = ItemThumb(ItemType.ENTRY, qt_driver.lib, qt_driver, (100, 100)) qt_driver.item_thumbs.append(thumb) - qt_driver.frame_content.append(entry) + qt_driver.frame_content.append(entry.id) # no filter, both items are returned qt_driver.update_browsing_state() @@ -82,12 +24,14 @@ def test_browsing_state_update(qt_driver: "QtDriver"): qt_driver.update_browsing_state(state) assert len(qt_driver.frame_content) == 1 entry = qt_driver.lib.get_entry_full(qt_driver.frame_content[0]) + assert entry assert list(entry.tags)[0].name == "foo" # When state is not changed, previous one is still applied qt_driver.update_browsing_state() assert len(qt_driver.frame_content) == 1 entry = qt_driver.lib.get_entry_full(qt_driver.frame_content[0]) + assert entry assert list(entry.tags)[0].name == "foo" # When state property is changed, previous one is overwritten @@ -95,10 +39,11 @@ def test_browsing_state_update(qt_driver: "QtDriver"): qt_driver.update_browsing_state(state) assert len(qt_driver.frame_content) == 1 entry = qt_driver.lib.get_entry_full(qt_driver.frame_content[0]) + assert entry assert list(entry.tags)[0].name == "bar" -def test_close_library(qt_driver): +def test_close_library(qt_driver: QtDriver): # Given qt_driver.close_library() diff --git a/tests/qt/test_tag_panel.py b/tests/qt/test_tag_panel.py index cfd5af1f..6b79fb44 100644 --- a/tests/qt/test_tag_panel.py +++ b/tests/qt/test_tag_panel.py @@ -1,24 +1,34 @@ +# Copyright (C) 2025 +# Licensed under the GPL-3.0 License. +# Created for TagStudio: https://github.com/CyanVoxel/TagStudio + + +from pytestqt.qtbot import QtBot + +from tagstudio.core.library.alchemy.library import Library from tagstudio.core.library.alchemy.models import Tag from tagstudio.qt.modals.build_tag import BuildTagPanel +from tagstudio.qt.ts_qt import QtDriver -def test_tag_panel(qtbot, library): +def test_tag_panel(qtbot: QtBot, library: Library): panel = BuildTagPanel(library) qtbot.addWidget(panel) -def test_add_tag_callback(qt_driver): +def test_add_tag_callback(qt_driver: QtDriver): # Given assert len(qt_driver.lib.tags) == 6 qt_driver.add_tag_action_callback() # When + assert isinstance(qt_driver.modal.widget, BuildTagPanel) qt_driver.modal.widget.name_field.setText("xxx") # qt_driver.modal.widget.color_field.setCurrentIndex(1) qt_driver.modal.saved.emit() # Then - tags: set[Tag] = qt_driver.lib.tags + tags: list[Tag] = qt_driver.lib.tags assert len(tags) == 7 assert "xxx" in {tag.name for tag in tags} diff --git a/tests/qt/test_tag_search_panel.py b/tests/qt/test_tag_search_panel.py index 01150de2..6bfe415b 100644 --- a/tests/qt/test_tag_search_panel.py +++ b/tests/qt/test_tag_search_panel.py @@ -1,7 +1,15 @@ +# Copyright (C) 2025 +# Licensed under the GPL-3.0 License. +# Created for TagStudio: https://github.com/CyanVoxel/TagStudio + + +from pytestqt.qtbot import QtBot + +from tagstudio.core.library.alchemy.library import Library from tagstudio.qt.modals.tag_search import TagSearchPanel -def test_update_tags(qtbot, library): +def test_update_tags(qtbot: QtBot, library: Library): # Given panel = TagSearchPanel(library) diff --git a/tests/test_driver.py b/tests/test_driver.py index b5251021..620f9e0a 100644 --- a/tests/test_driver.py +++ b/tests/test_driver.py @@ -1,3 +1,8 @@ +# Copyright (C) 2025 +# Licensed under the GPL-3.0 License. +# Created for TagStudio: https://github.com/CyanVoxel/TagStudio + + from pathlib import Path from PySide6.QtCore import QSettings diff --git a/tests/test_json_migration.py b/tests/test_json_migration.py index a4040457..c96e2809 100644 --- a/tests/test_json_migration.py +++ b/tests/test_json_migration.py @@ -1,4 +1,4 @@ -# Copyright (C) 2024 Travis Abendshien (CyanVoxel). +# Copyright (C) 2025 Travis Abendshien (CyanVoxel). # Licensed under the GPL-3.0 License. # Created for TagStudio: https://github.com/CyanVoxel/TagStudio @@ -48,5 +48,5 @@ def test_json_migration(): # List Type assert modal.check_ext_type() # No Leading Dot - for ext in modal.sql_lib.prefs(LibraryPrefs.EXTENSION_LIST): + for ext in modal.sql_lib.prefs(LibraryPrefs.EXTENSION_LIST): # pyright: ignore[reportUnknownVariableType] assert ext[0] != "." diff --git a/tests/test_library.py b/tests/test_library.py index 8ef83cf2..f96d81d6 100644 --- a/tests/test_library.py +++ b/tests/test_library.py @@ -1,16 +1,28 @@ +# Copyright (C) 2025 +# Licensed under the GPL-3.0 License. +# Created for TagStudio: https://github.com/CyanVoxel/TagStudio + + +from collections.abc import Callable from pathlib import Path from tempfile import TemporaryDirectory import pytest +import structlog from tagstudio.core.enums import DefaultEnum, LibraryPrefs from tagstudio.core.library.alchemy.enums import BrowsingState -from tagstudio.core.library.alchemy.fields import TextField, _FieldID +from tagstudio.core.library.alchemy.fields import ( + TextField, + _FieldID, # pyright: ignore[reportPrivateUsage] +) from tagstudio.core.library.alchemy.library import Library from tagstudio.core.library.alchemy.models import Entry, Tag +logger = structlog.get_logger() -def test_library_add_alias(library: Library, generate_tag): + +def test_library_add_alias(library: Library, generate_tag: Callable[..., Tag]): tag = library.add_tag(generate_tag("xxx", id=123)) assert tag @@ -26,7 +38,7 @@ def test_library_add_alias(library: Library, generate_tag): assert len(alias_ids) == 1 -def test_library_get_alias(library: Library, generate_tag): +def test_library_get_alias(library: Library, generate_tag: Callable[..., Tag]): tag = library.add_tag(generate_tag("xxx", id=123)) assert tag @@ -44,7 +56,7 @@ def test_library_get_alias(library: Library, generate_tag): assert alias.name == "test_alias" -def test_library_update_alias(library: Library, generate_tag): +def test_library_update_alias(library: Library, generate_tag: Callable[..., Tag]): tag: Tag | None = library.add_tag(generate_tag("xxx", id=123)) assert tag is not None @@ -89,7 +101,7 @@ def test_library_add_file(library: Library): assert library.has_path_entry(entry.path) -def test_create_tag(library: Library, generate_tag): +def test_create_tag(library: Library, generate_tag: Callable[..., Tag]): # tag already exists assert not library.add_tag(generate_tag("foo", id=1000)) @@ -103,7 +115,7 @@ def test_create_tag(library: Library, generate_tag): assert tag_inc.id > 1000 -def test_tag_self_parent(library: Library, generate_tag): +def test_tag_self_parent(library: Library, generate_tag: Callable[..., Tag]): # tag already exists assert not library.add_tag(generate_tag("foo", id=1000)) @@ -118,7 +130,7 @@ def test_tag_self_parent(library: Library, generate_tag): assert len(tag.parent_ids) == 0 -def test_library_search(library: Library, generate_tag, entry_full): +def test_library_search(library: Library, entry_full: Entry): assert library.entries_count == 2 tag = list(entry_full.tags)[0] @@ -140,7 +152,7 @@ def test_tag_search(library: Library): assert library.search_tags(tag.name * 2) == [set(), set()] -def test_get_entry(library: Library, entry_min): +def test_get_entry(library: Library, entry_min: Entry): assert entry_min.id result = library.get_entry_full(entry_min.id) assert result @@ -159,12 +171,13 @@ def test_entries_count(library: Library): assert len(results) == 5 -def test_parents_add(library: Library, generate_tag): +def test_parents_add(library: Library, generate_tag: Callable[..., Tag]): # Given tag: Tag | None = library.tags[0] assert tag.id is not None - parent_tag = generate_tag("parent_tag_01") + parent_tag: Tag | None = generate_tag("parent_tag_01") + assert parent_tag parent_tag = library.add_tag(parent_tag) assert parent_tag is not None assert parent_tag.id is not None @@ -179,7 +192,7 @@ def test_parents_add(library: Library, generate_tag): assert tag.parent_ids -def test_remove_tag(library: Library, generate_tag): +def test_remove_tag(library: Library, generate_tag: Callable[..., Tag]): tag = library.add_tag(generate_tag("food", id=123)) assert tag @@ -237,7 +250,7 @@ def test_preferences(library: Library): assert library.prefs(pref) == pref.default -def test_remove_entry_field(library: Library, entry_full): +def test_remove_entry_field(library: Library, entry_full: Entry): title_field = entry_full.text_fields[0] library.remove_entry_field(title_field, [entry_full.id]) @@ -246,7 +259,7 @@ def test_remove_entry_field(library: Library, entry_full): assert not entry.text_fields -def test_remove_field_entry_with_multiple_field(library: Library, entry_full): +def test_remove_field_entry_with_multiple_field(library: Library, entry_full: Entry): # Given title_field = entry_full.text_fields[0] @@ -262,7 +275,7 @@ def test_remove_field_entry_with_multiple_field(library: Library, entry_full): assert len(entry.text_fields) == 1 -def test_update_entry_field(library: Library, entry_full): +def test_update_entry_field(library: Library, entry_full: Entry): title_field = entry_full.text_fields[0] library.update_entry_field( @@ -275,7 +288,7 @@ def test_update_entry_field(library: Library, entry_full): assert entry.text_fields[0].value == "new value" -def test_update_entry_with_multiple_identical_fields(library: Library, entry_full): +def test_update_entry_with_multiple_identical_fields(library: Library, entry_full: Entry): # Given title_field = entry_full.text_fields[0] @@ -296,7 +309,7 @@ def test_update_entry_with_multiple_identical_fields(library: Library, entry_ful assert entry.text_fields[1].value == "new value" -def test_mirror_entry_fields(library: Library, entry_full): +def test_mirror_entry_fields(library: Library, entry_full: Entry): # new entry assert library.folder is not None target_entry = Entry( @@ -335,6 +348,14 @@ def test_mirror_entry_fields(library: Library, entry_full): def test_merge_entries(library: Library): assert library.folder is not None + + tag_0: Tag | None = library.add_tag(Tag(id=1010, name="tag_0")) + assert tag_0 is not None + tag_1: Tag | None = library.add_tag(Tag(id=1011, name="tag_1")) + assert tag_1 is not None + tag_2: Tag | None = library.add_tag(Tag(id=1012, name="tag_2")) + assert tag_2 is not None + a = Entry( folder=library.folder, path=Path("a"), @@ -348,32 +369,34 @@ def test_merge_entries(library: Library): path=Path("b"), fields=[TextField(type_key=_FieldID.NOTES.name, value="test note", position=1)], ) - try: - ids = library.add_entries([a, b]) - entry_a = library.get_entry_full(ids[0]) - assert entry_a is not None - entry_b = library.get_entry_full(ids[1]) - assert entry_b is not None - tag_0 = library.add_tag(Tag(id=1000, name="tag_0")) - tag_1 = library.add_tag(Tag(id=1001, name="tag_1")) - assert tag_1 is not None - tag_2 = library.add_tag(Tag(id=1002, name="tag_2")) - assert tag_2 is not None - library.add_tags_to_entries(ids[0], [tag_0.id, tag_2.id]) - library.add_tags_to_entries(ids[1], [tag_1.id]) - library.merge_entries(entry_a, entry_b) - assert library.has_path_entry(Path("b")) - assert not library.has_path_entry(Path("a")) - fields = [field.value for field in entry_a.fields] - assert "Author McAuthorson" in fields - assert "test description" in fields - assert "test note" in fields - assert b.has_tag(tag_0) and b.has_tag(tag_1) and b.has_tag(tag_2) - except AttributeError: - AssertionError() + ids = library.add_entries([a, b]) + + library.add_tags_to_entries(ids[0], [tag_0.id, tag_2.id]) + library.add_tags_to_entries(ids[1], [tag_1.id]) + + entry_a: Entry | None = library.get_entry_full(ids[0]) + assert entry_a is not None + entry_b: Entry | None = library.get_entry_full(ids[1]) + assert entry_b is not None + + assert library.merge_entries(entry_a, entry_b) + assert not library.has_path_entry(Path("a")) + assert library.has_path_entry(Path("b")) + + entry_b_merged = library.get_entry_full(ids[1]) + assert entry_b_merged + + fields = [field.value for field in entry_b_merged.fields] + assert "Author McAuthorson" in fields + assert "test description" in fields + assert "test note" in fields + b_tags = [t.id for t in entry_b_merged.tags] + assert tag_0.id in b_tags + assert tag_1.id in b_tags + assert tag_2.id in b_tags -def test_remove_tags_from_entries(library: Library, entry_full): +def test_remove_tags_from_entries(library: Library, entry_full: Entry): removed_tag_id = -1 for tag in entry_full.tags: removed_tag_id = tag.id @@ -392,13 +415,13 @@ def test_remove_tags_from_entries(library: Library, entry_full): (222, 0), ], ) -def test_search_entry_id(library: Library, query_name: int, has_result): +def test_search_entry_id(library: Library, query_name: int, has_result: bool): result = library.get_entry(query_name) assert (result is not None) == has_result -def test_update_field_order(library: Library, entry_full): +def test_update_field_order(library: Library, entry_full: Entry): # Given title_field = entry_full.text_fields[0] @@ -526,13 +549,15 @@ def test_path_search_like_glob_equality(library: Library): @pytest.mark.parametrize(["filetype", "num_of_filetype"], [("md", 1), ("txt", 1), ("png", 0)]) -def test_filetype_search(library: Library, filetype, num_of_filetype): +def test_filetype_search(library: Library, filetype: str, num_of_filetype: int): results = library.search_library(BrowsingState.from_filetype(filetype), page_size=500) assert len(results.ids) == num_of_filetype @pytest.mark.parametrize(["filetype", "num_of_filetype"], [("png", 2), ("apng", 1), ("ng", 0)]) -def test_filetype_return_one_filetype(file_mediatypes_library: Library, filetype, num_of_filetype): +def test_filetype_return_one_filetype( + file_mediatypes_library: Library, filetype: str, num_of_filetype: int +): results = file_mediatypes_library.search_library( BrowsingState.from_filetype(filetype), page_size=500 ) @@ -540,6 +565,6 @@ def test_filetype_return_one_filetype(file_mediatypes_library: Library, filetype @pytest.mark.parametrize(["mediatype", "num_of_mediatype"], [("plaintext", 2), ("image", 0)]) -def test_mediatype_search(library: Library, mediatype, num_of_mediatype): +def test_mediatype_search(library: Library, mediatype: str, num_of_mediatype: int): results = library.search_library(BrowsingState.from_mediatype(mediatype), page_size=500) assert len(results.ids) == num_of_mediatype diff --git a/tests/test_search.py b/tests/test_search.py index 39c85b45..e85e19b8 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -1,3 +1,8 @@ +# Copyright (C) 2025 +# Licensed under the GPL-3.0 License. +# Created for TagStudio: https://github.com/CyanVoxel/TagStudio + + import pytest import structlog @@ -139,5 +144,5 @@ def test_parent_tags(search_library: Library, query: str, count: int): "invalid_query", ["asd AND", "asd AND AND", "tag:(", "(asd", "asd[]", "asd]", ":", "tag: :"] ) def test_syntax(search_library: Library, invalid_query: str): - with pytest.raises(ParsingError) as e_info: # noqa: F841 + with pytest.raises(ParsingError) as e_info: # noqa: F841 # pyright: ignore[reportUnusedVariable] search_library.search_library(BrowsingState.from_search_query(invalid_query), page_size=500) diff --git a/tests/test_translations.py b/tests/test_translations.py index 1814a950..f01be2c6 100644 --- a/tests/test_translations.py +++ b/tests/test_translations.py @@ -1,3 +1,8 @@ +# Copyright (C) 2025 +# Licensed under the GPL-3.0 License. +# Created for TagStudio: https://github.com/CyanVoxel/TagStudio + + import string from pathlib import Path