Skip to content

Commit

Permalink
Adds tests and fixtures for authentication client
Browse files Browse the repository at this point in the history
  • Loading branch information
bendikrb committed Oct 13, 2024
1 parent e0e23f3 commit 2d0fdac
Show file tree
Hide file tree
Showing 8 changed files with 376 additions and 32 deletions.
70 changes: 59 additions & 11 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
from __future__ import annotations

import contextlib
from datetime import datetime, timezone
import logging

import pytest

from podme_api.auth import PodMeDefaultAuthClient, PodMeUserCredentials, SchibstedCredentials
from podme_api.client import PodMeClient

from .helpers import load_fixture
from .helpers import load_fixture_json

_LOGGER = logging.getLogger(__name__)

logging.basicConfig(level=logging.DEBUG)


@pytest.fixture
async def podme_client(default_credentials):
async def podme_client(default_credentials, user_credentials):
"""Return PodMeClient."""

@contextlib.asynccontextmanager
async def _podme_client(
username: str | None = None,
password: str | None = None,
credentials: SchibstedCredentials | None = None,
load_default_credentials: bool = True,
load_default_user_credentials: bool = False,
) -> PodMeClient:
if username is None or password is None:
user_creds = None
else:
user_creds = PodMeUserCredentials(username, password)
user_creds = user_credentials if load_default_user_credentials is True else None
auth_client = PodMeDefaultAuthClient(user_credentials=user_creds)
if load_default_credentials:
if credentials is not None:
auth_client.set_credentials(credentials)
elif load_default_credentials:
auth_client.set_credentials(default_credentials)
client = PodMeClient(auth_client=auth_client, disable_credentials_storage=True)
try:
Expand All @@ -42,7 +42,55 @@ async def _podme_client(
return _podme_client


@pytest.fixture
async def podme_default_auth_client(user_credentials, default_credentials):
"""Return PodMeDefaultAuthClient."""

@contextlib.asynccontextmanager
async def _podme_auth_client(
credentials: SchibstedCredentials | None = None,
load_default_credentials: bool = True,
load_default_user_credentials: bool = True,
) -> PodMeDefaultAuthClient:
auth_client = PodMeDefaultAuthClient()

if load_default_user_credentials:
auth_client.user_credentials = user_credentials
if credentials is not None:
auth_client.set_credentials(credentials)
elif load_default_credentials:
auth_client.set_credentials(default_credentials)

try:
await auth_client.__aenter__()
yield auth_client
finally:
await auth_client.__aexit__(None, None, None)

return _podme_auth_client


@pytest.fixture
def user_credentials():
return PodMeUserCredentials(email="[email protected]", password="securepassword123")


@pytest.fixture
def default_credentials():
data = load_fixture("default_credentials")
return SchibstedCredentials.from_json(data)
data = load_fixture_json("default_credentials")
data["expiration_time"] = int(datetime.now(tz=timezone.utc).timestamp() + data["expires_in"])
return SchibstedCredentials.from_dict(data)


@pytest.fixture
def expired_credentials():
data = load_fixture_json("default_credentials")
data["expiration_time"] = int(datetime.now(tz=timezone.utc).timestamp() - 1)
return SchibstedCredentials.from_dict(data)


@pytest.fixture
def refreshed_credentials(default_credentials):
data = load_fixture_json("default_credentials")
data["access_token"] = data["access_token"] + "_refreshed"
return SchibstedCredentials.from_dict(data)
1 change: 1 addition & 0 deletions tests/fixtures/auth_flow.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"login_form": "\n<html lang=\"en\">\n<body>\n <div id=\"bffData\" style=\"display: none\">\n {&quot;client&quot;:{&quot;birthdayFormat&quot;:&quot;default&quot;,&quot;css&quot;:{&quot;background_color&quot;:&quot;&quot;,&quot;color&quot;:&quot;&quot;},&quot;domain&quot;:&quot;https:&#x2F;&#x2F;podme.com&quot;,&quot;emailReceiptsEnabled&quot;:true,&quot;id&quot;:&quot;66fd26cdae6bde57ef206b35&quot;,&quot;locale&quot;:&quot;nb_NO&quot;,&quot;logoUrl&quot;:&quot;&#x2F;&#x2F;d3iwtia3ndepsv.cloudfront.net&#x2F;clients&#x2F;images&#x2F;logos&#x2F;66fd26cdae6bde57ef206b35_66fd27c2ebc06.png&quot;,&quot;logoMarkUrl&quot;:&quot;&#x2F;&#x2F;d3iwtia3ndepsv.cloudfront.net&#x2F;clients&#x2F;images&#x2F;logos&#x2F;60bf1d46c440077a8ccbfdf4_612e3cde466cb.png&quot;,&quot;company&quot;:&quot;media&quot;,&quot;merchantId&quot;:47099,&quot;merchantName&quot;:&quot;Podme&quot;,&quot;merchantType&quot;:&quot;external&quot;,&quot;name&quot;:&quot;New Podme&quot;,&quot;appType&quot;:&quot;web_client&quot;,&quot;pulseProviderId&quot;:&quot;sdrn:schibsted:client:schibsted-account&quot;,&quot;sessionServiceDomain&quot;:&quot;https:&#x2F;&#x2F;id.no.podme.com&quot;,&quot;teaser&quot;:null,&quot;terms&quot;:{&quot;clientTermsUrl&quot;:&quot;&#x2F;terms?client_id&#x3D;66fd26cdae6bde57ef206b35&quot;},&quot;supportUrl&quot;:&quot;&quot;,&quot;defaultClientId&quot;:&quot;4d06920474dea26227070000&quot;,&quot;uriScheme&quot;:&quot;&quot;},&quot;spidUrl&quot;:&quot;https:&#x2F;&#x2F;payment.schibsted.no&quot;,&quot;reCaptchaSiteKey&quot;:&quot;6Le5um4UAAAAABdQhjGRL1lLIVduoKSSuDCpggjg&quot;,&quot;defaultTermsAgreement&quot;:true,&quot;pulse&quot;:{&quot;enabled&quot;:true,&quot;providerId&quot;:&quot;sdrn:schibsted:client:schibsted-account&quot;,&quot;realm&quot;:&quot;spid.no&quot;,&quot;deployTag&quot;:&quot;schacc-v4.5.38&quot;,&quot;experiments&quot;:[],&quot;deployStage&quot;:&quot;pro&quot;},&quot;bff&quot;:{&quot;host&quot;:&quot;https:&#x2F;&#x2F;payment.schibsted.no&quot;,&quot;env&quot;:&quot;pro-no&quot;},&quot;initialState&quot;:{&quot;links&quot;:{&quot;self&quot;:{&quot;href&quot;:&quot;&#x2F;authn&#x2F;?client_sdrn&#x3D;sdrn%3Aspid.no%3Aclient%3A66fd26cdae6bde57ef206b35&amp;prompt&#x3D;select_account&amp;client_id&#x3D;66fd26cdae6bde57ef206b35&amp;nonce&#x3D;1b627c68-7b13-4c69-b0dd-f5e4b7fcb3d5&amp;state&#x3D;2322e273c61b6a46adc08c1c9c9c056dacaa05637dc94a96148df2d895e0d8657cf7db02c2be5952481b2e4c86689aff8f4d923fd92e8d261571b3cb8da91125150282b9026f9058f7655db018e7d3b8247a2090459aee8834da549e92e3339dcc75b3783896cce3ade64727db1b2b99679c1dd87c4175108f2c4e9c1d7afb36&quot;}},&quot;meta&quot;:{&quot;layoutOptions&quot;:[&quot;passwordless-flow&quot;]}},&quot;csrfToken&quot;:&quot;yucv27QQ-Tk7aNZFOJjlwdL-5sF9rD2LgZtA&quot;}\n </div>\n <div id=\"notBffData\"></div>\n</body>\n</html>\n", "final_html": "\n<!doctype html>\n<html lang=\"nb\">\n<head>\n <title>Oppdag podkaster p\u00e5 Podme.com</title>\n</head>\n<body>\n<div id=\"react-app\"></div>\n</body>\n</html>\n", "csrf": {"links": {"self": {"href": "/authn/api/settings/csrf?client_id=66fd26cdae6bde57ef206b35"}}, "data": {"type": "settings", "attributes": {"csrfToken": "W0MURPsg-XnD3Kyb8PW2YbU7Pa8t6li6ZFIk"}}}, "email_status": {"links": {"self": {"href": "/authn/api/identity/email-status?client_id=66fd26cdae6bde57ef206b35"}, "next": {"href": "/login-password"}}, "data": {"type": "email", "attributes": {"email": "[email protected]"}}, "meta": {"renderReCaptcha": false}}, "login": {"links": {"self": {"href": "/authn/api/identity/login/?client_id=66fd26cdae6bde57ef206b35"}, "next": {"href": "/success"}}, "meta": {"renderReCaptcha": false, "tracking": {"userIdentifier": "123456", "disabledTracking": true}}}}
1 change: 1 addition & 0 deletions tests/fixtures/bffdata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"client":{"birthdayFormat":"default","css":{"background_color":"","color":""},"domain":"https://podme.com","emailReceiptsEnabled":true,"id":"66fd26cdae6bde57ef206b35","locale":"nb_NO","logoUrl":"//d3iwtia3ndepsv.cloudfront.net/clients/images/logos/66fd26cdae6bde57ef206b35_66fd27c2ebc06.png","logoMarkUrl":"//d3iwtia3ndepsv.cloudfront.net/clients/images/logos/60bf1d46c440077a8ccbfdf4_612e3cde466cb.png","company":"media","merchantId":47099,"merchantName":"Podme","merchantType":"external","name":"Podme","appType":"web_client","pulseProviderId":"sdrn:schibsted:client:schibsted-account","sessionServiceDomain":"https://id.no.podme.com","teaser":null,"terms":{"clientTermsUrl":"/terms?client_id=66fd26cdae6bde57ef206b35"},"supportUrl":"","defaultClientId":"4d06920474dea26227070000","uriScheme":""},"spidUrl":"https://payment.schibsted.no","reCaptchaSiteKey":"6Le5um4UAAAAABdQhjGRL1lLIVduoKSSuDCpggjg","defaultTermsAgreement":true,"pulse":{"enabled":true,"providerId":"sdrn:schibsted:client:schibsted-account","realm":"spid.no","deployTag":"schacc-v4.5.38","experiments":[],"deployStage":"pro"},"bff":{"host":"https://payment.schibsted.no","env":"pro-no"},"initialState":{"links":{"self":{"href":"/authn/?client_sdrn=sdrn%3Aspid.no%3Aclient%3A66fd26cdae6bde57ef206b35&prompt=select_account&client_id=66fd26cdae6bde57ef206b35&nonce=5827bfc4-8580-447e-8222-9349102aa5e8&state=243ba7f5ee32d8b25c22a26d1e7f5db520fc6d064388783eb2bcdb7c203450b915e075ab449ebdc232cb0b173933d83d4f5262ca88a9e7cbd84db062103aa8d4694265f677ea354e63f8d291a968b747a667d695d818816232fba32465c5dddbdd5ae42016103e11400e3e9bf079d05b5e515a4556bdf5c275acfd6252718cbe"}},"meta":{"layoutOptions":["passwordless-flow"]}},"csrfToken":"vfZK4v6M-XB9hH8aiNFNepGS0P1I3jWQ-6ik"}
2 changes: 1 addition & 1 deletion tests/fixtures/default_credentials.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"scope":"openid email","user_id":"123456","is_admin":false,"token_type":"Bearer","access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3BheW1lbnQuc2NoaWJzdGVkLm5vLyIsImlhdCI6MTcyODUxODQwMCwiZXhwIjo0MTAyNDQ0ODAwLCJhdWQiOiJodHRwczovL3BvZG1lLmNvbSIsInN1YiI6IjAwMDAwMDAwLTAwMDAtMDAwMC0wMDAwLTAwMDAwMDAwZmZmZiIsInVzZXJfaWQiOiIxMjM0NTYiLCJjbGFzcyI6InRva2VuLk9BdXRoVXNlckFjY2Vzc1Rva2VuIiwianRpIjoiMDAwMDAwMDAtMDAwMC0wMDAwLTAwMDAtMDAwMDAwMDAwMGZmIiwic2NvcGUiOiJvcGVuaWQgZW1haWwiLCJhenAiOiI2NmZkMjZjZGFlNmJkZTU3ZWYyMDZiMzUiLCJjbGllbnRfaWQiOiI2NmZkMjZjZGFlNmJkZTU3ZWYyMDZiMzUifQ.eXMqS6ymgIthKCDKgGK3ZzFZaY0P-B-2z5QjuZlsIzc","refresh_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3BheW1lbnQuc2NoaWJzdGVkLm5vLyIsImlhdCI6MTcyODUxODQwMCwiZXhwIjo0MTAyNDQ0ODAwLCJhdWQiOiJodHRwczovL3BvZG1lLmNvbSIsInN1YiI6IjAwMDAwMDAwLTAwMDAtMDAwMC0wMDAwLTAwMDAwMDAwZmZmZiIsInVzZXJfaWQiOiIxMjM0NTYiLCJjbGFzcyI6InRva2VuLk9BdXRoVXNlclJlZnJlc2hUb2tlbiIsImp0aSI6IjAwMDAwMDAwLTAwMDAtMDAwMC0wMDAwLTAwMDAwMDAwMDBmZiIsInNjb3BlIjoib3BlbmlkIGVtYWlsIiwiYXpwIjoiNjZmZDI2Y2RhZTZiZGU1N2VmMjA2YjM1IiwiY2xpZW50X2lkIjoiNjZmZDI2Y2RhZTZiZGU1N2VmMjA2YjM1IiwiYWp0aSI6IjAwMDAwMDAwLTAwMDAtMDAwMC0wMDAwLTAwMDAwMDAwMDBkZCJ9.M_oRHsskpt0ODJIZJ4x9VlMWIFpTsXkbLucyH1Oj3_Q","expires_in":2373926400,"id_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3BheW1lbnQuc2NoaWJzdGVkLm5vLyIsImlhdCI6MTcyODUxODQwMCwiZXhwIjo0MTAyNDQ0ODAwLCJhdWQiOlsiNjZmZDI2Y2RhZTZiZGU1N2VmMjA2YjM1IiwiaHR0cHM6Ly9wb2RtZS5jb20iXSwic3ViIjoiMDAwMDAwMDAtMDAwMC0wMDAwLTAwMDAtMDAwMDAwMDBmZmZmIiwibGVnYWN5X3VzZXJfaWQiOiIxMjM0NTYiLCJjbGFzcyI6InRva2VuLklEVG9rZW4iLCJhenAiOiI2NmZkMjZjZGFlNmJkZTU3ZWYyMDZiMzUiLCJlbWFpbCI6InRlc3R1c2VyQGV4YW1wbGUuY29tIn0.N-7DexoBVhvLdPLP75cxZrXm0ToiFVCmouV2Ixynp6k","server_time":1728518400,"expiration_time":4102444800,"account_created":null,"email":null}
{"scope":"openid email","user_id":"123456","is_admin":false,"token_type":"Bearer","access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3BheW1lbnQuc2NoaWJzdGVkLm5vLyIsImlhdCI6MTcyODUxODQwMCwiZXhwIjo0MTAyNDQ0ODAwLCJhdWQiOiJodHRwczovL3BvZG1lLmNvbSIsInN1YiI6IjAwMDAwMDAwLTAwMDAtMDAwMC0wMDAwLTAwMDAwMDAwZmZmZiIsInVzZXJfaWQiOiIxMjM0NTYiLCJjbGFzcyI6InRva2VuLk9BdXRoVXNlckFjY2Vzc1Rva2VuIiwianRpIjoiMDAwMDAwMDAtMDAwMC0wMDAwLTAwMDAtMDAwMDAwMDAwMGZmIiwic2NvcGUiOiJvcGVuaWQgZW1haWwiLCJhenAiOiI2NmZkMjZjZGFlNmJkZTU3ZWYyMDZiMzUiLCJjbGllbnRfaWQiOiI2NmZkMjZjZGFlNmJkZTU3ZWYyMDZiMzUifQ.eXMqS6ymgIthKCDKgGK3ZzFZaY0P-B-2z5QjuZlsIzc","refresh_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3BheW1lbnQuc2NoaWJzdGVkLm5vLyIsImlhdCI6MTcyODUxODQwMCwiZXhwIjo0MTAyNDQ0ODAwLCJhdWQiOiJodHRwczovL3BvZG1lLmNvbSIsInN1YiI6IjAwMDAwMDAwLTAwMDAtMDAwMC0wMDAwLTAwMDAwMDAwZmZmZiIsInVzZXJfaWQiOiIxMjM0NTYiLCJjbGFzcyI6InRva2VuLk9BdXRoVXNlclJlZnJlc2hUb2tlbiIsImp0aSI6IjAwMDAwMDAwLTAwMDAtMDAwMC0wMDAwLTAwMDAwMDAwMDBmZiIsInNjb3BlIjoib3BlbmlkIGVtYWlsIiwiYXpwIjoiNjZmZDI2Y2RhZTZiZGU1N2VmMjA2YjM1IiwiY2xpZW50X2lkIjoiNjZmZDI2Y2RhZTZiZGU1N2VmMjA2YjM1IiwiYWp0aSI6IjAwMDAwMDAwLTAwMDAtMDAwMC0wMDAwLTAwMDAwMDAwMDBkZCJ9.M_oRHsskpt0ODJIZJ4x9VlMWIFpTsXkbLucyH1Oj3_Q","expires_in":600,"id_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3BheW1lbnQuc2NoaWJzdGVkLm5vLyIsImlhdCI6MTcyODUxODQwMCwiZXhwIjo0MTAyNDQ0ODAwLCJhdWQiOlsiNjZmZDI2Y2RhZTZiZGU1N2VmMjA2YjM1IiwiaHR0cHM6Ly9wb2RtZS5jb20iXSwic3ViIjoiMDAwMDAwMDAtMDAwMC0wMDAwLTAwMDAtMDAwMDAwMDBmZmZmIiwibGVnYWN5X3VzZXJfaWQiOiIxMjM0NTYiLCJjbGFzcyI6InRva2VuLklEVG9rZW4iLCJhenAiOiI2NmZkMjZjZGFlNmJkZTU3ZWYyMDZiMzUiLCJlbWFpbCI6InRlc3R1c2VyQGV4YW1wbGUuY29tIn0.N-7DexoBVhvLdPLP75cxZrXm0ToiFVCmouV2Ixynp6k","server_time":1704067200,"expiration_time":1704067800,"account_created":null,"email":null}
26 changes: 26 additions & 0 deletions tests/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,39 @@ def __init__(
match_partial_query=True,
repeat=1,
):
"""Initialize a CustomRoute instance.
Args:
method_pattern (str, optional): HTTP method to match. Defaults to ANY.
host_pattern (str, optional): Host to match. Defaults to ANY.
path_pattern (str, optional): Path to match. Defaults to ANY.
body_pattern (str, optional): Body to match. Defaults to ANY.
repeat (int, optional): Number of times to match. Defaults to 1.
path_qs (dict, optional): Query string parameters to match.
match_querystring (bool, optional): Whether to match the query string.
Defaults to True if `path_qs` is not None.
match_partial_query (bool, optional): Whether to match only part of the query string.
If `True`, the route will match if the query string contains all the specified query
parameters, regardless of other parameters present.
If `False`, the route will only match if the query string exactly matches the specified
query parameters in `path_qs`.
"""
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.
Args:
request: The incoming request to match against.
"""
path_to_match = urlparse(request.path_qs)
query_to_match = parse_qs(path_to_match.query)
parsed_path = urlparse(self.path_pattern)
Expand Down
12 changes: 7 additions & 5 deletions tests/ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@
extend = "../pyproject.toml"

lint.extend-select = [
"PT", # Use @pytest.fixture without parentheses
"PT", # Use @pytest.fixture without parentheses
]

lint.extend-ignore = [
"S101",
"SLF001",
"TCH002",
"PLR2004",
"S101",
"S105",
"S106",
"SLF001",
"TCH002",
"PLR2004",
]

lint.pylint.max-branches = 13
Loading

0 comments on commit 2d0fdac

Please sign in to comment.