Skip to content

Commit 517a47e

Browse files
committed
Implement updates for better performance
1 parent b9f4e3f commit 517a47e

10 files changed

Lines changed: 266 additions & 147 deletions

File tree

code/backend/config/database.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,9 @@ async def get_async_read_session() -> AsyncGenerator[AsyncSession, None]:
177177
Dependency function to get async read-only database session.
178178
Uses read replica if available, otherwise falls back to primary.
179179
"""
180-
session_maker = AsyncReadSessionLocal if AsyncReadSessionLocal else AsyncSessionLocal
180+
session_maker = (
181+
AsyncReadSessionLocal if AsyncReadSessionLocal else AsyncSessionLocal
182+
)
181183
async with session_maker() as session:
182184
try:
183185
yield session

code/backend/models/compliance.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,7 @@ class AuditLog(BaseModel, TimestampMixin):
9292
event_description = Column(Text, nullable=True)
9393

9494
# User and Session
95-
user_id = Column(
96-
GUID(), ForeignKey("users.id"), nullable=True, index=True
97-
)
95+
user_id = Column(GUID(), ForeignKey("users.id"), nullable=True, index=True)
9896
session_id = Column(String(255), nullable=True, index=True)
9997

10098
# Request Details
@@ -168,9 +166,7 @@ class ComplianceCheck(BaseModel, TimestampMixin, AuditMixin):
168166
check_description = Column(Text, nullable=True)
169167

170168
# Subject of Check
171-
user_id = Column(
172-
GUID(), ForeignKey("users.id"), nullable=True, index=True
173-
)
169+
user_id = Column(GUID(), ForeignKey("users.id"), nullable=True, index=True)
174170
transaction_id = Column(
175171
GUID(), ForeignKey("transactions.id"), nullable=True, index=True
176172
)
@@ -328,9 +324,7 @@ class SuspiciousActivityReport(BaseModel, TimestampMixin, AuditMixin):
328324
sar_number = Column(String(50), unique=True, nullable=True, index=True)
329325

330326
# Subject Information
331-
user_id = Column(
332-
GUID(), ForeignKey("users.id"), nullable=True, index=True
333-
)
327+
user_id = Column(GUID(), ForeignKey("users.id"), nullable=True, index=True)
334328
transaction_ids = Column(JSON, nullable=True) # List of related transaction IDs
335329

336330
# Suspicious Activity Details

code/backend/models/portfolio.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,7 @@ class Portfolio(BaseModel, TimestampMixin, AuditMixin):
7474

7575
__tablename__ = "portfolios"
7676

77-
user_id = Column(
78-
GUID(), ForeignKey("users.id"), nullable=False, index=True
79-
)
77+
user_id = Column(GUID(), ForeignKey("users.id"), nullable=False, index=True)
8078

8179
# Portfolio Details
8280
name = Column(String(100), nullable=False)

code/backend/models/risk.py

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,7 @@ class RiskAssessment(BaseModel, TimestampMixin, AuditMixin):
7373
__tablename__ = "risk_assessments"
7474

7575
# Assessment Subject
76-
user_id = Column(
77-
GUID(), ForeignKey("users.id"), nullable=True, index=True
78-
)
76+
user_id = Column(GUID(), ForeignKey("users.id"), nullable=True, index=True)
7977
portfolio_id = Column(
8078
GUID(), ForeignKey("portfolios.id"), nullable=True, index=True
8179
)
@@ -157,9 +155,7 @@ class RiskMetrics(BaseModel, TimestampMixin):
157155
__tablename__ = "risk_metrics"
158156

159157
# Metric Subject
160-
user_id = Column(
161-
GUID(), ForeignKey("users.id"), nullable=True, index=True
162-
)
158+
user_id = Column(GUID(), ForeignKey("users.id"), nullable=True, index=True)
163159
portfolio_id = Column(
164160
GUID(), ForeignKey("portfolios.id"), nullable=True, index=True
165161
)
@@ -304,9 +300,7 @@ class RiskAlert(BaseModel, TimestampMixin, AuditMixin):
304300
)
305301

306302
# Alert Subject
307-
user_id = Column(
308-
GUID(), ForeignKey("users.id"), nullable=True, index=True
309-
)
303+
user_id = Column(GUID(), ForeignKey("users.id"), nullable=True, index=True)
310304
portfolio_id = Column(
311305
GUID(), ForeignKey("portfolios.id"), nullable=True, index=True
312306
)
@@ -399,9 +393,7 @@ class RiskLimit(BaseModel, TimestampMixin, AuditMixin):
399393
__tablename__ = "risk_limits"
400394

401395
# Limit Subject
402-
user_id = Column(
403-
GUID(), ForeignKey("users.id"), nullable=True, index=True
404-
)
396+
user_id = Column(GUID(), ForeignKey("users.id"), nullable=True, index=True)
405397
portfolio_id = Column(
406398
GUID(), ForeignKey("portfolios.id"), nullable=True, index=True
407399
)

code/backend/models/transaction.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,7 @@ class Transaction(BaseModel, TimestampMixin, AuditMixin):
7575
__tablename__ = "transactions"
7676

7777
# User Association
78-
user_id = Column(
79-
GUID(), ForeignKey("users.id"), nullable=False, index=True
80-
)
78+
user_id = Column(GUID(), ForeignKey("users.id"), nullable=False, index=True)
8179

8280
# Blockchain Information
8381
tx_hash = Column(
@@ -226,9 +224,7 @@ class TransactionAlert(BaseModel, TimestampMixin, AuditMixin):
226224
transaction_id = Column(
227225
GUID(), ForeignKey("transactions.id"), nullable=False, index=True
228226
)
229-
user_id = Column(
230-
GUID(), ForeignKey("users.id"), nullable=False, index=True
231-
)
227+
user_id = Column(GUID(), ForeignKey("users.id"), nullable=False, index=True)
232228

233229
# Alert Details
234230
alert_type = Column(String(50), nullable=False, index=True)
@@ -273,9 +269,7 @@ class TransactionPattern(BaseModel, TimestampMixin):
273269

274270
__tablename__ = "transaction_patterns"
275271

276-
user_id = Column(
277-
GUID(), ForeignKey("users.id"), nullable=False, index=True
278-
)
272+
user_id = Column(GUID(), ForeignKey("users.id"), nullable=False, index=True)
279273

280274
# Pattern Details
281275
pattern_type = Column(String(50), nullable=False, index=True)

code/backend/models/user.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,14 @@
2121
)
2222
from sqlalchemy.orm import relationship
2323

24-
from .base import GUID, AuditMixin, BaseModel, EncryptedMixin, SoftDeleteMixin, TimestampMixin
24+
from .base import (
25+
GUID,
26+
AuditMixin,
27+
BaseModel,
28+
EncryptedMixin,
29+
SoftDeleteMixin,
30+
TimestampMixin,
31+
)
2532

2633

2734
class UserStatus(enum.Enum):

code/backend/tests/conftest.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
os.environ.setdefault("REDIS_URL", "redis://localhost:6379/0")
3434
os.environ.setdefault("FIELD_ENCRYPTION_ENABLED", "false")
3535

36+
3637
# ── 2. Cache mock ─────────────────────────────────────────────────────────────
3738
class _MockCacheManager:
3839
def __init__(self) -> None:
@@ -71,18 +72,19 @@ async def _noop():
7172
_db_module.init_database = _noop
7273
_db_module.close_database = _noop
7374

74-
from app.main import app # noqa: E402 (triggers app module import)
7575
import app.main as _app_main # noqa: E402
76+
from app.main import app # noqa: E402 (triggers app module import)
7677

7778
_app_main.init_database = _noop
7879
_app_main.close_database = _noop
7980

80-
# ── 4. Public test fixtures ───────────────────────────────────────────────────
81-
import pytest # noqa: E402
8281
from typing import Any # noqa: E402
8382
from unittest.mock import AsyncMock, MagicMock # noqa: E402
8483
from uuid import uuid4 # noqa: E402
8584

85+
# ── 4. Public test fixtures ───────────────────────────────────────────────────
86+
import pytest # noqa: E402
87+
8688

8789
@pytest.fixture
8890
def mock_db() -> Any:
@@ -104,10 +106,10 @@ def mock_db() -> Any:
104106
@pytest.fixture
105107
def mock_user():
106108
"""In-memory User object with sensible defaults."""
107-
from datetime import datetime, timezone
108-
from models.user import User, UserStatus
109109

110+
from models.user import User, UserStatus
110111
from passlib.context import CryptContext
112+
111113
pwd_ctx = CryptContext(schemes=["bcrypt"], deprecated="auto")
112114

113115
u = User.__new__(User)
@@ -134,8 +136,11 @@ def mock_user():
134136
def auth_token(mock_user) -> str:
135137
"""Real JWT access-token for the mock_user (no DB required)."""
136138
from services.auth.jwt_service import JWTService
139+
137140
jwt = JWTService()
138-
return jwt.create_access_token(data={"sub": str(mock_user.id), "email": mock_user.email})
141+
return jwt.create_access_token(
142+
data={"sub": str(mock_user.id), "email": mock_user.email}
143+
)
139144

140145

141146
@pytest.fixture
@@ -145,6 +150,7 @@ def auth_headers(auth_token) -> dict:
145150

146151
# ── Misc data fixtures ────────────────────────────────────────────────────────
147152

153+
148154
@pytest.fixture
149155
def sample_transaction_data() -> dict:
150156
return {

code/backend/tests/integration/test_auth_endpoints.py

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,34 @@
55
No aiosqlite, no async DB — all database interactions are AsyncMock.
66
"""
77

