Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: Pytest

on:
push:
branches: [main]
paths:
- 'apps/**'
- 'shared-py/**'
- 'pyproject.toml'
- 'uv.lock'
pull_request:
branches: [main]
paths:
- 'apps/**'
- 'shared-py/**'
- 'pyproject.toml'
- 'uv.lock'

jobs:
test:
name: ${{ matrix.service }} Tests
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
include:
- service: wallet
path: apps/wallet
proto_src: libs/proto/wallet.proto

steps:
- uses: actions/checkout@v4

- uses: astral-sh/setup-uv@v5
with:
enable-cache: true

- name: Install dependencies
run: uv sync --project ${{ matrix.path }} --extra test

- name: Generate proto files
if: matrix.proto_src != ''
run: uv run --project ${{ matrix.path }} python scripts/generate_proto.py

- name: Run tests
run: uv run --project ${{ matrix.path }} pytest ${{ matrix.path }}/tests/ -v
env:
APP_PORT: "4002"
APP_HOST: "localhost"
APP_RELOAD: "False"
DATABASE_URL: "postgresql+asyncpg://test:test@localhost/test"
KAFKA_BROKERS: "localhost:9092"
12 changes: 12 additions & 0 deletions apps/wallet/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,15 @@ sources = ["src"]

[tool.uv.sources]
mint-shared = { workspace = true }

[project.optional-dependencies]
test = [
"pytest>=8.0.0",
"pytest-asyncio>=0.24.0",
"httpx>=0.27.0",
]

[tool.pytest.ini_options]
asyncio_mode = "auto"
testpaths = ["tests"]
pythonpath = ["src", "tests"]
Empty file added apps/wallet/tests/__init__.py
Empty file.
50 changes: 50 additions & 0 deletions apps/wallet/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import pytest

from fastapi import FastAPI
from fastapi.testclient import TestClient

from wallet.core.deps import require_auth
from wallet.db import get_db
from wallet.routes.wallet_admin import wallet_admin_route
from wallet.routes.wallet_user import wallet_user_route

from helpers import make_session_mock


@pytest.fixture
def session():
return make_session_mock()


@pytest.fixture
def user_client(session):
app = FastAPI()
app.include_router(wallet_user_route, prefix="/api/v1/wallet")

async def override_auth():
return {"sub": "u-1", "roles": ["user"]}

async def override_db():
yield session

app.dependency_overrides[require_auth] = override_auth
app.dependency_overrides[get_db] = override_db

return TestClient(app)


@pytest.fixture
def admin_client(session):
app = FastAPI()
app.include_router(wallet_admin_route, prefix="/api/v1/wallet")

async def override_auth():
return {"sub": "admin-1", "roles": ["admin"]}

async def override_db():
yield session

app.dependency_overrides[require_auth] = override_auth
app.dependency_overrides[get_db] = override_db

return TestClient(app)
22 changes: 22 additions & 0 deletions apps/wallet/tests/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from unittest.mock import AsyncMock, MagicMock


def make_session_mock():
session = MagicMock()
session.execute = AsyncMock()
session.flush = AsyncMock()
session.commit = AsyncMock()
session.add = MagicMock()

cm = MagicMock()
cm.__aenter__ = AsyncMock(return_value=session)
cm.__aexit__ = AsyncMock(return_value=False)
session.begin.return_value = cm
return session


def make_get_db(session):
async def _get_db():
yield session

return _get_db
47 changes: 47 additions & 0 deletions apps/wallet/tests/test_auth_events.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import pytest

from unittest.mock import AsyncMock, MagicMock
from wallet.kafka.consumers.auth_events import handle_auth_events
from wallet.kafka.consumers.schema import KafkaEnvelope
from wallet.models.wallet import Wallet


def make_envelope(event: str, payload_extra: dict = {}) -> KafkaEnvelope:
return KafkaEnvelope(
topic="auth.events",
eventId="evt-1",
version="1",
actorId="u-1",
payload={"event": event, **payload_extra},
)


@pytest.fixture
def db_session():
session = AsyncMock()
session.commit = AsyncMock()
session.add = MagicMock()
return session


class TestHandleAuthEvents:
@pytest.mark.asyncio
async def test_user_registered_creates_wallet(self, db_session):
envelope = make_envelope("auth.user_registered", {"userId": "u-42"})

await handle_auth_events(envelope, db_session)

db_session.add.assert_called_once()
added = db_session.add.call_args[0][0]
assert isinstance(added, Wallet)
assert added.user_id == "u-42"
db_session.commit.assert_called_once()

@pytest.mark.asyncio
async def test_unknown_event_does_nothing(self, db_session):
envelope = make_envelope("auth.some_other_event", {"userId": "u-42"})

await handle_auth_events(envelope, db_session)

db_session.add.assert_not_called()
db_session.commit.assert_not_called()
Loading
Loading