Skip to content

Commit

Permalink
Merge pull request #50 from AndrewSergienko/2.x/auth
Browse files Browse the repository at this point in the history
Tests update
  • Loading branch information
andiserg authored Feb 29, 2024
2 parents a555c25 + 407d80d commit 922aa92
Show file tree
Hide file tree
Showing 28 changed files with 436 additions and 236 deletions.
25 changes: 14 additions & 11 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,26 @@ jobs:
runs-on: ubuntu-latest

env:
DB_USER: postgres
DB_PASSWORD: 123456
TEST_DB_NAME: test_database
AUTH0_CLIENT_ID: ${{ secrets.AUTH0_CLIENT_ID }}
AUTH0_CLIENT_SECRET: ${{ secrets.AUTH0_CLIENT_SECRET }}
TEST_AUTH_PASSWORD: ${{ secrets.TEST_AUTH_PASSWORD }}
TEST_AUTH_USER: ${{ secrets.TEST_AUTH_USER }}
TEST_AUTH_USER_SUB: ${{ secrets.TEST_AUTH_USER_SUB }}
AUTH0_AUDIENCE: ${{ vars.AUTH0_AUDIENCE }}
AUTH0_AUTHORIZE_URL: ${{ vars.AUTH0_AUTHORIZE_URL }}
AUTH0_CONNECTION: ${{ vars.AUTH0_CONNECTION }}
AUTH0_ISSUER: ${{ vars.AUTH0_ISSUER }}
AUTH0_JWKS_URI: ${{ vars.AUTH0_JWKS_URI }}
AUTH0_REGISTER_URL: ${{ vars.AUTH0_REGISTER_URL }}
TEST_DB_URL : ${{ vars.TEST_DB_URL }}

services:
postgres:
image: postgres
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: 123456
POSTGRES_DB: test_database
POSTGRES_PASSWORD: postgres
POSTGRES_DB: costy_test
ports: [ '5432:5432' ]
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5

Expand All @@ -34,12 +43,6 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Set env variables
run: |
echo "DB_USER=$DB_USER" >> $GITHUB_ENV
echo "DB_PASSWORD=$DB_PASSWORD" >> $GITHUB_ENV
echo "TEST_DB_NAME=$TEST_DB_NAME" >> $GITHUB_ENV
echo "DB_HOST=localhost:${{ job.services.postgres.ports[5432] }}" >> $GITHUB_ENV
- name: Install dependencies
run: |
python -m pip install --upgrade pip
Expand Down
28 changes: 14 additions & 14 deletions src/costy/adapters/auth/auth_gateway.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from aiohttp import ClientSession
from httpx import AsyncClient
from sqlalchemy import Table
from sqlalchemy.ext.asyncio import AsyncSession

Expand All @@ -12,7 +12,7 @@ class AuthGateway(AuthLoger, AuthRegister):
def __init__(
self,
db_session: AsyncSession,
web_session: ClientSession,
web_session: AsyncClient,
table: Table,
settings: AuthSettings
) -> None:
Expand All @@ -31,13 +31,13 @@ async def authenticate(self, email: str, password: str) -> str:
"audience": self.settings.audience,
"grant_type": self.settings.grant_type
}
async with self.web_session.post(url, data=data) as response:
response_data = await response.json()
if response.status == 200:
token: str | None = response_data.get("access_token")
if token:
return token
raise AuthenticationError(response_data)
response = await self.web_session.post(url, data=data)
response_data = response.json()
if response.status_code == 200:
token: str | None = response_data.get("access_token")
if token:
return token
raise AuthenticationError(response_data)

async def register(self, email: str, password: str) -> str:
url = self.settings.register_url
Expand All @@ -48,8 +48,8 @@ async def register(self, email: str, password: str) -> str:
"client_secret": self.settings.client_secret,
"connection": self.settings.connection
}
async with self.web_session.post(url, data=data) as response:
response_data = await response.json()
if response.status == 200:
return response_data["_id"]
raise RegisterError(response_data)
response = await self.web_session.post(url, data=data)
response_data = response.json()
if response.status_code == 200:
return response_data["_id"]
raise RegisterError(response_data)
10 changes: 5 additions & 5 deletions src/costy/adapters/auth/token.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from datetime import datetime, timedelta
from typing import Any, Literal

