-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test(pytest): add test configuration and fixtures
Add initial test setup for politikontroller_py including: - Basic pytest configuration with asyncio support - Client and Account fixtures - Custom aresponses route handler for API testing - Initial API check test
- Loading branch information
Showing
6 changed files
with
143 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
"""politikontroller_py tests.""" | ||
|
||
import pytest | ||
|
||
pytestmark = pytest.mark.asyncio(loop_scope="package") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
from __future__ import annotations | ||
|
||
import logging | ||
from typing import Callable | ||
|
||
import pytest | ||
|
||
from politikontroller_py import Account, Client | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
|
||
@pytest.fixture | ||
async def politikontroller_client(default_login_details) -> Callable[..., Client]: | ||
"""Return Politikontroller Client.""" | ||
|
||
def _politikontroller_client( | ||
credentials: Account | None = None, | ||
load_default_login_details: bool = True, | ||
) -> Client: | ||
login_details = default_login_details if load_default_login_details is True else credentials | ||
return Client.initialize(login_details.username, login_details.password) | ||
|
||
return _politikontroller_client | ||
|
||
|
||
@pytest.fixture | ||
def default_login_details(): | ||
return Account(username="4747474747", password="securepassword123") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
2sSIAJIbo5IKek7JJ/M6ng== |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
from __future__ import annotations | ||
|
||
from pathlib import Path | ||
from urllib.parse import parse_qs, urlencode, urlparse | ||
|
||
from aresponses.main import Route | ||
|
||
# noinspection PyProtectedMember | ||
from aresponses.utils import ANY, _text_matches_pattern | ||
|
||
from politikontroller_py.utils import aes_decrypt | ||
|
||
FIXTURE_DIR = Path(__file__).parent / "fixtures" | ||
|
||
|
||
def load_fixture(name: str) -> str: | ||
"""Load a fixture.""" | ||
path = FIXTURE_DIR / f"{name}.txt" | ||
if not path.exists(): # pragma: no cover | ||
raise FileNotFoundError(f"Fixture {name} not found") | ||
return path.read_text(encoding="utf-8") | ||
|
||
|
||
class CustomRoute(Route): | ||
"""Custom route for aresponses.""" | ||
|
||
def __init__( | ||
self, | ||
method_pattern=ANY, | ||
host_pattern=ANY, | ||
path_pattern=ANY, | ||
path_qs=None, | ||
body_pattern=ANY, | ||
match_querystring=False, | ||
match_partial_query=True, | ||
repeat=1, | ||
): | ||
super().__init__(method_pattern, host_pattern, path_pattern, body_pattern, match_querystring, repeat) | ||
if path_qs is not None: | ||
self.path_qs = urlencode({k: v for k, v in path_qs.items() if v is not None}) | ||
self.match_querystring = True | ||
self.match_partial_query = match_partial_query | ||
|
||
async def matches(self, request): | ||
"""Check if the request matches this route.""" | ||
path_to_match = urlparse(request.path_qs) | ||
query_to_match = parse_qs(aes_decrypt(path_to_match.query)) | ||
parsed_query = parse_qs(self.path_qs) if self.path_qs else {} | ||
|
||
if not _text_matches_pattern(self.host_pattern, request.host): | ||
return False | ||
|
||
if self.match_querystring: | ||
if not self.match_partial_query and query_to_match != parsed_query: | ||
return False | ||
for key, value in parsed_query.items(): | ||
if key not in query_to_match or query_to_match[key] != value: | ||
return False | ||
|
||
if not _text_matches_pattern(self.method_pattern, request.method): # noqa: SIM103 | ||
return False | ||
|
||
return True |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# This extends our general Ruff rules specifically for tests | ||
extend = "../pyproject.toml" | ||
|
||
lint.extend-select = [ | ||
"PT", # Use @pytest.fixture without parentheses | ||
] | ||
|
||
lint.extend-ignore = [ | ||
"S101", | ||
"S106", | ||
"S108", | ||
"SLF001", | ||
"TCH002", | ||
"PLR2004", | ||
] | ||
|
||
lint.pylint.max-branches = 13 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
"""Tests for NrkPodcastAPI.""" | ||
|
||
from __future__ import annotations | ||
|
||
import logging | ||
|
||
from aiohttp import ClientSession | ||
from aresponses import Response, ResponsesMockServer | ||
|
||
from politikontroller_py.models.api import APIEndpoint | ||
|
||
from .helpers import CustomRoute, load_fixture | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
async def test_check(aresponses: ResponsesMockServer, politikontroller_client): | ||
aresponses.add( | ||
response=Response(text=load_fixture("check")), | ||
route=CustomRoute( | ||
path_qs={"p": APIEndpoint.CHECK}, | ||
), | ||
) | ||
async with ClientSession() as session: | ||
client = politikontroller_client() | ||
client.session = session | ||
result = await client.check() | ||
assert result == "YES" |