Skip to content

Commit 3c0829c

Browse files
author
Alex Zorkin
committed
fix: transfer category null check fix
1 parent 36a6343 commit 3c0829c

File tree

2 files changed

+58
-1
lines changed

2 files changed

+58
-1
lines changed

backend/lcfs/tests/transfer/test_transfer_services.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import pytest
22
from unittest.mock import AsyncMock, MagicMock, patch
33
from datetime import date, datetime, timedelta
4+
from types import SimpleNamespace
45

56
from lcfs.db.models import UserProfile
67
from lcfs.db.models.user.Role import RoleEnum
@@ -300,3 +301,57 @@ async def should_not_be_called(*args, **kwargs):
300301

301302
assert dummy_transfer.to_transaction == "new_transaction"
302303
assert dummy_transfer.category == "Existing"
304+
305+
306+
@pytest.mark.anyio
307+
async def test_director_record_transfer_with_none_category(
308+
transfer_service, dummy_transfer, mock_director
309+
):
310+
"""
311+
Test that director_record_transfer correctly assigns a category
312+
when transfer_category is None. This tests the bug fix for the
313+
case where hasattr(None, 'category') would raise an AttributeError.
314+
"""
315+
# Set transfer_category to None to simulate the bug scenario
316+
dummy_transfer.transfer_category = None
317+
dummy_transfer.agreement_date = datetime.now() - timedelta(
318+
days=10
319+
) # Recent agreement date for category A
320+
321+
# Track if update_category was called
322+
category_updated = False
323+
324+
async def dummy_update_category_fn(transfer_id, category):
325+
nonlocal category_updated
326+
category_updated = True
327+
dummy_transfer.called_category = category
328+
dummy_transfer.transfer_category = SimpleNamespace(category=category)
329+
return dummy_transfer
330+
331+
transfer_service.update_category = dummy_update_category_fn
332+
333+
async def confirm_success(tx_id):
334+
return True
335+
336+
transfer_service.transaction_repo.confirm_transaction = confirm_success
337+
338+
async def dummy_adjust_balance_fn(
339+
*, transaction_action, compliance_units, organization_id
340+
):
341+
return "new_transaction"
342+
343+
transfer_service.org_service.adjust_balance = dummy_adjust_balance_fn
344+
345+
# Mock repo.refresh_transfer to do nothing
346+
async def mock_refresh(transfer):
347+
return
348+
349+
transfer_service.repo.refresh_transfer = mock_refresh
350+
transfer_service.repo.update_transfer = AsyncMock(return_value=dummy_transfer)
351+
352+
await transfer_service.director_record_transfer(dummy_transfer, mock_director)
353+
354+
# Verify that update_category was called and with the correct category
355+
assert category_updated is True
356+
assert getattr(dummy_transfer, "called_category", None) == "A"
357+
assert dummy_transfer.to_transaction == "new_transaction"

backend/lcfs/web/api/transfer/services.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,9 @@ async def director_record_transfer(self, transfer: Transfer, user: UserProfile):
453453

454454
await self.repo.refresh_transfer(transfer)
455455

456-
if not hasattr(transfer.transfer_category, "category"):
456+
if transfer.transfer_category is None or not hasattr(
457+
transfer.transfer_category, "category"
458+
):
457459
today = datetime.now()
458460
diff_seconds = today.timestamp() - transfer.agreement_date.timestamp()
459461
# Define approximate thresholds in seconds

0 commit comments

Comments
 (0)