From f770b9520f223fbe536b4b67126e48ab5db4e645 Mon Sep 17 00:00:00 2001 From: robbertuittenbroek Date: Wed, 14 Aug 2024 11:04:38 +0200 Subject: [PATCH] Add frontend search functionality --- alembic.ini | 2 +- amt/api/routes/projects.py | 9 ++-- amt/clients/clients.py | 2 - amt/locale/base.pot | 8 +++- amt/locale/en_US/LC_MESSAGES/messages.mo | Bin 451 -> 451 bytes amt/locale/en_US/LC_MESSAGES/messages.po | 8 +++- amt/locale/nl_FY/LC_MESSAGES/messages.mo | Bin 569 -> 617 bytes amt/locale/nl_FY/LC_MESSAGES/messages.po | 8 +++- amt/locale/nl_NL/LC_MESSAGES/messages.mo | Bin 1202 -> 1250 bytes amt/locale/nl_NL/LC_MESSAGES/messages.po | 8 +++- amt/repositories/projects.py | 12 +++-- amt/services/instruments.py | 1 - amt/services/projects.py | 4 +- amt/site/templates/projects/_list.html.j2 | 2 +- amt/site/templates/projects/index.html.j2 | 22 ++++++++- compose.yml | 2 + poetry.lock | 36 ++++++++++++-- pyproject.toml | 1 + tests/api/routes/test_projects.py | 6 +-- tests/database_e2e_setup.py | 4 +- tests/e2e/test_scroll_project.py | 8 ++-- tests/e2e/test_search_project.py | 36 ++++++++++++++ tests/repositories/test_projects.py | 55 +++++++++++++++++++--- 23 files changed, 191 insertions(+), 43 deletions(-) create mode 100644 tests/e2e/test_search_project.py diff --git a/alembic.ini b/alembic.ini index 3c6c3c2b..cf3cc971 100644 --- a/alembic.ini +++ b/alembic.ini @@ -95,7 +95,7 @@ keys = console keys = generic [logger_root] -level = WARN +level = INFO handlers = console qualname = diff --git a/amt/api/routes/projects.py b/amt/api/routes/projects.py index 0f82bfd4..2ed05440 100644 --- a/amt/api/routes/projects.py +++ b/amt/api/routes/projects.py @@ -19,15 +19,18 @@ async def get_root( projects_service: Annotated[ProjectsService, Depends(ProjectsService)], skip: int = Query(0, ge=0), limit: int = Query(100, ge=1), + search: str = Query(""), ) -> HTMLResponse: - projects = projects_service.paginate(skip=skip, limit=limit) + projects = projects_service.paginate(skip=skip, limit=limit, search=search) next = skip + limit if request.state.htmx: - return templates.TemplateResponse(request, "projects/_list.html.j2", {"projects": projects, "next": next}) + return templates.TemplateResponse( + request, "projects/_list.html.j2", {"projects": projects, "next": next, "search": search} + ) return templates.TemplateResponse( - request, "projects/index.html.j2", {"projects": projects, "next": next, "limit": limit} + request, "projects/index.html.j2", {"projects": projects, "next": next, "limit": limit, "search": search} ) diff --git a/amt/clients/clients.py b/amt/clients/clients.py index b650489a..bd9f2413 100644 --- a/amt/clients/clients.py +++ b/amt/clients/clients.py @@ -24,14 +24,12 @@ def get_content(self, url: str) -> bytes: """ This method should implement getting the content of an instrument from given URL. """ - ... @abstractmethod def list_content(self, url: str = "") -> RepositoryContent: """ This method should implement getting list of instruments from given URL. """ - ... def _get(self, url: str) -> httpx.Response: """ diff --git a/amt/locale/base.pot b/amt/locale/base.pot index cb74c929..ee6fad9f 100644 --- a/amt/locale/base.pot +++ b/amt/locale/base.pot @@ -8,14 +8,14 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2024-07-26 13:53+0200\n" +"POT-Creation-Date: 2024-08-15 15:18+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.15.0\n" +"Generated-By: Babel 2.16.0\n" #: amt/site/templates/header.html.j2:4 msgid "Algorithm Management Toolkit" @@ -66,6 +66,10 @@ msgstr "" msgid "Unknown" msgstr "" +#: amt/site/templates/projects/index.html.j2:11 +msgid "Find projects..." +msgstr "" + #: amt/site/templates/projects/new.html.j2:4 msgid "New Project" msgstr "" diff --git a/amt/locale/en_US/LC_MESSAGES/messages.mo b/amt/locale/en_US/LC_MESSAGES/messages.mo index 2e7f529c0161489e5b8112079c89d7fc9f10f612..42de0cd246cfd7bb1f68fa9ee3b0058eaf6bcfac 100644 GIT binary patch delta 27 icmX@ie3*GcAD4x$p{atQsg\n" "Language: en_US\n" @@ -16,7 +16,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.15.0\n" +"Generated-By: Babel 2.16.0\n" #: amt/site/templates/header.html.j2:4 msgid "Algorithm Management Toolkit" @@ -67,6 +67,10 @@ msgstr "" msgid "Unknown" msgstr "" +#: amt/site/templates/projects/index.html.j2:11 +msgid "Find project..." +msgstr "" + #: amt/site/templates/projects/new.html.j2:4 msgid "New Project" msgstr "" diff --git a/amt/locale/nl_FY/LC_MESSAGES/messages.mo b/amt/locale/nl_FY/LC_MESSAGES/messages.mo index de50608bee6a5253ecfdd748fbe494ea50dabc6e..f222f55c062add59dd8f0e19194531accb5a77d5 100644 GIT binary patch delta 225 zcmdnV@{*sTg%n!sJK+FlmGk_Q*z6gk? zGBPkM2h!|7Y|8`@cLmZQ^_52Pm*q9+@qhao7n OI5V#}3n&Xx#Q*@Rt0&0- delta 185 zcmaFKvXiC$o)F7a1|VPsVi_QI0dbH(4v-C!%LQT_52&~}Gp{%c$ODNo007N#9Yg>C diff --git a/amt/locale/nl_FY/LC_MESSAGES/messages.po b/amt/locale/nl_FY/LC_MESSAGES/messages.po index 0c895bae..ac1c5364 100644 --- a/amt/locale/nl_FY/LC_MESSAGES/messages.po +++ b/amt/locale/nl_FY/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2024-07-26 13:53+0200\n" +"POT-Creation-Date: 2024-08-15 15:18+0200\n" "PO-Revision-Date: 2024-07-25 21:01+0200\n" "Last-Translator: FULL NAME \n" "Language: nl_FY\n" @@ -16,7 +16,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.15.0\n" +"Generated-By: Babel 2.16.0\n" #: amt/site/templates/header.html.j2:4 msgid "Algorithm Management Toolkit" @@ -67,6 +67,10 @@ msgstr "Dien" msgid "Unknown" msgstr "" +#: amt/site/templates/projects/index.html.j2:11 +msgid "Find project..." +msgstr "Fine projekt..." + #: amt/site/templates/projects/new.html.j2:4 msgid "New Project" msgstr "" diff --git a/amt/locale/nl_NL/LC_MESSAGES/messages.mo b/amt/locale/nl_NL/LC_MESSAGES/messages.mo index 5ffafc35767fe7ec3dd3090b22bb4c39ec3745d4..1d5e5d0b5d6afd45a8811a57522897454bae794e 100644 GIT binary patch delta 386 zcmYk$u}cDR6u|NK(~G*xx)8w#2ayOa@ZbgysazUd{10pq%%KFWXbg^MsVz7hnwkVj zr-Fu7*B}}raA>Nr|Df;F27lb|KEL;V@7}#f`^%0dX{#r!G&{#0X)w3;%q(BQP4abI z!w6UL78mdyE$pL<54eOcxQyd}-!Mh|IlGVKo5MPZU$oIlh-5H>+R(#A^sz@xh&t$N zQlx-CsBwy7ZS0}`Fhm_-A9bJ_^2hLo173px$4=+#$xWO#O=-?1H@ELWGchtro zv1d-*>QUz~e{$A6ZZ*$+-;eLi#3>hpQa&i{1m&m8#FjB(JV;)-^=_*@U%72v{{g{P BE?)ou delta 338 zcmYk$ze>YU6vy#%n-qKdCsdKPNQV-nODt4FDmwH5N?)Kj`T(VKhZKZ5I)pCWLR|zw zuv_~Ab@TyraFmWdfZu}|ym0fm=iKC%^JQOc{CVUqgmuVnvLChhx6aI8zk{dr2Ux{1 zdN{*8KA?*WEZ`DLxXOINGW9mQ-(f=}mUj-8i*G!}AJib56FEQ+HME90;Ikv*;~Q$d zM-8@oq(7{o4&bBa1UxF(&FrJTKSV2m$cRIo#w`|ciYGWn9Uw&wd`hm&B-xmc;$`Qe W*E$ccqi~((+Qvl5&bdo&-1}chU?JWB diff --git a/amt/locale/nl_NL/LC_MESSAGES/messages.po b/amt/locale/nl_NL/LC_MESSAGES/messages.po index 8f80d459..8dcec56b 100644 --- a/amt/locale/nl_NL/LC_MESSAGES/messages.po +++ b/amt/locale/nl_NL/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2024-07-26 13:53+0200\n" +"POT-Creation-Date: 2024-08-15 15:18+0200\n" "PO-Revision-Date: 2024-07-25 21:01+0200\n" "Last-Translator: FULL NAME \n" "Language: nl_NL\n" @@ -16,7 +16,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.15.0\n" +"Generated-By: Babel 2.16.0\n" #: amt/site/templates/header.html.j2:4 msgid "Algorithm Management Toolkit" @@ -67,6 +67,10 @@ msgstr "Afgerond" msgid "Unknown" msgstr "Onbekend" +#: amt/site/templates/projects/index.html.j2:11 +msgid "Find project..." +msgstr "Zoek project..." + #: amt/site/templates/projects/new.html.j2:4 msgid "New Project" msgstr "Nieuw project" diff --git a/amt/repositories/projects.py b/amt/repositories/projects.py index eed9e555..efdf63b6 100644 --- a/amt/repositories/projects.py +++ b/amt/repositories/projects.py @@ -6,8 +6,9 @@ from sqlalchemy import func, select from sqlalchemy.exc import NoResultFound, SQLAlchemyError from sqlalchemy.orm import Session +from sqlalchemy_utils import escape_like # pyright: ignore[reportMissingTypeStubs, reportUnknownVariableType] -from amt.core.exceptions import RepositoryError, RepositoryNoResultFound +from amt.core.exceptions import RepositoryError from amt.models import Project from amt.repositories.deps import get_session @@ -52,9 +53,12 @@ def find_by_id(self, project_id: int) -> Project: except NoResultFound as e: raise RepositoryError from e - def paginate(self, skip: int, limit: int) -> list[Project]: + def paginate(self, skip: int, limit: int, search: str) -> list[Project]: try: - statement = select(Project).order_by(func.lower(Project.name)).offset(skip).limit(limit) + statement = select(Project) + if search != "": + statement = statement.filter(Project.name.ilike(f"%{escape_like(search)}%")) + statement = statement.order_by(func.lower(Project.name)).offset(skip).limit(limit) return list(self.session.execute(statement).scalars()) except Exception as e: - raise RepositoryNoResultFound from e + raise RepositoryError from e diff --git a/amt/services/instruments.py b/amt/services/instruments.py index 391e2d3d..443bf8cf 100644 --- a/amt/services/instruments.py +++ b/amt/services/instruments.py @@ -32,7 +32,6 @@ def fetch_github_content(self, url: str) -> Instrument: return Instrument(**data) def fetch_instruments(self, urns: Sequence[str] | None = None) -> list[Instrument]: - # todo (Robbert): we 'type ignore' Task.sort_order because it works correctly, but pyright does not agree content_list = self.fetch_github_content_list() instruments: list[Instrument] = [] diff --git a/amt/services/projects.py b/amt/services/projects.py index 5150e6fd..6d9db771 100644 --- a/amt/services/projects.py +++ b/amt/services/projects.py @@ -63,8 +63,8 @@ def create(self, project_new: ProjectNew) -> Project: return project - def paginate(self, skip: int, limit: int) -> list[Project]: - return self.repository.paginate(skip=skip, limit=limit) + def paginate(self, skip: int, limit: int, search: str) -> list[Project]: + return self.repository.paginate(skip=skip, limit=limit, search=search) def update(self, project: Project) -> Project: return self.repository.save(project) diff --git a/amt/site/templates/projects/_list.html.j2 b/amt/site/templates/projects/_list.html.j2 index 3ed40e3f..c1c29ee3 100644 --- a/amt/site/templates/projects/_list.html.j2 +++ b/amt/site/templates/projects/_list.html.j2 @@ -2,5 +2,5 @@
  • {{ project.name }}
  • {% endfor %} {% if projects|length > 0 %} -
  • More
  • +
  • More
  • {% endif %} diff --git a/amt/site/templates/projects/index.html.j2 b/amt/site/templates/projects/index.html.j2 index f3173a5b..c5e9db20 100644 --- a/amt/site/templates/projects/index.html.j2 +++ b/amt/site/templates/projects/index.html.j2 @@ -2,7 +2,27 @@ {% block content %}

    {% trans %}Projects{% endtrans %}

    -
      + +
      + + +
      + +
        {% include 'projects/_list.html.j2' %}
      diff --git a/compose.yml b/compose.yml index bcfa3a73..2aeb8250 100644 --- a/compose.yml +++ b/compose.yml @@ -33,6 +33,8 @@ services: - PGDATA=/var/lib/postgresql/data/pgdata healthcheck: test: ["CMD", "pg_isready", "-q", "-d", "amt", "-U", "amt"] + ports: + - 5432:5432 db-admin: image: dpage/pgadmin4:8.6 diff --git a/poetry.lock b/poetry.lock index b99259de..b0052600 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1089,13 +1089,13 @@ dev = ["black", "build", "flake8", "flake8-black", "isort", "jupyter-console", " [[package]] name = "pyright" -version = "1.1.375" +version = "1.1.376" description = "Command line wrapper for pyright" optional = false python-versions = ">=3.7" files = [ - {file = "pyright-1.1.375-py3-none-any.whl", hash = "sha256:4c5e27eddeaee8b41cc3120736a1dda6ae120edf8523bb2446b6073a52f286e3"}, - {file = "pyright-1.1.375.tar.gz", hash = "sha256:7765557b0d6782b2fadabff455da2014476404c9e9214f49977a4e49dec19a0f"}, + {file = "pyright-1.1.376-py3-none-any.whl", hash = "sha256:0f2473b12c15c46b3207f0eec224c3cea2bdc07cd45dd4a037687cbbca0fbeff"}, + {file = "pyright-1.1.376.tar.gz", hash = "sha256:bffd63b197cd0810395bb3245c06b01f95a85ddf6bfa0e5644ed69c841e954dd"}, ] [package.dependencies] @@ -1464,6 +1464,34 @@ postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] pymysql = ["pymysql"] sqlcipher = ["sqlcipher3_binary"] +[[package]] +name = "sqlalchemy-utils" +version = "0.41.2" +description = "Various utility functions for SQLAlchemy." +optional = false +python-versions = ">=3.7" +files = [ + {file = "SQLAlchemy-Utils-0.41.2.tar.gz", hash = "sha256:bc599c8c3b3319e53ce6c5c3c471120bd325d0071fb6f38a10e924e3d07b9990"}, + {file = "SQLAlchemy_Utils-0.41.2-py3-none-any.whl", hash = "sha256:85cf3842da2bf060760f955f8467b87983fb2e30f1764fd0e24a48307dc8ec6e"}, +] + +[package.dependencies] +SQLAlchemy = ">=1.3" + +[package.extras] +arrow = ["arrow (>=0.3.4)"] +babel = ["Babel (>=1.3)"] +color = ["colour (>=0.0.4)"] +encrypted = ["cryptography (>=0.6)"] +intervals = ["intervals (>=0.7.1)"] +password = ["passlib (>=1.6,<2.0)"] +pendulum = ["pendulum (>=2.0.5)"] +phone = ["phonenumbers (>=5.9.2)"] +test = ["Jinja2 (>=2.3)", "Pygments (>=1.2)", "backports.zoneinfo", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "isort (>=4.2.2)", "pg8000 (>=1.12.4)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (==7.4.4)", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +test-all = ["Babel (>=1.3)", "Jinja2 (>=2.3)", "Pygments (>=1.2)", "arrow (>=0.3.4)", "backports.zoneinfo", "colour (>=0.0.4)", "cryptography (>=0.6)", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "furl (>=0.4.1)", "intervals (>=0.7.1)", "isort (>=4.2.2)", "passlib (>=1.6,<2.0)", "pendulum (>=2.0.5)", "pg8000 (>=1.12.4)", "phonenumbers (>=5.9.2)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (==7.4.4)", "python-dateutil", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +timezone = ["python-dateutil"] +url = ["furl (>=0.4.1)"] + [[package]] name = "starlette" version = "0.37.2" @@ -1826,4 +1854,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "06cbbf4ccaed2f328ce6589b46e534342c1e525157f5ec499381df6a5a968339" +content-hash = "b7ade8425d8a93f9c938250d3e5fcd789f5cee2a945fdcf136fabf189519ef54" diff --git a/pyproject.toml b/pyproject.toml index 8f2c98b6..274365ef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,6 +37,7 @@ click = "^8.1.7" python-ulid = "^2.7.0" fastapi-csrf-protect = "^0.3.4" sqlalchemy = "^2.0.32" +sqlalchemy-utils = "^0.41.2" [tool.poetry.group.test.dependencies] diff --git a/tests/api/routes/test_projects.py b/tests/api/routes/test_projects.py index 63f61211..130f61e3 100644 --- a/tests/api/routes/test_projects.py +++ b/tests/api/routes/test_projects.py @@ -26,21 +26,21 @@ def test_projects_get_root(client: TestClient) -> None: response = client.get("/projects/") assert response.status_code == 200 - assert b'
        ' in response.content + assert b'
          ' in response.content def test_projects_get_root_missing_slash(client: TestClient) -> None: response = client.get("/projects") assert response.status_code == 200 - assert b'
            ' in response.content + assert b'
              ' in response.content def test_projects_get_root_htmx(client: TestClient) -> None: response = client.get("/projects/", headers={"HX-Request": "true"}) assert response.status_code == 200 - assert b'
                ' not in response.content + assert b'
                  ' not in response.content def test_get_new_projects(client: TestClient, init_instruments: Generator[None, None, None]) -> None: diff --git a/tests/database_e2e_setup.py b/tests/database_e2e_setup.py index 0339d11b..25468dc7 100644 --- a/tests/database_e2e_setup.py +++ b/tests/database_e2e_setup.py @@ -18,7 +18,7 @@ def setup_database_e2e(session: Session) -> None: db_e2e.given([task1, task2, task3]) projects: list[Project] = [] - for _ in range(120): - projects.append(default_project()) + for idx in range(120): + projects.append(default_project(name=f"Project {idx}")) db_e2e.given([*projects]) diff --git a/tests/e2e/test_scroll_project.py b/tests/e2e/test_scroll_project.py index ada89498..c7c05538 100644 --- a/tests/e2e/test_scroll_project.py +++ b/tests/e2e/test_scroll_project.py @@ -6,15 +6,15 @@ def test_e2e_scroll_projects(page: Page) -> None: page.goto("/projects/") - project_links = page.locator(".project-list > li", has_text="default project").count() + project_links = page.locator(".project-list > li").count() - assert 90 <= project_links <= 100 + assert 90 <= project_links <= 101 - with page.expect_response("/projects/?skip=100", timeout=3000) as response_info: + with page.expect_response("/projects/?skip=100&search=", timeout=3000) as response_info: page.get_by_text("More").scroll_into_view_if_needed() response = response_info.value assert response.status == 200 - project_links = page.locator(".project-list > li", has_text="default project").count() + project_links = page.locator(".project-list > li").count() assert project_links > 100 diff --git a/tests/e2e/test_search_project.py b/tests/e2e/test_search_project.py new file mode 100644 index 00000000..8af7dd25 --- /dev/null +++ b/tests/e2e/test_search_project.py @@ -0,0 +1,36 @@ +import pytest +from playwright.sync_api import Page, expect + + +@pytest.mark.slow() +def test_e2e_search_projects(page: Page) -> None: + page.goto("/projects/") + + project_links = page.locator(".project-list > li").count() + + assert 90 <= project_links <= 101 + + page.locator("#project-search-input").fill("10") + + with page.expect_response("/projects/?skip=0&search=10", timeout=3000) as response_info: + expect(page.get_by_text("Project 10", exact=True)).to_be_visible() + expect(page.get_by_text("Project 100", exact=True)).to_be_visible() + + response = response_info.value + assert response.status == 200 + + +@pytest.mark.slow() +def test_e2e_search_scroll_projects(page: Page) -> None: + page.goto("/projects/") + + project_links = page.locator(".project-list > li").count() + + assert 90 <= project_links <= 101 + + page.locator("#project-search-input").fill("Project") + + with page.expect_request("/projects/?skip=0&search=Project", timeout=3000) as _: + project_links = page.locator(".project-list > li").count() + expect(page.get_by_text("Project 100", exact=True)).to_be_visible() + assert 90 <= project_links <= 101 diff --git a/tests/repositories/test_projects.py b/tests/repositories/test_projects.py index 7837a779..4ccfd39a 100644 --- a/tests/repositories/test_projects.py +++ b/tests/repositories/test_projects.py @@ -1,5 +1,5 @@ import pytest -from amt.core.exceptions import RepositoryError, RepositoryNoResultFound +from amt.core.exceptions import RepositoryError from amt.repositories.projects import ProjectsRepository from tests.constants import default_project from tests.database_test_utils import DatabaseTestUtils @@ -85,7 +85,7 @@ def test_paginate(db: DatabaseTestUtils): db.given([default_project()]) project_repository = ProjectsRepository(db.get_session()) - result = project_repository.paginate(skip=0, limit=3) + result = project_repository.paginate(skip=0, limit=3, search="") assert len(result) == 1 @@ -94,7 +94,7 @@ def test_paginate_more(db: DatabaseTestUtils): db.given([default_project(), default_project(), default_project(), default_project()]) project_repository = ProjectsRepository(db.get_session()) - result = project_repository.paginate(skip=0, limit=3) + result = project_repository.paginate(skip=0, limit=3, search="") assert len(result) == 3 @@ -110,7 +110,7 @@ def test_paginate_capitalize(db: DatabaseTestUtils): ) project_repository = ProjectsRepository(db.get_session()) - result = project_repository.paginate(skip=0, limit=4) + result = project_repository.paginate(skip=0, limit=4, search="") assert len(result) == 4 assert result[0].name == "Aaa" @@ -119,9 +119,50 @@ def test_paginate_capitalize(db: DatabaseTestUtils): assert result[3].name == "Project1" -def test_paginate_not_found(db: DatabaseTestUtils): +def test_search(db: DatabaseTestUtils): + db.given( + [ + default_project(name="Project1"), + default_project(name="bbb"), + default_project(name="Aaa"), + default_project(name="aba"), + ] + ) + project_repository = ProjectsRepository(db.get_session()) + + result = project_repository.paginate(skip=0, limit=4, search="bbb") + + assert len(result) == 1 + assert result[0].name == "bbb" + + +def test_search_multiple(db: DatabaseTestUtils): + db.given( + [ + default_project(name="Project1"), + default_project(name="bbb"), + default_project(name="Aaa"), + default_project(name="aba"), + ] + ) + project_repository = ProjectsRepository(db.get_session()) + + result = project_repository.paginate(skip=0, limit=4, search="A") + + assert len(result) == 2 + assert result[0].name == "Aaa" + assert result[1].name == "aba" + + +def test_search_no_results(db: DatabaseTestUtils): + project_repository = ProjectsRepository(db.get_session()) + result = project_repository.paginate(skip=0, limit=4, search="A") + assert len(result) == 0 + + +def test_raises_exception(db: DatabaseTestUtils): db.given([default_project()]) project_repository = ProjectsRepository(db.get_session()) - with pytest.raises(RepositoryNoResultFound): - project_repository.paginate(skip="a", limit=3) # type: ignore + with pytest.raises(RepositoryError): + project_repository.paginate(skip="a", limit=3, search="") # type: ignore