diff --git a/CHANGELOG.md b/CHANGELOG.md index 854a71b..d0fc62e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ patch-level version changes can be found in [commit messages](../../commits/mast - use ruff - code quality improvements - fix 'Can't download packs with ":" (Colon) in it' - https://github.com/FHPythonUtils/SigStickers/issues/3 +- 98% test coverage ## 2024 - 2024/01/07 diff --git a/documentation/reference/sigstickers/caching.md b/documentation/reference/sigstickers/caching.md index 9aa1203..6fd77ac 100644 --- a/documentation/reference/sigstickers/caching.md +++ b/documentation/reference/sigstickers/caching.md @@ -25,12 +25,12 @@ Get the appropriate cache verification function based on version. #### Returns ------- - - `callable` - Cache verification function + Callable[[dict[str, Any]], bool]: Cache verification function #### Signature ```python -def _get_verify_function(version: int): ... +def _get_verify_function(version: int) -> Callable[[dict[str, Any]], bool]: ... ``` diff --git a/documentation/reference/sigstickers/cli.md b/documentation/reference/sigstickers/cli.md index e78927c..4e89c03 100644 --- a/documentation/reference/sigstickers/cli.md +++ b/documentation/reference/sigstickers/cli.md @@ -10,7 +10,7 @@ ## cli -[Show source in cli.py:12](../../../sigstickers/cli.py#L12) +[Show source in cli.py:17](../../../sigstickers/cli.py#L17) CLI entry point. @@ -24,12 +24,16 @@ def cli() -> None: ... ## main -[Show source in cli.py:35](../../../sigstickers/cli.py#L35) +[Show source in cli.py:40](../../../sigstickers/cli.py#L40) -Main function to download sticker packs. +Download, and convert sticker packs. #### Signature ```python -def main(packs): ... -``` \ No newline at end of file +def main(packs: list[str], cwd: Path = DEFAULT_CWD) -> int: ... +``` + +#### See also + +- [DEFAULT_CWD](./downloader.md#default_cwd) \ No newline at end of file diff --git a/documentation/reference/sigstickers/downloader.md b/documentation/reference/sigstickers/downloader.md index 31c7498..d4a12ad 100644 --- a/documentation/reference/sigstickers/downloader.md +++ b/documentation/reference/sigstickers/downloader.md @@ -9,12 +9,11 @@ - [convert_pack](#convert_pack) - [convert_with_pil](#convert_with_pil) - [download_pack](#download_pack) - - [files_to_str](#files_to_str) - [save_sticker](#save_sticker) ## assure_dir_exists -[Show source in downloader.py:21](../../../sigstickers/downloader.py#L21) +[Show source in downloader.py:22](../../../sigstickers/downloader.py#L22) Make the directory if it does not exist. @@ -38,7 +37,7 @@ def assure_dir_exists(*parts: Path | str) -> Path: ... ## convert_pack -[Show source in downloader.py:141](../../../sigstickers/downloader.py#L141) +[Show source in downloader.py:139](../../../sigstickers/downloader.py#L139) Convert the webp images into png and gif images. @@ -52,14 +51,14 @@ Convert the webp images into png and gif images. #### Signature ```python -async def convert_pack(swd: Path, pack_name: Path, no_cache=False): ... +async def convert_pack(swd: Path, pack_name: Path, no_cache: bool = False) -> None: ... ``` ## convert_with_pil -[Show source in downloader.py:107](../../../sigstickers/downloader.py#L107) +[Show source in downloader.py:105](../../../sigstickers/downloader.py#L105) Convert the webp file to png. @@ -83,7 +82,7 @@ def convert_with_pil(input_path: Path) -> list[str]: ... ## download_pack -[Show source in downloader.py:67](../../../sigstickers/downloader.py#L67) +[Show source in downloader.py:65](../../../sigstickers/downloader.py#L65) Download a sticker pack. @@ -104,27 +103,19 @@ Download a sticker pack. ```python async def download_pack( - pack_id: str, pack_key: str, cwd: Path = Path.cwd() + pack_id: str, pack_key: str, cwd: Path = DEFAULT_CWD ) -> tuple[Path, Path]: ... ``` +#### See also - -## files_to_str - -[Show source in downloader.py:186](../../../sigstickers/downloader.py#L186) - -#### Signature - -```python -def files_to_str(files: list[Path]) -> list[str]: ... -``` +- [DEFAULT_CWD](#default_cwd) ## save_sticker -[Show source in downloader.py:38](../../../sigstickers/downloader.py#L38) +[Show source in downloader.py:39](../../../sigstickers/downloader.py#L39) Save a sticker. diff --git a/pyproject.toml b/pyproject.toml index 436c450..a223886 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "sigstickers" -version = "2024" +version = "2024.1" license = "mit" description = "Download sticker packs from Signal" authors = ["FredHappyface"] @@ -37,8 +37,6 @@ handsdown = "^2.1.0" coverage = "^7.4.4" pytest-asyncio = "^0.23.5.post1" - - [tool.ruff] line-length = 100 indent-width = 4 @@ -72,6 +70,7 @@ line-ending = "lf" [tool.pyright] venvPath = "." venv = ".venv" + [tool.tox] legacy_tox_ini = """ [tox] diff --git a/sigstickers/caching.py b/sigstickers/caching.py index b823f08..99c37bb 100644 --- a/sigstickers/caching.py +++ b/sigstickers/caching.py @@ -4,7 +4,7 @@ import json from pathlib import Path -from typing import Any +from typing import Any, Callable from loguru import logger @@ -93,7 +93,7 @@ def create_converted(pack_name: Path, data: dict) -> None: cache.write_text(json.dumps(data), encoding="utf-8") -def _get_verify_function(version: int): +def _get_verify_function(version: int) -> Callable[[dict[str, Any]], bool]: """Get the appropriate cache verification function based on version. Args: @@ -102,7 +102,7 @@ def _get_verify_function(version: int): Returns: ------- - callable: Cache verification function + Callable[[dict[str, Any]], bool]: Cache verification function """ return { diff --git a/sigstickers/cli.py b/sigstickers/cli.py index 1678d9e..9d80634 100644 --- a/sigstickers/cli.py +++ b/sigstickers/cli.py @@ -5,15 +5,16 @@ import asyncio import functools import operator +from pathlib import Path from sys import exit as sysexit from urllib import parse from loguru import logger -from sigstickers.downloader import convert_pack, download_pack +from sigstickers.downloader import DEFAULT_CWD, convert_pack, download_pack -def cli() -> None: +def cli() -> None: # pragma: no cover """CLI entry point.""" parser = argparse.ArgumentParser("Welcome to SigSticker, providing all of your sticker needs") parser.add_argument( @@ -33,11 +34,11 @@ def cli() -> None: if not name: break packs.append(name) - return main(packs) + sysexit(main(packs)) -def main(packs) -> None: - """Main function to download sticker packs.""" +def main(packs: list[str], cwd: Path = DEFAULT_CWD) -> int: + """Download, and convert sticker packs.""" for pack in packs: pack_attrs: dict[str, list[str]] = parse.parse_qs(parse.urlparse(pack).fragment) if {"pack_id", "pack_key"} > pack_attrs.keys(): @@ -46,10 +47,11 @@ def main(packs) -> None: "addstickers/#pack_id=9acc9e8aba563d26a4994e69263e3b25&" "pack_key=5a6dff3948c28efb9b7aaf93ecc375c69fc316e78077ed26867a14d10a0f6a12" ) - sysexit(1) + return 1 downloaded = asyncio.run( - download_pack("".join(pack_attrs["pack_id"]), "".join(pack_attrs["pack_key"])) + download_pack("".join(pack_attrs["pack_id"]), "".join(pack_attrs["pack_key"]), cwd=cwd) ) asyncio.run(convert_pack(*downloaded)) + return 0 diff --git a/sigstickers/downloader.py b/sigstickers/downloader.py index fd49067..9045946 100644 --- a/sigstickers/downloader.py +++ b/sigstickers/downloader.py @@ -16,6 +16,7 @@ from sigstickers.caching import create_converted, verify_converted UNKNOWN = "🤷‍♂️" +DEFAULT_CWD = Path.cwd() def assure_dir_exists(*parts: Path | str) -> Path: @@ -61,7 +62,7 @@ def _sanitize_filename(filename: str) -> str: return unicodedata.normalize("NFKD", sanitized_filename).encode("ascii", "ignore").decode() -async def download_pack(pack_id: str, pack_key: str, cwd: Path = Path.cwd()) -> tuple[Path, Path]: +async def download_pack(pack_id: str, pack_key: str, cwd: Path = DEFAULT_CWD) -> tuple[Path, Path]: """Download a sticker pack. Args: @@ -135,7 +136,7 @@ def convert_with_pil(input_path: Path) -> list[str]: return [png_file, gif_file] -async def convert_pack(swd: Path, pack_name: Path, *, no_cache=False) -> None: +async def convert_pack(swd: Path, pack_name: Path, *, no_cache: bool = False) -> None: """Convert the webp images into png and gif images. Args: @@ -166,7 +167,8 @@ async def convert_pack(swd: Path, pack_name: Path, *, no_cache=False) -> None: converted_files.append(converted.result()) end = time.time() logger.info( - f"Time taken to convert {len(converted_files)}/{len(webp_files)} stickers - {end - start:.3f}s" + f"Time taken to convert {len(converted_files)}/{len(webp_files)} " + f"stickers - {end - start:.3f}s" ) logger.info("") @@ -175,10 +177,10 @@ async def convert_pack(swd: Path, pack_name: Path, *, no_cache=False) -> None: data={ "version": 2, "converted_files": converted_files, - "webp_files": files_to_str(webp_files), + "webp_files": _files_to_str(webp_files), }, ) -def files_to_str(files: list[Path]) -> list[str]: +def _files_to_str(files: list[Path]) -> list[str]: return [str(x.absolute()) for x in files] diff --git a/tests/test_caching.py b/tests/test_caching.py index f4ca498..f8dca47 100644 --- a/tests/test_caching.py +++ b/tests/test_caching.py @@ -10,6 +10,8 @@ file_exists = "pyproject.toml" file_exists_2 = ".gitignore" +file_not_exists = "file/not/exists" + def test_verify_converted_v1() -> None: pack_name = "Test_Pack_v1" @@ -23,6 +25,18 @@ def test_verify_converted_v1() -> None: assert verify_converted(Path(pack_name)) +def test_verify_converted_v1_not_exists() -> None: + pack_name = "Test_Pack_v1_not_exists" + cache_file = CACHE_DIR / pack_name + cache_data = { + "info": {"swd": file_not_exists}, + "converted": {"converted": 10, "total": 10}, + } + cache_file.write_text(json.dumps(cache_data)) + + assert not verify_converted(Path(pack_name)) + + def test_verify_converted_v2() -> None: pack_name = "Test_Pack_v2" cache_file = CACHE_DIR / pack_name @@ -36,6 +50,32 @@ def test_verify_converted_v2() -> None: assert verify_converted(Path(pack_name)) +def test_verify_converted_v2_partial_convert() -> None: + pack_name = "Test_Pack_v2_partial_convert" + cache_file = CACHE_DIR / pack_name + cache_data = { + "version": 2, + "webp_files": [file_exists, file_exists_2], + "converted_files": [[file_exists]], + } + cache_file.write_text(json.dumps(cache_data)) + + assert not verify_converted(Path(pack_name)) + + +def test_verify_converted_v2_not_exists() -> None: + pack_name = "Test_Pack_v2_not_exists" + cache_file = CACHE_DIR / pack_name + cache_data = { + "version": 2, + "webp_files": [file_exists, file_not_exists], + "converted_files": [[file_exists], [file_exists_2]], + } + cache_file.write_text(json.dumps(cache_data)) + + assert not verify_converted(Path(pack_name)) + + def test_create_converted() -> None: pack_name = "Test_Pack" cache_data = {"example_key": "example_value"} diff --git a/tests/test_cli.py b/tests/test_cli.py new file mode 100644 index 0000000..62cfec3 --- /dev/null +++ b/tests/test_cli.py @@ -0,0 +1,22 @@ +from __future__ import annotations + +import asyncio +import sys +from pathlib import Path + +THISDIR = Path(__file__).resolve().parent +PROJECT_DIR = THISDIR.parent +sys.path.insert(0, str(PROJECT_DIR)) + +from sigstickers.cli import main + +cwd = THISDIR / "data" + + +def test_cli() -> None: + assert main(["https://signal.art/addstickers/#pack_id=b676ec334ee2f771cadff5d095971e8c&pack_key=c957a57000626a2dc3cb69bf0e79c91c6b196b74d4d6ca1cbb830d3ad0ad4e36"], cwd=cwd,) == 0 + assert len(list(Path(f"{cwd}/downloads/DonutTheDog/png").iterdir())) == 28 + + +def test_cli_invalid_url() -> None: + assert main(["https://signal.art/addstickers/#pack_key=c957a57000626a2dc3cb69bf0e79c91c6b196b74d4d6ca1cbb830d3ad0ad4e36"], cwd=cwd,) == 1 diff --git a/tests/test_downloader.py b/tests/test_downloader.py index 2629bd2..c705635 100644 --- a/tests/test_downloader.py +++ b/tests/test_downloader.py @@ -1,5 +1,6 @@ import shutil from pathlib import Path +from typing import Generator import pytest @@ -7,14 +8,14 @@ @pytest.fixture() -def test_data(): +def test_data() -> Generator[Path, None, None]: test_dir = Path("test_data") yield test_dir shutil.rmtree(test_dir) @pytest.mark.asyncio() -async def test_download_pack(test_data) -> None: +async def test_download_pack(test_data: Path) -> None: pack_id = "b676ec334ee2f771cadff5d095971e8c" pack_key = "c957a57000626a2dc3cb69bf0e79c91c6b196b74d4d6ca1cbb830d3ad0ad4e36" sticker_dir, _pack_name = await download_pack(pack_id, pack_key, cwd=test_data) @@ -23,7 +24,7 @@ async def test_download_pack(test_data) -> None: @pytest.mark.asyncio() -async def test_download_pack_bad_name(test_data) -> None: +async def test_download_pack_bad_name(test_data: Path) -> None: pack_id = "4d92b5e3e92d1ac099830b17ac10793d" pack_key = "c8526aa2e25b911a405d39c1d4ee3977586e945550fddc33e1316626116da512" sticker_dir, _pack_name = await download_pack(pack_id, pack_key, cwd=test_data) diff --git a/tests/test_user.py b/tests/test_user.py index 383e8cd..df639b5 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -39,3 +39,11 @@ def test_convertPack() -> None: ) asyncio.run(downloader.convert_pack(swd, packName, no_cache=True)) assert len(list(Path(f"{cwd}/downloads/DonutTheDog/png").iterdir())) == packs[0]["len"] + + +def test_convertPack_cache() -> None: + swd, packName = asyncio.run( + downloader.download_pack(packs[0]["packId"], packs[0]["packKey"], cwd) + ) + asyncio.run(downloader.convert_pack(swd, packName, no_cache=False)) + assert len(list(Path(f"{cwd}/downloads/DonutTheDog/png").iterdir())) == packs[0]["len"]