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