diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 678ea057..d9f28a1b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.1.0 + rev: v4.3.0 hooks: - id: check-added-large-files - repo: https://github.com/pycqa/isort @@ -12,17 +12,17 @@ repos: name: isort (python) args: [--profile, black, --filter-files] - repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks - rev: v2.3.0 + rev: v2.4.0 hooks: - id: pretty-format-yaml args: [--autofix] - repo: https://github.com/akaihola/darker - rev: 1.4.1 + rev: 1.5.1 hooks: - id: darker args: [--line-length, '139'] - repo: https://github.com/PyCQA/pylint/ - rev: v2.12.2 + rev: v2.15.5 hooks: - id: pylint name: pylint @@ -34,3 +34,7 @@ repos: # "-sn", # Don't display the score # based on # https://pylint.pycqa.org/en/latest/user_guide/pre-commit-integration.html +- repo: https://github.com/PyCQA/autoflake + rev: v1.7.7 + hooks: + - id: autoflake diff --git a/bukuserver/views.py b/bukuserver/views.py index ea338a3b..f058b6a3 100644 --- a/bukuserver/views.py +++ b/bukuserver/views.py @@ -2,9 +2,9 @@ import functools import itertools import logging +import types from argparse import Namespace from collections import Counter -from types import SimpleNamespace from typing import Any, List, Optional, Tuple from urllib.parse import urlparse @@ -67,16 +67,6 @@ def search(self): return redirect(url) -class CustomBukuDbModel: # pylint: disable=too-few-public-methods - def __init__(self, bukudb_inst, name): - self.bukudb = bukudb_inst - self.name = name - - @property - def __name__(self): - return self.name - - class BookmarkModelView(BaseModelView): def _apply_filters(self, models, filters): for idx, _, value in filters: @@ -157,7 +147,7 @@ def _list_entry(self, context: Any, model: Namespace, name: str) -> Markup: def __init__(self, *args, **kwargs): self.bukudb: buku.BukuDb = args[0] - custom_model = CustomBukuDbModel(args[0], "bookmark") + custom_model = types.SimpleNamespace(bukudb=self.bukudb, __name__="bookmark") args = [ custom_model, ] + list(args[1:]) @@ -179,9 +169,7 @@ def create_form(self, obj=None): def create_model(self, form): try: - model = SimpleNamespace( - id=None, url=None, title=None, tags=None, description=None - ) + model = types.SimpleNamespace(id=None, url=None, title=None, tags=None, description=None) form.populate_obj(model) vars(model).pop("id") self._on_model_change(form, model, True) @@ -259,9 +247,7 @@ def get_list(self, page, sort_field, sort_desc, _, filters, page_size=None): bookmarks = [] data = [] for bookmark in bookmarks: - bm_sns = SimpleNamespace( - id=None, url=None, title=None, tags=None, description=None - ) + bm_sns = types.SimpleNamespace(id=None, url=None, title=None, tags=None, description=None) for field in list(BookmarkField): if field == BookmarkField.TAGS: value = bookmark[field.value] @@ -277,9 +263,7 @@ def get_list(self, page, sort_field, sort_desc, _, filters, page_size=None): def get_one(self, id): bookmark = self.model.bukudb.get_rec_by_id(id) - bm_sns = SimpleNamespace( - id=None, url=None, title=None, tags=None, description=None - ) + bm_sns = types.SimpleNamespace(id=None, url=None, title=None, tags=None, description=None) for field in list(BookmarkField): if field == BookmarkField.TAGS and bookmark[field.value].startswith(","): value = bookmark[field.value] @@ -368,34 +352,27 @@ def netloc_match_func(query, value, index): ) elif name == BookmarkField.TAGS.name.lower(): + def get_list_from_buku_tags(item): + return [x.strip() for x in item.split(",")] + def tags_contain_func(query, value, index): for item in query: - for tag in item[index].split(","): - if tag and tag == value: - yield item + if value in get_list_from_buku_tags(item[index]): + yield item def tags_not_contain_func(query, value, index): for item in query: - for tag in item[index].split(","): - if tag and tag != value: - yield item + if value not in get_list_from_buku_tags(item[index]): + yield item res.extend( [ bs_filters.BookmarkBaseFilter(name, "contain", tags_contain_func), - bs_filters.BookmarkBaseFilter( - name, "not contain", tags_not_contain_func - ), + bs_filters.BookmarkBaseFilter(name, "not contain", tags_not_contain_func), bs_filters.BookmarkTagNumberEqualFilter(name, "number equal"), - bs_filters.BookmarkTagNumberNotEqualFilter( - name, "number not equal" - ), - bs_filters.BookmarkTagNumberGreaterFilter( - name, "number greater than" - ), - bs_filters.BookmarkTagNumberSmallerFilter( - name, "number smaller than" - ), + bs_filters.BookmarkTagNumberNotEqualFilter(name, "number not equal"), + bs_filters.BookmarkTagNumberGreaterFilter(name, "number greater than"), + bs_filters.BookmarkTagNumberSmallerFilter(name, "number smaller than"), ] ) elif name in self.scaffold_list_columns(): @@ -471,7 +448,7 @@ def _name_formatter(self, _, model, name): def __init__(self, *args, **kwargs): self.bukudb = args[0] self.all_tags = self.bukudb.get_tag_all() - custom_model = CustomBukuDbModel(args[0], "tag") + custom_model = types.SimpleNamespace(bukudb=self.bukudb, __name__="tag") args = [ custom_model, ] + list(args[1:]) @@ -501,7 +478,7 @@ def get_list( search: Optional[Any], filters: List[Tuple[int, str, str]], page_size: int = None, - ) -> Tuple[int, List[SimpleNamespace]]: + ) -> Tuple[int, List[types.SimpleNamespace]]: logging.debug("search: %s", search) tags = self._apply_filters(sorted(self.all_tags[1].items()), filters) sort_field_dict = {"usage_count": 1, "name": 0} @@ -518,7 +495,7 @@ def get_list( tags = list(chunks(tags, page_size))[page] data = [] for name, usage_count in tags: - tag_sns = SimpleNamespace(name=None, usage_count=None) + tag_sns = types.SimpleNamespace(name=None, usage_count=None) tag_sns.name, tag_sns.usage_count = name, usage_count data.append(tag_sns) return count, data @@ -528,7 +505,7 @@ def get_pk_value(self, model): def get_one(self, id): tags = self.all_tags[1] - tag_sns = SimpleNamespace(name=id, usage_count=tags[id]) + tag_sns = types.SimpleNamespace(name=id, usage_count=tags[id]) return tag_sns def scaffold_filters(self, name): diff --git a/tests/test_views.py b/tests/test_views.py index 47769644..a3e8eafa 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -1,43 +1,57 @@ -import logging +"""test for views. + +resources: https://flask.palletsprojects.com/en/2.2.x/testing/ +""" from argparse import Namespace import pytest -from flask import current_app, request +from flask import request from buku import BukuDb from bukuserver import server from bukuserver.views import BookmarkModelView, TagModelView -@pytest.fixture -def client(tmp_path): - test_db = tmp_path / 'test.db' - app = server.create_app(test_db.as_posix()) - app_context = app.test_request_context() - app_context.push() - client = app.test_client() - return client +@pytest.fixture() +def app(tmp_path): + app = server.create_app((tmp_path / "test.db").as_posix()) + app.config.update( + { + "TESTING": True, + } + ) + # other setup can go here + yield app + # clean up / reset resources here + + +@pytest.fixture() +def client(app): + return app.test_client() + + +@pytest.fixture() +def runner(app): + return app.test_cli_runner() + + +def get_tmp_bukudb(tmp_path): + return BukuDb(dbfile=(tmp_path / "test.db").as_posix()) @pytest.mark.parametrize('disable_favicon', [False, True]) -def test_bookmark_model_view(tmp_path, client, disable_favicon): - logging.debug('client: %s', client) - test_db = tmp_path / 'test.db' - bukudb = BukuDb(dbfile=test_db.as_posix()) - inst = BookmarkModelView(bukudb) - model = Namespace( - description='randomdesc', id=1, tags='tags1', - title='Example Domain', url='http://example.com') - current_app.config['BUKUSERVER_DISABLE_FAVICON'] = disable_favicon - assert inst._list_entry(None, model, 'Entry') +def test_bookmark_model_view(tmp_path, disable_favicon, app): + inst = BookmarkModelView(get_tmp_bukudb(tmp_path)) + model = Namespace(description="randomdesc", id=1, tags="tags1", title="Example Domain", url="http://example.com") + app.config["BUKUSERVER_DISABLE_FAVICON"] = disable_favicon + with app.test_request_context(): + assert inst._list_entry(None, model, "Entry") @pytest.fixture def tmv_instance(tmp_path): """define tag model view instance""" - test_db = tmp_path / 'test.db' - bukudb = BukuDb(dbfile=test_db.as_posix()) - inst = TagModelView(bukudb) + inst = TagModelView(get_tmp_bukudb(tmp_path)) return inst @@ -67,9 +81,7 @@ def test_tag_model_view_get_list(tmv_instance, sort_field, sort_desc, filters, e @pytest.fixture def bmv_instance(tmp_path): """define tag model view instance""" - test_db = tmp_path / 'test.db' - bukudb = BukuDb(dbfile=test_db.as_posix()) - inst = BookmarkModelView(bukudb) + inst = BookmarkModelView(get_tmp_bukudb(tmp_path)) return inst @@ -77,7 +89,8 @@ def bmv_instance(tmp_path): ['http://example.com', 'http://example.com'], ['/bookmark/', None], ]) -def test_bmv_create_form(bmv_instance, url, exp_url): - request.args = {'url': url} - form = bmv_instance.create_form() - assert form.url.data == exp_url +def test_bmv_create_form(bmv_instance, url, exp_url, app): + with app.test_request_context(): + request.args = {"url": url} + form = bmv_instance.create_form() + assert form.url.data == exp_url