from aiohttp import ClientSession
from httpx import AsyncClient
from jose import exceptions as jwt_exc
from jose import jwt

Expand Down Expand Up @@ -73,7 +73,7 @@ def validate_token(self, token: str, jwks: dict[Any, Any]) -> str:


class KeySetProvider:
def __init__(self, uri: str, session: ClientSession, expired: timedelta):
def __init__(self, uri: str, session: AsyncClient, expired: timedelta):
self.session = session
self.jwks: dict[str, str] = {}
self.expired = expired
Expand All @@ -89,9 +89,9 @@ async def get_key_set(self) -> dict[Any, Any]:
return self.jwks

async def _request_new_key_set(self) -> None:
async with self.session.get(self.uri) as response:
self.jwks = await response.json()
self.last_updated = datetime.now()
response = await self.session.get(self.uri)
self.jwks = response.json()
self.last_updated = datetime.now()


class TokenIdProvider(IdProvider):
Expand Down
2 changes: 1 addition & 1 deletion src/costy/application/operation/dto.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
@dataclass(kw_only=True)
class NewOperationDTO:
amount: int
description: str | None
description: str | None = None
time: int = int(datetime.now().timestamp())
category_id: CategoryId

Expand Down
4 changes: 2 additions & 2 deletions src/costy/infrastructure/auth.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from datetime import timedelta
from typing import Any, Callable, Coroutine

from aiohttp import ClientSession
from httpx import AsyncClient