8-
from unittest.mock import AsyncMock, MagicMock, patch
98
from datetime import datetime, timezone
9+
from unittest.mock import AsyncMock, MagicMock, patch
1010
from uuid import uuid4
1111

1212
import pytest
13-
from fastapi import status
14-
from fastapi.testclient import TestClient
15-
16-
from app.main import app
1713
from app.api.dependencies import get_current_user
14+
from app.main import app
1815
from config.database import get_async_session
19-
16+
from fastapi import status
17+
from fastapi.testclient import TestClient
2018

2119
# ── helpers ───────────────────────────────────────────────────────────────────
2220

21+
2322
def _make_user(email: str = "test@example.com", password_plain: str = "testpassword"):
2423
"""Build an in-memory User object."""
2524
from datetime import datetime, timezone
25+
2626
from models.user import User, UserStatus
2727
from passlib.context import CryptContext
2828

2929
pwd = CryptContext(schemes=["bcrypt"], deprecated="auto")
30-
u = User(email=email, hashed_password=pwd.hash(password_plain), status=UserStatus.ACTIVE, email_verified=True)
30+
u = User(
31+
email=email,
32+
hashed_password=pwd.hash(password_plain),
33+
status=UserStatus.ACTIVE,
34+
email_verified=True,
35+
)
3136
u.id = uuid4()
3237
u.is_deleted = False
3338
u.mfa_enabled = False
@@ -61,9 +66,12 @@ def _mock_session():
6166

6267
def _tokens(user):
6368
from services.auth.jwt_service import JWTService
69+
6470
j = JWTService()
6571
return {
66-
"access_token": j.create_access_token({"sub": str(user.id), "email": user.email}),
72+
"access_token": j.create_access_token(
73+
{"sub": str(user.id), "email": user.email}
74+
),
6775
"refresh_token": j.create_refresh_token({"sub": str(user.id)}),
6876
"token_type": "bearer",
6977
"expires_in": 1800,
@@ -162,7 +170,9 @@ def test_login_success(self, client):
162170
async def _mock_auth(*a, **kw):
163171
return user, tokens
164172

165-
with patch("app.api.v1.endpoints.auth.auth_service.authenticate_user", _mock_auth):
173+
with patch(
174+
"app.api.v1.endpoints.auth.auth_service.authenticate_user", _mock_auth
175+
):
166176
response = c.post(
167177
"/api/v1/auth/login",
168178
json={"email": user.email, "password": "testpassword"},
@@ -183,7 +193,9 @@ def test_login_invalid_credentials(self, client):
183193
async def _mock_auth(*a, **kw):
184194
raise HTTPException(status_code=401, detail="Invalid credentials")
185195

186-
with patch("app.api.v1.endpoints.auth.auth_service.authenticate_user", _mock_auth):
196+
with patch(
197+
"app.api.v1.endpoints.auth.auth_service.authenticate_user", _mock_auth
198+
):
187199
response = c.post(
188200
"/api/v1/auth/login",
189201
json={"email": "test@example.com", "password": "wrongpassword"},
@@ -200,7 +212,9 @@ def test_login_nonexistent_user(self, client):
200212
async def _mock_auth(*a, **kw):
201213
raise HTTPException(status_code=401, detail="Invalid credentials")
202214

203-
with patch("app.api.v1.endpoints.auth.auth_service.authenticate_user", _mock_auth):
215+
with patch(
216+
"app.api.v1.endpoints.auth.auth_service.authenticate_user", _mock_auth
217+
):
204218
response = c.post(
205219
"/api/v1/auth/login",
206220
json={"email": "nobody@example.com", "password": "testpassword"},
@@ -223,7 +237,9 @@ def test_get_current_user(self, authed_client):
223237
def test_get_current_user_invalid_token(self, client):
224238
"""Invalid token → 401."""
225239
c, _ = client
226-
response = c.get("/api/v1/auth/me", headers={"Authorization": "Bearer bad_token"})
240+
response = c.get(
241+
"/api/v1/auth/me", headers={"Authorization": "Bearer bad_token"}
242+
)
227243
assert response.status_code == status.HTTP_401_UNAUTHORIZED
228244

229245
def test_get_current_user_no_token(self, client):
@@ -291,7 +307,10 @@ def test_logout_success(self, authed_client):
291307
"""Valid token → successful logout."""
292308
c, user, _ = authed_client
293309
from services.auth.jwt_service import JWTService
294-
token = JWTService().create_access_token({"sub": str(user.id), "email": user.email})
310+
311+
token = JWTService().create_access_token(
312+
{"sub": str(user.id), "email": user.email}
313+
)
295314

296315
async def _mock_logout(*a, **kw):
297316
pass
@@ -327,7 +346,9 @@ def test_oauth2_login_form(self, client):
327346
async def _mock_auth(*a, **kw):
328347
return user, tokens
329348

330-
with patch("app.api.v1.endpoints.auth.auth_service.authenticate_user", _mock_auth):
349+
with patch(
350+
"app.api.v1.endpoints.auth.auth_service.authenticate_user", _mock_auth
351+
):
331352
response = c.post(
332353
"/api/v1/auth/login/form",
333354
data={"username": user.email, "password": "testpassword"},
@@ -341,8 +362,11 @@ async def _mock_auth(*a, **kw):
341362

342363
# ── tiny async helper ─────────────────────────────────────────────────────────
343364

365+
344366
def _async_return(value):
345367
"""Return an async function that returns value."""
368+
346369
async def _inner(*args, **kwargs):
347370
return value
371+
348372
return _inner

0 commit comments

Comments
 (0)