from costy.adapters.auth.token import (
Algorithm,
Expand All @@ -16,7 +16,7 @@ def create_id_provider_factory(
algorithm: Algorithm,
issuer: str,
jwsk_uri: str,
web_session: ClientSession,
web_session: AsyncClient,
jwsk_expired: timedelta = timedelta(days=1)
) -> Callable[[], Coroutine[Any, Any, TokenIdProvider]]:
token_processor = JwtTokenProcessor(algorithm, audience, issuer)
Expand Down
8 changes: 1 addition & 7 deletions src/costy/infrastructure/db/main.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import pytest
from sqlalchemy import MetaData
from sqlalchemy.ext.asyncio import (
AsyncEngine,
Expand All @@ -7,14 +6,9 @@
create_async_engine,
)

from costy.infrastructure.config import SettingError


def get_engine(url: str) -> AsyncEngine:
try:
return create_async_engine(url, future=True)
except SettingError:
pytest.skip("Auth settings env var are not exists.")
return create_async_engine(url, future=True)


def get_sessionmaker(engine: AsyncEngine) -> async_sessionmaker[AsyncSession]:
Expand Down
6 changes: 3 additions & 3 deletions src/costy/main/ioc.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from typing import AsyncIterator

from adaptix import Retort
from aiohttp import ClientSession
from httpx import AsyncClient
from sqlalchemy import Table
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker

Expand Down Expand Up @@ -32,7 +32,7 @@
@dataclass
class Depends:
session: AsyncSession
web_session: ClientSession
web_session: AsyncClient
uow: OrmUoW
user_gateway: UserGateway

Expand All @@ -41,7 +41,7 @@ class IoC(InteractorFactory):
def __init__(
self,
session_factory: async_sessionmaker[AsyncSession],
web_session: ClientSession,
web_session: AsyncClient,
tables: dict[str, Table],
retort: Retort,
auth_settings: AuthSettings
Expand Down
16 changes: 11 additions & 5 deletions src/costy/main/web.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import Any, Callable, Coroutine, TypeVar

from adaptix import Retort
from aiohttp import ClientSession
from httpx import AsyncClient
from litestar import Litestar
from litestar.di import Provide

Expand Down Expand Up @@ -33,12 +33,15 @@ async def func() -> T:
return func


def init_app() -> Litestar:
def init_app(db_url: str | None = None) -> Litestar:
if not db_url:
db_url = get_db_connection_url()

base_metadata = get_metadata()
tables = create_tables(base_metadata)

session_factory = get_sessionmaker(get_engine(get_db_connection_url()))
web_session = ClientSession()
session_factory = get_sessionmaker(get_engine(db_url))
web_session = AsyncClient()

auth_settings = get_auth_settings()
ioc = IoC(session_factory, web_session, tables, Retort(), auth_settings)
Expand All @@ -51,6 +54,9 @@ def init_app() -> Litestar:
web_session
)

async def finalization():
await web_session.aclose()

return Litestar(
route_handlers=(
AuthenticationController,
Expand All @@ -63,6 +69,6 @@ def init_app() -> Litestar:
"id_provider": Provide(get_id_provider),
"id_provider_pure": Provide(id_provider_factory)
},
on_shutdown=[lambda: web_session.close()],
on_shutdown=[finalization],
debug=True
)
10 changes: 0 additions & 10 deletions src/costy/presentation/api/operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,6 @@
class OperationController(Controller):
path = '/operations'

@get("/{operation_id:int}")
async def get_operation(
self,
ioc: InteractorFactory,
id_provider: IdProvider,
operation_id: OperationId
) -> Operation | None:
async with ioc.read_operation(id_provider) as read_operation:
return await read_operation(operation_id)

@get()
async def get_list_operations(
self,
Expand Down
33 changes: 0 additions & 33 deletions tests/adapters/test_auth_adapter.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,10 @@
import os

import pytest
from pytest_asyncio import fixture
from sqlalchemy import Table, insert
from sqlalchemy.ext.asyncio import AsyncSession

from costy.adapters.auth.auth_gateway import AuthGateway
from costy.application.common.auth_gateway import AuthLoger
from costy.domain.models.user import UserId
from costy.infrastructure.config import AuthSettings, get_auth_settings


@fixture
async def auth_sub() -> str: # type: ignore
try:
return os.environ["TEST_AUTH_USER_SUB"]
except KeyError:
pytest.skip("No test user sub environment variable.")


@fixture
async def auth_settings() -> AuthSettings:
return get_auth_settings()


@fixture
async def auth_adapter(db_session, web_session, db_tables, auth_settings: AuthSettings) -> AuthLoger:
return AuthGateway(db_session, web_session, db_tables["users"], auth_settings)


@fixture
async def credentials() -> dict[str, str]: # type: ignore
try:
return {
"username": os.environ["TEST_AUTH_USER"],
"password": os.environ["TEST_AUTH_PASSWORD"]
}
except KeyError:
pytest.skip("No test user credentials.")


@fixture
Expand Down
15 changes: 0 additions & 15 deletions tests/adapters/test_category_adapter.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,6 @@
import pytest
from pytest_asyncio import fixture
from sqlalchemy import insert

from costy.adapters.db.category_gateway import CategoryGateway
from costy.domain.models.category import Category, CategoryType
from costy.domain.models.user import UserId


@fixture
def category_gateway(db_session, db_tables, retort) -> CategoryGateway:
return CategoryGateway(db_session, db_tables["categories"], retort)


@fixture()
async def db_user_id(db_session, db_tables) -> UserId:
created_user_record = await db_session.execute(insert(db_tables["users"]).values(auth_id="test"))
return UserId(created_user_record.inserted_primary_key[0])


@pytest.mark.asyncio
Expand Down
23 changes: 1 addition & 22 deletions tests/adapters/test_operation_adapter.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,11 @@
import pytest
from pytest_asyncio import fixture
from sqlalchemy import insert

from costy.adapters.db.operation_gateway import OperationGateway
from costy.domain.models.category import CategoryId
from costy.domain.models.operation import Operation
from costy.domain.models.user import UserId


@fixture
def operation_gateway(db_session, db_tables, retort) -> OperationGateway:
return OperationGateway(db_session, db_tables["operations"], retort)


@fixture()
async def db_user_id(db_session, db_tables) -> UserId:
created_user_record = await db_session.execute(insert(db_tables["users"]).values(auth_id="test"))
return UserId(created_user_record.inserted_primary_key[0])


@fixture
async def db_category_id(db_session, db_tables) -> CategoryId:
created_category_record = await db_session.execute(insert(db_tables["categories"]).values(name="test"))
return CategoryId(created_category_record.inserted_primary_key[0])


@fixture
def operation_entity(db_user_id, db_category_id) -> Operation:
async def operation_entity(db_user_id, db_category_id) -> Operation:
return Operation(
id=None,
amount=100,
Expand Down
Loading

0 comments on commit 922aa92

Please sign in to comment.